Skip to content

Commit

Permalink
Make TS-style path mappings work for all files with extensions (#673)
Browse files Browse the repository at this point in the history
  • Loading branch information
geigerzaehler authored Jun 13, 2024
1 parent 8697dbe commit e9b3e66
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 32 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/knip/fixtures/paths/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import index from '@lib';
import fn from '@lib/fn';
import customExt from '@lib/data.ext';
import js from 'xyz/main.js';
import anything from '~/my-module';
customExt;
index;
fn;
js;
Expand Down
Empty file.
1 change: 1 addition & 0 deletions packages/knip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"smol-toml": "^1.1.4",
"strip-json-comments": "5.0.1",
"summary": "2.1.0",
"tsconfig-paths": "^4.2.0",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.3"
},
Expand Down
91 changes: 59 additions & 32 deletions packages/knip/src/typescript/resolveModuleNames.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync } from 'node:fs';
import { isBuiltin } from 'node:module';
import { createMatchPath } from 'tsconfig-paths';
import ts from 'typescript';
import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from '../constants.js';
import { timerify } from '../util/Performance.js';
Expand Down Expand Up @@ -54,6 +55,13 @@ export function createCustomModuleResolver(
});
}

const tsMatchPath = createMatchPath(
// If `baseUrl` is undefined we have already modified `paths` so that all
// entries are absolute. See `mergePaths` in `src/PrincipalFactory.ts`.
compilerOptions.baseUrl ?? '/',
compilerOptions.paths || {}
);

/**
* - Virtual files have built-in or custom compiler, return as JS
* - Foreign files have path resolved verbatim (file manager will return empty source file)
Expand All @@ -65,30 +73,28 @@ export function createCustomModuleResolver(
// No need to try and resolve builtins or externals, bail out
if (isBuiltin(sanitizedSpecifier) || isInNodeModules(name)) return undefined;

{
const resolvedFileName = resolveSync(sanitizedSpecifier, containingFile, extensions);
if (resolvedFileName) {
const ext = extname(resolvedFileName);

if (!virtualFileExtensions.includes(ext) && !FOREIGN_FILE_EXTENSIONS.has(ext)) {
const srcFilePath = toSourceFilePath(resolvedFileName);
if (srcFilePath) {
return {
resolvedFileName: srcFilePath,
extension: extname(srcFilePath),
isExternalLibraryImport: false,
resolvedUsingTsExtension: false,
};
}
}
const resolvedFileName = resolveSync(sanitizedSpecifier, containingFile, extensions);
if (resolvedFileName) {
const ext = extname(resolvedFileName);

return {
resolvedFileName,
extension: virtualFileExtensions.includes(ext) ? ts.Extension.Js : ext,
isExternalLibraryImport: isInNodeModules(resolvedFileName),
resolvedUsingTsExtension: false,
};
if (!virtualFileExtensions.includes(ext) && !FOREIGN_FILE_EXTENSIONS.has(ext)) {
const srcFilePath = toSourceFilePath(resolvedFileName);
if (srcFilePath) {
return {
resolvedFileName: srcFilePath,
extension: extname(srcFilePath),
isExternalLibraryImport: false,
resolvedUsingTsExtension: false,
};
}
}

return {
resolvedFileName,
extension: virtualFileExtensions.includes(ext) ? ts.Extension.Js : ext,
isExternalLibraryImport: isInNodeModules(resolvedFileName),
resolvedUsingTsExtension: false,
};
}

const tsResolvedModule = ts.resolveModuleName(
Expand Down Expand Up @@ -122,21 +128,42 @@ export function createCustomModuleResolver(
customSys
).resolvedModule;

if (!customResolvedModule || !isVirtualFilePath(customResolvedModule.resolvedFileName, virtualFileExtensions)) {
const module = fileExists(sanitizedSpecifier, containingFile);
if (module) return module;
if (customResolvedModule) {
if (isVirtualFilePath(customResolvedModule.resolvedFileName, virtualFileExtensions)) {
const resolvedFileName = ensureRealFilePath(customResolvedModule.resolvedFileName, virtualFileExtensions);

return {
extension: ts.Extension.Js,
resolvedFileName,
isExternalLibraryImport: customResolvedModule.isExternalLibraryImport,
};
}

return customResolvedModule;
}

const resolvedFileName = ensureRealFilePath(customResolvedModule.resolvedFileName, virtualFileExtensions);
const module = fileExists(sanitizedSpecifier, containingFile);
if (module) return module;

const resolvedModule: ts.ResolvedModuleFull = {
extension: ts.Extension.Js,
resolvedFileName,
isExternalLibraryImport: customResolvedModule.isExternalLibraryImport,
};
const resolvedPathMap = tsMatchPath(
sanitizedSpecifier,
undefined,
undefined,
// Leave extensions empty. When resolving "@foo/bar.ext",
// "@foo/bar.ext.js" will not be tried if "./foo/bar.ext" does not exist.
// This case has been handled by the resolvers above. This makes the
// resolution faster.
[]
);
if (resolvedPathMap) {
return {
resolvedFileName: resolvedPathMap,
extension: extname(resolvedPathMap),
isExternalLibraryImport: false,
};
}

return resolvedModule;
return undefined;
}

return timerify(resolveModuleNames);
Expand Down

0 comments on commit e9b3e66

Please sign in to comment.