knip 5.42.1 → 5.42.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -171,7 +171,7 @@ export class WorkspaceWorker {
171
171
  const inputsFromManifest = _getInputsFromScripts(Object.values(developmentScripts), baseOptions);
172
172
  const productionInputsFromManifest = _getInputsFromScripts(Object.values(productionScripts), baseOptions);
173
173
  const hasProductionInput = (input) => productionInputsFromManifest.find(d => d.specifier === input.specifier && d.type === input.type);
174
- const getInputsFromScripts = (scripts, options) => _getInputsFromScripts(scripts, { ...baseScriptOptions, ...options });
174
+ const createGetInputsFromScripts = (containingFilePath) => (scripts, options) => _getInputsFromScripts(scripts, { ...baseOptions, ...options, containingFilePath });
175
175
  const inputs = [];
176
176
  const configFiles = new Map();
177
177
  const remainingPlugins = new Set(this.enabledPlugins);
@@ -217,12 +217,13 @@ export class WorkspaceWorker {
217
217
  configFilePath: containingFilePath,
218
218
  configFileDir: cwd,
219
219
  configFileName: '',
220
- getInputsFromScripts,
220
+ getInputsFromScripts: createGetInputsFromScripts(containingFilePath),
221
221
  };
222
222
  const configEntryPaths = [];
223
223
  for (const configFilePath of remainingConfigFilePaths) {
224
224
  const opts = {
225
225
  ...options,
226
+ getInputsFromScripts: createGetInputsFromScripts(configFilePath),
226
227
  configFilePath,
227
228
  configFileDir: dirname(configFilePath),
228
229
  configFileName: basename(configFilePath),
@@ -251,8 +252,9 @@ export class WorkspaceWorker {
251
252
  if (hasResolveConfig) {
252
253
  const inputs = (await plugin.resolveConfig?.(config, opts)) ?? [];
253
254
  for (const input of inputs) {
254
- if (isConfigPattern(input))
255
+ if (isConfigPattern(input)) {
255
256
  handleConfigInput(input.pluginName, { ...input, containingFilePath: configFilePath });
257
+ }
256
258
  addInput(input, configFilePath);
257
259
  }
258
260
  data.resolveConfig = inputs;
@@ -3,6 +3,8 @@ import { Plugins, pluginArgsMap } from '../plugins.js';
3
3
  import { debugLogObject } from '../util/debug.js';
4
4
  import { toBinary, toDeferResolve } from '../util/input.js';
5
5
  import { extractBinary } from '../util/modules.js';
6
+ import { relative } from '../util/path.js';
7
+ import { truncate } from '../util/string.js';
6
8
  import { resolve as fallbackResolve } from './fallback.js';
7
9
  import PackageManagerResolvers from './package-manager/index.js';
8
10
  import { resolve as resolverFromPlugins } from './plugins.js';
@@ -85,7 +87,8 @@ export const getDependenciesFromScript = (script, options) => {
85
87
  return parsed?.commands ? getDependenciesFromNodes(parsed.commands) : [];
86
88
  }
87
89
  catch (error) {
88
- debugLogObject('*', 'Bash parser error', error);
90
+ const msg = `Warning: failed to parse and ignoring script in ${relative(options.containingFilePath)} (${truncate(script, 30)})`;
91
+ debugLogObject('*', msg, error);
89
92
  return [];
90
93
  }
91
94
  };
@@ -1,5 +1,6 @@
1
1
  import parseArgs from 'minimist';
2
- import { toBinary } from '../../util/input.js';
2
+ import { isBinary, isDependency, toBinary, toDependency } from '../../util/input.js';
3
+ import { stripVersionFromSpecifier } from '../../util/modules.js';
3
4
  import { join } from '../../util/path.js';
4
5
  const commands = [
5
6
  'add',
@@ -34,17 +35,39 @@ const commands = [
34
35
  'workspace',
35
36
  'workspaces',
36
37
  ];
37
- export const resolve = (_binary, args, { manifestScriptNames, fromArgs, cwd, rootCwd }) => {
38
+ const resolveDlx = (args, options) => {
39
+ const parsed = parseArgs(args, {
40
+ boolean: ['quiet'],
41
+ alias: { package: 'p', quiet: 'q' },
42
+ });
43
+ const packageSpecifier = parsed._[0];
44
+ const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';
45
+ const packages = parsed.package && !parsed.yes ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];
46
+ const command = specifier ? options.fromArgs(parsed._) : [];
47
+ return [...packages.map(id => toDependency(id)), ...command].map(id => isDependency(id) || isBinary(id) ? Object.assign(id, { optional: true }) : id);
48
+ };
49
+ export const resolve = (_binary, args, options) => {
50
+ const { manifestScriptNames, fromArgs, cwd, rootCwd } = options;
38
51
  const parsed = parseArgs(args, { boolean: ['top-level'], string: ['cwd'] });
39
- const [command, binary] = parsed._;
40
52
  const dir = parsed['top-level'] ? rootCwd : parsed.cwd ? join(cwd, parsed.cwd) : undefined;
41
- if ((!dir && manifestScriptNames.has(command)) || commands.includes(command))
42
- return [];
43
- if (!dir && command === 'run' && manifestScriptNames.has(binary))
44
- return [];
53
+ const [command, binary] = parsed._;
54
+ if (command === 'run') {
55
+ if (manifestScriptNames.has(binary))
56
+ return [];
57
+ const bin = toBinary(binary, { optional: true });
58
+ if (dir)
59
+ Object.assign(bin, { dir });
60
+ return [bin];
61
+ }
45
62
  if (command === 'node')
46
63
  return fromArgs(parsed._);
47
- const bin = command === 'run' || command === 'exec' ? toBinary(binary) : toBinary(command);
64
+ if (command === 'dlx') {
65
+ const argsForDlx = args.filter(arg => arg !== 'dlx');
66
+ return resolveDlx(argsForDlx, options);
67
+ }
68
+ if ((!dir && manifestScriptNames.has(command)) || commands.includes(command))
69
+ return [];
70
+ const bin = command === 'exec' ? toBinary(binary) : toBinary(command);
48
71
  if (dir)
49
72
  Object.assign(bin, { dir });
50
73
  return [bin];
package/dist/index.js CHANGED
@@ -252,18 +252,18 @@ export const main = async (unresolvedConfiguration) => {
252
252
  if (scripts && scripts.size > 0) {
253
253
  const dependencies = deputy.getDependencies(workspace.name);
254
254
  const manifestScriptNames = new Set(Object.keys(chief.getManifestForWorkspace(workspace.name)?.scripts ?? {}));
255
- const rootCwd = cwd;
255
+ const dir = dirname(filePath);
256
256
  const options = {
257
- cwd: dirname(filePath),
258
- rootCwd,
257
+ cwd: dir,
258
+ rootCwd: cwd,
259
259
  containingFilePath: filePath,
260
260
  dependencies,
261
261
  manifestScriptNames,
262
262
  };
263
263
  const inputs = _getInputsFromScripts(scripts, options);
264
264
  for (const input of inputs) {
265
- input.containingFilePath = filePath;
266
- input.dir = cwd;
265
+ input.containingFilePath ??= filePath;
266
+ input.dir ??= dir;
267
267
  const specifierFilePath = getReferencedInternalFilePath(input, workspace);
268
268
  if (specifierFilePath)
269
269
  analyzeSourceFile(specifierFilePath, principal);
@@ -1,5 +1,6 @@
1
- import { toConfig, toDependency, toEntry, toProductionEntry } from '../../util/input.js';
2
- import { join } from '../../util/path.js';
1
+ import { existsSync } from 'node:fs';
2
+ import { toConfig, toDeferResolve, toDependency, toEntry, toProductionEntry } from '../../util/input.js';
3
+ import { isInternal, join } from '../../util/path.js';
3
4
  import { hasDependency } from '../../util/plugin.js';
4
5
  import * as karma from '../karma/helpers.js';
5
6
  const title = 'Angular';
@@ -27,21 +28,31 @@ const resolveConfig = async (config, options) => {
27
28
  const defaultEntriesByOption = opts ? entriesByOption(opts) : new Map();
28
29
  const entriesByOptionByConfig = new Map(configs ? Object.entries(configs).map(([name, opts]) => [name, entriesByOption(opts)]) : []);
29
30
  const productionEntriesByOption = entriesByOptionByConfig.get(PRODUCTION_CONFIG_NAME) ?? new Map();
30
- const normalizePath = (path) => join(cwd, path);
31
+ const isBuildTarget = targetName === BUILD_TARGET_NAME;
32
+ const maybeExternal = (option) => option === 'polyfills';
33
+ const toInput = (specifier, opts) => {
34
+ const normalizedPath = join(cwd, specifier);
35
+ if (opts.maybeExternal && !isInternal(specifier) && !existsSync(normalizedPath)) {
36
+ return toDeferResolve(specifier);
37
+ }
38
+ return opts.isProduction ? toProductionEntry(normalizedPath) : toEntry(normalizedPath);
39
+ };
31
40
  for (const [configName, entriesByOption] of entriesByOptionByConfig.entries()) {
32
- for (const entries of entriesByOption.values()) {
41
+ for (const [option, entries] of entriesByOption.entries()) {
33
42
  for (const entry of entries) {
34
- inputs.add(targetName === BUILD_TARGET_NAME && configName === PRODUCTION_CONFIG_NAME
35
- ? toProductionEntry(normalizePath(entry))
36
- : toEntry(normalizePath(entry)));
43
+ inputs.add(toInput(entry, {
44
+ isProduction: isBuildTarget && configName === PRODUCTION_CONFIG_NAME,
45
+ maybeExternal: maybeExternal(option),
46
+ }));
37
47
  }
38
48
  }
39
49
  }
40
50
  for (const [option, entries] of defaultEntriesByOption.entries()) {
41
51
  for (const entry of entries) {
42
- inputs.add(targetName === BUILD_TARGET_NAME && !productionEntriesByOption.get(option)?.length
43
- ? toProductionEntry(normalizePath(entry))
44
- : toEntry(normalizePath(entry)));
52
+ inputs.add(toInput(entry, {
53
+ isProduction: isBuildTarget && !productionEntriesByOption.get(option)?.length,
54
+ maybeExternal: maybeExternal(option),
55
+ }));
45
56
  }
46
57
  }
47
58
  if (target.builder === '@angular-devkit/build-angular:karma' && opts) {
@@ -74,6 +85,11 @@ const entriesByOption = (opts) => new Map(Object.entries({
74
85
  scripts: 'scripts' in opts && opts.scripts && Array.isArray(opts.scripts)
75
86
  ? opts.scripts.map(scriptStringOrObject => typeof scriptStringOrObject === 'string' ? scriptStringOrObject : scriptStringOrObject.input)
76
87
  : [],
88
+ polyfills: 'polyfills' in opts && opts.polyfills
89
+ ? Array.isArray(opts.polyfills)
90
+ ? opts.polyfills
91
+ : [opts.polyfills]
92
+ : [],
77
93
  fileReplacements: 'fileReplacements' in opts && opts.fileReplacements && Array.isArray(opts.fileReplacements)
78
94
  ? opts.fileReplacements.map(fileReplacement => 'with' in fileReplacement ? fileReplacement.with : fileReplacement.replaceWith)
79
95
  : [],
@@ -1,4 +1,4 @@
1
1
  import type { PluginOptions } from '../../types/config.js';
2
2
  import { type Input } from '../../util/input.js';
3
3
  import type { ExpoConfig } from './types.js';
4
- export declare const getDependencies: (expoConfig: ExpoConfig, { manifest }: PluginOptions) => Promise<Input[]>;
4
+ export declare const getDependencies: (localConfig: ExpoConfig, { manifest }: PluginOptions) => Promise<Input[]>;
@@ -1,6 +1,7 @@
1
1
  import { toDependency, toProductionDependency } from '../../util/input.js';
2
2
  import { getPackageNameFromModuleSpecifier } from '../../util/modules.js';
3
- export const getDependencies = async (expoConfig, { manifest }) => {
3
+ export const getDependencies = async (localConfig, { manifest }) => {
4
+ const expoConfig = typeof localConfig === 'function' ? localConfig() : localConfig;
4
5
  const config = 'expo' in expoConfig ? expoConfig.expo : expoConfig;
5
6
  const platforms = config.platforms ?? ['ios', 'android'];
6
7
  const pluginPackages = config.plugins
@@ -8,7 +8,8 @@ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
8
  const config = ['app.json', 'app.config.{ts,js}'];
9
9
  const production = ['app/**/*.{js,jsx,ts,tsx}', 'src/app/**/*.{js,jsx,ts,tsx}'];
10
10
  export const docs = { production };
11
- const resolveEntryPaths = async (expoConfig, { manifest }) => {
11
+ const resolveEntryPaths = async (localConfig, { manifest }) => {
12
+ const expoConfig = typeof localConfig === 'function' ? localConfig() : localConfig;
12
13
  const config = 'expo' in expoConfig ? expoConfig.expo : expoConfig;
13
14
  let patterns = [];
14
15
  if (manifest.main === 'expo-router/entry') {
@@ -15,7 +15,8 @@ type AppConfig = {
15
15
  androidNavigationBar?: Record<string, unknown>;
16
16
  plugins?: (string | [string, Record<string, unknown>])[];
17
17
  };
18
- export type ExpoConfig = AppConfig | {
18
+ type Config = AppConfig | {
19
19
  expo: AppConfig;
20
20
  };
21
+ export type ExpoConfig = Config | (() => Config);
21
22
  export {};
@@ -8,8 +8,17 @@ const isEnabled = async ({ cwd }) => Boolean(await _firstGlob({ cwd, patterns: [
8
8
  const isRootOnly = true;
9
9
  const config = ['.github/workflows/*.{yml,yaml}', '.github/**/action.{yml,yaml}'];
10
10
  const isString = (value) => typeof value === 'string';
11
+ const getActionDependencies = (config, options) => {
12
+ const { configFileDir, configFileName } = options;
13
+ const isActionManifest = configFileName === 'action.yml' || configFileName === 'action.yaml';
14
+ if (!(isActionManifest && config?.runs?.using?.startsWith('node')))
15
+ return [];
16
+ const runs = config.runs;
17
+ const scripts = [runs.pre, runs.main, runs.post].filter(isString);
18
+ return scripts.map(script => join(configFileDir, script));
19
+ };
11
20
  const resolveConfig = async (config, options) => {
12
- const { configFileDir, configFileName, rootCwd, getInputsFromScripts } = options;
21
+ const { rootCwd, getInputsFromScripts, isProduction } = options;
13
22
  const inputs = new Set();
14
23
  const jobs = findByKeyDeep(config, 'steps');
15
24
  for (const steps of jobs) {
@@ -22,21 +31,17 @@ const resolveConfig = async (config, options) => {
22
31
  const dir = join(rootCwd, path && workingDir ? relative(workingDir, path) : workingDir ? workingDir : '.');
23
32
  if (step.run) {
24
33
  for (const input of getInputsFromScripts([step.run], { knownBinsOnly: true })) {
25
- if (isDeferResolveEntry(input) && path && !workingDir)
34
+ if (isDeferResolveEntry(input) && path && !workingDir) {
26
35
  input.specifier = relative(join(dir, path), join(rootCwd, input.specifier));
36
+ }
37
+ if (isProduction)
38
+ Object.assign(input, { optional: true });
27
39
  inputs.add({ ...input, dir });
28
40
  }
29
41
  }
30
42
  }
31
43
  }
32
- const getActionDependencies = () => {
33
- const isActionManifest = configFileName === 'action.yml' || configFileName === 'action.yaml';
34
- if (!(isActionManifest && config?.runs?.using?.startsWith('node')))
35
- return [];
36
- const scripts = [config.runs.pre, config.runs.main, config.runs.post].filter(isString);
37
- return scripts.map(script => join(configFileDir, script));
38
- };
39
- return [...getActionDependencies().map(toEntry), ...inputs];
44
+ return [...inputs, ...getActionDependencies(config, options).map(toEntry)];
40
45
  };
41
46
  export default {
42
47
  title,
@@ -0,0 +1,20 @@
1
+ type Step = {
2
+ run?: string;
3
+ uses?: string;
4
+ with?: {
5
+ repository: string;
6
+ path: string;
7
+ };
8
+ 'working-directory'?: string;
9
+ };
10
+ type Steps = Step[];
11
+ export type Job = {
12
+ steps: Steps;
13
+ };
14
+ export type Runs = {
15
+ using: string;
16
+ main?: string;
17
+ pre?: string;
18
+ post?: string;
19
+ };
20
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -29,7 +29,8 @@ const resolveConfig = async (localConfig) => {
29
29
  ? [`@storybook/builder-${builder}`, `@storybook/manager-${builder}`]
30
30
  : [builder]
31
31
  : [];
32
- const frameworks = localConfig.framework?.name ? [localConfig.framework.name] : [];
32
+ const framework = typeof localConfig.framework === 'string' ? localConfig.framework : localConfig.framework?.name;
33
+ const frameworks = framework ? [framework] : [];
33
34
  return [
34
35
  ...addons.map(toDeferResolve),
35
36
  ...builderPackages.map(id => toDependency(id)),
@@ -13,7 +13,7 @@ export type StorybookConfig = {
13
13
  name?: string;
14
14
  };
15
15
  };
16
- framework?: {
16
+ framework?: string | {
17
17
  name?: string;
18
18
  };
19
19
  };
@@ -30,5 +30,6 @@ const builtInReporters = [
30
30
  export const getExternalReporters = (reporters) => reporters
31
31
  ? [reporters]
32
32
  .flat()
33
+ .map(reporter => (Array.isArray(reporter) ? reporter[0] : reporter))
33
34
  .filter((reporter) => typeof reporter === 'string' && !builtInReporters.includes(reporter))
34
35
  : [];
@@ -8,7 +8,7 @@ interface VitestConfig {
8
8
  root?: string;
9
9
  environment?: string;
10
10
  globalSetup?: string | string[];
11
- reporters?: (string | unknown)[];
11
+ reporters?: (string | [string, unknown] | unknown)[];
12
12
  setupFiles?: string | string[];
13
13
  };
14
14
  }
@@ -2,11 +2,11 @@ import EasyTable from 'easy-table';
2
2
  import picocolors from 'picocolors';
3
3
  import { ROOT_WORKSPACE_NAME } from '../constants.js';
4
4
  import { relative, toRelative } from '../util/path.js';
5
+ import { truncate } from '../util/string.js';
5
6
  import { getTitle, identity, logTitle } from './util.js';
6
7
  const dim = picocolors.gray;
7
8
  const bright = picocolors.whiteBright;
8
9
  const TRUNCATE_WIDTH = 40;
9
- const truncate = (text) => (text.length > TRUNCATE_WIDTH ? `${text.slice(0, TRUNCATE_WIDTH - 3)}...` : text);
10
10
  const truncateStart = (text, width) => (text.length > width ? `...${text.slice(-(width - 3))}` : text);
11
11
  const sortByPos = (a, b) => {
12
12
  const [f, r, c] = a.filePath.split(':');
@@ -28,7 +28,8 @@ const logIssueRecord = (issues) => {
28
28
  const table = new EasyTable();
29
29
  for (const issue of issues) {
30
30
  const print = issue.isFixed || issue.severity === 'warn' ? dim : identity;
31
- table.cell('symbol', print(issue.symbols ? truncate(issue.symbols.map(s => s.symbol).join(', ')) : hl(issue)));
31
+ const symbols = issue.symbols;
32
+ table.cell('symbol', print(symbols ? truncate(symbols.map(s => s.symbol).join(', '), TRUNCATE_WIDTH) : hl(issue)));
32
33
  issue.parentSymbol && table.cell('parentSymbol', print(issue.parentSymbol));
33
34
  issue.symbolType && table.cell('symbolType', print(issue.symbolType));
34
35
  const pos = issue.line === undefined ? '' : `:${issue.line}${issue.col === undefined ? '' : `:${issue.col}`}`;
@@ -16,7 +16,7 @@ export const getReferencedInputsHandler = (collector, deputy, chief, isGitIgnore
16
16
  const binaryName = fromBinary(input);
17
17
  const inputWorkspace = getWorkspaceFor(input, chief, workspace);
18
18
  const isHandled = deputy.maybeAddReferencedBinary(inputWorkspace, binaryName);
19
- if (isHandled)
19
+ if (isHandled || input.optional)
20
20
  return;
21
21
  collector.addIssue({
22
22
  type: 'binaries',
@@ -0,0 +1 @@
1
+ export declare const truncate: (text: string, width: number) => string;
@@ -0,0 +1 @@
1
+ export const truncate = (text, width) => text.length > width ? `${text.slice(0, width - 3)}...` : text;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.42.1";
1
+ export declare const version = "5.42.3";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.42.1';
1
+ export const version = '5.42.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.42.1",
3
+ "version": "5.42.3",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {