knip
Version:
Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects
88 lines (87 loc) • 3.72 kB
JavaScript
import { IMPORT_FLAGS } from '../../../constants.js';
import { getStringValue, isStringLiteral } from '../../../typescript/ast-nodes.js';
import { isInternal } from '../../../util/path.js';
const PINO = 'pino';
export function createPinoTransportVisitor(ctx) {
let pinoName;
let transportName;
const addTarget = (specifier, pos) => {
ctx.addImport(specifier, pos, isInternal(specifier) ? IMPORT_FLAGS.ENTRY : IMPORT_FLAGS.NONE);
};
const collectTargets = (obj) => {
for (const prop of obj.properties) {
if (prop.type !== 'Property' || prop.computed || prop.key.type !== 'Identifier')
continue;
const key = prop.key.name;
if (key === 'target' && isStringLiteral(prop.value)) {
addTarget(getStringValue(prop.value), prop.value.start);
}
else if ((key === 'targets' || key === 'pipeline') && prop.value.type === 'ArrayExpression') {
for (const el of prop.value.elements) {
if (el?.type === 'ObjectExpression')
collectTargets(el);
}
}
}
};
return {
Program() {
pinoName = undefined;
transportName = undefined;
},
ImportDeclaration(node) {
if (!isStringLiteral(node.source) || getStringValue(node.source) !== PINO)
return;
for (const spec of node.specifiers ?? []) {
if (spec.type === 'ImportDefaultSpecifier') {
pinoName = spec.local.name;
}
else if (spec.type === 'ImportSpecifier') {
if (spec.imported.type === 'Identifier' && spec.imported.name === 'transport') {
transportName = spec.local.name;
}
}
}
},
VariableDeclarator(node) {
if (node.id.type !== 'Identifier' ||
node.init?.type !== 'CallExpression' ||
node.init.callee.type !== 'Identifier' ||
node.init.callee.name !== 'require' ||
!isStringLiteral(node.init.arguments[0]) ||
getStringValue(node.init.arguments[0]) !== PINO)
return;
pinoName ??= node.id.name;
},
CallExpression(node) {
if (!pinoName && !transportName)
return;
const callee = node.callee;
const arg = node.arguments[0];
if (arg?.type !== 'ObjectExpression')
return;
const isPinoTransport = callee.type === 'MemberExpression' &&
!callee.computed &&
callee.object.type === 'Identifier' &&
callee.object.name === pinoName &&
callee.property.type === 'Identifier' &&
callee.property.name === 'transport';
const isNamedTransport = callee.type === 'Identifier' && transportName !== undefined && callee.name === transportName;
if (isPinoTransport || isNamedTransport) {
collectTargets(arg);
return;
}
if (callee.type === 'Identifier' && callee.name === pinoName) {
for (const prop of arg.properties) {
if (prop.type === 'Property' &&
!prop.computed &&
prop.key.type === 'Identifier' &&
prop.key.name === 'transport' &&
prop.value.type === 'ObjectExpression') {
collectTargets(prop.value);
}
}
}
},
};
}