@@ -119,2 +119,4 @@ import { _getInputsFromScripts } from "../binaries/index.js"; | ||
| principal.addPaths(config.paths, dir); | ||
| if (compilerOptions.rootDirs) | ||
| principal.addRootDirs(compilerOptions.rootDirs); | ||
| const inputsFromPlugins = await worker.runPlugins(); | ||
@@ -121,0 +123,0 @@ for (const id of inputsFromPlugins) |
@@ -21,2 +21,3 @@ import type { ParseResult } from 'oxc-parser'; | ||
| private paths; | ||
| private rootDirs; | ||
| private extensions; | ||
@@ -32,2 +33,3 @@ cache: CacheConsultant<FileNode>; | ||
| addPaths(paths: Paths, basePath: string): void; | ||
| addRootDirs(rootDirs: string[]): void; | ||
| init(): void; | ||
@@ -34,0 +36,0 @@ readFile(filePath: string): string; |
@@ -30,2 +30,3 @@ import { extractSpecifiers } from "./typescript/follow-imports.js"; | ||
| paths = {}; | ||
| rootDirs = []; | ||
| extensions = new Set(DEFAULT_EXTENSIONS); | ||
@@ -73,2 +74,8 @@ cache; | ||
| } | ||
| addRootDirs(rootDirs) { | ||
| for (const dir of rootDirs) { | ||
| if (!this.rootDirs.includes(dir)) | ||
| this.rootDirs.push(dir); | ||
| } | ||
| } | ||
| init() { | ||
@@ -81,3 +88,4 @@ this.extensions = new Set([ | ||
| const pathsOrUndefined = Object.keys(this.paths).length > 0 ? this.paths : undefined; | ||
| this.resolveModule = createCustomModuleResolver({ paths: pathsOrUndefined }, customCompilerExtensions, this.toSourceFilePath); | ||
| const rootDirsOrUndefined = this.rootDirs.length > 1 ? this.rootDirs : undefined; | ||
| this.resolveModule = createCustomModuleResolver({ paths: pathsOrUndefined, rootDirs: rootDirsOrUndefined }, customCompilerExtensions, this.toSourceFilePath); | ||
| } | ||
@@ -228,3 +236,3 @@ readFile(filePath) { | ||
| if (!this._visitor) | ||
| this._visitor = buildVisitor(this.pluginVisitorObjects); | ||
| this._visitor = buildVisitor(this.pluginVisitorObjects, !!ignoreExportsUsedInFile); | ||
| return _getImportsAndExports(filePath, sourceText, this.resolveModule, options, ignoreExportsUsedInFile, skipExports, this._visitor, this.pluginVisitorObjects.length > 0 ? this.pluginCtx : undefined, parseResult); | ||
@@ -231,0 +239,0 @@ } |
@@ -25,2 +25,3 @@ export type Paths = Record<string, string[]> | undefined; | ||
| rootDir?: string; | ||
| rootDirs?: string[]; | ||
| skipDefaultLibCheck?: boolean; | ||
@@ -27,0 +28,0 @@ skipLibCheck?: boolean; |
@@ -10,3 +10,2 @@ import { isBuiltin } from 'node:module'; | ||
| import { buildJSDocTagLookup } from "./visitors/jsdoc.js"; | ||
| import { collectLocalRefs } from "./visitors/local-refs.js"; | ||
| import { walkAST } from "./visitors/walk.js"; | ||
@@ -248,3 +247,3 @@ const jsDocImportRe = /import\(\s*['"]([^'"]+)['"]\s*\)(?:\.(\w+))?/g; | ||
| } | ||
| walkAST(result.program, sourceText, filePath, { | ||
| const localRefs = walkAST(result.program, sourceText, filePath, { | ||
| lineStarts, | ||
@@ -266,2 +265,3 @@ skipExports, | ||
| skipBareExprRefs: !!ignoreExportsUsedInFile, | ||
| localRefs: ignoreExportsUsedInFile ? new Set() : undefined, | ||
| destructuredExports, | ||
@@ -315,3 +315,2 @@ hasNodeModuleImport, | ||
| } | ||
| const localRefs = ignoreExportsUsedInFile ? collectLocalRefs(result.program, localImportMap, exports) : undefined; | ||
| for (const [id, item] of exports) { | ||
@@ -318,0 +317,0 @@ item.referencedIn = referencedInExport.get(id); |
@@ -5,2 +5,3 @@ import type { ToSourceFilePath } from '../util/to-source-path.ts'; | ||
| paths?: Record<string, string[]>; | ||
| rootDirs?: string[]; | ||
| }, customCompilerExtensions: string[], toSourceFilePath: ToSourceFilePath): ResolveModule; |
@@ -15,2 +15,3 @@ import { existsSync } from 'node:fs'; | ||
| const resolveWithAlias = alias ? _createSyncModuleResolver(extensions, alias) : undefined; | ||
| const rootDirs = compilerOptions.rootDirs; | ||
| function toSourcePath(resolvedFileName) { | ||
@@ -47,4 +48,20 @@ if (!hasCustomExts || !customCompilerExtensionsSet.has(extname(resolvedFileName))) { | ||
| } | ||
| if (rootDirs && !isAbsolute(sanitizedSpecifier)) { | ||
| 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, sanitizedSpecifier); | ||
| const resolved = resolveSync(mapped, containingFile); | ||
| if (resolved) | ||
| return toResult(resolved); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return timerify(resolveModuleName); | ||
| } |
@@ -136,3 +136,2 @@ import { ALIAS_TAG, FIX_FLAGS, IMPORT_FLAGS, IMPORT_STAR, SYMBOL_TYPE } from "../../constants.js"; | ||
| s.addExport(decl.id.name, SYMBOL_TYPE.FUNCTION, decl.id.start, [], fix, false, s.getJSDocTags(exportStart)); | ||
| s.collectRefsInType(decl, decl.id.name, true); | ||
| } | ||
@@ -152,2 +151,6 @@ else if (decl.type === 'ClassDeclaration' && decl.id) { | ||
| s.collectRefsInType(decl.body, decl.id.name, false); | ||
| for (const ext of decl.extends ?? []) { | ||
| if (ext.expression?.type === 'Identifier') | ||
| s.addRefInExport(ext.expression.name, decl.id.name); | ||
| } | ||
| } | ||
@@ -212,3 +215,2 @@ else if (decl.type === 'TSEnumDeclaration') { | ||
| pos = decl.id?.start ?? decl.start; | ||
| s.collectRefsInType(decl, 'default', false); | ||
| } | ||
@@ -215,0 +217,0 @@ else if (decl.type === 'ClassDeclaration') { |
| import type { MemberExpression, JSXMemberExpression } from 'oxc-parser'; | ||
| import type { WalkState } from './walk.ts'; | ||
| import { type WalkState } from './walk.ts'; | ||
| export declare function handleMemberExpression(node: MemberExpression, s: WalkState): void; | ||
| export declare function handleJSXMemberExpression(node: JSXMemberExpression, s: WalkState): void; |
| import { OPAQUE } from "../../constants.js"; | ||
| import { addValue } from "../../util/module-graph.js"; | ||
| import { getStringValue, isStringLiteral } from "./helpers.js"; | ||
| import { isShadowed } from "./walk.js"; | ||
| export function handleMemberExpression(node, s) { | ||
@@ -10,3 +11,4 @@ if (node.object.type === 'MemberExpression' && node.object.object.type === 'MemberExpression') { | ||
| const localName = node.object.name; | ||
| const _import = s.localImportMap.get(localName); | ||
| const shadowed = isShadowed(localName, node.object.start); | ||
| const _import = !shadowed ? s.localImportMap.get(localName) : undefined; | ||
| if (_import) { | ||
@@ -36,3 +38,3 @@ const internalImport = s.internal.get(_import.filePath); | ||
| } | ||
| else { | ||
| else if (!shadowed) { | ||
| const memberName = node.computed === false && node.property.type === 'Identifier' ? node.property.name : undefined; | ||
@@ -76,30 +78,32 @@ if (memberName) { | ||
| const rootName = node.object.object.name; | ||
| const _import = s.localImportMap.get(rootName); | ||
| if (_import) { | ||
| const internalImport = s.internal.get(_import.filePath); | ||
| if (internalImport) { | ||
| const mid = node.object.property.name; | ||
| s.addNsMemberRefs(internalImport, rootName, mid); | ||
| if (!s.chainedMemberExprs.has(node)) { | ||
| s.addNsMemberRefs(internalImport, rootName, `${mid}.${node.property.name}`); | ||
| if (!isShadowed(rootName, node.object.object.start)) { | ||
| const _import = s.localImportMap.get(rootName); | ||
| if (_import) { | ||
| const internalImport = s.internal.get(_import.filePath); | ||
| if (internalImport) { | ||
| const mid = node.object.property.name; | ||
| s.addNsMemberRefs(internalImport, rootName, mid); | ||
| if (!s.chainedMemberExprs.has(node)) { | ||
| s.addNsMemberRefs(internalImport, rootName, `${mid}.${node.property.name}`); | ||
| } | ||
| } | ||
| } | ||
| if (!_import.isNamespace) { | ||
| const mid = node.object.property.name; | ||
| const _import = s.localImportMap.get(mid); | ||
| if (_import) { | ||
| const midImport = s.internal.get(_import.filePath); | ||
| if (midImport) | ||
| s.addNsMemberRefs(midImport, mid, node.property.name); | ||
| if (!_import.isNamespace) { | ||
| const mid = node.object.property.name; | ||
| const _import = s.localImportMap.get(mid); | ||
| if (_import) { | ||
| const midImport = s.internal.get(_import.filePath); | ||
| if (midImport) | ||
| s.addNsMemberRefs(midImport, mid, node.property.name); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| const exp = s.exports.get(rootName); | ||
| if (exp && exp.members.length > 0) { | ||
| const mid = node.object.property.name; | ||
| const dottedName = `${mid}.${node.property.name}`; | ||
| for (const member of exp.members) { | ||
| if (member.identifier === mid || member.identifier === dottedName) | ||
| member.hasRefsInFile = true; | ||
| else { | ||
| const exp = s.exports.get(rootName); | ||
| if (exp && exp.members.length > 0) { | ||
| const mid = node.object.property.name; | ||
| const dottedName = `${mid}.${node.property.name}`; | ||
| for (const member of exp.members) { | ||
| if (member.identifier === mid || member.identifier === dottedName) | ||
| member.hasRefsInFile = true; | ||
| } | ||
| } | ||
@@ -119,24 +123,26 @@ } | ||
| const rootName = node.object.object.object.name; | ||
| const _import = s.localImportMap.get(rootName); | ||
| if (_import) { | ||
| const internalImport = s.internal.get(_import.filePath); | ||
| if (internalImport) { | ||
| const a = node.object.object.property.name; | ||
| const b = node.object.property.name; | ||
| const c = node.property.name; | ||
| s.addNsMemberRefs(internalImport, rootName, a); | ||
| s.addNsMemberRefs(internalImport, rootName, `${a}.${b}`); | ||
| s.addNsMemberRefs(internalImport, rootName, `${a}.${b}.${c}`); | ||
| if (!isShadowed(rootName, node.object.object.object.start)) { | ||
| const _import = s.localImportMap.get(rootName); | ||
| if (_import) { | ||
| const internalImport = s.internal.get(_import.filePath); | ||
| if (internalImport) { | ||
| const a = node.object.object.property.name; | ||
| const b = node.object.property.name; | ||
| const c = node.property.name; | ||
| s.addNsMemberRefs(internalImport, rootName, a); | ||
| s.addNsMemberRefs(internalImport, rootName, `${a}.${b}`); | ||
| s.addNsMemberRefs(internalImport, rootName, `${a}.${b}.${c}`); | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| const exp = s.exports.get(rootName); | ||
| if (exp && exp.members.length > 0) { | ||
| const a = node.object.object.property.name; | ||
| const b = node.object.property.name; | ||
| const c = node.property.name; | ||
| const dottedName = `${a}.${b}.${c}`; | ||
| for (const member of exp.members) { | ||
| if (member.identifier === a || member.identifier === `${a}.${b}` || member.identifier === dottedName) | ||
| member.hasRefsInFile = true; | ||
| else { | ||
| const exp = s.exports.get(rootName); | ||
| if (exp && exp.members.length > 0) { | ||
| const a = node.object.object.property.name; | ||
| const b = node.object.property.name; | ||
| const c = node.property.name; | ||
| const dottedName = `${a}.${b}.${c}`; | ||
| for (const member of exp.members) { | ||
| if (member.identifier === a || member.identifier === `${a}.${b}` || member.identifier === dottedName) | ||
| member.hasRefsInFile = true; | ||
| } | ||
| } | ||
@@ -143,0 +149,0 @@ } |
@@ -33,2 +33,3 @@ import { Visitor, type Program, type Span } from 'oxc-parser'; | ||
| skipBareExprRefs: boolean; | ||
| localRefs: Set<string> | undefined; | ||
| destructuredExports: Set<string>; | ||
@@ -54,2 +55,6 @@ hasNodeModuleImport: boolean; | ||
| nsRanges: [number, number][]; | ||
| scopeDepth: number; | ||
| scopeStarts: number[]; | ||
| scopeEnds: number[]; | ||
| shadowScopes: Map<string, [number, number][]>; | ||
| addExport: (identifier: string, type: SymbolType, pos: number, members: ExportMember[], fix: Fix, isReExport: boolean, jsDocTags: Set<string>) => void; | ||
@@ -59,6 +64,8 @@ getFix: (start: number, end: number, flags?: number) => Fix; | ||
| collectRefsInType: (node: any, exportName: string, signatureOnly: boolean) => void; | ||
| addRefInExport: (name: string, exportName: string) => void; | ||
| isInNamespace: (node: Span) => boolean; | ||
| } | ||
| export declare function buildVisitor(pluginVisitorObjects: PluginVisitorObject[]): Visitor; | ||
| export declare function walkAST(program: Program, sourceText: string, filePath: string, ctx: WalkContext): void; | ||
| export declare const isShadowed: (name: string, pos: number) => boolean; | ||
| export declare function buildVisitor(pluginVisitorObjects: PluginVisitorObject[], includeLocalRefs?: boolean): Visitor; | ||
| export declare function walkAST(program: Program, sourceText: string, filePath: string, ctx: WalkContext): Set<string> | undefined; | ||
| export {}; |
@@ -89,4 +89,46 @@ import { Visitor } from 'oxc-parser'; | ||
| }; | ||
| const _addRefInExport = (name, exportName) => { | ||
| const refs = state.referencedInExport.get(name); | ||
| if (refs) | ||
| refs.add(exportName); | ||
| else | ||
| state.referencedInExport.set(name, new Set([exportName])); | ||
| }; | ||
| const _isInNamespace = (node) => state.nsRanges.length > 0 && state.nsRanges.some(([start, end]) => node.start >= start && node.end <= end); | ||
| export const isShadowed = (name, pos) => { | ||
| if (state.shadowScopes.size === 0) | ||
| return false; | ||
| const ranges = state.shadowScopes.get(name); | ||
| if (!ranges) | ||
| return false; | ||
| if (state.localImportMap.get(name)?.isDynamicImport) | ||
| return false; | ||
| for (const range of ranges) { | ||
| if (pos >= range[0] && pos <= range[1]) | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
| const _addLocalRef = (name, pos) => { | ||
| if (!state.localImportMap.has(name) && !isShadowed(name, pos)) | ||
| state.localRefs.add(name); | ||
| }; | ||
| const _addShadow = (name) => { | ||
| const i = state.scopeDepth - 1; | ||
| const range = [state.scopeStarts[i], state.scopeEnds[i]]; | ||
| const ranges = state.shadowScopes.get(name); | ||
| if (ranges) | ||
| ranges.push(range); | ||
| else | ||
| state.shadowScopes.set(name, [range]); | ||
| }; | ||
| const coreVisitorObject = { | ||
| BlockStatement(node) { | ||
| state.scopeStarts[state.scopeDepth] = node.start; | ||
| state.scopeEnds[state.scopeDepth] = node.end; | ||
| state.scopeDepth++; | ||
| }, | ||
| 'BlockStatement:exit'() { | ||
| state.scopeDepth--; | ||
| }, | ||
| TSModuleDeclaration(node) { | ||
@@ -100,11 +142,24 @@ state.nsRanges.push([node.start, node.end]); | ||
| FunctionDeclaration(node) { | ||
| if (node.id?.name) | ||
| if (node.id?.name) { | ||
| state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.FUNCTION); | ||
| if (state.scopeDepth > 0) | ||
| _addShadow(node.id.name); | ||
| } | ||
| }, | ||
| VariableDeclaration(node) { | ||
| state.currentVarDeclStart = node.start; | ||
| for (const decl of node.declarations) { | ||
| if (decl.id.type === 'Identifier') | ||
| state.localDeclarationTypes.set(decl.id.name, SYMBOL_TYPE.VARIABLE); | ||
| if (state.scopeDepth > 0) { | ||
| for (const decl of node.declarations) { | ||
| if (decl.id.type === 'Identifier') { | ||
| state.localDeclarationTypes.set(decl.id.name, SYMBOL_TYPE.VARIABLE); | ||
| _addShadow(decl.id.name); | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| for (const decl of node.declarations) { | ||
| if (decl.id.type === 'Identifier') | ||
| state.localDeclarationTypes.set(decl.id.name, SYMBOL_TYPE.VARIABLE); | ||
| } | ||
| } | ||
| }, | ||
@@ -146,3 +201,3 @@ TSEnumDeclaration(node) { | ||
| ForInStatement(node) { | ||
| if (node.right.type === 'Identifier') { | ||
| if (node.right.type === 'Identifier' && !isShadowed(node.right.name, node.right.start)) { | ||
| const _import = state.localImportMap.get(node.right.name); | ||
@@ -157,3 +212,3 @@ if (_import?.isNamespace) { | ||
| ForOfStatement(node) { | ||
| if (node.right.type === 'Identifier') { | ||
| if (node.right.type === 'Identifier' && !isShadowed(node.right.name, node.right.start)) { | ||
| const _import = state.localImportMap.get(node.right.name); | ||
@@ -175,3 +230,3 @@ if (_import?.isNamespace) { | ||
| } | ||
| if (left.type === 'Identifier') { | ||
| if (left.type === 'Identifier' && !isShadowed(left.name, left.start)) { | ||
| const rootName = left.name; | ||
@@ -212,7 +267,9 @@ const _import = state.localImportMap.get(rootName); | ||
| const name = node.typeName.name; | ||
| const _import = state.localImportMap.get(name); | ||
| if (_import) { | ||
| const internalImport = state.internal.get(_import.filePath); | ||
| if (internalImport) | ||
| internalImport.refs.add(name); | ||
| if (!isShadowed(name, node.typeName.start)) { | ||
| const _import = state.localImportMap.get(name); | ||
| if (_import) { | ||
| const internalImport = state.internal.get(_import.filePath); | ||
| if (internalImport) | ||
| internalImport.refs.add(name); | ||
| } | ||
| } | ||
@@ -224,7 +281,9 @@ } | ||
| const name = node.exprName.name; | ||
| const _import = state.localImportMap.get(name); | ||
| if (_import) { | ||
| const internalImport = state.internal.get(_import.filePath); | ||
| if (internalImport) | ||
| internalImport.refs.add(name); | ||
| if (!isShadowed(name, node.exprName.start)) { | ||
| const _import = state.localImportMap.get(name); | ||
| if (_import) { | ||
| const internalImport = state.internal.get(_import.filePath); | ||
| if (internalImport) | ||
| internalImport.refs.add(name); | ||
| } | ||
| } | ||
@@ -274,5 +333,231 @@ } | ||
| }; | ||
| export function buildVisitor(pluginVisitorObjects) { | ||
| if (pluginVisitorObjects.length === 0) | ||
| return new Visitor(coreVisitorObject); | ||
| const localRefsVisitorObject = { | ||
| ClassDeclaration(node) { | ||
| if (node.superClass?.type === 'Identifier') | ||
| _addLocalRef(node.superClass.name, node.superClass.start); | ||
| for (const impl of node.implements ?? []) { | ||
| if (impl.expression?.type === 'Identifier') | ||
| _addLocalRef(impl.expression.name, impl.expression.start); | ||
| } | ||
| }, | ||
| TSInterfaceDeclaration(node) { | ||
| for (const ext of node.extends ?? []) { | ||
| if (ext.expression?.type === 'Identifier') | ||
| _addLocalRef(ext.expression.name, ext.expression.start); | ||
| } | ||
| }, | ||
| Property(node) { | ||
| if (node.value?.type === 'Identifier') | ||
| _addLocalRef(node.value.name, node.value.start); | ||
| }, | ||
| ReturnStatement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| AssignmentExpression(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| SpreadElement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| ConditionalExpression(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| _addLocalRef(node.test.name, node.test.start); | ||
| if (node.consequent?.type === 'Identifier') | ||
| _addLocalRef(node.consequent.name, node.consequent.start); | ||
| if (node.alternate?.type === 'Identifier') | ||
| _addLocalRef(node.alternate.name, node.alternate.start); | ||
| }, | ||
| ArrayExpression(node) { | ||
| for (const el of node.elements ?? []) { | ||
| if (el?.type === 'Identifier') | ||
| _addLocalRef(el.name, el.start); | ||
| } | ||
| }, | ||
| TemplateLiteral(node) { | ||
| for (const expr of node.expressions ?? []) { | ||
| if (expr.type === 'Identifier') | ||
| _addLocalRef(expr.name, expr.start); | ||
| } | ||
| }, | ||
| BinaryExpression(node) { | ||
| if (node.left?.type === 'Identifier') | ||
| _addLocalRef(node.left.name, node.left.start); | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| LogicalExpression(node) { | ||
| if (node.left?.type === 'Identifier') | ||
| _addLocalRef(node.left.name, node.left.start); | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| UnaryExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| SwitchStatement(node) { | ||
| if (node.discriminant?.type === 'Identifier') | ||
| _addLocalRef(node.discriminant.name, node.discriminant.start); | ||
| for (const c of node.cases ?? []) { | ||
| if (c.test?.type === 'Identifier') | ||
| _addLocalRef(c.test.name, c.test.start); | ||
| } | ||
| }, | ||
| IfStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| _addLocalRef(node.test.name, node.test.start); | ||
| }, | ||
| ThrowStatement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| WhileStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| _addLocalRef(node.test.name, node.test.start); | ||
| }, | ||
| DoWhileStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| _addLocalRef(node.test.name, node.test.start); | ||
| }, | ||
| YieldExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| AwaitExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| _addLocalRef(node.argument.name, node.argument.start); | ||
| }, | ||
| ArrowFunctionExpression(node) { | ||
| if (node.body?.type === 'Identifier') | ||
| _addLocalRef(node.body.name, node.body.start); | ||
| }, | ||
| AssignmentPattern(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| SequenceExpression(node) { | ||
| for (const expr of node.expressions ?? []) { | ||
| if (expr.type === 'Identifier') | ||
| _addLocalRef(expr.name, expr.start); | ||
| } | ||
| }, | ||
| TSAsExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| TSSatisfiesExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| TSNonNullExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| TSTypeAssertion(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| ParenthesizedExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| PropertyDefinition(node) { | ||
| if (node.value?.type === 'Identifier') | ||
| _addLocalRef(node.value.name, node.value.start); | ||
| }, | ||
| ForInStatement(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| ForOfStatement(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| _addLocalRef(node.right.name, node.right.start); | ||
| }, | ||
| JSXOpeningElement(node) { | ||
| if (node.name?.type === 'JSXIdentifier') | ||
| _addLocalRef(node.name.name, node.name.start); | ||
| for (const attr of node.attributes ?? []) { | ||
| if (attr.type === 'JSXSpreadAttribute' && attr.argument?.type === 'Identifier') | ||
| _addLocalRef(attr.argument.name, attr.argument.start); | ||
| } | ||
| }, | ||
| JSXExpressionContainer(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| VariableDeclarator(node) { | ||
| if (node.init?.type === 'Identifier') | ||
| _addLocalRef(node.init.name, node.init.start); | ||
| }, | ||
| ExpressionStatement(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| _addLocalRef(node.expression.name, node.expression.start); | ||
| }, | ||
| CallExpression(node) { | ||
| if (node.callee?.type === 'Identifier') | ||
| _addLocalRef(node.callee.name, node.callee.start); | ||
| for (const arg of node.arguments ?? []) { | ||
| if (arg.type === 'Identifier') | ||
| _addLocalRef(arg.name, arg.start); | ||
| } | ||
| }, | ||
| NewExpression(node) { | ||
| if (node.callee?.type === 'Identifier') | ||
| _addLocalRef(node.callee.name, node.callee.start); | ||
| for (const arg of node.arguments ?? []) { | ||
| if (arg.type === 'Identifier') | ||
| _addLocalRef(arg.name, arg.start); | ||
| } | ||
| }, | ||
| MemberExpression(node) { | ||
| if (node.object?.type === 'Identifier') | ||
| _addLocalRef(node.object.name, node.object.start); | ||
| if (node.computed && node.property?.type === 'Identifier') | ||
| _addLocalRef(node.property.name, node.property.start); | ||
| }, | ||
| TaggedTemplateExpression(node) { | ||
| if (node.tag?.type === 'Identifier') | ||
| _addLocalRef(node.tag.name, node.tag.start); | ||
| }, | ||
| TSQualifiedName(node) { | ||
| let left = node; | ||
| const parts = []; | ||
| while (left.type === 'TSQualifiedName') { | ||
| if (left.right.type === 'Identifier') | ||
| parts.unshift(left.right.name); | ||
| left = left.left; | ||
| } | ||
| if (left.type === 'Identifier') { | ||
| const rootName = left.name; | ||
| if (!state.localImportMap.has(rootName) && !isShadowed(rootName, left.start) && parts.length > 0) { | ||
| const exp = state.exports.get(rootName); | ||
| if (exp) { | ||
| state.localRefs.add(rootName); | ||
| for (const member of exp.members) { | ||
| if (member.identifier === parts[0]) | ||
| member.hasRefsInFile = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| TSTypeReference(node) { | ||
| if (node.typeName?.type === 'Identifier') { | ||
| const name = node.typeName.name; | ||
| if (!state.localImportMap.has(name)) | ||
| _addLocalRef(name, node.typeName.start); | ||
| } | ||
| }, | ||
| TSTypeQuery(node) { | ||
| if (node.exprName?.type === 'Identifier') { | ||
| const name = node.exprName.name; | ||
| if (!state.localImportMap.has(name)) | ||
| _addLocalRef(name, node.exprName.start); | ||
| } | ||
| }, | ||
| }; | ||
| export function buildVisitor(pluginVisitorObjects, includeLocalRefs) { | ||
| const handlerLists = new Map(); | ||
@@ -285,6 +570,8 @@ const coreHandlers = coreVisitorObject; | ||
| } | ||
| for (const obj of pluginVisitorObjects) { | ||
| const handlers = obj; | ||
| for (const key in handlers) { | ||
| const fn = handlers[key]; | ||
| const extras = includeLocalRefs | ||
| ? [localRefsVisitorObject, ...pluginVisitorObjects] | ||
| : pluginVisitorObjects; | ||
| for (const obj of extras) { | ||
| for (const key in obj) { | ||
| const fn = obj[key]; | ||
| if (!fn) | ||
@@ -299,2 +586,4 @@ continue; | ||
| } | ||
| if (extras.length === 0) | ||
| return new Visitor(coreVisitorObject); | ||
| const merged = {}; | ||
@@ -330,2 +619,6 @@ for (const [key, list] of handlerLists) { | ||
| nsRanges: [], | ||
| scopeDepth: 0, | ||
| scopeStarts: [], | ||
| scopeEnds: [], | ||
| shadowScopes: new Map(), | ||
| addExport: _addExport, | ||
@@ -335,2 +628,3 @@ getFix: _getFix, | ||
| collectRefsInType: _collectRefsInType, | ||
| addRefInExport: _addRefInExport, | ||
| isInNamespace: _isInNamespace, | ||
@@ -372,3 +666,5 @@ }; | ||
| } | ||
| const localRefs = state.localRefs; | ||
| state = undefined; | ||
| return localRefs; | ||
| } |
@@ -50,2 +50,15 @@ import { readFileSync } from 'node:fs'; | ||
| }; | ||
| const findRootDirsBase = (tsConfigFilePath) => { | ||
| try { | ||
| const raw = JSON.parse(stripJsonComments(readFileSync(tsConfigFilePath, 'utf8'))); | ||
| if (raw.compilerOptions?.rootDirs) | ||
| return dirname(tsConfigFilePath); | ||
| if (raw.extends) { | ||
| const extPath = join(dirname(tsConfigFilePath), raw.extends); | ||
| return findRootDirsBase(extPath); | ||
| } | ||
| } | ||
| catch { } | ||
| return undefined; | ||
| }; | ||
| const resolveConfig = (tsConfigFilePath) => { | ||
@@ -81,2 +94,6 @@ try { | ||
| } | ||
| if (compilerOptions.rootDirs) { | ||
| const rootDirsBase = findRootDirsBase(tsConfigFilePath) ?? dir; | ||
| compilerOptions.rootDirs = compilerOptions.rootDirs.map((d) => isAbsolute(d) ? d : join(rootDirsBase, d)); | ||
| } | ||
| const include = resolvePatterns(config.include, dir, true); | ||
@@ -83,0 +100,0 @@ const exclude = resolvePatterns(config.exclude, dir, true); |
@@ -1,1 +0,1 @@ | ||
| export declare const version = "6.0.1"; | ||
| export declare const version = "6.0.2"; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| export const version = '6.0.1'; | ||
| export const version = '6.0.2'; |
+2
-2
| { | ||
| "name": "knip", | ||
| "version": "6.0.1", | ||
| "version": "6.0.2", | ||
| "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects", | ||
@@ -83,3 +83,3 @@ "keywords": [ | ||
| "formatly": "^0.3.0", | ||
| "get-tsconfig": "4.13.6", | ||
| "get-tsconfig": "4.13.7", | ||
| "jiti": "^2.6.0", | ||
@@ -86,0 +86,0 @@ "minimist": "^1.2.8", |
| import { type Program } from 'oxc-parser'; | ||
| import type { Export } from '../../types/module-graph.ts'; | ||
| export declare function collectLocalRefs(program: Program, localImportMap: Map<string, { | ||
| importedName: string; | ||
| filePath: string; | ||
| isNamespace: boolean; | ||
| }>, fileExports: Map<string, Export>): Set<string>; |
| import { Visitor } from 'oxc-parser'; | ||
| let refs; | ||
| let importNames; | ||
| let exportsMap; | ||
| const add = (name) => { | ||
| if (!importNames.has(name)) | ||
| refs.add(name); | ||
| }; | ||
| const visitor = new Visitor({ | ||
| ClassDeclaration(node) { | ||
| if (node.superClass?.type === 'Identifier') | ||
| add(node.superClass.name); | ||
| for (const impl of node.implements ?? []) { | ||
| if (impl.expression?.type === 'Identifier') | ||
| add(impl.expression.name); | ||
| } | ||
| }, | ||
| TSInterfaceDeclaration(node) { | ||
| for (const ext of node.extends ?? []) { | ||
| if (ext.expression?.type === 'Identifier') | ||
| add(ext.expression.name); | ||
| } | ||
| }, | ||
| Property(node) { | ||
| if (node.value?.type === 'Identifier') | ||
| add(node.value.name); | ||
| }, | ||
| ReturnStatement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| AssignmentExpression(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| SpreadElement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| ConditionalExpression(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| add(node.test.name); | ||
| if (node.consequent?.type === 'Identifier') | ||
| add(node.consequent.name); | ||
| if (node.alternate?.type === 'Identifier') | ||
| add(node.alternate.name); | ||
| }, | ||
| ArrayExpression(node) { | ||
| for (const el of node.elements ?? []) { | ||
| if (el?.type === 'Identifier') | ||
| add(el.name); | ||
| } | ||
| }, | ||
| TemplateLiteral(node) { | ||
| for (const expr of node.expressions ?? []) { | ||
| if (expr.type === 'Identifier') | ||
| add(expr.name); | ||
| } | ||
| }, | ||
| BinaryExpression(node) { | ||
| if (node.left?.type === 'Identifier') | ||
| add(node.left.name); | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| LogicalExpression(node) { | ||
| if (node.left?.type === 'Identifier') | ||
| add(node.left.name); | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| UnaryExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| SwitchStatement(node) { | ||
| if (node.discriminant?.type === 'Identifier') | ||
| add(node.discriminant.name); | ||
| for (const c of node.cases ?? []) { | ||
| if (c.test?.type === 'Identifier') | ||
| add(c.test.name); | ||
| } | ||
| }, | ||
| IfStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| add(node.test.name); | ||
| }, | ||
| ThrowStatement(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| WhileStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| add(node.test.name); | ||
| }, | ||
| DoWhileStatement(node) { | ||
| if (node.test?.type === 'Identifier') | ||
| add(node.test.name); | ||
| }, | ||
| YieldExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| AwaitExpression(node) { | ||
| if (node.argument?.type === 'Identifier') | ||
| add(node.argument.name); | ||
| }, | ||
| ArrowFunctionExpression(node) { | ||
| if (node.body?.type === 'Identifier') | ||
| add(node.body.name); | ||
| }, | ||
| AssignmentPattern(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| SequenceExpression(node) { | ||
| for (const expr of node.expressions ?? []) { | ||
| if (expr.type === 'Identifier') | ||
| add(expr.name); | ||
| } | ||
| }, | ||
| TSAsExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| TSSatisfiesExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| TSNonNullExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| TSTypeAssertion(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| ParenthesizedExpression(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| PropertyDefinition(node) { | ||
| if (node.value?.type === 'Identifier') | ||
| add(node.value.name); | ||
| }, | ||
| ForInStatement(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| ForOfStatement(node) { | ||
| if (node.right?.type === 'Identifier') | ||
| add(node.right.name); | ||
| }, | ||
| JSXOpeningElement(node) { | ||
| if (node.name?.type === 'JSXIdentifier') | ||
| add(node.name.name); | ||
| for (const attr of node.attributes ?? []) { | ||
| if (attr.type === 'JSXSpreadAttribute' && attr.argument?.type === 'Identifier') | ||
| add(attr.argument.name); | ||
| } | ||
| }, | ||
| JSXExpressionContainer(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| VariableDeclarator(node) { | ||
| if (node.init?.type === 'Identifier') | ||
| add(node.init.name); | ||
| }, | ||
| ExpressionStatement(node) { | ||
| if (node.expression?.type === 'Identifier') | ||
| add(node.expression.name); | ||
| }, | ||
| CallExpression(node) { | ||
| if (node.callee?.type === 'Identifier') | ||
| add(node.callee.name); | ||
| for (const arg of node.arguments ?? []) { | ||
| if (arg.type === 'Identifier') | ||
| add(arg.name); | ||
| } | ||
| }, | ||
| NewExpression(node) { | ||
| if (node.callee?.type === 'Identifier') | ||
| add(node.callee.name); | ||
| for (const arg of node.arguments ?? []) { | ||
| if (arg.type === 'Identifier') | ||
| add(arg.name); | ||
| } | ||
| }, | ||
| MemberExpression(node) { | ||
| if (node.object?.type === 'Identifier') | ||
| add(node.object.name); | ||
| if (node.computed && node.property?.type === 'Identifier') | ||
| add(node.property.name); | ||
| }, | ||
| TaggedTemplateExpression(node) { | ||
| if (node.tag?.type === 'Identifier') | ||
| add(node.tag.name); | ||
| }, | ||
| TSQualifiedName(node) { | ||
| let left = node; | ||
| const parts = []; | ||
| while (left.type === 'TSQualifiedName') { | ||
| if (left.right.type === 'Identifier') | ||
| parts.unshift(left.right.name); | ||
| left = left.left; | ||
| } | ||
| if (left.type === 'Identifier') { | ||
| const rootName = left.name; | ||
| if (!importNames.has(rootName) && parts.length > 0) { | ||
| const exp = exportsMap.get(rootName); | ||
| if (exp) { | ||
| refs.add(rootName); | ||
| for (const member of exp.members) { | ||
| if (member.identifier === parts[0]) | ||
| member.hasRefsInFile = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| TSTypeReference(node) { | ||
| if (node.typeName?.type === 'Identifier') { | ||
| const name = node.typeName.name; | ||
| if (!importNames.has(name)) | ||
| refs.add(name); | ||
| } | ||
| }, | ||
| TSTypeQuery(node) { | ||
| if (node.exprName?.type === 'Identifier') { | ||
| const name = node.exprName.name; | ||
| if (!importNames.has(name)) | ||
| refs.add(name); | ||
| } | ||
| }, | ||
| }); | ||
| export function collectLocalRefs(program, localImportMap, fileExports) { | ||
| refs = new Set(); | ||
| importNames = localImportMap; | ||
| exportsMap = fileExports; | ||
| visitor.visit(program); | ||
| return refs; | ||
| } |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
1506015
0.4%30733
0.35%826
-0.24%+ Added
- Removed
Updated