Skip to content

Commit 7be11aa

Browse files
committed
Fix npm alias attribution under Yarn PnP (resolve #2024)
1 parent 2659063 commit 7be11aa

9 files changed

Lines changed: 46 additions & 30 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.yarn/
1+
.yarn/*
2+
!.yarn/cache/

‎packages/knip/fixtures/yarn-pnp/.pnp.cjs‎

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
nodeLinker: pnp
2+
enableGlobalCache: false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
import 'host-with-hidden-manifest';
2+
import 'isarray-alias';

‎packages/knip/fixtures/yarn-pnp/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
],
88
"dependencies": {
99
"host-with-hidden-manifest": "workspace:*",
10+
"isarray": "2.0.5",
11+
"isarray-alias": "npm:isarray@2.0.5",
1012
"peer-package": "workspace:*"
1113
}
1214
}

‎packages/knip/fixtures/yarn-pnp/yarn.lock‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ __metadata:
1010
resolution: "@fixtures/yarn-pnp@workspace:."
1111
dependencies:
1212
host-with-hidden-manifest: "workspace:*"
13+
isarray: "npm:2.0.5"
14+
isarray-alias: "npm:isarray@2.0.5"
1315
peer-package: "workspace:*"
1416
languageName: unknown
1517
linkType: soft
@@ -22,6 +24,13 @@ __metadata:
2224
languageName: unknown
2325
linkType: soft
2426

27+
"isarray-alias@npm:isarray@2.0.5, isarray@npm:2.0.5":
28+
version: 2.0.5
29+
resolution: "isarray@npm:2.0.5"
30+
checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd
31+
languageName: node
32+
linkType: hard
33+
2534
"peer-package@workspace:*, peer-package@workspace:packages/peer-package":
2635
version: 0.0.0-use.local
2736
resolution: "peer-package@workspace:packages/peer-package"

‎packages/knip/src/typescript/resolve-module-names.ts‎

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { existsSync, realpathSync } from 'node:fs';
1+
import { existsSync } from 'node:fs';
22
import { isBuiltin } from 'node:module';
33
import { DEFAULT_EXTENSIONS, DTS_EXTENSIONS, IS_DTS } from '../constants.ts';
44
import { isFile } from '../util/fs.ts';
55
import { getPackageNameFromFilePath, getPackageNameFromModuleSpecifier, sanitizeSpecifier } from '../util/modules.ts';
66
import { timerify } from '../util/Performance.ts';
77
import { dirname, extname, isAbsolute, isInNodeModules, join, toPosix } from '../util/path.ts';
8-
import { _createSyncModuleResolver, _resolveModuleSync } from '../util/resolve.ts';
8+
import { _createSyncModuleResolver, _resolveModuleSync, resolvePackageManifestPath } from '../util/resolve.ts';
99
import type { ToSourceFilePath, WorkspacePackageTargetHandler } from '../util/to-source-path.ts';
1010
import type { ResolveModule, ResolvedModule } from './ast-nodes.ts';
1111

@@ -75,38 +75,19 @@ interface PathMapping {
7575
}
7676

7777
const moduleResolutionCaches: Array<Map<string, Map<string, ResolvedModule | undefined>>> = [];
78-
const installedPackageRootCache = new Map<string, string | undefined>();
7978

8079
export function clearModuleResolutionCaches() {
8180
for (const cache of moduleResolutionCaches) cache.clear();
82-
installedPackageRootCache.clear();
83-
}
84-
85-
function getInstalledPackageRoot(candidate: string) {
86-
if (installedPackageRootCache.has(candidate)) return installedPackageRootCache.get(candidate);
87-
let packageRoot: string | undefined;
88-
try {
89-
packageRoot = toPosix(realpathSync(candidate));
90-
} catch {}
91-
installedPackageRootCache.set(candidate, packageRoot);
92-
return packageRoot;
9381
}
9482

9583
function getAttributedPackageName(specifier: string, containingFile: string, resolvedFileName: string) {
9684
const packageName = getPackageNameFromFilePath(resolvedFileName);
9785
const specifierPackageName = getPackageNameFromModuleSpecifier(specifier);
9886
if (!specifierPackageName || specifierPackageName === packageName) return packageName;
99-
let dir = dirname(containingFile);
100-
while (true) {
101-
const packageRoot = getInstalledPackageRoot(join(dir, 'node_modules', specifierPackageName));
102-
if (packageRoot) {
103-
const isResolvedInstall = resolvedFileName === packageRoot || resolvedFileName.startsWith(`${packageRoot}/`);
104-
return isResolvedInstall ? specifierPackageName : packageName;
105-
}
106-
const parent = dirname(dir);
107-
if (parent === dir) return packageName;
108-
dir = parent;
109-
}
87+
const manifestPath = resolvePackageManifestPath(specifierPackageName, dirname(containingFile));
88+
if (!manifestPath) return packageName;
89+
const packageRoot = dirname(manifestPath);
90+
return resolvedFileName.startsWith(`${packageRoot}/`) ? specifierPackageName : packageName;
11091
}
11192

11293
function compilePathMappings(scopedPaths: ScopedPaths | undefined): PathMapping[] | undefined {

‎packages/knip/test/e2e/yarn-pnp.test.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ const cliPath = resolve('src/cli.ts');
99
const reporterPath = resolve('test/e2e/yarn-pnp-reporter.mjs');
1010
const pnpCjsPath = join(resolve('fixtures/yarn-pnp'), '.pnp.cjs');
1111

12-
test('Resolve package manifests under Yarn PnP', () => {
12+
test('Resolve package manifests and attribute npm aliases under Yarn PnP', () => {
1313
const cwd = resolve('fixtures/yarn-pnp');
14-
const { stdout } = spawnSync('node', ['--require', pnpCjsPath, cliPath, '--reporter', reporterPath], {
14+
const { stdout, stderr, status } = spawnSync('node', ['--require', pnpCjsPath, cliPath, '--reporter', reporterPath], {
1515
cwd,
1616
env: { PATH: process.env.PATH },
1717
encoding: 'utf8',
1818
});
1919

20+
assert.equal(status, 1, stderr);
2021
const result: Results = JSON.parse(stdout);
21-
assert.deepEqual(result.issues.dependencies, {});
22+
assert.deepEqual(Object.keys(result.issues.dependencies), ['package.json']);
23+
assert.deepEqual(Object.keys(result.issues.dependencies['package.json']), ['isarray']);
2224
assert.deepEqual(result.counters, {
23-
dependencies: 0,
25+
dependencies: 1,
2426
processed: 3,
2527
total: 4,
2628
});

0 commit comments

Comments
 (0)