knip
Version:
Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects
262 lines (261 loc) • 12.7 kB
JavaScript
import { IMPORT_FLAGS, IMPORT_STAR, OPAQUE } from "../../constants.js";
import { addValue } from "../../util/module-graph.js";
import { isInNodeModules } from "../../util/path.js";
import { getStringValue, isStringLiteral } from "./helpers.js";
export function handleVariableDeclarator(node, s) {
const init = node.init;
if (!init)
return;
let importExpr = null;
if (init.type === 'AwaitExpression' && init.argument.type === 'ImportExpression') {
importExpr = init.argument;
}
else if (init.type === 'ImportExpression') {
importExpr = init;
}
if (importExpr && isStringLiteral(importExpr.source)) {
s.handledImportExpressions.add(importExpr.start);
const specifier = getStringValue(importExpr.source);
if (node.id.type === 'ObjectPattern') {
for (const prop of node.id.properties) {
if (prop.type === 'Property') {
const importedName = prop.key?.type === 'Identifier'
? prop.key.name
: prop.key?.type === 'Literal' && typeof prop.key.value === 'string'
? prop.key.value
: undefined;
const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;
const alias = localName !== importedName ? localName : undefined;
s.addImport(specifier, importedName, alias, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.NONE);
}
else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {
s.addImport(specifier, IMPORT_STAR, prop.argument.name, undefined, prop.start, IMPORT_FLAGS.NONE);
}
}
}
else if (node.id.type === 'Identifier') {
if (init.type === 'AwaitExpression') {
s.addImport(specifier, 'default', node.id.name, undefined, importExpr.source.start, IMPORT_FLAGS.NONE);
const resolved = s.resolveModule(specifier, s.filePath);
if (resolved && !resolved.isExternalLibraryImport && !isInNodeModules(resolved.resolvedFileName)) {
s.localImportMap.set(node.id.name, {
importedName: IMPORT_STAR,
filePath: resolved.resolvedFileName,
isNamespace: true,
isDynamicImport: true,
});
}
}
else {
s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.OPAQUE);
}
}
else {
s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.OPAQUE);
}
return;
}
if (init.type === 'CallExpression' &&
init.callee.type === 'Identifier' &&
init.callee.name === 'require' &&
init.arguments.length === 1 &&
isStringLiteral(init.arguments[0])) {
const specifier = getStringValue(init.arguments[0]);
const reqTags = s.currentVarDeclStart >= 0 ? s.getJSDocTags(s.currentVarDeclStart) : undefined;
if (node.id.type === 'ObjectPattern') {
for (const prop of node.id.properties) {
if (prop.type === 'Property') {
const importedName = prop.key?.type === 'Identifier'
? prop.key.name
: prop.key?.type === 'Literal' && typeof prop.key.value === 'string'
? prop.key.value
: undefined;
const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;
const alias = localName !== importedName ? localName : undefined;
s.addImport(specifier, importedName, alias, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.NONE, undefined, reqTags);
}
else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {
s.addImport(specifier, IMPORT_STAR, prop.argument.name, undefined, prop.start, IMPORT_FLAGS.NONE, undefined, reqTags);
}
}
}
else if (node.id.type === 'Identifier') {
s.addImport(specifier, 'default', node.id.name, undefined, init.arguments[0].start, IMPORT_FLAGS.NONE, undefined, reqTags);
}
else {
s.addImport(specifier, undefined, undefined, undefined, init.arguments[0].start, IMPORT_FLAGS.SIDE_EFFECTS, undefined, reqTags);
}
return;
}
if (init.type === 'AwaitExpression' &&
init.argument.type === 'CallExpression' &&
init.argument.callee.type === 'MemberExpression' &&
!init.argument.callee.computed &&
init.argument.callee.object.type === 'Identifier' &&
init.argument.callee.object.name === 'Promise' &&
init.argument.callee.property.name === 'all' &&
init.argument.arguments[0]?.type === 'ArrayExpression' &&
node.id.type === 'ArrayPattern') {
const imports = init.argument.arguments[0].elements;
const bindings = node.id.elements;
for (let i = 0; i < imports.length; i++) {
const imp = imports[i];
const binding = bindings[i];
if (imp?.type === 'ImportExpression' && isStringLiteral(imp.source)) {
s.handledImportExpressions.add(imp.start);
const specifier = getStringValue(imp.source);
if (binding?.type === 'ObjectPattern') {
for (const prop of binding.properties) {
if (prop.type === 'Property') {
const importedName = prop.key?.type === 'Identifier'
? prop.key.name
: prop.key?.type === 'Literal' && typeof prop.key.value === 'string'
? prop.key.value
: undefined;
s.addImport(specifier, importedName, undefined, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.NONE);
}
}
}
else if (binding?.type === 'Identifier') {
s.addImport(specifier, 'default', binding.name, undefined, imp.source.start, IMPORT_FLAGS.NONE);
}
else {
s.addImport(specifier, undefined, undefined, undefined, imp.source.start, IMPORT_FLAGS.SIDE_EFFECTS);
}
}
}
}
if (node.id.type === 'Identifier') {
const aliasName = node.id.name;
const registerAlias = (expr) => {
if (expr?.type === 'Identifier') {
const _import = s.localImportMap.get(expr.name);
if (_import) {
s.addImportAlias(aliasName, expr.name, _import.filePath);
if (_import.isNamespace) {
const internalImport = s.internal.get(_import.filePath);
if (internalImport)
internalImport.refs.add(expr.name);
}
}
}
};
if (init.type === 'ConditionalExpression') {
registerAlias(init.consequent);
registerAlias(init.alternate);
}
else if (init.type === 'Identifier') {
registerAlias(init);
}
if (init.type === 'ObjectExpression') {
for (const prop of init.properties) {
if (prop.type === 'SpreadElement' && prop.argument?.type === 'Identifier') {
const _import = s.localImportMap.get(prop.argument.name);
if (_import) {
s.addImportAlias(aliasName, prop.argument.name, _import.filePath);
if (_import.isNamespace) {
const internalImport = s.internal.get(_import.filePath);
if (internalImport)
internalImport.refs.add(prop.argument.name);
}
}
}
if (prop.type === 'Property' &&
!prop.computed &&
prop.value?.type === 'Identifier' &&
prop.key?.type === 'Identifier') {
const nsName = prop.value.name;
const _import = s.localImportMap.get(nsName);
if (_import?.isNamespace) {
const internalImport = s.internal.get(_import.filePath);
if (internalImport)
internalImport.refs.add(nsName);
let map = s.nsContainers.get(aliasName);
if (!map) {
map = new Map();
s.nsContainers.set(aliasName, map);
}
map.set(prop.key.name, nsName);
}
}
}
}
}
if (node.id.type === 'ObjectPattern') {
let rootName;
let memberPath = [];
if (init.type === 'Identifier') {
rootName = init.name;
}
else if (init.type === 'MemberExpression' && !init.computed) {
const parts = [];
let cur = init;
while (cur.type === 'MemberExpression' && !cur.computed && cur.property.type === 'Identifier') {
parts.unshift(cur.property.name);
cur = cur.object;
}
if (cur.type === 'Identifier') {
rootName = cur.name;
memberPath = parts;
}
}
if (rootName) {
const _import = s.localImportMap.get(rootName);
if (_import) {
const internalImport = s.internal.get(_import.filePath);
if (internalImport) {
if (_import.isDynamicImport) {
for (const prop of node.id.properties) {
if (prop.type === 'Property' && prop.key?.type === 'Identifier') {
addValue(internalImport.import, prop.key.name, s.filePath);
}
else if (prop.type === 'RestElement') {
addValue(internalImport.import, OPAQUE, s.filePath);
}
}
}
else {
const ns = _import.isNamespace ? rootName : _import.importedName;
const prefix = [ns, ...memberPath].join('.');
for (const prop of node.id.properties) {
if (prop.type === 'Property' && prop.key?.type === 'Identifier') {
internalImport.refs.add(`${prefix}.${prop.key.name}`);
}
else if (prop.type === 'RestElement') {
addValue(internalImport.import, OPAQUE, s.filePath);
}
}
}
}
}
}
for (const prop of node.id.properties) {
if (prop.type === 'Property' && prop.value?.type === 'AssignmentPattern') {
const defaultValue = prop.value.right;
if (defaultValue?.type === 'Identifier') {
const _import = s.localImportMap.get(defaultValue.name);
if (_import) {
const internalImport = s.internal.get(_import.filePath);
if (internalImport) {
if (_import.isNamespace)
addValue(internalImport.import, OPAQUE, s.filePath);
else
internalImport.refs.add(defaultValue.name);
}
if (prop.value.left?.type === 'Identifier') {
s.addImportAlias(prop.value.left.name, defaultValue.name, _import.filePath);
}
}
}
}
}
}
}
export function handleImportExpression(node, s) {
if (s.handledImportExpressions.has(node.start))
return;
const specifier = getStringValue(node.source);
if (specifier) {
s.addImport(specifier, undefined, undefined, undefined, node.source.start, IMPORT_FLAGS.OPAQUE);
}
}