UNPKG

knip

Version:

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

146 lines (145 loc) 7.31 kB
import { IMPORT_STAR } from '../constants.js'; import { addNodes, createNode, isTrace } from './trace.js'; export const getIsIdentifierReferencedHandler = (graph, entryPaths) => { const isIdentifierReferenced = (filePath, id, isIncludeEntryExports = false, traceNode = createNode(filePath), seen = new Set()) => { let isReferenced = false; let reExportingEntryFile = entryPaths.has(filePath) ? filePath : undefined; if (reExportingEntryFile) traceNode.isEntry = true; if (!isIncludeEntryExports && reExportingEntryFile) return { isReferenced, reExportingEntryFile, traceNode }; seen.add(filePath); const ids = id.split('.'); const [identifier, ...rest] = ids; const file = graph.get(filePath)?.imported; if (!file) return { isReferenced, reExportingEntryFile, traceNode }; if (((identifier !== id && file.refs.has(id)) || identifier === id) && (file.imported.has(identifier) || file.importedAs.has(identifier))) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; if (file.importedAs.has(identifier)) { for (const aliases of file.importedAs.values()) { for (const alias of aliases.keys()) addNodes(traceNode, alias, graph, aliases.get(alias)); } } else addNodes(traceNode, id, graph, file.imported.get(identifier)); } for (const [exportId, aliases] of file.importedAs.entries()) { if (identifier === exportId) { for (const alias of aliases.keys()) { const aliasedRef = [alias, ...rest].join('.'); if (file.refs.has(aliasedRef)) { isReferenced = true; if (!isTrace) { return { isReferenced, reExportingEntryFile, traceNode }; } addNodes(traceNode, aliasedRef, graph, aliases.get(alias)); } } } } for (const [namespace, byFilePaths] of file.importedNs) { if (file.refs.has(`${namespace}.${id}`)) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; addNodes(traceNode, `${namespace}.${id}`, graph, byFilePaths); } const reExportedAs = file.reExportedAs.get(namespace); if (reExportedAs) { for (const [alias, byFilePaths] of reExportedAs) { for (const byFilePath of byFilePaths) { if (!seen.has(byFilePath)) { const child = createNode(byFilePath); traceNode.children.add(child); const result = isIdentifierReferenced(byFilePath, `${alias}.${id}`, isIncludeEntryExports, child, seen); if (result.reExportingEntryFile) reExportingEntryFile = result.reExportingEntryFile; if (result.isReferenced) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; } } } } } const reExportedNs = file.reExportedNs.get(namespace); if (reExportedNs) { for (const byFilePath of reExportedNs) { if (!seen.has(byFilePath)) { const child = createNode(byFilePath); traceNode.children.add(child); const result = isIdentifierReferenced(byFilePath, `${namespace}.${id}`, isIncludeEntryExports, child, seen); if (result.reExportingEntryFile) reExportingEntryFile = result.reExportingEntryFile; if (result.isReferenced) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; } } } } } const reExported = file.reExported.get(identifier) ?? file.reExported.get(IMPORT_STAR); if (reExported) { for (const byFilePath of reExported) { if (!seen.has(byFilePath)) { const child = createNode(byFilePath); traceNode.children.add(child); const result = isIdentifierReferenced(byFilePath, id, isIncludeEntryExports, child, seen); if (result.reExportingEntryFile) reExportingEntryFile = result.reExportingEntryFile; if (result.isReferenced) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; } } } } const reExportedAs = file.reExportedAs.get(identifier); if (reExportedAs) { for (const [alias, byFilePaths] of reExportedAs) { for (const byFilePath of byFilePaths) { if (!seen.has(byFilePath)) { const child = createNode(byFilePath); traceNode.children.add(child); const ref = [alias, ...rest].join('.'); const result = isIdentifierReferenced(byFilePath, ref, isIncludeEntryExports, child, seen); if (result.reExportingEntryFile) reExportingEntryFile = result.reExportingEntryFile; if (result.isReferenced) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; } } } } } for (const [namespace, byFilePaths] of file.reExportedNs.entries()) { for (const byFilePath of byFilePaths) { if (!seen.has(byFilePath)) { const child = createNode(byFilePath); traceNode.children.add(child); const result = isIdentifierReferenced(byFilePath, `${namespace}.${id}`, isIncludeEntryExports, child, seen); if (result.reExportingEntryFile) reExportingEntryFile = result.reExportingEntryFile; if (result.isReferenced) { isReferenced = true; if (!isTrace) return { isReferenced, reExportingEntryFile, traceNode }; } } } } return { isReferenced, reExportingEntryFile, traceNode }; }; return isIdentifierReferenced; };