knip 2.32.2 → 2.32.4

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.
@@ -1,7 +1,7 @@
1
- /// <reference types="npmcli__package-json" />
1
+ import { createPkgGraph } from '@pnpm/workspace.pkgs-graph';
2
2
  import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
3
3
  import type { Configuration, WorkspaceConfiguration } from './types/config.js';
4
- import type { PackageJson } from '@npmcli/package-json';
4
+ import type { PackageJsonWithPlugins } from './types/plugins.js';
5
5
  type ConfigurationManagerOptions = {
6
6
  cwd: string;
7
7
  isProduction: boolean;
@@ -12,26 +12,38 @@ export type Workspace = {
12
12
  dir: string;
13
13
  ancestors: string[];
14
14
  config: WorkspaceConfiguration;
15
+ manifestPath: string;
16
+ manifest: PackageJsonWithPlugins;
15
17
  };
16
18
  export declare class ConfigurationChief {
17
19
  cwd: string;
18
20
  isProduction: boolean;
19
21
  config: Configuration;
20
22
  manifestPath?: string;
21
- manifest?: PackageJson;
23
+ manifest?: PackageJsonWithPlugins;
22
24
  ignoredWorkspacePatterns: string[];
23
25
  manifestWorkspaces: Map<string, string>;
24
26
  additionalWorkspaceNames: Set<string>;
25
27
  availableWorkspaceNames: string[];
28
+ availableWorkspacePkgNames: Set<string | undefined>;
26
29
  availableWorkspaceDirs: string[];
30
+ availableWorkspaceManifests: {
31
+ dir: string;
32
+ manifest: PackageJsonWithPlugins;
33
+ }[];
34
+ workspacesGraph: ReturnType<typeof createPkgGraph> | undefined;
27
35
  enabledWorkspaces: Workspace[];
28
- localWorkspaces: Set<string>;
29
36
  resolvedConfigFilePath?: string;
30
37
  rawConfig?: any;
31
38
  constructor({ cwd, isProduction }: ConfigurationManagerOptions);
32
39
  init(): Promise<void>;
33
40
  getCompilers(): [SyncCompilers, AsyncCompilers];
34
41
  getRules(): import("./types/issues.js").Rules;
42
+ getFilters(): {
43
+ dir: string;
44
+ } | {
45
+ dir?: undefined;
46
+ };
35
47
  private normalize;
36
48
  private setWorkspaces;
37
49
  private getListedWorkspaces;
@@ -40,6 +52,7 @@ export declare class ConfigurationChief {
40
52
  private getConfiguredWorkspaceKeys;
41
53
  private getAdditionalWorkspaceNames;
42
54
  private getAvailableWorkspaceNames;
55
+ private getAvailableWorkspaceManifests;
43
56
  private getEnabledWorkspaces;
44
57
  getWorkspaces(): Workspace[];
45
58
  private getDescendentWorkspaces;
@@ -49,7 +62,6 @@ export declare class ConfigurationChief {
49
62
  private getConfigForWorkspace;
50
63
  getIssueTypesToReport(): import("./types/issues.js").Report;
51
64
  findWorkspaceByFilePath(filePath: string): Workspace | undefined;
52
- findWorkspaceByPackageName(packageName: string): Workspace | undefined;
53
65
  getUnusedIgnoredWorkspaces(): string[];
54
66
  }
55
67
  export {};
@@ -1,14 +1,15 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import mapWorkspaces from '@npmcli/map-workspaces';
3
+ import { createPkgGraph } from '@pnpm/workspace.pkgs-graph';
3
4
  import micromatch from 'micromatch';
4
5
  import { ConfigurationValidator } from './ConfigurationValidator.js';
5
6
  import { ROOT_WORKSPACE_NAME, DEFAULT_EXTENSIONS, KNIP_CONFIG_LOCATIONS } from './constants.js';
6
7
  import { defaultRules } from './issues/initializers.js';
7
8
  import * as plugins from './plugins/index.js';
8
- import { arrayify, compact } from './util/array.js';
9
+ import { arrayify } from './util/array.js';
9
10
  import parsedArgValues from './util/cli-arguments.js';
10
11
  import { partitionCompilers } from './util/compilers.js';
11
- import { ConfigurationError } from './util/errors.js';
12
+ import { ConfigurationError, LoaderError } from './util/errors.js';
12
13
  import { findFile, loadJSON } from './util/fs.js';
13
14
  import { getIncludedIssueTypes } from './util/get-included-issue-types.js';
14
15
  import { _dirGlob } from './util/glob.js';
@@ -16,6 +17,7 @@ import { _load } from './util/loader.js';
16
17
  import { getKeysByValue } from './util/object.js';
17
18
  import { join, relative, toPosix } from './util/path.js';
18
19
  import { normalizePluginConfig, toCamelCase } from './util/plugin.js';
20
+ import { _require } from './util/require.js';
19
21
  import { byPathDepth } from './util/workspace.js';
20
22
  const { config: rawConfigArg, workspace: rawWorkspaceArg, include = [], exclude = [], dependencies = false, exports = false, } = parsedArgValues;
21
23
  const workspaceArg = rawWorkspaceArg ? toPosix(rawWorkspaceArg).replace(/^\.\//, '').replace(/\/$/, '') : undefined;
@@ -55,9 +57,11 @@ export class ConfigurationChief {
55
57
  manifestWorkspaces = new Map();
56
58
  additionalWorkspaceNames = new Set();
57
59
  availableWorkspaceNames = [];
60
+ availableWorkspacePkgNames = new Set();
58
61
  availableWorkspaceDirs = [];
62
+ availableWorkspaceManifests = [];
63
+ workspacesGraph;
59
64
  enabledWorkspaces = [];
60
- localWorkspaces = new Set();
61
65
  resolvedConfigFilePath;
62
66
  rawConfig;
63
67
  constructor({ cwd, isProduction }) {
@@ -97,6 +101,11 @@ export class ConfigurationChief {
97
101
  getRules() {
98
102
  return this.config.rules;
99
103
  }
104
+ getFilters() {
105
+ if (this.workspacesGraph?.graph && workspaceArg)
106
+ return { dir: join(this.cwd, workspaceArg) };
107
+ return {};
108
+ }
100
109
  normalize(rawConfig) {
101
110
  const rules = { ...defaultRules, ...rawConfig.rules };
102
111
  const include = rawConfig.include ?? defaultConfig.include;
@@ -140,8 +149,10 @@ export class ConfigurationChief {
140
149
  .sort(byPathDepth)
141
150
  .reverse()
142
151
  .map(dir => join(this.cwd, dir));
152
+ this.availableWorkspaceManifests = this.getAvailableWorkspaceManifests(this.availableWorkspaceDirs);
153
+ this.availableWorkspacePkgNames = new Set(this.availableWorkspaceManifests.map(w => w.manifest.name));
154
+ this.workspacesGraph = createPkgGraph(this.availableWorkspaceManifests);
143
155
  this.enabledWorkspaces = this.getEnabledWorkspaces();
144
- this.localWorkspaces = new Set(compact(this.enabledWorkspaces.map(w => w.pkgName)));
145
156
  }
146
157
  getListedWorkspaces() {
147
158
  return this.manifest?.workspaces
@@ -186,6 +197,14 @@ export class ConfigurationChief {
186
197
  getAvailableWorkspaceNames() {
187
198
  return [ROOT_WORKSPACE_NAME, ...this.manifestWorkspaces.keys(), ...this.additionalWorkspaceNames].filter(name => !micromatch.isMatch(name, this.ignoredWorkspacePatterns));
188
199
  }
200
+ getAvailableWorkspaceManifests(availableWorkspaceDirs) {
201
+ return availableWorkspaceDirs.map(dir => {
202
+ const manifest = _require(join(dir, 'package.json'));
203
+ if (!manifest)
204
+ throw new LoaderError(`Unable to load package.json for ${dir}`);
205
+ return { dir, manifest };
206
+ });
207
+ }
189
208
  getEnabledWorkspaces() {
190
209
  if (workspaceArg && !existsSync(workspaceArg)) {
191
210
  throw new ConfigurationError(`Directory does not exist: ${workspaceArg}`);
@@ -198,18 +217,42 @@ export class ConfigurationChief {
198
217
  return ancestors;
199
218
  };
200
219
  const workspaceNames = workspaceArg
201
- ? [
202
- ...this.availableWorkspaceNames.reduce(getAncestors(workspaceArg), []),
203
- ...this.availableWorkspaceNames.filter(name => name === workspaceArg),
204
- ]
220
+ ? [...this.availableWorkspaceNames.reduce(getAncestors(workspaceArg), []), workspaceArg]
205
221
  : this.availableWorkspaceNames;
206
- return workspaceNames.sort(byPathDepth).map((name) => ({
207
- name,
208
- pkgName: this.manifestWorkspaces.get(name) ?? this.manifest?.name ?? `NOT_FOUND_${name}`,
209
- dir: join(this.cwd, name),
210
- config: this.getConfigForWorkspace(name),
211
- ancestors: this.availableWorkspaceNames.reduce(getAncestors(name), []),
212
- }));
222
+ const graph = this.workspacesGraph?.graph;
223
+ const ws = new Set();
224
+ if (graph && workspaceArg) {
225
+ const seen = new Set();
226
+ const initialWorkspaces = new Set(workspaceNames.map(name => join(this.cwd, name)));
227
+ const workspaceDirsWithDependants = new Set(initialWorkspaces);
228
+ const addDependents = (dir) => {
229
+ seen.add(dir);
230
+ const deps = graph[dir]?.dependencies ?? [];
231
+ if (deps.length > 0 && Array.from(initialWorkspaces).some(dir => deps.includes(dir))) {
232
+ workspaceDirsWithDependants.add(dir);
233
+ }
234
+ deps.filter(dir => !seen.has(dir)).forEach(addDependents);
235
+ };
236
+ this.availableWorkspaceNames.map(name => join(this.cwd, name)).forEach(addDependents);
237
+ workspaceDirsWithDependants.forEach(dir => ws.add(relative(this.cwd, dir) || ROOT_WORKSPACE_NAME));
238
+ }
239
+ else {
240
+ workspaceNames.forEach(name => ws.add(name));
241
+ }
242
+ return Array.from(ws)
243
+ .sort(byPathDepth)
244
+ .map((name) => {
245
+ const dir = join(this.cwd, name);
246
+ return {
247
+ name,
248
+ pkgName: this.manifestWorkspaces.get(name) ?? this.manifest?.name ?? `NOT_FOUND_${name}`,
249
+ dir,
250
+ config: this.getConfigForWorkspace(name),
251
+ ancestors: this.availableWorkspaceNames.reduce(getAncestors(name), []),
252
+ manifestPath: join(dir, 'package.json'),
253
+ manifest: this.availableWorkspaceManifests?.find(item => item.dir === dir)?.manifest ?? {},
254
+ };
255
+ });
213
256
  }
214
257
  getWorkspaces() {
215
258
  return this.enabledWorkspaces;
@@ -281,9 +324,6 @@ export class ConfigurationChief {
281
324
  const workspaceDir = this.availableWorkspaceDirs.find(workspaceDir => filePath.startsWith(workspaceDir + '/'));
282
325
  return this.enabledWorkspaces.find(workspace => workspace.dir === workspaceDir);
283
326
  }
284
- findWorkspaceByPackageName(packageName) {
285
- return this.enabledWorkspaces.find(workspace => workspace.pkgName === packageName);
286
- }
287
327
  getUnusedIgnoredWorkspaces() {
288
328
  const ignoredWorkspaceNames = this.config.ignoreWorkspaces;
289
329
  const workspaceNames = [...this.manifestWorkspaces.keys(), ...this.additionalWorkspaceNames];
@@ -233,9 +233,10 @@ export class DependencyDeputy {
233
233
  const installedBinaries = this.getInstalledBinaries(workspaceName);
234
234
  referencedDependencies?.forEach(pkg => pkg in rootIgnoreDependencies && rootIgnoreDependencies[pkg]++);
235
235
  referencedBinaries?.forEach(binaryName => binaryName in rootIgnoreBinaries && rootIgnoreBinaries[binaryName]++);
236
- const dependencies = this.isStrict
237
- ? this.getProductionDependencies(workspaceName)
238
- : [...this.getProductionDependencies(workspaceName), ...this.getDevDependencies(workspaceName)];
236
+ const dependencies = [
237
+ ...this.getProductionDependencies(workspaceName),
238
+ ...this.getDevDependencies(workspaceName),
239
+ ];
239
240
  const peerDependencies = this.getPeerDependencies(workspaceName);
240
241
  const isReferencedDep = (name) => referencedDependencies?.has(name) && dependencies.includes(name);
241
242
  const isReferencedBin = (name) => referencedBinaries?.has(name) && installedBinaries?.has(name);
@@ -251,16 +252,17 @@ export class DependencyDeputy {
251
252
  .forEach(identifier => configurationHints.add({ workspaceName, identifier, type: 'ignoreBinaries' }));
252
253
  }
253
254
  const installedBinaries = this.getInstalledBinaries(ROOT_WORKSPACE_NAME);
254
- const dependencies = this.isStrict
255
- ? this.getProductionDependencies(ROOT_WORKSPACE_NAME)
256
- : [...this.getProductionDependencies(ROOT_WORKSPACE_NAME), ...this.getDevDependencies(ROOT_WORKSPACE_NAME)];
255
+ const dependencies = [
256
+ ...this.getProductionDependencies(ROOT_WORKSPACE_NAME),
257
+ ...this.getDevDependencies(ROOT_WORKSPACE_NAME),
258
+ ];
257
259
  const peerDependencies = this.getPeerDependencies(ROOT_WORKSPACE_NAME);
258
260
  Object.keys(rootIgnoreBinaries)
259
261
  .filter(key => IGNORED_GLOBAL_BINARIES.includes(key) || (rootIgnoreBinaries[key] !== 0 && installedBinaries?.has(key)))
260
262
  .forEach(identifier => configurationHints.add({ workspaceName: ROOT_WORKSPACE_NAME, identifier, type: 'ignoreBinaries' }));
261
263
  Object.keys(rootIgnoreDependencies)
262
264
  .filter(key => IGNORED_DEPENDENCIES.includes(key) ||
263
- (rootIgnoreDependencies[key] !== 0 && !peerDependencies.includes(key) && dependencies.includes(key)))
265
+ (rootIgnoreDependencies[key] === 0 && !peerDependencies.includes(key) && !dependencies.includes(key)))
264
266
  .forEach(identifier => configurationHints.add({ workspaceName: ROOT_WORKSPACE_NAME, identifier, type: 'ignoreDependencies' }));
265
267
  return { configurationHints };
266
268
  }
@@ -1,16 +1,21 @@
1
1
  import type { ConfigurationHint, Issue, Rules } from './types/issues.js';
2
+ type Filters = Partial<{
3
+ dir: string;
4
+ }>;
2
5
  type IssueCollectorOptions = {
3
6
  cwd: string;
4
7
  rules: Rules;
8
+ filters: Filters;
5
9
  };
6
10
  export declare class IssueCollector {
7
11
  private cwd;
8
12
  private rules;
13
+ private filters;
9
14
  private issues;
10
15
  private counters;
11
16
  private referencedFiles;
12
17
  private configurationHints;
13
- constructor({ cwd, rules }: IssueCollectorOptions);
18
+ constructor({ cwd, rules, filters }: IssueCollectorOptions);
14
19
  addFileCounts({ processed, unused }: {
15
20
  processed: number;
16
21
  unused: number;
@@ -7,13 +7,15 @@ function objectInSet(set, obj) {
7
7
  export class IssueCollector {
8
8
  cwd;
9
9
  rules;
10
+ filters;
10
11
  issues = initIssues();
11
12
  counters = initCounters();
12
13
  referencedFiles = new Set();
13
14
  configurationHints = new Set();
14
- constructor({ cwd, rules }) {
15
+ constructor({ cwd, rules, filters }) {
15
16
  this.cwd = cwd;
16
17
  this.rules = rules;
18
+ this.filters = filters;
17
19
  }
18
20
  addFileCounts({ processed, unused }) {
19
21
  this.counters.processed += processed;
@@ -21,14 +23,18 @@ export class IssueCollector {
21
23
  }
22
24
  addFilesIssues(filePaths) {
23
25
  filePaths.forEach(filePath => {
24
- if (!this.referencedFiles.has(filePath)) {
25
- this.issues.files.add(filePath);
26
- this.counters.files++;
27
- this.counters.processed++;
28
- }
26
+ if (this.filters.dir && !filePath.startsWith(this.filters.dir + '/'))
27
+ return;
28
+ if (this.referencedFiles.has(filePath))
29
+ return;
30
+ this.issues.files.add(filePath);
31
+ this.counters.files++;
32
+ this.counters.processed++;
29
33
  });
30
34
  }
31
35
  addIssue(issue) {
36
+ if (this.filters.dir && !issue.filePath.startsWith(this.filters.dir + '/'))
37
+ return;
32
38
  const key = relative(this.cwd, issue.filePath);
33
39
  issue.severity = this.rules[issue.type];
34
40
  this.issues[issue.type][key] = this.issues[issue.type][key] ?? {};
@@ -53,7 +53,7 @@ export declare class ProjectPrincipal {
53
53
  };
54
54
  scripts: Set<string>;
55
55
  };
56
- private resolveModule;
56
+ resolveModule(specifier: string, filePath?: string): ts.ResolvedModuleFull | undefined;
57
57
  getHasReferences(filePath: string, exportedItem: ExportItem): {
58
58
  external: boolean;
59
59
  internal: boolean;
@@ -2,6 +2,7 @@ import * as npm from './manifest/index.js';
2
2
  import * as plugins from './plugins/index.js';
3
3
  import { debugLogArray, debugLogObject } from './util/debug.js';
4
4
  import { _pureGlob, negate, hasProductionSuffix, hasNoProductionSuffix, prependDirToPattern } from './util/glob.js';
5
+ import { FAKE_PATH } from './util/loader.js';
5
6
  import { get, getKeysByValue } from './util/object.js';
6
7
  import { join, toPosix } from './util/path.js';
7
8
  import { fromEntryPattern, fromProductionEntryPattern, isEntryPattern, isProductionEntryPattern, } from './util/protocols.js';
@@ -184,7 +185,7 @@ export class WorkspaceWorker {
184
185
  if (patterns.length > 0 && configFilePaths.length === 0)
185
186
  continue;
186
187
  if (patterns.length === 0)
187
- configFilePaths.push('__FAKE__');
188
+ configFilePaths.push(FAKE_PATH);
188
189
  const pluginDependencies = new Set();
189
190
  for (const configFilePath of configFilePaths) {
190
191
  const dependencies = await plugin.findDependencies(configFilePath, {
package/dist/index.js CHANGED
@@ -2,21 +2,17 @@ import micromatch from 'micromatch';
2
2
  import { _getDependenciesFromScripts } from './binaries/index.js';
3
3
  import { ConfigurationChief } from './ConfigurationChief.js';
4
4
  import { ConsoleStreamer } from './ConsoleStreamer.js';
5
- import { ROOT_WORKSPACE_NAME } from './constants.js';
6
5
  import { DependencyDeputy } from './DependencyDeputy.js';
7
6
  import { IssueCollector } from './IssueCollector.js';
8
7
  import { PrincipalFactory } from './PrincipalFactory.js';
9
8
  import { ProjectPrincipal } from './ProjectPrincipal.js';
10
9
  import { compact } from './util/array.js';
11
10
  import { debugLogObject, debugLogArray, debugLog } from './util/debug.js';
12
- import { LoaderError } from './util/errors.js';
13
- import { findFile } from './util/fs.js';
14
11
  import { _glob, negate } from './util/glob.js';
15
12
  import { getEntryPathFromManifest, getPackageNameFromFilePath, getPackageNameFromModuleSpecifier, } from './util/modules.js';
16
- import { dirname, isInNodeModules, join, isInternal, toAbsolute } from './util/path.js';
13
+ import { dirname, isInNodeModules, join, isInternal } from './util/path.js';
17
14
  import { fromBinary, isBinary } from './util/protocols.js';
18
- import { _resolveSpecifier, _tryResolve } from './util/require.js';
19
- import { _require } from './util/require.js';
15
+ import { _resolveSpecifier } from './util/require.js';
20
16
  import { loadTSConfig } from './util/tsconfig-loader.js';
21
17
  import { WorkspaceWorker } from './WorkspaceWorker.js';
22
18
  export const main = async (unresolvedConfiguration) => {
@@ -32,17 +28,18 @@ export const main = async (unresolvedConfiguration) => {
32
28
  const workspaces = chief.getWorkspaces();
33
29
  const report = chief.getIssueTypesToReport();
34
30
  const rules = chief.getRules();
31
+ const filters = chief.getFilters();
35
32
  const isReportDependencies = report.dependencies || report.unlisted || report.unresolved;
36
33
  const isReportValues = report.exports || report.nsExports || report.classMembers;
37
34
  const isReportTypes = report.types || report.nsTypes || report.enumMembers;
38
- const collector = new IssueCollector({ cwd, rules });
35
+ const collector = new IssueCollector({ cwd, rules, filters });
39
36
  const enabledPluginsStore = new Map();
40
37
  deputy.addIgnored(chief.config.ignoreBinaries, chief.config.ignoreDependencies);
41
- debugLogObject('Included workspaces', workspaces);
38
+ debugLogObject('Included workspaces', workspaces.map(w => w.pkgName));
39
+ debugLogObject('Included workspace configs', workspaces.map(w => ({ name: w.name, pkgName: w.pkgName, config: w.config, ancestors: w.ancestors })));
42
40
  const handleReferencedDependency = ({ specifier, containingFilePath, principal, workspace, }) => {
43
41
  if (isInternal(specifier)) {
44
- const absSpecifier = toAbsolute(specifier, dirname(containingFilePath));
45
- const filePath = _tryResolve(absSpecifier, containingFilePath);
42
+ const filePath = principal.resolveModule(specifier, containingFilePath)?.resolvedFileName;
46
43
  if (filePath) {
47
44
  const ignorePatterns = workspace.config.ignore.map(pattern => join(dirname(containingFilePath), pattern));
48
45
  const isIgnored = micromatch.isMatch(filePath, ignorePatterns);
@@ -68,7 +65,7 @@ export const main = async (unresolvedConfiguration) => {
68
65
  if (!isHandled)
69
66
  collector.addIssue({ type: 'unlisted', filePath: containingFilePath, symbol: specifier });
70
67
  if (packageName && specifier !== packageName) {
71
- const otherWorkspace = chief.findWorkspaceByPackageName(packageName);
68
+ const otherWorkspace = chief.availableWorkspaceManifests.find(w => w.manifest.name === packageName);
72
69
  if (otherWorkspace) {
73
70
  const filePath = _resolveSpecifier(otherWorkspace.dir, specifier);
74
71
  if (filePath) {
@@ -83,14 +80,9 @@ export const main = async (unresolvedConfiguration) => {
83
80
  }
84
81
  };
85
82
  for (const workspace of workspaces) {
86
- const { name, dir, config, ancestors, pkgName } = workspace;
83
+ const { name, dir, config, ancestors, pkgName, manifestPath, manifest } = workspace;
87
84
  const { paths, ignoreDependencies, ignoreBinaries } = config;
88
- const isRoot = name === ROOT_WORKSPACE_NAME;
89
85
  streamer.cast(`Analyzing workspace (${name})...`);
90
- const manifestPath = isRoot ? chief.manifestPath : findFile(dir, 'package.json');
91
- const manifest = isRoot ? chief.manifest : manifestPath && _require(manifestPath);
92
- if (!manifestPath || !manifest)
93
- throw new LoaderError(`Unable to load package.json for ${name}`);
94
86
  deputy.addWorkspace({ name, dir, manifestPath, manifest, ignoreDependencies, ignoreBinaries });
95
87
  const { compilerOptions, definitionPaths } = await loadTSConfig(join(dir, tsConfigFile ?? 'tsconfig.json'));
96
88
  const principal = factory.getPrincipal({ cwd: dir, paths, compilerOptions, compilers, pkgName });
@@ -195,7 +187,7 @@ export const main = async (unresolvedConfiguration) => {
195
187
  exportedSymbols.set(filePath, exported);
196
188
  for (const [specifierFilePath, importItems] of internal.entries()) {
197
189
  const packageName = getPackageNameFromModuleSpecifier(importItems.specifier);
198
- if (packageName && chief.localWorkspaces.has(packageName)) {
190
+ if (packageName && chief.availableWorkspacePkgNames.has(packageName)) {
199
191
  external.add(packageName);
200
192
  if (_principal === principal) {
201
193
  const workspace = chief.findWorkspaceByFilePath(specifierFilePath);
@@ -7,19 +7,21 @@ export const ENABLERS = ['ava'];
7
7
  export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
8
8
  export const CONFIG_FILE_PATTERNS = ['ava.config.{js,cjs,mjs}', 'package.json'];
9
9
  export const ENTRY_FILE_PATTERNS = [
10
- `test.{js,cjs,mjs}`,
11
- `{src,source}/test.{js,cjs,mjs}`,
12
- `**/__tests__/**/*.{js,cjs,mjs}`,
13
- `**/*.spec.{js,cjs,mjs}`,
14
- `**/*.test.{js,cjs,mjs}`,
15
- `**/test-*.{js,cjs,mjs}`,
16
- `**/test/**/*.{js,cjs,mjs}`,
17
- `**/tests/**/*.{js,cjs,mjs}`,
10
+ `test.{js,cjs,mjs,ts}`,
11
+ `{src,source}/test.{js,cjs,mjs,ts}`,
12
+ `**/__tests__/**/*.{js,cjs,mjs,ts}`,
13
+ `**/*.spec.{js,cjs,mjs,ts}`,
14
+ `**/*.test.{js,cjs,mjs,ts}`,
15
+ `**/test-*.{js,cjs,mjs,ts}`,
16
+ `**/test/**/*.{js,cjs,mjs,ts}`,
17
+ `**/tests/**/*.{js,cjs,mjs,ts}`,
18
18
  '!**/__tests__/**/__{helper,fixture}?(s)__/**/*',
19
19
  '!**/test?(s)/**/{helper,fixture}?(s)/**/*',
20
20
  ];
21
21
  const findAvaDependencies = async (configFilePath, { cwd, manifest, isProduction }) => {
22
- const config = configFilePath.endsWith('package.json') ? manifest.ava : await load(configFilePath);
22
+ let config = configFilePath.endsWith('package.json') ? manifest.ava : await load(configFilePath);
23
+ if (typeof config === 'function')
24
+ config = config();
23
25
  const entryPatterns = (config?.files ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);
24
26
  if (isProduction)
25
27
  return entryPatterns;
@@ -1,5 +1,8 @@
1
- export type PluginConfig = {
1
+ type Config = {
2
2
  files?: string[];
3
3
  require?: string[];
4
4
  nodeArguments?: string[];
5
+ extensions?: string[];
5
6
  };
7
+ export type AvaConfig = Config | (() => Config);
8
+ export {};
@@ -2,7 +2,19 @@ import { existsSync } from 'node:fs';
2
2
  import ts from 'typescript';
3
3
  import { sanitizeSpecifier } from '../util/modules.js';
4
4
  import { dirname, extname, isInternal, join } from '../util/path.js';
5
+ import { isDeclarationFileExtension } from './ast-helpers.js';
5
6
  import { ensureRealFilePath, isVirtualFilePath } from './utils.js';
7
+ const simpleResolver = (name, containingFile) => {
8
+ const resolvedFileName = join(dirname(containingFile), name);
9
+ if (existsSync(resolvedFileName)) {
10
+ return {
11
+ resolvedFileName,
12
+ extension: extname(resolvedFileName),
13
+ isExternalLibraryImport: false,
14
+ resolvedUsingTsExtension: false,
15
+ };
16
+ }
17
+ };
6
18
  export function createCustomModuleResolver(customSys, compilerOptions, virtualFileExtensions) {
7
19
  function resolveModuleNames(moduleNames, containingFile) {
8
20
  return moduleNames.map(moduleName => resolveModuleName(moduleName, containingFile));
@@ -13,17 +25,18 @@ export function createCustomModuleResolver(customSys, compilerOptions, virtualFi
13
25
  if (!tsResolvedModule) {
14
26
  const extension = extname(sanitizedSpecifier);
15
27
  if (extension && isInternal(sanitizedSpecifier) && !virtualFileExtensions.includes(extension)) {
16
- const resolvedFileName = join(dirname(containingFile), sanitizedSpecifier);
17
- if (existsSync(resolvedFileName)) {
18
- return {
19
- resolvedFileName,
20
- extension,
21
- isExternalLibraryImport: false,
22
- resolvedUsingTsExtension: false,
23
- };
24
- }
28
+ const module = simpleResolver(sanitizedSpecifier, containingFile);
29
+ if (module)
30
+ return module;
25
31
  }
26
32
  }
33
+ if (tsResolvedModule &&
34
+ isDeclarationFileExtension(tsResolvedModule?.extension) &&
35
+ isInternal(tsResolvedModule.resolvedFileName)) {
36
+ const module = simpleResolver(sanitizedSpecifier, containingFile);
37
+ if (module)
38
+ return module;
39
+ }
27
40
  if (virtualFileExtensions.length === 0)
28
41
  return tsResolvedModule;
29
42
  if (tsResolvedModule && !isVirtualFilePath(tsResolvedModule.resolvedFileName, virtualFileExtensions)) {
@@ -1 +1,2 @@
1
+ export declare const FAKE_PATH = "__FAKE__";
1
2
  export declare const _load: (filePath: string) => Promise<any>;
@@ -4,7 +4,10 @@ import { loadJSON, loadYAML, loadFile, parseJSON, parseYAML } from './fs.js';
4
4
  import { extname } from './path.js';
5
5
  import { timerify } from './Performance.js';
6
6
  import { jiti } from './register.js';
7
+ export const FAKE_PATH = '__FAKE__';
7
8
  const load = async (filePath) => {
9
+ if (filePath === FAKE_PATH)
10
+ return;
8
11
  try {
9
12
  const ext = extname(filePath);
10
13
  if (/rc$/.test(filePath)) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.32.2";
1
+ export declare const version = "2.32.4";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.32.2';
1
+ export const version = '2.32.4';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.32.2",
3
+ "version": "2.32.4",
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",
@@ -19,12 +19,12 @@
19
19
  "scripts": {
20
20
  "knip": "node ./dist/cli.js",
21
21
  "knip:production": "node ./dist/cli.js --production --strict --ignore-internal",
22
- "lint": "eslint scripts src tests",
23
- "lint:fix": "eslint scripts src tests --fix",
24
- "format": "prettier scripts src tests schema.json --with-node-modules --write --config .prettierrc",
25
- "pretest": "node rmdir.js tmp && swc src -d tmp/src && swc tests -d tmp/tests",
22
+ "lint": "eslint scripts src test",
23
+ "lint:fix": "eslint scripts src test --fix",
24
+ "format": "prettier scripts src test schema.json --with-node-modules --write --config .prettierrc",
25
+ "pretest": "node rmdir.js tmp && swc src -d tmp/src && swc test -d tmp/test",
26
26
  "test": "node --no-warnings --test tmp",
27
- "coverage": "c8 --reporter html node --no-warnings --loader tsx --test tests/*.test.ts tests/*/*.test.ts",
27
+ "coverage": "c8 --reporter html node --no-warnings --loader tsx --test test/*.test.ts test/*/*.test.ts",
28
28
  "watch": "rm $(which knip); npm link && tsc --watch",
29
29
  "prebuild": "node rmdir.js dist",
30
30
  "build": "tsc",
@@ -44,6 +44,8 @@
44
44
  "dependencies": {
45
45
  "@ericcornelissen/bash-parser": "^0.5.2",
46
46
  "@npmcli/map-workspaces": "^3.0.4",
47
+ "@pnpm/logger": "5.0.0",
48
+ "@pnpm/workspace.pkgs-graph": "2.0.6",
47
49
  "@snyk/github-codeowners": "^1.1.0",
48
50
  "chalk": "^5.2.0",
49
51
  "easy-table": "^1.2.0",