diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/other b/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/other new file mode 120000 index 000000000..28610cf10 --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/other @@ -0,0 +1 @@ +../other \ No newline at end of file diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/self b/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/self new file mode 120000 index 000000000..82ba269eb --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/node_modules/self @@ -0,0 +1 @@ +../self \ No newline at end of file diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/other/absolute.css b/packages/knip/fixtures/module-resolution-non-std-absolute/other/absolute.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/other/absolute.svg b/packages/knip/fixtures/module-resolution-non-std-absolute/other/absolute.svg new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/other/package.json b/packages/knip/fixtures/module-resolution-non-std-absolute/other/package.json new file mode 100644 index 000000000..42426dc4b --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/other/package.json @@ -0,0 +1,4 @@ +{ + "name": "other", + "version": "1.0.0" +} diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/package.json b/packages/knip/fixtures/module-resolution-non-std-absolute/package.json new file mode 100644 index 000000000..5f4b01da4 --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/package.json @@ -0,0 +1,5 @@ +{ + "name": "@fixtures/module-resolution-non-std-absolute", + "version": "1.0.0", + "workspaces": ["other", "self"] +} diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/self/absolute.css b/packages/knip/fixtures/module-resolution-non-std-absolute/self/absolute.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/self/absolute.svg b/packages/knip/fixtures/module-resolution-non-std-absolute/self/absolute.svg new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/self/index.ts b/packages/knip/fixtures/module-resolution-non-std-absolute/self/index.ts new file mode 100644 index 000000000..42bc152bd --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/self/index.ts @@ -0,0 +1,4 @@ +import 'other/absolute.svg'; +import 'other/absolute.css'; +import 'self/absolute.svg'; +import 'self/absolute.css'; diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/self/package.json b/packages/knip/fixtures/module-resolution-non-std-absolute/self/package.json new file mode 100644 index 000000000..0ea7a438d --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/self/package.json @@ -0,0 +1,4 @@ +{ + "name": "self", + "version": "1.0.0" +} diff --git a/packages/knip/fixtures/module-resolution-non-std-absolute/tsconfig.json b/packages/knip/fixtures/module-resolution-non-std-absolute/tsconfig.json new file mode 100644 index 000000000..875cb6001 --- /dev/null +++ b/packages/knip/fixtures/module-resolution-non-std-absolute/tsconfig.json @@ -0,0 +1,3 @@ +{ + "compilerOptions": {} +} diff --git a/packages/knip/fixtures/module-resolution-non-std-implicit/src/index.ts b/packages/knip/fixtures/module-resolution-non-std-implicit/src/index.ts index f0ca94300..e3e06bce6 100644 --- a/packages/knip/fixtures/module-resolution-non-std-implicit/src/index.ts +++ b/packages/knip/fixtures/module-resolution-non-std-implicit/src/index.ts @@ -6,6 +6,4 @@ import Icon from './icon.svg'; import './global.css'; -// By exception, .css files will be resolved because `.css` is not added to virtual path extensions, since `.css.ts` -// files on disk are common (in contrast to e.g. `.svg.ts`; binaries like `.png.ts` can be safely ignored). import 'styles/base.css'; diff --git a/packages/knip/src/typescript/SourceFileManager.ts b/packages/knip/src/typescript/SourceFileManager.ts index 5cbade7b5..ea729bb9c 100644 --- a/packages/knip/src/typescript/SourceFileManager.ts +++ b/packages/knip/src/typescript/SourceFileManager.ts @@ -34,7 +34,7 @@ export class SourceFileManager { if (this.sourceFileCache.has(filePath)) return this.sourceFileCache.get(filePath); const ext = extname(filePath); const compiler = this.syncCompilers.get(ext); - if (FOREIGN_FILE_EXTENSIONS.has(ext) && !compiler) return undefined; + if (FOREIGN_FILE_EXTENSIONS.has(ext) && !compiler) return this.createSourceFile(filePath, ''); if (this.isSkipLibs && isInNodeModules(filePath)) { if (isDeclarationFileExtension(ext)) return undefined; return this.createSourceFile(filePath, ''); diff --git a/packages/knip/src/typescript/resolveModuleNames.ts b/packages/knip/src/typescript/resolveModuleNames.ts index 8936947ac..c178e1205 100644 --- a/packages/knip/src/typescript/resolveModuleNames.ts +++ b/packages/knip/src/typescript/resolveModuleNames.ts @@ -43,6 +43,10 @@ export function createCustomModuleResolver( }); } + /** + * - Virtual files have built-in or custom compiler, return as JS + * - Foreign files have path resolved verbatim (file manager will return empty source file) + */ function resolveModuleName(name: string, containingFile: string): ts.ResolvedModuleFull | undefined { const sanitizedSpecifier = sanitizeSpecifier(name); diff --git a/packages/knip/test/module-resolution-non-std-absolute.test.ts b/packages/knip/test/module-resolution-non-std-absolute.test.ts new file mode 100644 index 000000000..d6cc564e9 --- /dev/null +++ b/packages/knip/test/module-resolution-non-std-absolute.test.ts @@ -0,0 +1,24 @@ +import { test } from 'bun:test'; +import assert from 'node:assert/strict'; +import { main } from '../src/index.js'; +import { resolve } from '../src/util/path.js'; +import baseArguments from './helpers/baseArguments.js'; +import baseCounters from './helpers/baseCounters.js'; + +const cwd = resolve('fixtures/module-resolution-non-std-absolute'); + +test('Resolve non-standard absolute specifiers', async () => { + const { issues, counters } = await main({ + ...baseArguments, + cwd, + }); + + assert(issues.unlisted['self/index.ts']['other']); + + assert.deepEqual(counters, { + ...baseCounters, + unlisted: 1, + processed: 5, + total: 5, + }); +});