knip 1.10.0 → 1.11.0

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.
Files changed (56) hide show
  1. package/README.md +6 -0
  2. package/dist/constants.js +2 -0
  3. package/dist/dependency-deputy.d.ts +0 -2
  4. package/dist/dependency-deputy.js +0 -5
  5. package/dist/index.js +10 -5
  6. package/dist/manifest/index.d.ts +1 -0
  7. package/dist/manifest/index.js +7 -2
  8. package/dist/plugins/github-actions/index.js +6 -8
  9. package/dist/plugins/husky/index.js +5 -7
  10. package/dist/plugins/lint-staged/index.js +4 -3
  11. package/dist/plugins/nx/index.js +4 -4
  12. package/dist/plugins/release-it/index.js +8 -2
  13. package/dist/plugins/release-it/types.d.ts +1 -0
  14. package/dist/project-principal.d.ts +1 -0
  15. package/dist/project-principal.js +3 -0
  16. package/dist/types/plugins.d.ts +4 -1
  17. package/dist/util/binaries/bash-parser.d.ts +6 -0
  18. package/dist/util/binaries/bash-parser.js +54 -0
  19. package/dist/util/binaries/index.d.ts +12 -7
  20. package/dist/util/binaries/index.js +24 -46
  21. package/dist/util/binaries/resolvers/fallback.d.ts +1 -0
  22. package/dist/util/binaries/resolvers/fallback.js +28 -0
  23. package/dist/util/binaries/{globals → resolvers}/index.d.ts +0 -0
  24. package/dist/util/binaries/{globals → resolvers}/index.js +0 -0
  25. package/dist/util/binaries/resolvers/node.d.ts +1 -0
  26. package/dist/util/binaries/resolvers/node.js +19 -0
  27. package/dist/util/binaries/resolvers/npx.d.ts +4 -0
  28. package/dist/util/binaries/resolvers/npx.js +5 -0
  29. package/dist/util/binaries/resolvers/pnpm.d.ts +2 -0
  30. package/dist/util/binaries/{globals → resolvers}/pnpm.js +8 -7
  31. package/dist/util/binaries/resolvers/yarn.d.ts +2 -0
  32. package/dist/util/binaries/{globals → resolvers}/yarn.js +7 -6
  33. package/dist/util/debug.d.ts +0 -2
  34. package/dist/util/debug.js +0 -7
  35. package/dist/util/fs.js +8 -2
  36. package/dist/util/modules.d.ts +1 -0
  37. package/dist/util/modules.js +4 -0
  38. package/dist/util/require.d.ts +1 -0
  39. package/dist/util/require.js +7 -0
  40. package/dist/version.d.ts +1 -1
  41. package/dist/version.js +1 -1
  42. package/dist/workspace-worker.d.ts +12 -12
  43. package/dist/workspace-worker.js +55 -50
  44. package/package.json +9 -6
  45. package/dist/util/binaries/binaries/cross-env.d.ts +0 -1
  46. package/dist/util/binaries/binaries/cross-env.js +0 -5
  47. package/dist/util/binaries/binaries/dotenv.d.ts +0 -1
  48. package/dist/util/binaries/binaries/dotenv.js +0 -5
  49. package/dist/util/binaries/binaries/index.d.ts +0 -2
  50. package/dist/util/binaries/binaries/index.js +0 -2
  51. package/dist/util/binaries/globals/node.d.ts +0 -1
  52. package/dist/util/binaries/globals/node.js +0 -5
  53. package/dist/util/binaries/globals/npx.d.ts +0 -1
  54. package/dist/util/binaries/globals/npx.js +0 -8
  55. package/dist/util/binaries/globals/pnpm.d.ts +0 -2
  56. package/dist/util/binaries/globals/yarn.d.ts +0 -2
