Skip to content

Commit

Permalink
Support more execa methods in visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Jan 15, 2025
1 parent 1731ee5 commit 5f2cf34
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ await $`cat package.json | grep name`;
let branch = await $`git branch --show-current`;
await $`dep deploy --branch=${branch}`;

await Promise.all([$`sleep 1; echo 1`, $`sleep 2; echo 2`, $`sleep 3; echo 3`]);
await Promise.all([$`executable1; echo 1`, $`executable2; echo 2`, $`executable3; echo 3`]);

let name = 'foo bar';
await $`mkdir /tmp/${name}`;
11 changes: 11 additions & 0 deletions packages/knip/fixtures/script-visitors/execa/methods.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { execa, execaSync, execaCommand, execaCommandSync, execaNode, $sync } from 'execa';

$sync`pnpm dlx executable4`;

await execa('bun x', ['executable5']);

execaSync('npx', ['executable6']);

await execaCommand('bunx executable7');

execaCommandSync('pnpx executable8');
14 changes: 12 additions & 2 deletions packages/knip/fixtures/script-visitors/execa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"execa1": "node ./execa-docs.mjs",
"execa2": "node ./script.js",
"execa3": "node ./node.mjs",
"execa4": "node ./options.mjs"
"execa4": "node ./options.mjs",
"execa5": "node ./methods.mjs"
},
"dependencies": {
"dep": "*"
Expand All @@ -16,6 +17,15 @@
"execa": "*"
},
"knip": {
"ignoreBinaries": ["sleep"]
"ignoreBinaries": [
"executable1",
"executable2",
"executable3",
"executable4",
"executable5",
"executable6",
"executable7",
"executable8"
]
}
}
4 changes: 2 additions & 2 deletions packages/knip/fixtures/script-visitors/execa/script.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { $ } from 'execa';
import { $, $sync } from 'execa';

/* global $ */
import { EOL } from 'node:os';
import { Octokit } from 'octokit';

await $`pnpm all-contributors generate`;

await $`npx -y all-contributors-cli@6.25 add user`;
await $sync`npx -y all-contributors-cli@6.25 add user`;
4 changes: 2 additions & 2 deletions packages/knip/src/typescript/visitors/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ export function getImportsFromPragmas(sourceFile: BoundSourceFile) {
return importNodes;
}

export function hasImportSpecifier(node: ts.Statement, name: string): boolean {
export function hasImportSpecifier(node: ts.Statement, name: string, id?: string): boolean {
return (
ts.isImportDeclaration(node) &&
ts.isStringLiteral(node.moduleSpecifier) &&
node.moduleSpecifier.text === name &&
!!node.importClause?.namedBindings &&
ts.isNamedImports(node.importClause.namedBindings) &&
node.importClause.namedBindings.elements.some(element => element.name.text === '$')
(!id || node.importClause.namedBindings.elements.some(element => element.name.text === id))
);
}
2 changes: 1 addition & 1 deletion packages/knip/src/typescript/visitors/scripts/bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { hasImportSpecifier } from '../helpers.js';
import { scriptVisitor as visit } from '../index.js';

export default visit(
sourceFile => sourceFile.statements.some(node => hasImportSpecifier(node, 'bun')),
sourceFile => sourceFile.statements.some(node => hasImportSpecifier(node, 'bun', '$')),
node => {
if (ts.isTaggedTemplateExpression(node) && node.tag.getText() === '$') {
return stripQuotes(node.template.getText());
Expand Down
26 changes: 25 additions & 1 deletion packages/knip/src/typescript/visitors/scripts/execa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,37 @@ import { stripQuotes } from '../../ast-helpers.js';
import { hasImportSpecifier } from '../helpers.js';
import { scriptVisitor as visit } from '../index.js';

const tags = new Set(['$', '$sync']);
const methods = new Set(['execa', 'execaSync', 'execaCommand', 'execaCommandSync', '$sync']);

export default visit(
sourceFile => sourceFile.statements.some(node => hasImportSpecifier(node, 'execa')),
node => {
if (ts.isTaggedTemplateExpression(node)) {
if (node.tag.getText() === '$' || (ts.isCallExpression(node.tag) && node.tag.expression.getText() === '$')) {
if (tags.has(node.tag.getText()) || (ts.isCallExpression(node.tag) && tags.has(node.tag.expression.getText()))) {
return stripQuotes(node.template.getText());
}
}

if (ts.isCallExpression(node)) {
const functionName = node.expression.getText();
if (methods.has(functionName)) {
if (functionName.startsWith('execaCommand')) {
if (node.arguments[0] && ts.isStringLiteral(node.arguments[0])) {
return stripQuotes(node.arguments[0].getText());
}
} else {
const [executable, args] = node.arguments;
if (executable && ts.isStringLiteral(executable)) {
const executableStr = stripQuotes(executable.getText());
if (args && ts.isArrayLiteralExpression(args)) {
const argStrings = args.elements.filter(ts.isStringLiteral).map(arg => stripQuotes(arg.getText()));
return [executableStr, ...argStrings].join(' ');
}
return executableStr;
}
}
}
}
}
);
4 changes: 2 additions & 2 deletions packages/knip/test/script-visitors-execa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test('Find dependencies with custom script visitors (execa)', async () => {

assert.deepEqual(counters, {
...baseCounters,
processed: 5,
total: 5,
processed: 6,
total: 6,
});
});

0 comments on commit 5f2cf34

Please sign in to comment.