Skip to content

Commit 1731ee5

Browse files
committedJan 15, 2025
Introduce optional dependencies & improve pm cli arg handling
1 parent be1f9d4 commit 1731ee5

File tree

30 files changed

+158
-64
lines changed

30 files changed

+158
-64
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import parseArgs from 'minimist';
22
import type { BinaryResolver } from '../../types/config.js';
33
import { isFile } from '../../util/fs.js';
4-
import { toEntry } from '../../util/input.js';
4+
import { toBinary, toEntry } from '../../util/input.js';
55
import { isAbsolute, join } from '../../util/path.js';
6+
import { resolveX } from './bunx.js';
67

7-
const commands = ['add', 'create', 'init', 'install', 'link', 'pm', 'remove', 'run', 'test', 'update', 'upgrade'];
8+
const commands = ['add', 'create', 'init', 'install', 'link', 'pm', 'remove', 'run', 'test', 'update', 'upgrade', 'x'];
89

9-
export const resolve: BinaryResolver = (_binary, args, { manifestScriptNames, cwd, fromArgs }) => {
10+
export const resolve: BinaryResolver = (binary, args, options) => {
11+
const bin = toBinary(binary);
1012
const parsed = parseArgs(args);
1113
const [command, script] = parsed._;
12-
if (command === 'run' && manifestScriptNames.has(script)) return [];
13-
if (manifestScriptNames.has(command) || commands.includes(command)) return [];
14+
15+
if (command === 'x') {
16+
const argsForX = args.filter(arg => arg !== 'x');
17+
return [bin, ...resolveX(argsForX, options)];
18+
}
19+
20+
const { manifestScriptNames, cwd, fromArgs } = options;
21+
22+
if (command === 'run' && manifestScriptNames.has(script)) return [bin];
23+
if (manifestScriptNames.has(command) || commands.includes(command)) return [bin];
1424
const filePath = command === 'run' ? script : command;
1525
const absFilePath = isAbsolute(filePath) ? filePath : join(cwd, filePath);
16-
if (isFile(absFilePath)) return [toEntry(absFilePath)];
17-
return fromArgs(args);
26+
if (isFile(absFilePath)) return [bin, toEntry(absFilePath)];
27+
return [bin, ...fromArgs(args)];
1828
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import parseArgs from 'minimist';
2+
import type { BinaryResolver, BinaryResolverOptions } from '../../types/config.js';
3+
import { toBinary, toDependency } from '../../util/input.js';
4+
import { stripVersionFromSpecifier } from '../../util/modules.js';
5+
import { argsFrom } from '../util.js';
6+
7+
export const resolveX = (args: string[], options: BinaryResolverOptions) => {
8+
const { fromArgs } = options;
9+
const parsed = parseArgs(args);
10+
const packageSpecifier = parsed._[0];
11+
const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';
12+
const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];
13+
const command = parsed['shell-mode'] ? fromArgs([parsed['shell-mode']]) : [];
14+
const restArgs = argsFrom(args, packageSpecifier);
15+
const dependency = specifier ? [toDependency(specifier, { optional: true })] : [];
16+
return [...dependency, ...packages.map(id => toDependency(id)), ...command, ...fromArgs(restArgs).slice(1)];
17+
};
18+
19+
export const resolve: BinaryResolver = (_binary, args, options) => {
20+
return [toBinary(_binary), ...resolveX(args, options)];
21+
};
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import * as bun from './bun.js';
2+
import * as bunx from './bunx.js';
23
import * as npm from './npm.js';
34
import * as npx from './npx.js';
45
import * as pnpm from './pnpm.js';
6+
import * as pnpx from './pnpx.js';
57
import * as yarn from './yarn.js';
68

79
export default {
810
bun: bun.resolve,
11+
bunx: bunx.resolve,
912
npm: npm.resolve,
1013
npx: npx.resolve,
1114
pnpm: pnpm.resolve,
15+
pnpx: pnpx.resolve,
1216
yarn: yarn.resolve,
1317
};

‎packages/knip/src/binaries/package-manager/npm.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import parseArgs from 'minimist';
22
import type { BinaryResolver } from '../../types/config.js';
33
import { toBinary } from '../../util/input.js';
44

5-
export const resolve: BinaryResolver = (_binary, args, options) => {
5+
export const resolve: BinaryResolver = (binary, args, options) => {
66
const { fromArgs } = options;
77
const parsed = parseArgs(args);
88
const [command] = parsed._;
9-
return [toBinary(_binary), ...(command !== 'exec' ? [] : fromArgs(parsed._.slice(1)))];
9+
return [toBinary(binary), ...(command !== 'exec' ? [] : fromArgs(parsed._.slice(1)))];
1010
};

‎packages/knip/src/binaries/package-manager/npx.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@ export const resolve: BinaryResolver = (_binary, args, options) => {
2020
const restArgs = argsFrom(args, packageSpecifier);
2121

2222
const isBinary = specifier && !packageSpecifier.includes('@') && !isInternal(specifier);
23-
const dependency = isBinary ? toBinary(specifier) : toDependency(specifier);
23+
const dependency = isBinary ? toBinary(specifier) : toDependency(specifier, { optional: !parsed.no });
2424
const specifiers = dependency && !parsed.yes ? [dependency] : [];
2525

26-
return [toBinary(_binary), ...specifiers, ...packages.map(toDependency), ...command, ...fromArgs(restArgs).slice(1)];
26+
return [
27+
toBinary(_binary),
28+
...specifiers,
29+
...packages.map(id => toDependency(id, { optional: true })),
30+
...command,
31+
...fromArgs(restArgs).slice(1),
32+
];
2733
};

‎packages/knip/src/binaries/package-manager/pnpm.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import parseArgs from 'minimist';
22
import type { BinaryResolver } from '../../types/config.js';
33
import { toBinary } from '../../util/input.js';
4+
import { resolveDlx } from './pnpx.js';
45

56
// https://pnpm.io/cli/add
67

@@ -56,16 +57,24 @@ const commands = [
5657
'why',
5758
];
5859

59-
export const resolve: BinaryResolver = (_binary, args, { manifestScriptNames, fromArgs }) => {
60+
export const resolve: BinaryResolver = (binary, args, options) => {
61+
const bin = toBinary(binary);
6062
const parsed = parseArgs(args, {
6163
boolean: ['recursive', 'silent', 'shell-mode'],
6264
alias: { recursive: 'r', silent: 's', 'shell-mode': 'c' },
6365
});
64-
const [command, binary] = parsed._;
65-
if (manifestScriptNames.has(command) || commands.includes(command)) return [];
66+
const [command, __binary] = parsed._;
67+
68+
if (command === 'dlx') {
69+
const argsForDlx = args.filter(arg => arg !== 'dlx');
70+
return [toBinary(binary), ...resolveDlx(argsForDlx, options)];
71+
}
72+
73+
const { manifestScriptNames, fromArgs } = options;
74+
if (manifestScriptNames.has(command) || commands.includes(command)) return [bin];
6675
if (command === 'exec') {
67-
if (parsed._.length > 2) return [toBinary(binary), ...fromArgs(parsed._.slice(1))];
68-
return [toBinary(binary)];
76+
if (parsed._.length > 2) return [bin, toBinary(__binary), ...fromArgs(parsed._.slice(1))];
77+
return [bin, toBinary(__binary)];
6978
}
70-
return command ? [toBinary(command)] : [];
79+
return command ? [bin, toBinary(command)] : [bin];
7180
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import parseArgs from 'minimist';
2+
import type { BinaryResolver, BinaryResolverOptions } from '../../types/config.js';
3+
import { toBinary, toDependency } from '../../util/input.js';
4+
import { stripVersionFromSpecifier } from '../../util/modules.js';
5+
6+
export const resolveDlx = (args: string[], options: BinaryResolverOptions) => {
7+
const parsed = parseArgs(args, {
8+
boolean: ['silent'],
9+
alias: { package: 'p', 'shell-mode': 'c' },
10+
});
11+
const packageSpecifier = parsed._[0];
12+
const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';
13+
const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];
14+
const command = parsed['shell-mode'] ? options.fromArgs([parsed['shell-mode']]) : [];
15+
const dependency = specifier ? [toDependency(specifier, { optional: true })] : [];
16+
return [...dependency, ...packages.map(id => toDependency(id, { optional: true })), ...command];
17+
};
18+
19+
export const resolve: BinaryResolver = (_binary, args, options) => {
20+
return [toBinary(_binary), ...resolveDlx(args, options)];
21+
};

‎packages/knip/src/plugins/capacitor/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const resolveConfig: ResolveConfig<CapacitorConfig> = async (config, { configFil
2222
const android = (await exists('android/capacitor.settings.gradle')) ? ['@capacitor/android'] : [];
2323
const ios = (await exists('ios/App/Podfile')) ? ['@capacitor/ios'] : [];
2424

25-
return [...plugins, ...android, ...ios].map(toDependency);
25+
return [...plugins, ...android, ...ios].map(id => toDependency(id));
2626
};
2727

2828
export default {

‎packages/knip/src/plugins/changesets/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const resolveConfig: ResolveConfig<ChangesetsConfig> = config => {
2222
: typeof config.changelog === 'string'
2323
? [config.changelog]
2424
: []
25-
).map(toDependency);
25+
).map(id => toDependency(id));
2626
};
2727

2828
export default {

‎packages/knip/src/plugins/commitlint/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const resolveConfig: ResolveConfig<CommitLintConfig> = async config => {
3232
? [parserPreset.path ?? parserPreset]
3333
: []
3434
: [];
35-
return [...extendsConfigs, ...plugins, ...formatter, ...parserPresetPaths].map(toDependency);
35+
return [...extendsConfigs, ...plugins, ...formatter, ...parserPresetPaths].map(id => toDependency(id));
3636
};
3737

3838
export default {

‎packages/knip/src/plugins/expo/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const getDependencies = async (expoConfig: ExpoConfig, { manifest }: Plug
1818
})
1919
.filter(Boolean) as string[]) ?? [];
2020

21-
const inputs = new Set<Input>(pluginPackages.map(toDependency));
21+
const inputs = new Set<Input>(pluginPackages.map(id => toDependency(id)));
2222

2323
const allowedPackages = ['expo-atlas', 'expo-dev-client'];
2424
const allowedProductionPackages = ['expo-insights'];

‎packages/knip/src/plugins/gatsby/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const resolveConfig: ResolveConfig<GatsbyConfig | GatsbyNode> = async (localConf
3939
if (typeof _config.onCreateBabelConfig === 'function') {
4040
_config.onCreateBabelConfig({ actions });
4141
}
42-
return Array.from(plugins).map(toDependency);
42+
return Array.from(plugins).map(id => toDependency(id));
4343
}
4444

4545
return [];

‎packages/knip/src/plugins/graphql-codegen/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const resolveConfig: ResolveConfig<GraphqlCodegenTypes | GraphqlConfigTypes | Gr
7878
.flatMap(plugin => {
7979
if (typeof plugin !== 'string') return [];
8080
if (isInternal(plugin)) return [toEntry(plugin)];
81-
return [plugin.includes('codegen-') ? plugin : `@graphql-codegen/${plugin}`].map(toDependency);
81+
return [plugin.includes('codegen-') ? plugin : `@graphql-codegen/${plugin}`].map(id => toDependency(id));
8282
});
8383

8484
return [...presets, ...flatPlugins, ...nestedPlugins].map(id => (typeof id === 'string' ? toDependency(id) : id));

‎packages/knip/src/plugins/lefthook/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type Command = {
2525
const resolveConfig: ResolveConfig = async (localConfig, options) => {
2626
const { manifest, configFileName, cwd, getInputsFromScripts } = options;
2727

28-
const inputs = manifest.devDependencies ? Object.keys(manifest.devDependencies).map(toDependency) : [];
28+
const inputs = manifest.devDependencies ? Object.keys(manifest.devDependencies).map(id => toDependency(id)) : [];
2929

3030
if (extname(configFileName) === '.yml') {
3131
const scripts = findByKeyDeep<Command>(localConfig, 'run').flatMap(command => {
@@ -35,7 +35,7 @@ const resolveConfig: ResolveConfig = async (localConfig, options) => {
3535
});
3636

3737
const lefthook = process.env.CI
38-
? enablers.filter(dependency => inputs.some(d => d.specifier === dependency)).map(toDependency)
38+
? enablers.filter(dependency => inputs.some(d => d.specifier === dependency)).map(id => toDependency(id))
3939
: [];
4040

4141
return [...scripts, ...lefthook];

‎packages/knip/src/plugins/markdownlint/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const resolveConfig: ResolveConfig<MarkdownlintConfig> = (config, options) => {
2323
const uses = scripts
2424
.filter(script => script.includes('markdownlint '))
2525
.flatMap(script => getArgumentValues(script, / (--rules|-r)[ =]([^ ]+)/g));
26-
return [...extend, ...uses].map(toDependency);
26+
return [...extend, ...uses].map(id => toDependency(id));
2727
};
2828

2929
export default {

‎packages/knip/src/plugins/nest/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const config = ['nest-cli.json', '.nestcli.json', '.nest-cli.json', 'nest.json']
1515

1616
const resolveConfig: ResolveConfig<NestConfig> = async config => {
1717
const inputs = config?.collection ? [config.collection] : [];
18-
return [...inputs].map(toDependency);
18+
return [...inputs].map(id => toDependency(id));
1919
};
2020

2121
export default {

‎packages/knip/src/plugins/netlify/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ const resolveEntryPaths: ResolveEntryPaths<NetlifyConfig> = localConfig => {
3232

3333
const resolveConfig: ResolveConfig<NetlifyConfig> = async localConfig => {
3434
return [
35-
...(localConfig?.plugins?.map(plugin => plugin.package) ?? []).map(toDependency),
36-
...extractFunctionsConfigProperty(localConfig.functions || {}, 'external_node_modules').map(toDependency),
35+
...(localConfig?.plugins?.map(plugin => plugin.package) ?? []).map(id => toDependency(id)),
36+
...extractFunctionsConfigProperty(localConfig.functions || {}, 'external_node_modules').map(id => toDependency(id)),
3737
];
3838
};
3939

‎packages/knip/src/plugins/npm-package-json-lint/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const packageJsonPath = 'npmpackagejsonlint';
1717
const config = ['package.json', ...toCosmiconfig('npmpackagejsonlint')];
1818

1919
const resolveConfig: ResolveConfig<NpmPkgJsonLintConfig> = localConfig => {
20-
return localConfig?.extends ? [localConfig.extends].map(toDependency) : [];
20+
return localConfig?.extends ? [localConfig.extends].map(id => toDependency(id)) : [];
2121
};
2222

2323
export default {

‎packages/knip/src/plugins/nx/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const findNxDependenciesInNxJson: ResolveConfig<NxConfigRoot> = async localConfi
3535
.map(value => value.split(':')[0])
3636
: [];
3737

38-
return compact([...targetsDefault, ...plugins, ...generators]).map(toDependency);
38+
return compact([...targetsDefault, ...plugins, ...generators]).map(id => toDependency(id));
3939
};
4040

4141
const resolveConfig: ResolveConfig<NxProjectConfiguration | NxConfigRoot> = async (localConfig, options) => {

‎packages/knip/src/plugins/oclif/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const config = ['package.json'];
1616
const resolveConfig: ResolveConfig<OclifConfig> = async config => {
1717
const plugins = config?.plugins ?? [];
1818
const devPlugins = config?.devPlugins ?? [];
19-
return [...plugins, ...devPlugins].map(toDependency);
19+
return [...plugins, ...devPlugins].map(id => toDependency(id));
2020
};
2121

2222
export default {

‎packages/knip/src/plugins/prettier/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const resolveConfig: ResolveConfig<PrettierConfig> = config => {
2424
if (typeof config === 'string') return [toDeferResolve(config)];
2525

2626
return Array.isArray(config.plugins)
27-
? config.plugins.filter((plugin): plugin is string => typeof plugin === 'string').map(toDependency)
27+
? config.plugins.filter((plugin): plugin is string => typeof plugin === 'string').map(id => toDependency(id))
2828
: [];
2929
};
3030

‎packages/knip/src/plugins/release-it/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const resolveConfig: ResolveConfig<ReleaseItConfig> = (config, options) => {
2626
}
2727
const inputs = options.getInputsFromScripts(scripts);
2828

29-
return [...plugins.map(toDependency), ...inputs];
29+
return [...plugins.map(id => toDependency(id)), ...inputs];
3030
};
3131

3232
export default {

‎packages/knip/src/plugins/storybook/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ const resolveConfig: ResolveConfig<StorybookConfig> = async localConfig => {
4444
: [builder]
4545
: [];
4646
const frameworks = localConfig.framework?.name ? [localConfig.framework.name] : [];
47-
return [...addons.map(toDeferResolve), ...builderPackages.map(toDependency), ...frameworks.map(toDependency)];
47+
return [
48+
...addons.map(toDeferResolve),
49+
...builderPackages.map(id => toDependency(id)),
50+
...frameworks.map(id => toDependency(id)),
51+
];
4852
};
4953

5054
export default {

‎packages/knip/src/plugins/vitest/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const findConfigDependencies = (localConfig: ViteConfig, options: PluginOptions)
4545
const setupFiles = [testConfig.setupFiles ?? []].flat().map(specifier => ({ ...toDeferResolve(specifier), dir }));
4646
const globalSetup = [testConfig.globalSetup ?? []].flat().map(specifier => ({ ...toDeferResolve(specifier), dir }));
4747

48-
return [...[...environments, ...reporters, ...coverage].map(toDependency), ...setupFiles, ...globalSetup];
48+
return [...[...environments, ...reporters, ...coverage].map(id => toDependency(id)), ...setupFiles, ...globalSetup];
4949
};
5050

5151
const getConfigs = async (localConfig: ViteConfigOrFn | VitestWorkspaceConfig) => {

‎packages/knip/src/plugins/webdriver-io/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const resolveConfig: ResolveConfig<WebdriverIOConfig> = async config => {
3434
.map(reporter => `@wdio/${reporter}-reporter`)
3535
: [];
3636

37-
return [...frameworks, ...runners, ...reporters].map(toDependency);
37+
return [...frameworks, ...runners, ...reporters].map(id => toDependency(id));
3838
};
3939

4040
export default {

‎packages/knip/src/types/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export type GetInputsFromScriptsPartial = (
2626

2727
type FromArgs = (args: string[]) => Input[];
2828

29-
interface BinaryResolverOptions extends GetInputsFromScriptsOptions {
29+
export interface BinaryResolverOptions extends GetInputsFromScriptsOptions {
3030
fromArgs: FromArgs;
3131
}
3232

‎packages/knip/src/util/get-referenced-inputs.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const getReferencedInputsHandler =
5959

6060
if (isWorkspace || isDependency(input)) {
6161
if (!isHandled) {
62-
if ((deputy.isProduction && input.production) || !deputy.isProduction) {
62+
if (!input.optional && ((deputy.isProduction && input.production) || !deputy.isProduction)) {
6363
// Unlisted dependency
6464
collector.addIssue({
6565
type: 'unlisted',
@@ -93,6 +93,8 @@ export const getReferencedInputsHandler =
9393
return isGitIgnored(resolvedFilePath) ? undefined : resolvedFilePath;
9494
}
9595

96+
if (input.optional) return;
97+
9698
if (!isInternal(filePath)) {
9799
collector.addIssue({
98100
type: 'unlisted',

‎packages/knip/src/util/input.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface Input {
77
type: InputType;
88
specifier: string;
99
production?: boolean;
10+
optional?: boolean;
1011
dir?: string;
1112
containingFilePath?: string;
1213
}
@@ -19,6 +20,7 @@ export interface ConfigInput extends Input {
1920

2021
type Options = {
2122
production?: boolean;
23+
optional?: boolean;
2224
dir?: string;
2325
containingFilePath?: string;
2426
};
@@ -55,7 +57,11 @@ export const toConfig = (pluginName: PluginName, specifier: string, containingFi
5557

5658
export const isConfigPattern = (input: Input): input is ConfigInput => input.type === 'config';
5759

58-
export const toDependency = (specifier: string): Input => ({ type: 'dependency', specifier });
60+
export const toDependency = (specifier: string, options: Options = {}): Input => ({
61+
type: 'dependency',
62+
specifier,
63+
...options,
64+
});
5965

6066
export const isDependency = (input: Input) => input.type === 'dependency';
6167

‎packages/knip/test/plugins/github-actions.test.ts

-4
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ test('Find dependencies with the GitHub Actions plugin', async () => {
1313
cwd,
1414
});
1515

16-
assert(issues.unlisted['.github/workflows/test.yml']['@scope/retry-cli']);
17-
assert(issues.unlisted['.github/workflows/test.yml']['retry-cli']);
18-
1916
assert(issues.unresolved['.github/workflows/test.yml']['esbuild-register']);
2017

2118
// Let's start out conservatively
@@ -37,7 +34,6 @@ test('Find dependencies with the GitHub Actions plugin', async () => {
3734
assert.deepEqual(counters, {
3835
...baseCounters,
3936
binaries: 10,
40-
unlisted: 2,
4137
unresolved: 2,
4238
processed: 10,
4339
total: 10,

‎packages/knip/test/util/get-inputs-from-scripts.test.ts

+35-20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const cwd = resolve('fixtures/binaries');
88
const containingFilePath = join(cwd, 'package.json');
99
const pkgScripts = { cwd, manifestScriptNames: new Set(['program']) };
1010
const knownOnly = { cwd, knownBinsOnly: true };
11+
const optional = { optional: true };
1112

1213
const js = toDeferResolveEntry('./script.js');
1314
const ts = toDeferResolveEntry('./main.ts');
@@ -132,41 +133,55 @@ test('getInputsFromScripts (npm)', () => {
132133
test('getInputsFromScripts (npx)', () => {
133134
t('npx pkg', [toBinary('npx'), toBinary('pkg')]);
134135
t('npx prisma migrate reset --force', [toBinary('npx'), toBinary('prisma')]);
135-
t('npx @scope/pkg', [toBinary('npx'), toDependency('@scope/pkg')]);
136+
t('npx @scope/pkg', [toBinary('npx'), toDependency('@scope/pkg', optional)]);
136137
t('npx tsx watch main', [toBinary('npx'), toBinary('tsx'), toDeferResolveEntry('main')]);
137138
t('npx -y pkg', [toBinary('npx')]);
138139
t('npx --yes pkg', [toBinary('npx')]);
139140
t('npx --no pkg --edit ${1}', [toBinary('npx'), toBinary('pkg')]);
140141
t('npx --no -- pkg --edit ${1}', [toBinary('npx'), toBinary('pkg')]);
141142
t('npx pkg install --with-deps', [toBinary('npx'), toBinary('pkg')]);
142143
t('npx pkg migrate reset --force', [toBinary('npx'), toBinary('pkg')]);
143-
t('npx pkg@1.0.0 migrate reset --force', [toBinary('npx'), toDependency('pkg')]);
144-
t('npx @scope/cli migrate reset --force', [toBinary('npx'), toDependency('@scope/cli')]);
144+
t('npx pkg@1.0.0 migrate reset --force', [toBinary('npx'), toDependency('pkg', optional)]);
145+
t('npx @scope/cli migrate reset --force', [toBinary('npx'), toDependency('@scope/cli', optional)]);
145146
t('npx -- pkg', [toBinary('npx'), toBinary('pkg')]);
146-
t('npx -- @scope/cli@1.0.0 migrate reset --force', [toBinary('npx'), toDependency('@scope/cli')]);
147-
t('npx retry-cli@0.6.0 -- curl --output /dev/null ', [toBinary('npx'), toDependency('retry-cli'), toBinary('curl')]);
148-
t('npx --package pkg@0.6.0 -- curl --output /dev/null', [toBinary('npx'), toBinary('curl'), toDependency('pkg')]);
149-
t('npx --package @scope/pkg@0.6.0 --package pkg -- curl', [toBinary('npx'), toBinary('curl'), toDependency('@scope/pkg'), toDependency('pkg')]);
150-
t("npx --package=foo -c 'curl --output /dev/null'", [toBinary('npx'), toDependency('foo'), toBinary('curl')]);
147+
t('npx -- @scope/cli@1.0.0 migrate reset --force', [toBinary('npx'), toDependency('@scope/cli', optional)]);
148+
t('npx retry-cli@0.6.0 -- curl --output /dev/null ', [toBinary('npx'), toDependency('retry-cli', optional), toBinary('curl')]);
149+
t('npx --package pkg@0.6.0 -- curl --output /dev/null', [toBinary('npx'), toBinary('curl'), toDependency('pkg', optional)]);
150+
t('npx --package @scope/pkg@0.6.0 --package pkg -- curl', [toBinary('npx'), toBinary('curl'), toDependency('@scope/pkg', optional), toDependency('pkg', optional)]);
151+
t("npx --package=foo -c 'curl --output /dev/null'", [toBinary('npx'), toDependency('foo', optional), toBinary('curl')]);
151152
t('npx swagger-typescript-api -p http://localhost:3030/swagger.v1.json', [toBinary('npx'), toBinary('swagger-typescript-api')]);
152153
t('npx swagger-typescript-api -- -p http://localhost:3030/swagger.v1.json', [toBinary('npx'), toBinary('swagger-typescript-api')]);
153154
t('npx tsx main', [toBinary('npx'), toBinary('tsx'), toDeferResolveEntry('main')]);
154155
t('npx tsx ./main.ts build', [toBinary('npx'), toBinary('tsx'), ts]);
155156
t('npx tsx ./main.ts -- build', [toBinary('npx'), toBinary('tsx'), ts]);
156157
});
157158

159+
test('getInputsFromScripts (pnpx/pnpm dlx)', () => {
160+
t('pnpx pkg', [toBinary('pnpx'), toDependency('pkg', optional)]);
161+
const s = [toDependency('cowsay', optional), toDependency('lolcatjs', optional), toBinary('echo'), toBinary('cowsay'), toBinary('lolcatjs')];
162+
t('pnpx --package cowsay --package lolcatjs -c \'echo "hi pnpm" | cowsay | lolcatjs\'', [toBinary('pnpx'), ...s]);
163+
t('pnpm --package cowsay --package lolcatjs -c dlx \'echo "hi pnpm" | cowsay | lolcatjs\'', [toBinary('pnpm'), ...s]);
164+
});
165+
166+
test('getInputsFromScripts (bunx/bun x)', () => {
167+
t('bunx pkg', [toBinary('bunx'), toDependency('pkg', optional)]);
168+
t('bunx cowsay "Hello world!"', [toBinary('bunx'), toDependency('cowsay', optional)]);
169+
t('bunx my-cli --foo bar', [toBinary('bunx'), toDependency('my-cli', optional)]);
170+
t('bun x pkg', [toBinary('bun'), toDependency('pkg', optional)]);
171+
});
172+
158173
test('getInputsFromScripts (pnpm)', () => {
159-
t('pnpm exec program', [toBinary('program')]);
160-
t('pnpm run program', []);
161-
t('pnpm program', [toBinary('program')]);
162-
t('pnpm run program', [], pkgScripts);
163-
t('pnpm program', [], pkgScripts);
164-
t('pnpm dlx pkg', []);
165-
t('pnpm --package=pkg-a dlx pkg', []);
166-
t('pnpm --recursive --parallel test -- --sequence.seed=1700316221712', []);
167-
t('pnpm program script.js', [], pkgScripts);
168-
t('pnpm --silent program script.js', [], pkgScripts);
169-
t('pnpm --silent run program script.js', [], pkgScripts);
174+
t('pnpm exec program', [toBinary('pnpm'), toBinary('program')]);
175+
t('pnpm run program', [toBinary('pnpm')]);
176+
t('pnpm program', [toBinary('pnpm'), toBinary('program')]);
177+
t('pnpm run program', [toBinary('pnpm')], pkgScripts);
178+
t('pnpm program', [toBinary('pnpm')], pkgScripts);
179+
t('pnpm dlx pkg', [toBinary('pnpm'), toDependency('pkg', optional)]);
180+
t('pnpm --package=pkg-a dlx pkg', [toBinary('pnpm'), toDependency('pkg', optional), toDependency('pkg-a', optional)]);
181+
t('pnpm --recursive --parallel test -- --sequence.seed=1700316221712', [toBinary('pnpm')]);
182+
t('pnpm program script.js', [toBinary('pnpm')], pkgScripts);
183+
t('pnpm --silent program script.js', [toBinary('pnpm')], pkgScripts);
184+
t('pnpm --silent run program script.js', [toBinary('pnpm')], pkgScripts);
170185
});
171186

172187
test('getInputsFromScripts (yarn)', () => {
@@ -229,7 +244,7 @@ test('getInputsFromScripts (bash expansion)', () => {
229244

230245
test('getInputsFromScripts (multiline)', () => {
231246
t('#!/bin/sh\n. "$(dirname "$0")/_/husky.sh"\nnpx lint-staged', [toBinary('npx'), toBinary('lint-staged')]);
232-
t(`for S in "s"; do\n\tnpx rc@0.6.0\n\tnpx @scope/rc@0.6.0\ndone`, [toBinary('npx'), toDependency('rc'), toBinary('npx'), toDependency('@scope/rc')]);
247+
t(`for S in "s"; do\n\tnpx rc@0.6.0\n\tnpx @scope/rc@0.6.0\ndone`, [toBinary('npx'), toDependency('rc', optional), toBinary('npx'), toDependency('@scope/rc', optional)]);
233248
});
234249

235250
test('getInputsFromScripts (bail outs)', () => {

0 commit comments

Comments
 (0)
Please sign in to comment.