knip
Version:
Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects
87 lines (86 loc) • 4.06 kB
JavaScript
import { existsSync } from 'node:fs';
import { isBuiltin } from 'node:module';
import { DEFAULT_EXTENSIONS, DTS_EXTENSIONS } from "../constants.js";
import { sanitizeSpecifier } from "../util/modules.js";
import { timerify } from "../util/Performance.js";
import { dirname, extname, isAbsolute, isInNodeModules, join } from "../util/path.js";
import { _createSyncModuleResolver, _resolveModuleSync } from "../util/resolve.js";
function compilePathMappings(paths) {
if (!paths)
return undefined;
const mappings = [];
for (const key in paths) {
const starIdx = key.indexOf('*');
if (starIdx >= 0) {
mappings.push({ prefix: key.slice(0, starIdx), wildcard: true, values: paths[key] });
}
else {
mappings.push({ prefix: key, wildcard: false, values: paths[key] });
}
}
return mappings.length > 0 ? mappings : undefined;
}
export function createCustomModuleResolver(compilerOptions, customCompilerExtensions, toSourceFilePath) {
const customCompilerExtensionsSet = new Set(customCompilerExtensions);
const hasCustomExts = customCompilerExtensionsSet.size > 0;
const extensions = [...DEFAULT_EXTENSIONS, ...customCompilerExtensions, ...DTS_EXTENSIONS];
const resolveSync = hasCustomExts ? _createSyncModuleResolver(extensions) : _resolveModuleSync;
const pathMappings = compilePathMappings(compilerOptions.paths);
const rootDirs = compilerOptions.rootDirs;
function toSourcePath(resolvedFileName) {
if (!hasCustomExts || !customCompilerExtensionsSet.has(extname(resolvedFileName))) {
return toSourceFilePath(resolvedFileName) || resolvedFileName;
}
return resolvedFileName;
}
function toResult(resolvedFileName) {
const mapped = toSourcePath(resolvedFileName);
return {
resolvedFileName: mapped,
isExternalLibraryImport: mapped === resolvedFileName && isInNodeModules(resolvedFileName),
};
}
function resolveModuleName(name, containingFile) {
const specifier = sanitizeSpecifier(name);
if (isBuiltin(specifier))
return undefined;
const resolvedFileName = resolveSync(specifier, containingFile);
if (resolvedFileName)
return toResult(resolvedFileName);
if (pathMappings) {
for (const { prefix, wildcard, values } of pathMappings) {
if (wildcard ? specifier.startsWith(prefix) : specifier === prefix) {
const captured = wildcard ? specifier.slice(prefix.length) : '';
for (const value of values) {
const starIdx = value.indexOf('*');
const mapped = starIdx >= 0 ? value.slice(0, starIdx) + captured + value.slice(starIdx + 1) : value;
const resolved = resolveSync(mapped, containingFile);
if (resolved)
return toResult(resolved);
}
}
}
}
if (rootDirs && !isAbsolute(specifier)) {
const containingDir = dirname(containingFile);
for (const srcRoot of rootDirs) {
if (!containingDir.startsWith(srcRoot))
continue;
const relPath = containingDir.slice(srcRoot.length);
for (const targetRoot of rootDirs) {
if (targetRoot === srcRoot)
continue;
const mapped = join(targetRoot, relPath, specifier);
const resolved = resolveSync(mapped, containingFile);
if (resolved)
return toResult(resolved);
}
}
}
const candidate = isAbsolute(specifier) ? specifier : join(dirname(containingFile), specifier);
if (existsSync(candidate)) {
return { resolvedFileName: candidate, isExternalLibraryImport: false };
}
}
return timerify(resolveModuleName);
}