package/README.md CHANGED
@@ -229,6 +229,10 @@ be part of the analysis. Here's an example:
229
229
  {
230
230
  "ignoreWorkspaces": ["packages/ignore-me"],
231
231
  "workspaces": {
232
+ ".": {
233
+ "entry": "src/index.ts",
234
+ "project": "src/**/*.ts"
235
+ },
232
236
  "packages/*": {
233
237
  "entry": "{index,cli}.ts",
234
238
  "project": "**/*.ts"
@@ -240,6 +244,8 @@ be part of the analysis. Here's an example:
240
244
  }
241
245
  ```
242
246
 
247
+ Note that if you have a root workspace, it must be under `workspaces` and have the `"."` key like in the example.
248
+
243
249
  Knip supports workspaces as defined in three possible locations:
244
250
 
245
251
  - In the `workspaces` array in `package.json`.
package/dist/constants.js CHANGED
@@ -8,6 +8,7 @@ export const DEFAULT_WORKSPACE_CONFIG = {
8
8
  };
9
9
  export const TEST_FILE_PATTERNS = ['**/*.{test,spec}.{js,jsx,ts,tsx}', '**/__tests__/**/*.{js,jsx,ts,tsx}'];
10
10
  export const IGNORED_GLOBAL_BINARIES = [
11
+ 'bun',
11
12
  'deno',
12
13
  'git',
13
14
  'node',
@@ -22,6 +23,7 @@ export const IGNORED_GLOBAL_BINARIES = [
22
23
  'mkdir',
23
24
  'mv',
24
25
  'rm',
26
+ 'sh',
25
27
  'sudo',
26
28
  ];
27
29
  export const IGNORE_DEFINITELY_TYPED = ['node'];
@@ -10,7 +10,6 @@ type Options = {
10
10
  export default class DependencyDeputy {
11
11
  isStrict: boolean;
12
12
  _manifests: WorkspaceManifests;
13
- manifests: Map<string, PackageJson>;
14
13
  ignoreDependencies: string[];
15
14
  referencedDependencies: Map<string, Set<string>>;
16
15
  peerDependencies: Map<string, PeerDependencies>;
@@ -23,7 +22,6 @@ export default class DependencyDeputy {
23
22
  manifestPath: string;
24
23
  manifest: PackageJson;
25
24
  }): void;
26
- getManifest(workspaceName: string): PackageJson | undefined;
27
25
  getWorkspaceManifest(workspaceName: string): {
28
26
  workspaceDir: string;
29
27
  manifestPath: string;
@@ -5,7 +5,6 @@ import { getPackageNameFromModuleSpecifier, isDefinitelyTyped, getDefinitelyType
5
5
  export default class DependencyDeputy {
6
6
  isStrict;
7
7
  _manifests = new Map();
8
- manifests = new Map();
9
8
  ignoreDependencies;
10
9
  referencedDependencies;
11
10
  peerDependencies;
@@ -25,7 +24,6 @@ export default class DependencyDeputy {
25
24
  const optionalDependencies = Object.keys(manifest.optionalDependencies ?? {});
26
25
  const devDependencies = Object.keys(manifest.devDependencies ?? {});
27
26
  const allDependencies = [...dependencies, ...devDependencies, ...peerDependencies, ...optionalDependencies];
28
- this.manifests.set(name, manifest);
29
27
  this._manifests.set(name, {
30
28
  workspaceDir: dir,
31
29
  manifestPath,
@@ -37,9 +35,6 @@ export default class DependencyDeputy {
37
35
  allDependencies,
38
36
  });
39
37
  }
40
- getManifest(workspaceName) {
41
- return this.manifests.get(workspaceName);
42
- }
43
38
  getWorkspaceManifest(workspaceName) {
44
39
  return this._manifests.get(workspaceName);
45
40
  }
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ export const main = async (unresolvedConfiguration) => {
31
31
  .reverse();
32
32
  const lab = new SourceLab({ report, workspaceDirs, isIncludeEntryExports });
33
33
  const principal = new ProjectPrincipal();
34
+ const enabledPluginsStore = new Map();
34
35
  for (const { name, dir, config, ancestors } of workspaces) {
35
36
  const isRoot = name === ROOT_WORKSPACE_NAME;
36
37
  const suffix = isRoot ? '' : ` (${name})`;
@@ -63,15 +64,14 @@ export const main = async (unresolvedConfiguration) => {
63
64
  config,
64
65
  rootWorkspaceConfig: chief.getConfigForWorkspace(ROOT_WORKSPACE_NAME),
65
66
  manifest,
66
- ancestorManifests: ancestors.map(name => deputy.getManifest(name)),
67
67
  rootConfig: chief.config,
68
68
  negatedWorkspacePatterns,
69
69
  rootWorkspaceDir: cwd,
70
70
  isProduction,
71
71
  });
72
- await worker.init();
73
- deputy.addPeerDependencies(name, worker.peerDependencies);
74
- deputy.setInstalledBinaries(name, worker.installedBinaries);
72
+ const enabledPluginsInAncestors = ancestors.flatMap(ancestor => enabledPluginsStore.get(ancestor) ?? []);
73
+ await worker.setEnabledPlugins(enabledPluginsInAncestors);
74
+ await worker.initReferencedDependencies();
75
75
  if (config?.entry && config?.project) {
76
76
  if (isProduction) {
77
77
  {
@@ -183,13 +183,18 @@ export const main = async (unresolvedConfiguration) => {
183
183
  }
184
184
  }
185
185
  if (report.dependencies || report.unlisted || report.files) {
186
- const { referencedDependencyIssues } = await worker.findDependenciesByPlugins();
186
+ await worker.findDependenciesByPlugins();
187
+ const { referencedDependencyIssues, peerDependencies, installedBinaries, entryFiles, enabledPlugins } = worker.getFinalDependencies();
187
188
  referencedDependencyIssues.forEach(issue => {
188
189
  const workspace = { name, dir, config, ancestors };
189
190
  const unlistedDependency = deputy.maybeAddListedReferencedDependency(workspace, issue.symbol);
190
191
  if (unlistedDependency)
191
192
  collector.addIssue(issue);
192
193
  });
194
+ deputy.addPeerDependencies(name, peerDependencies);
195
+ deputy.setInstalledBinaries(name, installedBinaries);
196
+ principal.addEntryPaths(entryFiles);
197
+ enabledPluginsStore.set(name, enabledPlugins);
193
198
  }
194
199
  }
195
200
  }
@@ -13,5 +13,6 @@ export declare const findDependencies: ({ rootConfig, manifest, isRoot, isProduc
13
13
  dependencies: string[];
14
14
  peerDependencies: PeerDependencies;
15
15
  installedBinaries: InstalledBinaries;
16
+ entryFiles: string[];
16
17
  }>;
17
18
  export {};
@@ -1,4 +1,4 @@
1
- import { getBinariesFromScripts } from '../util/binaries/index.js';
1
+ import { _getReferencesFromScripts } from '../util/binaries/index.js';
2
2
  import { timerify } from '../util/performance.js';
3
3
  import { getPackageManifest } from './helpers.js';
4
4
  const findManifestDependencies = async ({ rootConfig, manifest, isRoot, isProduction, dir, cwd }) => {
@@ -12,7 +12,11 @@ const findManifestDependencies = async ({ rootConfig, manifest, isRoot, isProduc
12
12
  }
13
13
  return scripts;
14
14
  }, []);
15
- const referencedBinaries = getBinariesFromScripts(scripts, { manifest, ignore: rootConfig.ignoreBinaries });
15
+ const { entryFiles, binaries: referencedBinaries } = _getReferencesFromScripts(scripts, {
16
+ cwd: dir,
17
+ manifest,
18
+ ignore: rootConfig.ignoreBinaries,
19
+ });
16
20
  const installedBinaries = new Map();
17
21
  const packageNames = [...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.devDependencies ?? {})];
18
22
  for (const packageName of packageNames) {
@@ -56,6 +60,7 @@ const findManifestDependencies = async ({ rootConfig, manifest, isRoot, isProduc
56
60
  dependencies: Array.from(referencedDependencies),
57
61
  peerDependencies,
58
62
  installedBinaries,
63
+ entryFiles,
59
64
  };
60
65
  };
61
66
  export const findDependencies = timerify(findManifestDependencies);
@@ -1,4 +1,4 @@
1
- import { getBinariesFromScripts } from '../../util/binaries/index.js';
1
+ import { _getReferencesFromScripts } from '../../util/binaries/index.js';
2
2
  import { _firstGlob } from '../../util/glob.js';
3
3
  import { _load } from '../../util/loader.js';
4
4
  import { getValuesByKeyDeep } from '../../util/object.js';
@@ -7,19 +7,17 @@ export const NAME = 'GitHub Actions';
7
7
  export const ENABLERS = 'This plugin is enabled when a `.yml` file is found in the `.github/workflows` folder.';
8
8
  export const isEnabled = async ({ cwd }) => Boolean(await _firstGlob({ cwd, patterns: ['.github/workflows/*.yml'] }));
9
9
  export const CONFIG_FILE_PATTERNS = ['.github/workflows/*.yml', '.github/**/action.{yml,yaml}'];
10
- const findGithubActionsDependencies = async (configFilePath, { manifest, rootConfig }) => {
10
+ const findGithubActionsDependencies = async (configFilePath, { cwd, manifest, rootConfig }) => {
11
11
  const config = await _load(configFilePath);
12
12
  if (!config)
13
13
  return [];
14
- const scripts = getValuesByKeyDeep(config, 'run')
15
- .filter((value) => typeof value === 'string')
16
- .flatMap(script => script.split('\n'))
17
- .map(script => script.trim());
18
- const binaries = getBinariesFromScripts(scripts, {
14
+ const scripts = getValuesByKeyDeep(config, 'run').filter((value) => typeof value === 'string');
15
+ const { binaries, entryFiles } = _getReferencesFromScripts(scripts, {
16
+ cwd,
19
17
  manifest,
20
18
  ignore: rootConfig.ignoreBinaries,
21
19
  knownGlobalsOnly: true,
22
20
  });
23
- return binaries;
21
+ return { dependencies: binaries, entryFiles };
24
22
  };
25
23
  export const findDependencies = timerify(findGithubActionsDependencies);
@@ -1,5 +1,5 @@
1
1
  import { readFileSync } from 'fs';
2
- import { getBinariesFromScripts } from '../../util/binaries/index.js';
2
+ import { _getReferencesFromScripts } from '../../util/binaries/index.js';
3
3
  import { timerify } from '../../util/performance.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
5
5
  export const NAME = 'husky';
@@ -10,16 +10,14 @@ export const CONFIG_FILE_PATTERNS = [
10
10
  '.husky/pre-{applypatch,commit,merge-commit,push,rebase,receive}',
11
11
  '.husky/post-{checkout,commit,merge,rewrite}',
12
12
  ];
13
- const findHuskyDependencies = async (configFilePath, { manifest, rootConfig }) => {
13
+ const findHuskyDependencies = async (configFilePath, { cwd, manifest, rootConfig }) => {
14
14
  const script = readFileSync(configFilePath);
15
- const scripts = String(script)
16
- .split('\n')
17
- .map(script => script.trim());
18
- const binaries = getBinariesFromScripts(scripts, {
15
+ const { binaries, entryFiles } = _getReferencesFromScripts(String(script), {
16
+ cwd,
19
17
  manifest,
20
18
  ignore: rootConfig.ignoreBinaries,
21
19
  knownGlobalsOnly: true,
22
20
  });
23
- return binaries;
21
+ return { dependencies: binaries, entryFiles };
24
22
  };
25
23
  export const findDependencies = timerify(findHuskyDependencies);
@@ -1,4 +1,4 @@
1
- import { getBinariesFromScripts } from '../../util/binaries/index.js';
1
+ import { _getReferencesFromScripts } from '../../util/binaries/index.js';
2
2
  import { _load } from '../../util/loader.js';
3
3
  import { timerify } from '../../util/performance.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
@@ -13,7 +13,7 @@ export const CONFIG_FILE_PATTERNS = [
13
13
  'lint-staged.config.{js,mjs,cjs}',
14
14
  'package.json',
15
15
  ];
16
- const findLintStagedDependencies = async (configFilePath, { manifest, rootConfig }) => {
16
+ const findLintStagedDependencies = async (configFilePath, { cwd, manifest, rootConfig }) => {
17
17
  let config = configFilePath.endsWith('package.json')
18
18
  ? manifest['lint-staged']
19
19
  : await _load(configFilePath);
@@ -25,7 +25,8 @@ const findLintStagedDependencies = async (configFilePath, { manifest, rootConfig
25
25
  const binaries = new Set();
26
26
  for (const entry of Object.values(config).flat()) {
27
27
  const scripts = [typeof entry === 'function' ? await entry([]) : entry].flat();
28
- getBinariesFromScripts(scripts, { manifest, ignore: rootConfig.ignoreBinaries }).forEach(bin => binaries.add(bin));
28
+ const options = { cwd, manifest, ignore: rootConfig.ignoreBinaries };
29
+ _getReferencesFromScripts(scripts, options).binaries.forEach(bin => binaries.add(bin));
29
30
  }
30
31
  return Array.from(binaries);
31
32
  };
@@ -1,5 +1,5 @@
1
1
  import { compact } from '../../util/array.js';
2
- import { getBinariesFromScripts } from '../../util/binaries/index.js';
2
+ import { _getReferencesFromScripts } from '../../util/binaries/index.js';
3
3
  import { _load } from '../../util/loader.js';
4
4
  import { timerify } from '../../util/performance.js';
5
5
  import { hasDependency } from '../../util/plugin.js';
@@ -7,7 +7,7 @@ export const NAME = 'Nx';
7
7
  export const ENABLERS = [/^@nrwl\//];
8
8
  export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
9
9
  export const CONFIG_FILE_PATTERNS = ['{apps,libs}/**/project.json'];
10
- const findNxDependencies = async (configFilePath, { manifest }) => {
10
+ const findNxDependencies = async (configFilePath, { cwd, manifest }) => {
11
11
  const config = await _load(configFilePath);
12
12
  if (!config)
13
13
  return [];
@@ -19,7 +19,7 @@ const findNxDependencies = async (configFilePath, { manifest }) => {
19
19
  const scripts = compact(targets
20
20
  .filter(target => target.executor === 'nx:run-commands')
21
21
  .flatMap(target => (target.options?.commands ?? target.options?.command ? [target.options.command] : [])));
22
- const binaries = getBinariesFromScripts(scripts, { manifest, knownGlobalsOnly: true });
23
- return [...executors, ...binaries];
22
+ const { binaries, entryFiles } = _getReferencesFromScripts(scripts, { cwd, manifest, knownGlobalsOnly: true });
23
+ return { dependencies: [...executors, ...binaries], entryFiles };
24
24
  };
25
25
  export const findDependencies = timerify(findNxDependencies);
@@ -1,3 +1,4 @@
1
+ import { _getReferencesFromScripts } from '../../util/binaries/index.js';
1
2
  import { _load } from '../../util/loader.js';
2
3
  import { timerify } from '../../util/performance.js';
3
4
  import { hasDependency } from '../../util/plugin.js';
@@ -10,10 +11,15 @@ export const CONFIG_FILE_PATTERNS = [
10
11
  '.release-it.{yml,yaml}',
11
12
  'package.json',
12
13
  ];
13
- const findReleaseItDependencies = async (configFilePath, { manifest }) => {
14
+ const findReleaseItDependencies = async (configFilePath, { cwd, manifest }) => {
14
15
  const config = configFilePath.endsWith('package.json')
15
16
  ? manifest['release-it']
16
17
  : await _load(configFilePath);
17
- return config?.plugins ? Object.keys(config.plugins) : [];
18
+ if (!config)
19
+ return [];
20
+ const plugins = config.plugins ? Object.keys(config.plugins) : [];
21
+ const scripts = config.hooks ? Object.values(config.hooks).flat() : [];
22
+ const { binaries } = _getReferencesFromScripts(scripts, { cwd, manifest });
23
+ return [...plugins, ...binaries];
18
24
  };
19
25
  export const findDependencies = timerify(findReleaseItDependencies);
@@ -1,3 +1,4 @@
1
1
  export type ReleaseItConfig = {
2
2
  plugins?: Record<string, unknown>;
3
+ hooks?: Record<string, string | string[]>;
3
4
  };
@@ -10,6 +10,7 @@ export default class ProjectPrincipal {
10
10
  projectWorkspace?: Project;
11
11
  entryFiles?: SourceFile[];
12
12
  addEntryPath(filePath: string): void;
13
+ addEntryPaths(filePaths: Set<string>): void;
13
14
  addSourceFile(filePath: string): void;
14
15
  addProjectPath(filePath: string): void;
15
16
  removeProjectPath(filePath: string): void;
@@ -26,6 +26,9 @@ export default class ProjectPrincipal {
26
26
  addEntryPath(filePath) {
27
27
  this.entryPaths.add(filePath);
28
28
  }
29
+ addEntryPaths(filePaths) {
30
+ filePaths.forEach(filePath => this.entryPaths.add(filePath));
31
+ }
29
32
  addSourceFile(filePath) {
30
33
  this.entryWorkspace?.addSourceFileAtPath(filePath);
31
34
  }
@@ -14,5 +14,8 @@ type GenericPluginCallbackOptions = {
14
14
  rootConfig: Configuration;
15
15
  isProduction: boolean;
16
16
  };
17
- export type GenericPluginCallback = (configFilePath: string, { cwd, manifest, config, workspaceConfig, isProduction }: GenericPluginCallbackOptions) => Promise<string[]> | string[];
17
+ export type GenericPluginCallback = (configFilePath: string, { cwd, manifest, config, workspaceConfig, isProduction }: GenericPluginCallbackOptions) => Promise<string[] | {
18
+ dependencies: string[];
19
+ entryFiles?: string[];
20
+ }> | string[];
18
21
  export {};
@@ -0,0 +1,6 @@
1
+ import type { PackageJson } from 'type-fest';
2
+ export declare const getBinariesFromScript: (script: string, { cwd, manifest, knownGlobalsOnly }: {
3
+ cwd: string;
4
+ manifest: PackageJson;
5
+ knownGlobalsOnly?: boolean | undefined;
6
+ }) => string[];
@@ -0,0 +1,54 @@
1
+ import parse from 'bash-parser';
2
+ import parseArgs from 'minimist';
3
+ import * as FallbackResolver from './resolvers/fallback.js';
4
+ import * as KnownResolvers from './resolvers/index.js';
5
+ const spawningBinaries = ['cross-env', 'dotenv'];
6
+ export const getBinariesFromScript = (script, { cwd, manifest, knownGlobalsOnly = false }) => {
7
+ const findByArgs = (args) => (args.length > 0 ? findBinaries(parse(args.join(' ')).commands) : []);
8
+ const findBinaries = (items) => items.flatMap(item => {
9
+ switch (item.type) {
10
+ case 'Command': {
11
+ const binary = item.name?.text;
12
+ if (!binary || binary === '.' || binary === 'source')
13
+ return [];
14
+ if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
15
+ return [];
16
+ if (['bun', 'deno'].includes(binary))
17
+ return [];
18
+ const args = item.suffix?.map(arg => arg.text) ?? [];
19
+ if (['!', 'test'].includes(binary))
20
+ return findByArgs(args);
21
+ if (binary in KnownResolvers) {
22
+ return KnownResolvers[binary].resolve(binary, args, cwd, manifest, (args) => {
23
+ const [binary, ...rest] = args;
24
+ return FallbackResolver.resolve(binary, rest, cwd);
25
+ });
26
+ }
27
+ if (knownGlobalsOnly)
28
+ return [];
29
+ if (spawningBinaries.includes(binary)) {
30
+ const parsedArgs = parseArgs(args);
31
+ const [spawnedBinary] = parsedArgs._;
32
+ if (spawnedBinary) {
33
+ const restArgs = args.slice(args.indexOf(spawnedBinary));
34
+ return [binary, ...findByArgs(restArgs)];
35
+ }
36
+ else {
37
+ return [];
38
+ }
39
+ }
40
+ return FallbackResolver.resolve(binary, args, cwd);
41
+ }
42
+ case 'LogicalExpression':
43
+ return findBinaries([item.left, item.right]);
44
+ case 'If':
45
+ return findBinaries([...item.clause.commands, ...item.then.commands]);
46
+ case 'For':
47
+ return findBinaries(item.do.commands);
48
+ default:
49
+ return [];
50
+ }
51
+ });
52
+ const parsed = parse(script);
53
+ return parsed?.commands ? findBinaries(parsed.commands) : [];
54
+ };
@@ -1,8 +1,13 @@
1
1
  import type { PackageJson } from 'type-fest';
2
- export declare const getFirstPositionalArg: (args: string[]) => string | undefined;
3
- export declare const getDependenciesFromLoaderArguments: (args: string[]) => string[];
4
- export declare const getBinariesFromScripts: (npmScripts: string[], { manifest, ignore, knownGlobalsOnly, }: {
5
- manifest: PackageJson;
6
- ignore?: string[] | undefined;
7
- knownGlobalsOnly?: boolean | undefined;
8
- }) => string[];
2
+ type Options = {
3
+ cwd?: string;
4
+ manifest?: PackageJson;
5
+ ignore?: string[];
6
+ knownGlobalsOnly?: boolean;
7
+ };
8
+ type GetBinariesFromScripts = (npmScripts: string | string[], options?: Options) => {
9
+ entryFiles: string[];
10
+ binaries: string[];
11
+ };
12
+ export declare const _getReferencesFromScripts: GetBinariesFromScripts;
13
+ export {};
@@ -1,47 +1,25 @@
1
1
  import { IGNORED_GLOBAL_BINARIES } from '../../constants.js';
2
- import { getArgumentValues } from '../plugin.js';
3
- import { toCamelCase } from '../plugin.js';
4
- import * as BinaryResolvers from './binaries/index.js';
5
- import * as GlobalBinaryResolvers from './globals/index.js';
6
- const normalizeBinaries = (command) => command
7
- .replace(/(\.\/)?node_modules\/\.bin\/(\w+)/, '$2')
8
- .replace(/\$\(npm bin\)\/(\w+)/, '$1')
9
- .replace(/(\S+)@.*/, '$1');
10
- const stripEnvironmentVariables = (value) => value.replace(/([A-Z][^ ]*)=([^ ])+ /g, '');
11
- const getLoaderArgumentValues = (value) => getArgumentValues(value, / (--(experimental-)?loader|--require|-r)[ =]([^ ]+)/g);
12
- export const getFirstPositionalArg = (args) => args.find(arg => /^[a-zA-Z@]/.test(arg));
13
- export const getDependenciesFromLoaderArguments = (args) => getLoaderArgumentValues(' ' + args.join(' '))
14
- .filter(scripts => !scripts.startsWith('.'))
15
- .map(script => {
16
- if (script.startsWith('@')) {
17
- const [scope, packageName] = script.split('/');
18
- return [scope, packageName].join('/');
19
- }
20
- return script.split('/')[0];
21
- });
22
- export const getBinariesFromScripts = (npmScripts, { manifest, ignore = [], knownGlobalsOnly = false, }) => Array.from(npmScripts.reduce((binaries, script) => {
23
- script
24
- .split(' && ')
25
- .flatMap(command => command.split(' -- '))
26
- .map(normalizeBinaries)
27
- .filter(command => /^\w/.test(command))
28
- .map(stripEnvironmentVariables)
29
- .flatMap(command => {
30
- const [binary, ...args] = command.trim().split(' ');
31
- const camelCased = toCamelCase(binary);
32
- if (['bun', 'deno'].includes(binary))
33
- return [];
34
- if (camelCased in GlobalBinaryResolvers) {
35
- return GlobalBinaryResolvers[camelCased].resolve(binary, args, manifest);
36
- }
37
- if (knownGlobalsOnly)
38
- return [];
39
- if (camelCased in BinaryResolvers) {
40
- return BinaryResolvers[camelCased].resolve(binary, args);
41
- }
42
- const dependenciesFromArguments = getDependenciesFromLoaderArguments(args);
43
- return [binary, ...dependenciesFromArguments];
44
- })
45
- .forEach(binary => binary && binaries.add(binary));
46
- return binaries;
47
- }, new Set())).filter(binaryName => !IGNORED_GLOBAL_BINARIES.includes(binaryName) && !ignore.includes(binaryName));
2
+ import { compact } from '../array.js';
3
+ import { getPackageNameFromModuleSpecifier, stripBinary } from '../modules.js';
4
+ import { timerify } from '../performance.js';
5
+ import { getBinariesFromScript } from './bash-parser.js';
6
+ const partition = (values) => values.reduce((acc, value) => {
7
+ acc[/^(\/|[A-Z]:)/.test(value) ? 1 : 0].push(value);
8
+ return acc;
9
+ }, [[], []]);
10
+ const getReferencesFromScripts = (npmScripts, options = {}) => {
11
+ const { cwd = process.cwd(), manifest = {}, ignore = [], knownGlobalsOnly = false } = options;
12
+ const results = [npmScripts]
13
+ .flat()
14
+ .flatMap(script => getBinariesFromScript(script, { cwd, manifest, knownGlobalsOnly }));
15
+ const [binaries, entryFiles] = partition(compact(results));
16
+ return {
17
+ entryFiles,
18
+ binaries: binaries
19
+ .map(stripBinary)
20
+ .filter(binary => !binary.startsWith('.'))
21
+ .map(getPackageNameFromModuleSpecifier)
22
+ .filter(binaryName => !IGNORED_GLOBAL_BINARIES.includes(binaryName) && !ignore.includes(binaryName)),
23
+ };
24
+ };
25
+ export const _getReferencesFromScripts = timerify(getReferencesFromScripts);
@@ -0,0 +1 @@
1
+ export declare const resolve: (binary: string, args: string[], cwd: string) => string[];
@@ -0,0 +1,28 @@
1
+ import path from 'node:path';
2
+ import parseArgs from 'minimist';
3
+ import { tryResolve } from '../../require.js';
4
+ const resolveArguments = {
5
+ 'ts-node': (parsed) => [parsed._[0], parsed.require].flat(),
6
+ tsx: (parsed) => parsed._.filter(p => p !== 'watch'),
7
+ default: (parsed) => [parsed.require].flat(),
8
+ };
9
+ export const resolve = (binary, args, cwd) => {
10
+ const parsed = parseArgs(args, { string: ['r'], alias: { require: ['r', 'loader'] } });
11
+ const resolver = resolveArguments[binary] ?? resolveArguments.default;
12
+ const resolve = resolver(parsed);
13
+ return [
14
+ binary,
15
+ ...resolve.flatMap(specifier => {
16
+ if (specifier) {
17
+ const filePath = path.join(cwd, specifier);
18
+ if (filePath.startsWith(cwd)) {
19
+ const resolvedFilePath = tryResolve(filePath);
20
+ if (resolvedFilePath)
21
+ return [resolvedFilePath];
22
+ }
23
+ return [specifier];
24
+ }
25
+ return [];
26
+ }),
27
+ ];
28
+ };
@@ -0,0 +1 @@
1
+ export declare const resolve: (binary: string, args: string[], cwd: string) => string[];
@@ -0,0 +1,19 @@
1
+ import path from 'node:path';
2
+ import parseArgs from 'minimist';
3
+ import { tryResolve } from '../../require.js';
4
+ export const resolve = (binary, args, cwd) => {
5
+ const parsed = parseArgs(args, { string: ['r'], alias: { require: ['r', 'loader', 'experimental-loader'] } });
6
+ const resolve = [parsed._[0], parsed.require].flat();
7
+ return resolve.flatMap(specifier => {
8
+ if (specifier) {
9
+ const filePath = path.join(cwd, specifier);
10
+ if (filePath.startsWith(cwd)) {
11
+ const resolvedFilePath = tryResolve(filePath);
12
+ if (resolvedFilePath)
13
+ return [resolvedFilePath];
14
+ }
15
+ return [specifier];
16
+ }
17
+ return [];
18
+ });
19
+ };
@@ -0,0 +1,4 @@
1
+ import type { PackageJson } from 'type-fest';
2
+ type FindByArgs = (args: string[]) => string[];
3
+ export declare const resolve: (binary: string, args: string[], cwd: string, manifest: PackageJson, findByArgs: FindByArgs) => string[];
4
+ export {};
@@ -0,0 +1,5 @@
1
+ import parseArgs from 'minimist';
2
+ export const resolve = (binary, args, cwd, manifest, findByArgs) => {
3
+ const parsed = parseArgs(args, { '--': true, stopEarly: true, boolean: ['yes', 'no'], alias: { yes: 'y', no: 'n' } });
4
+ return [...(parsed.yes ? [] : findByArgs(parsed._)), ...(parsed['--'] ? findByArgs(parsed['--']) : [])];
5
+ };
@@ -0,0 +1,2 @@
1
+ import type { PackageJson } from 'type-fest';
2
+ export declare const resolve: (binary: string, args: string[], cwd: string, manifest: PackageJson) => string[];
@@ -1,4 +1,4 @@
1
- import { getFirstPositionalArg } from '../index.js';
1
+ import parseArgs from 'minimist';
2
2
  const commands = [
3
3
  'add',
4
4
  'i',
@@ -31,14 +31,15 @@ const commands = [
31
31
  't',
32
32
  'tst',
33
33
  ];
34
- export const resolve = (binary, args, manifest) => {
34
+ export const resolve = (binary, args, cwd, manifest) => {
35
35
  const scripts = manifest.scripts ? Object.keys(manifest.scripts) : [];
36
- const [command, ...commandArgs] = args;
36
+ const parsed = parseArgs(args, {});
37
+ const [command, result] = parsed._;
37
38
  if (scripts.includes(command) || commands.includes(command))
38
39
  return [];
39
- if (command === 'run' && scripts.includes(commandArgs[0]))
40
+ if (command === 'run' && scripts.includes(result))
40
41
  return [];
41
- if (command === 'run' || command === 'exec')
42
- return getFirstPositionalArg(commandArgs);
43
- return getFirstPositionalArg(args);
42
+ if (command === 'exec' || command === 'run')
43
+ return [result];
44
+ return command ? [command] : [];
44
45
  };
@@ -0,0 +1,2 @@
1
+ import type { PackageJson } from 'type-fest';
2
+ export declare const resolve: (binary: string, args: string[], cwd: string, manifest: PackageJson) => string[];
@@ -1,4 +1,4 @@
1
- import { getFirstPositionalArg } from '../index.js';
1
+ import parseArgs from 'minimist';
2
2
  const commands = [
3
3
  'add',
4
4
  'bin',
@@ -30,14 +30,15 @@ const commands = [
30
30
  'workspace',
31
31
  'workspaces',
32
32
  ];
33
- export const resolve = (binary, args, manifest) => {
33
+ export const resolve = (binary, args, cwd, manifest) => {
34
34
  const scripts = manifest.scripts ? Object.keys(manifest.scripts) : [];
35
- const [command, ...commandArgs] = args;
35
+ const parsed = parseArgs(args, {});
36
+ const [command, result] = parsed._;
36
37
  if (scripts.includes(command) || commands.includes(command))
37
38
  return [];
38
- if (command === 'run' && scripts.includes(commandArgs[0]))
39
+ if (command === 'run' && scripts.includes(result))
39
40
  return [];
40
41
  if (command === 'run' || command === 'exec')
41
- return getFirstPositionalArg(commandArgs);
42
- return getFirstPositionalArg(args);
42
+ return [result];
43
+ return command ? [command] : [];
43
44
  };
@@ -1,6 +1,4 @@
1
- import type { Issue } from '../types/issues.js';
2
1
  import type { SourceFile } from 'ts-morph';
3
2
  export declare const debugLogObject: (name: string, obj: unknown) => void;
4
3
  export declare const debugLogFiles: (name: string, filePaths: Set<string> | string[]) => void;
5
4
  export declare const debugLogSourceFiles: (name: string, sourceFiles: Set<SourceFile> | SourceFile[]) => void;
6
- export declare const debugLogIssues: (name: string, issues: Issue[]) => void;
@@ -40,10 +40,3 @@ export const debugLogSourceFiles = (name, sourceFiles) => {
40
40
  logArray(files);
41
41
  }
42
42
  };
43
- export const debugLogIssues = (name, issues) => {
44
- if (!IS_ENABLED)
45
- return;
46
- const symbols = Array.from(new Set(issues.map(issue => issue.symbol)));
47
- console.debug(`[knip] ${name} (${symbols.length})`);
48
- logArray(symbols);
49
- };
package/dist/util/fs.js CHANGED
@@ -2,6 +2,7 @@ import { statSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import stripJsonComments from 'strip-json-comments';
5
+ import { LoaderError } from './errors.js';
5
6
  export const isFile = (filePath) => {
6
7
  const stat = statSync(filePath, { throwIfNoEntry: false });
7
8
  return stat !== undefined && stat.isFile();
@@ -11,6 +12,11 @@ export const findFile = (workingDir, fileName) => {
11
12
  return isFile(filePath) ? filePath : undefined;
12
13
  };
13
14
  export const loadJSON = async (filePath) => {
14
- const contents = await readFile(filePath);
15
- return JSON.parse(stripJsonComments(contents.toString()));
15
+ try {
16
+ const contents = await readFile(filePath);
17
+ return JSON.parse(stripJsonComments(contents.toString()));
18
+ }
19
+ catch (error) {
20
+ throw new LoaderError(`Error loading ${filePath}`, { cause: error });
21
+ }
16
22
  };
@@ -1,5 +1,6 @@
1
1
  export declare const getPackageNameFromModuleSpecifier: (moduleSpecifier: string) => string;
2
2
  export declare const getPackageName: (value: string) => string;
3
+ export declare const stripBinary: (command: string) => string;
3
4
  export declare const isDefinitelyTyped: (packageName: string) => boolean;
4
5
  export declare const getDefinitelyTypedFor: (packageName: string) => string;
5
6
  export declare const getPackageFromDefinitelyTyped: (typedDependency: string) => string;
@@ -13,6 +13,10 @@ export const getPackageName = (value) => {
13
13
  }
14
14
  return value.startsWith('/') ? value : value.split('/')[0];
15
15
  };
16
+ export const stripBinary = (command) => command
17
+ .replace(/(\.\/)?node_modules\/(\.bin\/)?(\w+)/, '$3')
18
+ .replace(/\$\(npm bin\)\/(\w+)/, '$1')
19
+ .replace(/(\S+)@.*/, '$1');
16
20
  export const isDefinitelyTyped = (packageName) => packageName.startsWith('@types/');
17
21
  export const getDefinitelyTypedFor = (packageName) => {
18
22
  if (isDefinitelyTyped(packageName))
@@ -1,2 +1,3 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  export declare const require: NodeRequire;
3
+ export declare const tryResolve: (specifier: string) => string | undefined;
@@ -1,2 +1,9 @@
1
1
  import { createRequire } from 'node:module';
2
2
  export const require = createRequire(process.cwd());
3
+ export const tryResolve = (specifier) => {
4
+ try {
5
+ return require.resolve(specifier);
6
+ }
7
+ catch (error) {
8
+ }
9
+ };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "1.10.0";
1
+ export declare const version = "1.11.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.10.0';
1
+ export const version = '1.11.0';
@@ -7,7 +7,6 @@ type WorkspaceManagerOptions = {
7
7
  dir: string;
8
8
  config: WorkspaceConfiguration;
9
9
  manifest: PackageJson;
10
- ancestorManifests: (PackageJson | undefined)[];
11
10
  rootWorkspaceConfig: WorkspaceConfiguration;
12
11
  rootConfig: Configuration;
13
12
  negatedWorkspacePatterns: string[];
@@ -15,28 +14,26 @@ type WorkspaceManagerOptions = {
15
14
  isProduction: boolean;
16
15
  };
17
16
  type ReferencedDependencyIssues = Set<Issue>;
18
- type ReferencedDependencies = Set<string>;
19
17
  export default class WorkspaceWorker {
20
18
  name: string;
21
19
  dir: string;
22
20
  config: WorkspaceConfiguration;
23
- ancestorManifests: (PackageJson | undefined)[];
24
21
  rootWorkspaceConfig: WorkspaceConfiguration;
25
22
  rootConfig: Configuration;
26
- referencedDependencyIssues: ReferencedDependencyIssues;
27
23
  manifest: PackageJson;
28
24
  rootWorkspaceDir: string;
29
- referencedDependencies: ReferencedDependencies;
25
+ referencedDependencyIssues: ReferencedDependencyIssues;
30
26
  peerDependencies: PeerDependencies;
31
27
  installedBinaries: InstalledBinaries;
28
+ entryFiles: Set<string>;
32
29
  negatedWorkspacePatterns: string[];
33
30
  enabled: Record<PluginName, boolean>;
31
+ enabledPlugins: PluginName[];
34
32
  isRoot: boolean;
35
33
  isProduction: boolean;
36
- constructor({ name, dir, config, ancestorManifests, rootWorkspaceConfig, rootConfig, negatedWorkspacePatterns, manifest, rootWorkspaceDir, isProduction, }: WorkspaceManagerOptions);
34
+ constructor({ name, dir, config, rootWorkspaceConfig, rootConfig, negatedWorkspacePatterns, manifest, rootWorkspaceDir, isProduction, }: WorkspaceManagerOptions);
37
35
  getConfigForPlugin(pluginName: PluginName): PluginConfiguration;
38
- init(): Promise<void>;
39
- setEnabledPlugins(): Promise<void>;
36
+ setEnabledPlugins(enabledPluginsInAncestors: string[]): Promise<void>;
40
37
  initReferencedDependencies(): Promise<void>;
41
38
  getEntryFilePatterns(): string[];
42
39
  getProjectFilePatterns(): string[];
@@ -48,10 +45,13 @@ export default class WorkspaceWorker {
48
45
  getProductionPluginEntryFilePatterns(): string[];
49
46
  getConfigurationEntryFilePattern(pluginName: PluginName): string[];
50
47
  getWorkspaceIgnorePatterns(): string[];
51
- findDependenciesByPlugins(): Promise<{
48
+ findDependenciesByPlugins(): Promise<void>;
49
+ getFinalDependencies(): {
50
+ peerDependencies: PeerDependencies;
51
+ installedBinaries: InstalledBinaries;
52
52
  referencedDependencyIssues: ReferencedDependencyIssues;
53
- referencedDependencies: ReferencedDependencies;
54
- }>;
55
- private findDependenciesByPlugin;
53
+ entryFiles: Set<string>;
54
+ enabledPlugins: ("babel" | "capacitor" | "changesets" | "commitlint" | "cypress" | "eslint" | "gatsby" | "husky" | "jest" | "markdownlint" | "mocha" | "next" | "nx" | "nyc" | "playwright" | "postcss" | "prettier" | "remark" | "remix" | "rollup" | "sentry" | "storybook" | "stryker" | "typescript" | "vitest" | "webpack" | "githubActions" | "lintStaged" | "npmPackageJsonLint" | "releaseIt")[];
55
+ };
56
56
  }
57
57
  export {};
@@ -2,31 +2,30 @@ import path from 'node:path';
2
2
  import { ROOT_WORKSPACE_NAME, TEST_FILE_PATTERNS } from './constants.js';
3
3
  import * as npm from './manifest/index.js';
4
4
  import * as plugins from './plugins/index.js';
5
- import { debugLogFiles, debugLogIssues, debugLogObject } from './util/debug.js';
5
+ import { debugLogFiles, debugLogObject } from './util/debug.js';
6
6
  import { _pureGlob, negate, hasProductionSuffix, hasNoProductionSuffix } from './util/glob.js';
7
7
  const negatedTestFilePatterns = TEST_FILE_PATTERNS.map(negate);
8
8
  export default class WorkspaceWorker {
9
9
  name;
10
10
  dir;
11
11
  config;
12
- ancestorManifests;
13
12
  rootWorkspaceConfig;
14
13
  rootConfig;
15
- referencedDependencyIssues = new Set();
16
14
  manifest;
17
15
  rootWorkspaceDir;
18
- referencedDependencies = new Set();
16
+ referencedDependencyIssues = new Set();
19
17
  peerDependencies = new Map();
20
18
  installedBinaries = new Map();
19
+ entryFiles = new Set();
21
20
  negatedWorkspacePatterns = [];
22
21
  enabled;
22
+ enabledPlugins = [];
23
23
  isRoot;
24
24
  isProduction;
25
- constructor({ name, dir, config, ancestorManifests, rootWorkspaceConfig, rootConfig, negatedWorkspacePatterns, manifest, rootWorkspaceDir, isProduction, }) {
25
+ constructor({ name, dir, config, rootWorkspaceConfig, rootConfig, negatedWorkspacePatterns, manifest, rootWorkspaceDir, isProduction, }) {
26
26
  this.name = name;
27
27
  this.dir = dir;
28
28
  this.config = config;
29
- this.ancestorManifests = ancestorManifests;
30
29
  this.isRoot = name === ROOT_WORKSPACE_NAME;
31
30
  this.isProduction = isProduction;
32
31
  this.rootConfig = rootConfig;
@@ -39,27 +38,23 @@ export default class WorkspaceWorker {
39
38
  getConfigForPlugin(pluginName) {
40
39
  return this.config[pluginName] ?? { config: null, entry: null, project: null };
41
40
  }
42
- async init() {
43
- await this.setEnabledPlugins();
44
- await this.initReferencedDependencies();
45
- }
46
- async setEnabledPlugins() {
47
- const dependencies = new Set([this.manifest, ...this.ancestorManifests]
48
- .flatMap(manifest => [Object.keys(manifest?.dependencies ?? {}), Object.keys(manifest?.devDependencies ?? {})])
49
- .flat());
41
+ async setEnabledPlugins(enabledPluginsInAncestors) {
42
+ const { manifest } = this;
43
+ const dependencies = new Set([Object.keys(manifest?.dependencies ?? {}), Object.keys(manifest?.devDependencies ?? {})].flat());
50
44
  const pluginEntries = Object.entries(plugins);
51
45
  for (const [pluginName, plugin] of pluginEntries) {
52
46
  const hasIsEnabled = typeof plugin.isEnabled === 'function';
53
47
  this.enabled[pluginName] =
54
48
  this.config[pluginName] !== false &&
55
- hasIsEnabled &&
56
- (await plugin.isEnabled({ cwd: this.dir, manifest: this.manifest, dependencies }));
49
+ (enabledPluginsInAncestors.includes(pluginName) ||
50
+ (hasIsEnabled && (await plugin.isEnabled({ cwd: this.dir, manifest: this.manifest, dependencies }))));
57
51
  }
58
- const enabledPlugins = pluginEntries.filter(([name]) => this.enabled[name]).map(([, plugin]) => plugin.NAME);
59
- debugLogObject(`Enabled plugins (${this.name})`, enabledPlugins);
52
+ this.enabledPlugins = pluginEntries.filter(([name]) => this.enabled[name]).map(([name]) => name);
53
+ const enabledPluginNames = this.enabledPlugins.map(name => plugins[name].NAME);
54
+ debugLogObject(`Enabled plugins (${this.name})`, enabledPluginNames);
60
55
  }
61
56
  async initReferencedDependencies() {
62
- const { dependencies, peerDependencies, installedBinaries } = await npm.findDependencies({
57
+ const { dependencies, peerDependencies, installedBinaries, entryFiles } = await npm.findDependencies({
63
58
  rootConfig: this.rootConfig,
64
59
  manifest: this.manifest,
65
60
  isRoot: this.isRoot,
@@ -68,8 +63,9 @@ export default class WorkspaceWorker {
68
63
  cwd: this.rootWorkspaceDir,
69
64
  });
70
65
  const filePath = path.join(this.dir, 'package.json');
71
- dependencies.forEach(dependency => this.referencedDependencyIssues.add({ type: 'unlisted', filePath, symbol: dependency }));
72
- dependencies.forEach(dependency => this.referencedDependencies.add(dependency));
66
+ const issues = this.referencedDependencyIssues;
67
+ dependencies.forEach(dependency => issues.add({ type: 'unlisted', filePath, symbol: dependency }));
68
+ entryFiles.forEach(entryFile => this.entryFiles.add(entryFile));
73
69
  this.peerDependencies = peerDependencies;
74
70
  this.installedBinaries = installedBinaries;
75
71
  }
@@ -202,40 +198,49 @@ export default class WorkspaceWorker {
202
198
  if (this.enabled[pluginName] && isIncludePlugin) {
203
199
  const hasDependencyFinder = 'findDependencies' in plugin && typeof plugin.findDependencies === 'function';
204
200
  if (hasDependencyFinder) {
205
- const dependencies = await this.findDependenciesByPlugin(pluginName, plugin.NAME, plugin.findDependencies);
206
- dependencies.forEach(dependency => this.referencedDependencyIssues.add(dependency));
207
- dependencies.forEach(dependency => this.referencedDependencies.add(dependency.symbol));
201
+ const pluginConfig = this.getConfigForPlugin(pluginName);
202
+ if (!pluginConfig)
203
+ continue;
204
+ const patterns = this.getConfigurationEntryFilePattern(pluginName);
205
+ const cwd = this.dir;
206
+ const ignore = this.getWorkspaceIgnorePatterns();
207
+ const configFilePaths = await _pureGlob({ patterns, cwd, ignore });
208
+ debugLogFiles(`Globbed ${plugin.NAME} config file paths`, configFilePaths);
209
+ if (configFilePaths.length === 0)
210
+ continue;
211
+ const pluginDependencies = new Set();
212
+ const pluginEntryFiles = new Set();
213
+ for (const configFilePath of configFilePaths) {
214
+ const results = await plugin.findDependencies(configFilePath, {
215
+ cwd,
216
+ manifest: this.manifest,
217
+ config: pluginConfig,
218
+ rootConfig: this.rootConfig,
219
+ workspaceConfig: this.config,
220
+ isProduction: this.isProduction,
221
+ });
222
+ const dependencies = Array.isArray(results) ? results : results.dependencies;
223
+ const entryFiles = !Array.isArray(results) && results.entryFiles ? results.entryFiles : [];
224
+ const issues = this.referencedDependencyIssues;
225
+ dependencies.forEach(symbol => issues.add({ type: 'unlisted', filePath: configFilePath, symbol }));
226
+ entryFiles.forEach(entryFile => this.entryFiles.add(entryFile));
227
+ dependencies.forEach(dependency => pluginDependencies.add(dependency));
228
+ entryFiles.forEach(entryFile => pluginEntryFiles.add(entryFile));
229
+ }
230
+ debugLogFiles(`Dependencies referenced in ${plugin.NAME}`, pluginDependencies);
231
+ if (pluginEntryFiles.size > 0)
232
+ debugLogFiles(`Entry files referenced in ${plugin.NAME}`, pluginEntryFiles);
208
233
  }
209
234
  }
210
235
  }
236
+ }
237
+ getFinalDependencies() {
211
238
  return {
239
+ peerDependencies: this.peerDependencies,
240
+ installedBinaries: this.installedBinaries,
212
241
  referencedDependencyIssues: this.referencedDependencyIssues,
213
- referencedDependencies: this.referencedDependencies,
242
+ entryFiles: this.entryFiles,
243
+ enabledPlugins: this.enabledPlugins,
214
244
  };
215
245
  }
216
- async findDependenciesByPlugin(pluginName, pluginTitle, pluginCallback) {
217
- const pluginConfig = this.getConfigForPlugin(pluginName);
218
- if (!pluginConfig)
219
- return [];
220
- const patterns = this.getConfigurationEntryFilePattern(pluginName);
221
- const cwd = this.dir;
222
- const ignore = this.getWorkspaceIgnorePatterns();
223
- const configFilePaths = await _pureGlob({ patterns, cwd, ignore });
224
- debugLogFiles(`Globbed ${pluginTitle} config file paths`, configFilePaths);
225
- if (configFilePaths.length === 0)
226
- return [];
227
- const referencedDependencyIssues = (await Promise.all(configFilePaths.map(async (configFilePath) => {
228
- const dependencies = await pluginCallback(configFilePath, {
229
- cwd,
230
- manifest: this.manifest,
231
- config: pluginConfig,
232
- rootConfig: this.rootConfig,
233
- workspaceConfig: this.config,
234
- isProduction: this.isProduction,
235
- });
236
- return dependencies.map(symbol => ({ type: 'unlisted', filePath: configFilePath, symbol }));
237
- }))).flat();
238
- debugLogIssues(`Dependencies used by ${pluginTitle} configuration`, referencedDependencyIssues);
239
- return referencedDependencyIssues;
240
- }
241
246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://github.com/webpro/knip",
6
6
  "repository": "github:webpro/knip",
@@ -42,9 +42,10 @@
42
42
  "@esbuild-kit/esm-loader": "^2.5.4",
43
43
  "@npmcli/map-workspaces": "^3.0.1",
44
44
  "@snyk/github-codeowners": "^1.1.0",
45
+ "bash-parser": "^0.5.0",
45
46
  "chalk": "^5.2.0",
46
47
  "easy-table": "^1.2.0",
47
- "esbuild": "^0.17.3",
48
+ "esbuild": "^0.17.4",
48
49
  "esbuild-register": "3.4.2",
49
50
  "eslint": "^8.32.0",
50
51
  "fast-glob": "^3.2.12",
@@ -52,6 +53,7 @@
52
53
  "globby": "^13.1.3",
53
54
  "js-yaml": "^4.1.0",
54
55
  "micromatch": "^4.0.5",
56
+ "minimist": "^1.2.7",
55
57
  "nano-memoize": "^2.0.0",
56
58
  "patch-package": "^6.5.1",
57
59
  "pretty-ms": "^8.0.0",
@@ -62,17 +64,18 @@
62
64
  "zod": "^3.20.2"
63
65
  },
64
66
  "devDependencies": {
65
- "@jest/types": "29.3.1",
67
+ "@jest/types": "^29.4.0",
66
68
  "@npmcli/package-json": "3.0.0",
67
69
  "@release-it/bumper": "^4.0.2",
68
70
  "@types/eslint": "8.4.10",
69
71
  "@types/js-yaml": "4.0.5",
70
72
  "@types/micromatch": "4.0.2",
73
+ "@types/minimist": "^1.2.2",
71
74
  "@types/node": "18.11.18",
72
75
  "@types/npmcli__map-workspaces": "3.0.0",
73
76
  "@types/webpack": "5.28.0",
74
- "@typescript-eslint/eslint-plugin": "5.48.2",
75
- "@typescript-eslint/parser": "5.48.2",
77
+ "@typescript-eslint/eslint-plugin": "^5.49.0",
78
+ "@typescript-eslint/parser": "^5.49.0",
76
79
  "eslint-import-resolver-typescript": "3.5.3",
77
80
  "eslint-plugin-import": "2.27.5",
78
81
  "globstar": "1.0.0",
@@ -81,7 +84,7 @@
81
84
  "remark-cli": "11.0.0",
82
85
  "remark-preset-webpro": "0.0.1",
83
86
  "tsx": "3.12.2",
84
- "type-fest": "3.5.2",
87
+ "type-fest": "^3.5.3",
85
88
  "typescript": "4.9.4"
86
89
  },
87
90
  "engines": {
@@ -1 +0,0 @@
1
- export declare const resolve: (binary: string, args: string[]) => string[];
@@ -1,5 +0,0 @@
1
- import { getDependenciesFromLoaderArguments } from '../index.js';
2
- export const resolve = (binary, args) => {
3
- const dependenciesFromArguments = getDependenciesFromLoaderArguments(args);
4
- return [binary, args[0], ...dependenciesFromArguments];
5
- };
@@ -1 +0,0 @@
1
- export declare const resolve: (binary: string, args: string[]) => string[];
@@ -1,5 +0,0 @@
1
- import { getDependenciesFromLoaderArguments } from '../index.js';
2
- export const resolve = (binary, args) => {
3
- const dependenciesFromArguments = getDependenciesFromLoaderArguments(args);
4
- return [binary, args[0], ...dependenciesFromArguments];
5
- };
@@ -1,2 +0,0 @@
1
- export * as crossEnv from './cross-env.js';
2
- export * as dotenv from './dotenv.js';
@@ -1,2 +0,0 @@
1
- export * as crossEnv from './cross-env.js';
2
- export * as dotenv from './dotenv.js';
@@ -1 +0,0 @@
1
- export declare const resolve: (binary: string, args: string[]) => string[];
@@ -1,5 +0,0 @@
1
- import { getDependenciesFromLoaderArguments } from '../index.js';
2
- export const resolve = (binary, args) => {
3
- const dependenciesFromArguments = getDependenciesFromLoaderArguments(args);
4
- return dependenciesFromArguments;
5
- };
@@ -1 +0,0 @@
1
- export declare const resolve: (binary: string, args: string[]) => (string | undefined)[];
@@ -1,8 +0,0 @@
1
- import { getDependenciesFromLoaderArguments, getFirstPositionalArg } from '../index.js';
2
- export const resolve = (binary, args) => {
3
- if (/-y|--yes/.test(args[0]))
4
- return [];
5
- const dependenciesFromArguments = getDependenciesFromLoaderArguments(args);
6
- const firstArgument = getFirstPositionalArg(args);
7
- return [firstArgument, ...dependenciesFromArguments];
8
- };
@@ -1,2 +0,0 @@
1
- import type { PackageJson } from 'type-fest';
2
- export declare const resolve: (binary: string, args: string[], manifest: PackageJson) => string | never[] | undefined;
@@ -1,2 +0,0 @@
1
- import type { PackageJson } from 'type-fest';
2
- export declare const resolve: (binary: string, args: string[], manifest: PackageJson) => string | never[] | undefined;