Skip to content

Commit 9422d06

Browse files
committed
Handle var interpolations in shell cmd scripts (resolve #1867)
1 parent e3f0e5c commit 9422d06

17 files changed

Lines changed: 62 additions & 30 deletions

File tree

knip.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"entry": ["{remark,scripts}/*.ts"]
1515
},
1616
"packages/vscode-knip": {
17-
"entry": ["src/index.js!", "scripts/*.js", "test/*.mjs"]
17+
"entry": ["src/index.js!", "scripts/*.js", "test/*.mjs"],
18+
"ignoreBinaries": ["vsce", "ovsx"]
1819
}
1920
}
2021
}

packages/knip/fixtures/plugin-config/child-process-exec/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ execFileSync('apricot', undefined, { cwd: '/tmp' });
3636
execFile('cherry', ['commit', '-m', 'ship it && phantomdeploy']);
3737
spawn('grape', ['render', 'a | phantomgrep']);
3838

39+
const target = 'prod';
40+
execSync(`fig deploy --target=${target}`);
41+
exec(`melon ${target} --watch`);
42+
const bin = 'watermelon';
43+
execSync(`${bin} phantomserve`);
44+
3945
// not referenced: dynamic command, and a non-child_process `.exec`
4046
const dynamic = 'kiwi';
4147
execSync(dynamic);

packages/knip/fixtures/plugin-config/script-visitors-bun/script.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ await $`bun boxen I ❤ unicorns`;
77
await $`oh-my not supported yet`;
88

99
await $`ls *.*`;
10+
11+
const directoryPath = '/tmp/repo';
12+
await $`git -C ${directoryPath} config fetch.prune false`;

packages/knip/fixtures/plugin-config/script-visitors-execa/execa-docs.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ await Promise.all([$`executable1; echo 1`, $`executable2; echo 2`, $`executable3
99

1010
const name = 'content';
1111
await $`mkdir /tmp/${name}`;
12+
13+
await $`git -C ${name} config fetch.prune false`;

packages/knip/fixtures/plugin-config/script-visitors-execa/methods.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ await execaNode('execa-node.mjs');
1515
await execaNode`execa-node-tag.mjs`;
1616

1717
await execa('node', ['build && phantomexeca']);
18+
19+
const dir = '/tmp';
20+
await execaCommand(`git -C ${dir} config fetch.prune false`);

packages/knip/fixtures/plugin-config/script-visitors-zx/zx-docs.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ await Promise.all([$`sleep 1; echo 1`, $`sleep 2; echo 2`, $`sleep 3; echo 3`]);
99

1010
const name = 'content';
1111
await $`mkdir /tmp/${name}`;
12+
13+
await $`git -C ${name} config fetch.prune false`;

packages/knip/src/binaries/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SCRIPT_INTERPOLATION } from '../constants.ts';
12
import type { GetInputsFromScripts } from '../types/config.ts';
23
import { fromBinary, type Input, isBinary, isDependency } from '../util/input.ts';
34
import { timerify } from '../util/Performance.ts';
@@ -10,6 +11,7 @@ const getInputsFromScripts: GetInputsFromScripts = (npmScripts, options) => {
1011

1112
for (const input of results) {
1213
if (!input.specifier) continue;
14+
if (input.specifier.includes(SCRIPT_INTERPOLATION)) continue;
1315
if (isDependency(input) && input.specifier.startsWith('http')) continue;
1416
if (isBinary(input) && !/^\b/.test(fromBinary(input))) continue;
1517
inputs.add(input);

packages/knip/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ export const SIDE_EFFECTS = '__side-effects';
251251

252252
export const OPAQUE = '__opaque';
253253

254+
export const SCRIPT_INTERPOLATION = '$__knip__';
255+
254256
export const IMPORT_FLAGS = {
255257
NONE: 0,
256258
RE_EXPORT: 1 << 0,

packages/knip/src/plugins/execa/visitors/execa.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';
2-
import { getSafeScriptFromArgs, getStringValue, isStringLiteral } from '../../../typescript/ast-nodes.ts';
2+
import { getSafeScriptFromArgs, getScriptFromArg, getScriptFromTemplate } from '../../../typescript/ast-nodes.ts';
33

44
const tags = new Set(['$', '$sync']);
55
const methods = new Set(['execa', 'execaSync', 'execaCommand', 'execaCommandSync', 'execaNode', '$sync']);
@@ -14,15 +14,10 @@ export function createExecaVisitor(ctx: PluginVisitorContext): PluginVisitorObje
1414
: tag.type === 'CallExpression' && tag.callee.type === 'Identifier'
1515
? tag.callee.name
1616
: undefined;
17-
if (tagName === 'execaNode') {
18-
for (const q of node.quasi.quasis) {
19-
if (q.value.raw) ctx.addScript(`node ${q.value.raw}`);
20-
}
21-
} else if (tagName && tags.has(tagName)) {
22-
for (const q of node.quasi.quasis) {
23-
if (q.value.raw) ctx.addScript(q.value.raw);
24-
}
25-
}
17+
const isNode = tagName === 'execaNode';
18+
if (!isNode && !(tagName && tags.has(tagName))) return;
19+
const script = getScriptFromTemplate(node.quasi);
20+
if (script) ctx.addScript(isNode ? `node ${script}` : script);
2621
},
2722
CallExpression(node) {
2823
if (node.callee.type !== 'Identifier' || !methods.has(node.callee.name)) return;
@@ -31,10 +26,8 @@ export function createExecaVisitor(ctx: PluginVisitorContext): PluginVisitorObje
3126
const script = getSafeScriptFromArgs(node.arguments[0], node.arguments[1]);
3227
if (script) ctx.addScript(`node ${script}`);
3328
} else if (fnName.startsWith('execaCommand')) {
34-
if (node.arguments[0] && isStringLiteral(node.arguments[0])) {
35-
const val = getStringValue(node.arguments[0]);
36-
if (val) ctx.addScript(val);
37-
}
29+
const script = getScriptFromArg(node.arguments[0]);
30+
if (script) ctx.addScript(script);
3831
} else {
3932
const script = getSafeScriptFromArgs(node.arguments[0], node.arguments[1]);
4033
if (script) ctx.addScript(script);

packages/knip/src/plugins/zx/visitors/zx.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';
2+
import { getScriptFromTemplate } from '../../../typescript/ast-nodes.ts';
23

34
export function createZxVisitor(ctx: PluginVisitorContext): PluginVisitorObject {
45
return {
56
TaggedTemplateExpression(node) {
67
if (!ctx.sourceText.startsWith('#!/usr/bin/env zx')) return;
78
if (node.tag.type === 'Identifier' && node.tag.name === '$') {
8-
for (const q of node.quasi.quasis) {
9-
if (q.value.raw) ctx.addScript(q.value.raw);
10-
}
9+
const script = getScriptFromTemplate(node.quasi);
10+
if (script) ctx.addScript(script);
1111
}
1212
},
1313
};

0 commit comments

Comments
 (0)