UNPKG

knip

Version:

Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects

114 lines (113 loc) 4.87 kB
import { IGNORED_RUNTIME_DEPENDENCIES } from '../constants.js'; import { debugLog } from './debug.js'; import { fromBinary, isBinary, isConfig, isDeferResolve, isDeferResolveEntry, isDependency, toDebugString, } from './input.js'; import { getPackageNameFromSpecifier } from './modules.js'; import { dirname, isAbsolute, isInNodeModules, isInternal, join } from './path.js'; import { _resolveSync } from './resolve.js'; const getWorkspaceFor = (input, chief, workspace) => (input.dir && chief.findWorkspaceByFilePath(`${input.dir}/`)) || (input.containingFilePath && chief.findWorkspaceByFilePath(input.containingFilePath)) || workspace; const addExternalRef = (map, containingFilePath, ref) => { if (!map.has(containingFilePath)) map.set(containingFilePath, new Set()); map.get(containingFilePath).add(ref); }; export const createInputHandler = (deputy, chief, isGitIgnored, addIssue, externalRefs, options) => (input, workspace) => { const { specifier, containingFilePath } = input; if (!containingFilePath || IGNORED_RUNTIME_DEPENDENCIES.has(specifier)) return; if (isBinary(input)) { const binaryName = fromBinary(input); const inputWorkspace = getWorkspaceFor(input, chief, workspace); const dependencies = deputy.maybeAddReferencedBinary(inputWorkspace, binaryName); if (dependencies) { if (externalRefs) { for (const dependency of dependencies) { addExternalRef(externalRefs, containingFilePath, { specifier: dependency, identifier: binaryName }); } } return; } if (dependencies || input.optional) return; addIssue({ type: 'binaries', filePath: containingFilePath, workspace: workspace.name, symbol: binaryName, specifier, fixes: [], }); return; } const packageName = getPackageNameFromSpecifier(specifier); if (packageName && (isDependency(input) || isDeferResolve(input) || (isDeferResolveEntry(input) && isInNodeModules(specifier)) || isConfig(input))) { const isWorkspace = chief.workspacesByPkgName.has(packageName); const inputWorkspace = getWorkspaceFor(input, chief, workspace); if (inputWorkspace) { const isHandled = deputy.maybeAddReferencedExternalDependency(inputWorkspace, packageName); if (externalRefs && !isWorkspace) { addExternalRef(externalRefs, containingFilePath, { specifier: packageName, identifier: undefined }); } if (isWorkspace || isDependency(input)) { if (!isHandled) { if (!input.optional && ((options.isProduction && input.production) || !options.isProduction)) { addIssue({ type: 'unlisted', filePath: containingFilePath, workspace: inputWorkspace.name, symbol: packageName ?? specifier, specifier, fixes: [], }); } return; } const internalPath = _resolveSync(specifier, dirname(containingFilePath)); if (internalPath && isInternal(internalPath) && !isGitIgnored(internalPath)) return internalPath; } if (isHandled) return; } } if (isDeferResolve(input) && deputy.isProduction && !input.production) { return; } const baseDir = input.dir ?? dirname(containingFilePath); const filePath = isAbsolute(specifier) || specifier.startsWith('#') ? specifier : join(baseDir, specifier); const resolvedFilePath = _resolveSync(filePath, baseDir); if (resolvedFilePath && isInternal(resolvedFilePath)) { return isGitIgnored(resolvedFilePath) ? undefined : resolvedFilePath; } if (input.optional) return; if (!isInternal(filePath)) { addIssue({ type: 'unlisted', filePath: containingFilePath, workspace: workspace.name, symbol: packageName ?? specifier, specifier, fixes: [], }); } else if (!isGitIgnored(filePath)) { if (!isDeferResolveEntry(input) && !isConfig(input)) { addIssue({ type: 'unresolved', filePath: containingFilePath, workspace: workspace.name, symbol: specifier, fixes: [], }); } else { debugLog(workspace.name, `Unable to resolve ${toDebugString(input, options.cwd)}`); } } };