knip 2.3.2 → 2.6.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 (43) hide show
  1. package/README.md +139 -133
  2. package/dist/ConfigurationChief.d.ts +15 -0
  3. package/dist/ConfigurationChief.js +10 -1
  4. package/dist/ConfigurationValidator.d.ts +6 -3
  5. package/dist/ConfigurationValidator.js +16 -0
  6. package/dist/IssueCollector.d.ts +12 -7
  7. package/dist/IssueCollector.js +7 -3
  8. package/dist/PrincipalFactory.d.ts +1 -3
  9. package/dist/PrincipalFactory.js +6 -6
  10. package/dist/ProjectPrincipal.d.ts +4 -5
  11. package/dist/ProjectPrincipal.js +2 -5
  12. package/dist/WorkspaceWorker.js +4 -3
  13. package/dist/binaries/resolvers/fallback.js +4 -2
  14. package/dist/binaries/resolvers/yarn.js +1 -0
  15. package/dist/cli.js +2 -2
  16. package/dist/constants.js +2 -3
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +47 -42
  19. package/dist/issues/initializers.d.ts +2 -1
  20. package/dist/issues/initializers.js +1 -0
  21. package/dist/plugins/eslint/helpers.js +3 -3
  22. package/dist/plugins/eslint/types.d.ts +1 -1
  23. package/dist/plugins/jest/index.js +2 -3
  24. package/dist/reporters/codeowners.js +2 -2
  25. package/dist/reporters/compact.js +1 -1
  26. package/dist/reporters/symbols.js +7 -5
  27. package/dist/reporters/util.d.ts +4 -1
  28. package/dist/reporters/util.js +6 -4
  29. package/dist/types/config.d.ts +2 -0
  30. package/dist/types/issues.d.ts +3 -0
  31. package/dist/typescript/visitors/scripts/execa.d.ts +3 -0
  32. package/dist/typescript/visitors/scripts/execa.js +16 -0
  33. package/dist/typescript/visitors/scripts/index.js +2 -1
  34. package/dist/util/compilers.d.ts +2 -1
  35. package/dist/util/object.d.ts +1 -0
  36. package/dist/util/object.js +8 -0
  37. package/dist/util/path.d.ts +5 -4
  38. package/dist/util/path.js +6 -4
  39. package/dist/util/tsconfig-loader.js +2 -2
  40. package/dist/version.d.ts +1 -1
  41. package/dist/version.js +1 -1
  42. package/package.json +10 -6
  43. package/schema.json +20 -0
@@ -1,14 +1,19 @@
1
- import type { Issue } from './types/issues.js';
1
+ import type { Issue, Rules } from './types/issues.js';
2
2
  type IssueCollectorOptions = {
3
3
  cwd: string;
4
+ rules: Rules;
4
5
  };
5
6
  export declare class IssueCollector {
6
- cwd: string;
7
- issues: import("./types/issues.js").Issues;
8
- counters: import("./types/issues.js").Counters;
9
- referencedFiles: Set<string>;
10
- constructor({ cwd }: IssueCollectorOptions);
11
- addTotalFileCount(count: number): void;
7
+ private cwd;
8
+ private rules;
9
+ private issues;
10
+ private counters;
11
+ private referencedFiles;
12
+ constructor({ cwd, rules }: IssueCollectorOptions);
13
+ addFileCounts({ processed, unused }: {
14
+ processed: number;
15
+ unused: number;
16
+ }): void;
12
17
  addFilesIssues(filePaths: string[]): void;
13
18
  addIssue(issue: Issue): void;
14
19
  getIssues(): {
@@ -2,14 +2,17 @@ import { initIssues, initCounters } from './issues/initializers.js';
2
2
  import { relative } from './util/path.js';
3
3
  export class IssueCollector {
4
4
  cwd;
5
+ rules;
5
6
  issues = initIssues();
6
7
  counters = initCounters();
7
8
  referencedFiles = new Set();
8
- constructor({ cwd }) {
9
+ constructor({ cwd, rules }) {
9
10
  this.cwd = cwd;
11
+ this.rules = rules;
10
12
  }
11
- addTotalFileCount(count) {
12
- this.counters.total += count;
13
+ addFileCounts({ processed, unused }) {
14
+ this.counters.processed += processed;
15
+ this.counters.total += processed + unused;
13
16
  }
14
17
  addFilesIssues(filePaths) {
15
18
  filePaths.forEach(filePath => {
@@ -22,6 +25,7 @@ export class IssueCollector {
22
25
  }
23
26
  addIssue(issue) {
24
27
  const key = relative(this.cwd, issue.filePath);
28
+ issue.severity = this.rules[issue.type];
25
29
  this.issues[issue.type][key] = this.issues[issue.type][key] ?? {};
26
30
  if (!this.issues[issue.type][key][issue.symbol]) {
27
31
  this.issues[issue.type][key][issue.symbol] = issue;
@@ -1,7 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { ProjectPrincipal } from './ProjectPrincipal.js';
3
3
  import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
4
- import type { Report } from './types/issues.js';
5
4
  type Paths = ts.CompilerOptions['paths'];
6
5
  type Principal = {
7
6
  principal: ProjectPrincipal;
@@ -12,13 +11,12 @@ type Principals = Set<Principal>;
12
11
  type Options = {
13
12
  cwd: string;
14
13
  compilerOptions: ts.CompilerOptions;
15
- report: Report;
16
14
  paths: Paths;
17
15
  compilers: [SyncCompilers, AsyncCompilers];
18
16
  };
19
17
  export declare class PrincipalFactory {
20
18
  principals: Principals;
21
- getPrincipal({ cwd, compilerOptions, report, paths, compilers }: Options): ProjectPrincipal;
19
+ getPrincipal({ cwd, compilerOptions, paths, compilers }: Options): ProjectPrincipal;
22
20
  private findReusablePrincipal;
23
21
  private linkPrincipal;
24
22
  private addNewPrincipal;
@@ -1,8 +1,8 @@
1
1
  import { ProjectPrincipal } from './ProjectPrincipal.js';
2
- import { join, isAbsolute } from './util/path.js';
2
+ import { join, toAbsolute } from './util/path.js';
3
3
  const mergePaths = (cwd, compilerOptions, paths = {}) => {
4
4
  const overridePaths = Object.keys(paths).reduce((overridePaths, key) => {
5
- overridePaths[key] = paths[key].map(entry => (isAbsolute(entry) ? entry : join(cwd, entry)));
5
+ overridePaths[key] = paths[key].map(entry => toAbsolute(entry, cwd));
6
6
  return overridePaths;
7
7
  }, {});
8
8
  compilerOptions.paths = { ...compilerOptions.paths, ...overridePaths };
@@ -10,7 +10,7 @@ const mergePaths = (cwd, compilerOptions, paths = {}) => {
10
10
  };
11
11
  export class PrincipalFactory {
12
12
  principals = new Set();
13
- getPrincipal({ cwd, compilerOptions, report, paths, compilers }) {
13
+ getPrincipal({ cwd, compilerOptions, paths, compilers }) {
14
14
  compilerOptions = mergePaths(cwd, compilerOptions, paths);
15
15
  const principal = this.findReusablePrincipal(compilerOptions);
16
16
  if (principal) {
@@ -18,7 +18,7 @@ export class PrincipalFactory {
18
18
  return principal.principal;
19
19
  }
20
20
  else {
21
- return this.addNewPrincipal({ cwd, compilerOptions, report, paths, compilers });
21
+ return this.addNewPrincipal({ cwd, compilerOptions, paths, compilers });
22
22
  }
23
23
  }
24
24
  findReusablePrincipal(compilerOptions) {
@@ -36,8 +36,8 @@ export class PrincipalFactory {
36
36
  principal.principal.compilerOptions.paths = { ...principal.principal.compilerOptions.paths, ...paths };
37
37
  principal.cwds.add(cwd);
38
38
  }
39
- addNewPrincipal({ cwd, compilerOptions, report, compilers }) {
40
- const principal = new ProjectPrincipal({ cwd, compilerOptions, report, compilers });
39
+ addNewPrincipal({ cwd, compilerOptions, compilers }) {
40
+ const principal = new ProjectPrincipal({ cwd, compilerOptions, compilers });
41
41
  const pathKeys = new Set(Object.keys(compilerOptions?.paths ?? {}));
42
42
  compilerOptions.baseUrl = join(cwd, compilerOptions.baseUrl ?? '.');
43
43
  this.principals.add({ principal, cwds: new Set([cwd]), pathKeys });
@@ -2,11 +2,9 @@ import ts from 'typescript';
2
2
  import { SourceFileManager } from './typescript/SourceFileManager.js';
3
3
  import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
4
4
  import type { ExportItem, ExportItemMember } from './types/exports.js';
5
- import type { Report } from './types/issues.js';
6
5
  type ProjectPrincipalOptions = {
7
6
  compilerOptions: ts.CompilerOptions;
8
7
  cwd: string;
9
- report: Report;
10
8
  compilers: [SyncCompilers, AsyncCompilers];
11
9
  };
12
10
  export declare class ProjectPrincipal {
@@ -15,7 +13,6 @@ export declare class ProjectPrincipal {
15
13
  skipExportsAnalysis: Set<string>;
16
14
  cwd: string;
17
15
  compilerOptions: ts.CompilerOptions;
18
- isReportTypes: boolean;
19
16
  extensions: Set<string>;
20
17
  syncCompilers: SyncCompilers;
21
18
  asyncCompilers: AsyncCompilers;
@@ -26,7 +23,7 @@ export declare class ProjectPrincipal {
26
23
  lsFindReferences: ts.LanguageService['findReferences'];
27
24
  program?: ts.Program;
28
25
  };
29
- constructor({ compilerOptions, cwd, report, compilers }: ProjectPrincipalOptions);
26
+ constructor({ compilerOptions, cwd, compilers }: ProjectPrincipalOptions);
30
27
  private createProgram;
31
28
  private hasAcceptedExtension;
32
29
  addEntryPath(filePath: string): void;
@@ -37,7 +34,9 @@ export declare class ProjectPrincipal {
37
34
  getUsedResolvedFiles(): string[];
38
35
  private getProgramSourceFiles;
39
36
  getUnreferencedFiles(): string[];
40
- analyzeSourceFile(filePath: string): {
37
+ analyzeSourceFile(filePath: string, { skipTypeOnly }: {
38
+ skipTypeOnly: boolean;
39
+ }): {
41
40
  imports: {
42
41
  internal: import("./types/imports.js").Imports;
43
42
  unresolved: Set<string>;
@@ -25,12 +25,11 @@ export class ProjectPrincipal {
25
25
  skipExportsAnalysis = new Set();
26
26
  cwd;
27
27
  compilerOptions;
28
- isReportTypes;
29
28
  extensions;
30
29
  syncCompilers;
31
30
  asyncCompilers;
32
31
  backend;
33
- constructor({ compilerOptions, cwd, report, compilers }) {
32
+ constructor({ compilerOptions, cwd, compilers }) {
34
33
  this.cwd = cwd;
35
34
  this.compilerOptions = {
36
35
  ...compilerOptions,
@@ -38,7 +37,6 @@ export class ProjectPrincipal {
38
37
  allowNonTsExtensions: [...compilers].flat().length > 0,
39
38
  };
40
39
  const [syncCompilers, asyncCompilers] = compilers;
41
- this.isReportTypes = report.types || report.nsTypes || report.enumMembers;
42
40
  this.extensions = new Set([...DEFAULT_EXTENSIONS, ...syncCompilers.keys(), ...asyncCompilers.keys()]);
43
41
  this.syncCompilers = syncCompilers;
44
42
  this.asyncCompilers = asyncCompilers;
@@ -103,11 +101,10 @@ export class ProjectPrincipal {
103
101
  const sourceFiles = this.getProgramSourceFiles();
104
102
  return Array.from(this.projectPaths).filter(filePath => !sourceFiles.has(filePath));
105
103
  }
106
- analyzeSourceFile(filePath) {
104
+ analyzeSourceFile(filePath, { skipTypeOnly }) {
107
105
  const sourceFile = this.backend.program?.getSourceFile(filePath);
108
106
  if (!sourceFile)
109
107
  throw new Error(`Unable to find ${filePath}`);
110
- const skipTypeOnly = !this.isReportTypes;
111
108
  const skipExports = this.skipExportsAnalysis.has(filePath);
112
109
  const { imports, exports, scripts } = getImportsAndExports(sourceFile, { skipTypeOnly, skipExports });
113
110
  const { internal, unresolved, external } = imports;
@@ -3,7 +3,8 @@ import * as npm from './manifest/index.js';
3
3
  import * as plugins from './plugins/index.js';
4
4
  import { debugLogArray, debugLogObject } from './util/debug.js';
5
5
  import { _pureGlob, negate, hasProductionSuffix, hasNoProductionSuffix, prependDirToPattern } from './util/glob.js';
6
- import { join } from './util/path.js';
6
+ import { getKeysByValue } from './util/object.js';
7
+ import { join, toPosix } from './util/path.js';
7
8
  const negatedTestFilePatterns = TEST_FILE_PATTERNS.map(negate);
8
9
  export class WorkspaceWorker {
9
10
  name;
@@ -52,7 +53,7 @@ export class WorkspaceWorker {
52
53
  this.enabled[pluginName] = true;
53
54
  }
54
55
  }
55
- this.enabledPlugins = pluginEntries.filter(([name]) => this.enabled[name]).map(([name]) => name);
56
+ this.enabledPlugins = getKeysByValue(this.enabled, true);
56
57
  const enabledPluginNames = this.enabledPlugins.map(name => plugins[name].NAME);
57
58
  debugLogObject(`Enabled plugins (${this.name})`, enabledPluginNames);
58
59
  }
@@ -219,7 +220,7 @@ export class WorkspaceWorker {
219
220
  config: pluginConfig,
220
221
  isProduction: this.isProduction,
221
222
  });
222
- dependencies.forEach(specifier => {
223
+ dependencies.map(toPosix).forEach(specifier => {
223
224
  pluginDependencies.add(specifier);
224
225
  this.referencedDependencies.add([configFilePath, specifier]);
225
226
  });
@@ -1,11 +1,12 @@
1
1
  import parseArgs from 'minimist';
2
2
  import { compact } from '../../util/array.js';
3
- import { toBinary, tryResolveFilePaths } from '../util.js';
3
+ import { toBinary, tryResolveFilePath, tryResolveFilePaths } from '../util.js';
4
4
  const withPositional = parsed => [parsed._[0], parsed.require].flat();
5
5
  const withoutPositional = parsed => [parsed.require].flat();
6
6
  const argFilters = {
7
7
  'babel-node': withPositional,
8
8
  esbuild: withPositional,
9
+ execa: withPositional,
9
10
  nodemon: withPositional,
10
11
  'ts-node': withPositional,
11
12
  zx: withPositional,
@@ -16,5 +17,6 @@ export const resolve = (binary, args, { cwd }) => {
16
17
  const parsed = parseArgs(args, { string: ['r'], alias: { require: ['r', 'loader'] }, boolean: ['quiet', 'verbose'] });
17
18
  const argFilter = argFilters[binary] ?? argFilters.default;
18
19
  const filteredArgs = compact(argFilter(parsed));
19
- return [toBinary(binary), ...tryResolveFilePaths(cwd, filteredArgs)];
20
+ const bin = binary.startsWith('.') ? tryResolveFilePath(cwd, binary, binary) : [toBinary(binary)];
21
+ return [...bin, ...tryResolveFilePaths(cwd, filteredArgs)];
20
22
  };
@@ -25,6 +25,7 @@ const commands = [
25
25
  'unlink',
26
26
  'unplug',
27
27
  'up',
28
+ 'upgrade',
28
29
  'upgrade-interactive',
29
30
  'version',
30
31
  'why',
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ const printReport = reporter in reporters ? reporters[reporter] : await _load(re
23
23
  const run = async () => {
24
24
  try {
25
25
  const perfObserver = new Performance(isObservePerf);
26
- const { report, issues, counters } = await main({
26
+ const { report, issues, counters, rules } = await main({
27
27
  cwd,
28
28
  tsConfigFile: tsConfig,
29
29
  gitignore: !isNoGitIgnore,
@@ -33,7 +33,7 @@ const run = async () => {
33
33
  });
34
34
  await printReport({ report, issues, cwd, isProduction, options: reporterOptions });
35
35
  const totalErrorCount = Object.keys(report)
36
- .filter(reportGroup => report[reportGroup])
36
+ .filter(reportGroup => report[reportGroup] && rules[reportGroup] === 'error')
37
37
  .reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
38
38
  if (isObservePerf) {
39
39
  await perfObserver.finalize();
package/dist/constants.js CHANGED
@@ -2,9 +2,8 @@ export const ROOT_WORKSPACE_NAME = '.';
2
2
  export const KNIP_CONFIG_LOCATIONS = ['knip.json', 'knip.jsonc', '.knip.json', '.knip.jsonc', 'knip.ts', 'knip.js'];
3
3
  export const DEFAULT_EXTENSIONS = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.mts', '.cts'];
4
4
  export const TEST_FILE_PATTERNS = [
5
- '**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs}',
6
- '**/__tests__/**/*.{js,jsx,ts,tsx,mjs,cjs}',
7
- '**/test/**/*.{js,jsx,ts,tsx,mjs,cjs}',
5
+ '**/*{.,-}{test,spec}.{js,jsx,ts,tsx,mjs,cjs}',
6
+ '**/{test,__tests__}/**/*.{js,jsx,ts,tsx,mjs,cjs}',
8
7
  ];
9
8
  export const IGNORED_GLOBAL_BINARIES = [
10
9
  'bun',
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export declare const main: (unresolvedConfiguration: CommandLineOptions) => Prom
5
5
  report: import("./types/issues.js").Report;
6
6
  issues: import("./types/issues.js").Issues;
7
7
  counters: import("./types/issues.js").Counters;
8
+ rules: import("./types/issues.js").Rules;
8
9
  }>;
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { LoaderError } from './util/errors.js';
11
11
  import { findFile } from './util/fs.js';
12
12
  import { _glob } from './util/glob.js';
13
13
  import { getEntryPathFromManifest, getPackageNameFromFilePath, getPackageNameFromModuleSpecifier, } from './util/modules.js';
14
- import { dirname, isInNodeModules, join, isInternal, isAbsolute } from './util/path.js';
14
+ import { dirname, isInNodeModules, join, isInternal, toAbsolute } from './util/path.js';
15
15
  import { _resolveSpecifier, _tryResolve } from './util/require.js';
16
16
  import { _require } from './util/require.js';
17
17
  import { loadTSConfig as loadCompilerOptions } from './util/tsconfig-loader.js';
@@ -22,17 +22,22 @@ export const main = async (unresolvedConfiguration) => {
22
22
  const chief = new ConfigurationChief({ cwd, isProduction });
23
23
  const deputy = new DependencyDeputy({ isStrict });
24
24
  const factory = new PrincipalFactory();
25
- const collector = new IssueCollector({ cwd });
26
25
  const streamer = new ConsoleStreamer({ isEnabled: isShowProgress });
27
26
  streamer.cast('Reading workspace configuration(s)...');
28
27
  await chief.init();
29
28
  const compilers = chief.getCompilers();
30
29
  const workspaces = chief.getEnabledWorkspaces();
31
30
  const report = chief.getIssueTypesToReport();
31
+ const rules = chief.getRules();
32
+ const isReportDependencies = report.dependencies || report.unlisted || report.unresolved;
33
+ const isReportValues = report.exports || report.nsExports || report.classMembers;
34
+ const isReportTypes = report.types || report.nsTypes || report.enumMembers;
35
+ const collector = new IssueCollector({ cwd, rules });
36
+ const enabledPluginsStore = new Map();
32
37
  debugLogObject('Included workspaces', workspaces);
33
38
  const handleReferencedDependency = ({ specifier, containingFilePath, principal, workspace, }) => {
34
39
  if (isInternal(specifier)) {
35
- const absSpecifier = isAbsolute(specifier) ? specifier : join(dirname(containingFilePath), specifier);
40
+ const absSpecifier = toAbsolute(specifier, dirname(containingFilePath));
36
41
  const filePath = _tryResolve(absSpecifier, containingFilePath);
37
42
  if (filePath) {
38
43
  principal.addEntryPath(filePath);
@@ -68,7 +73,6 @@ export const main = async (unresolvedConfiguration) => {
68
73
  }
69
74
  }
70
75
  };
71
- const enabledPluginsStore = new Map();
72
76
  for (const workspace of workspaces) {
73
77
  const { name, dir, config, ancestors } = workspace;
74
78
  const { paths, ignoreDependencies, ignoreBinaries } = config;
@@ -80,7 +84,7 @@ export const main = async (unresolvedConfiguration) => {
80
84
  throw new LoaderError(`Unable to load package.json for ${name}`);
81
85
  deputy.addWorkspace({ name, dir, manifestPath, manifest, ignoreDependencies, ignoreBinaries });
82
86
  const compilerOptions = await loadCompilerOptions(join(dir, tsConfigFile ?? 'tsconfig.json'));
83
- const principal = factory.getPrincipal({ cwd: dir, report: report, paths, compilerOptions, compilers });
87
+ const principal = factory.getPrincipal({ cwd: dir, paths, compilerOptions, compilers });
84
88
  const worker = new WorkspaceWorker({
85
89
  name,
86
90
  dir,
@@ -173,10 +177,9 @@ export const main = async (unresolvedConfiguration) => {
173
177
  const exportedSymbols = new Map();
174
178
  const importedSymbols = new Map();
175
179
  const analyzeSourceFile = (filePath) => {
176
- collector.counters.processed++;
177
180
  const workspace = chief.findWorkspaceByFilePath(filePath);
178
181
  if (workspace) {
179
- const { imports, exports, scripts } = principal.analyzeSourceFile(filePath);
182
+ const { imports, exports, scripts } = principal.analyzeSourceFile(filePath, { skipTypeOnly: !isReportTypes });
180
183
  const { internal, external, unresolved } = imports;
181
184
  const { exported, duplicate } = exports;
182
185
  if (exported.size > 0)
@@ -246,51 +249,53 @@ export const main = async (unresolvedConfiguration) => {
246
249
  } while (size !== principal.entryPaths.size);
247
250
  const unusedFiles = principal.getUnreferencedFiles();
248
251
  collector.addFilesIssues(unusedFiles);
249
- collector.addTotalFileCount(analyzedFiles.size + unusedFiles.length);
250
- streamer.cast('Analyzing source files...');
251
- for (const [filePath, exportItems] of exportedSymbols.entries()) {
252
- const importedModule = importedSymbols.get(filePath);
253
- if (importedModule) {
254
- for (const [symbol, exportedItem] of exportItems.entries()) {
255
- if (principal.isPublicExport(exportedItem))
256
- continue;
257
- if (importedModule.symbols.has(symbol)) {
258
- if (report.enumMembers && exportedItem.type === 'enum' && exportedItem.members) {
259
- principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
260
- collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
261
- });
252
+ collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });
253
+ if (isReportValues || isReportTypes) {
254
+ streamer.cast('Analyzing source files...');
255
+ for (const [filePath, exportItems] of exportedSymbols.entries()) {
256
+ const importedModule = importedSymbols.get(filePath);
257
+ if (importedModule) {
258
+ for (const [symbol, exportedItem] of exportItems.entries()) {
259
+ if (principal.isPublicExport(exportedItem))
260
+ continue;
261
+ if (importedModule.symbols.has(symbol)) {
262
+ if (report.enumMembers && exportedItem.type === 'enum' && exportedItem.members) {
263
+ principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
264
+ collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
265
+ });
266
+ }
267
+ if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
268
+ principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
269
+ collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
270
+ });
271
+ }
272
+ continue;
262
273
  }
263
- if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
264
- principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
265
- collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
266
- });
274
+ if (importedModule.isReExported || importedModule.isStar) {
275
+ const isReExportedByEntryFile = isExportedInEntryFile(importedModule);
276
+ if (!isReExportedByEntryFile && !principal.hasExternalReferences(filePath, exportedItem)) {
277
+ if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
278
+ collector.addIssue({ type: 'nsTypes', filePath, symbol, symbolType: exportedItem.type });
279
+ }
280
+ else {
281
+ collector.addIssue({ type: 'nsExports', filePath, symbol });
282
+ }
283
+ }
267
284
  }
268
- continue;
269
- }
270
- if (importedModule.isReExported || importedModule.isStar) {
271
- const isReExportedByEntryFile = isExportedInEntryFile(importedModule);
272
- if (!isReExportedByEntryFile && !principal.hasExternalReferences(filePath, exportedItem)) {
285
+ else {
273
286
  if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
274
- collector.addIssue({ type: 'nsTypes', filePath, symbol, symbolType: exportedItem.type });
287
+ collector.addIssue({ type: 'types', filePath, symbol, symbolType: exportedItem.type });
275
288
  }
276
- else {
277
- collector.addIssue({ type: 'nsExports', filePath, symbol });
289
+ else if (!importedModule.isDynamic || !principal.hasExternalReferences(filePath, exportedItem)) {
290
+ collector.addIssue({ type: 'exports', filePath, symbol });
278
291
  }
279
292
  }
280
293
  }
281
- else {
282
- if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
283
- collector.addIssue({ type: 'types', filePath, symbol, symbolType: exportedItem.type });
284
- }
285
- else if (!importedModule.isDynamic || !principal.hasExternalReferences(filePath, exportedItem)) {
286
- collector.addIssue({ type: 'exports', filePath, symbol });
287
- }
288
- }
289
294
  }
290
295
  }
291
296
  }
292
297
  }
293
- if (report.dependencies) {
298
+ if (isReportDependencies) {
294
299
  const { dependencyIssues, devDependencyIssues } = deputy.settleDependencyIssues();
295
300
  dependencyIssues.forEach(issue => collector.addIssue(issue));
296
301
  if (!isProduction)
@@ -298,5 +303,5 @@ export const main = async (unresolvedConfiguration) => {
298
303
  }
299
304
  const { issues, counters } = collector.getIssues();
300
305
  streamer.clear();
301
- return { report, issues, counters };
306
+ return { report, issues, counters, rules };
302
307
  };
@@ -1,3 +1,4 @@
1
- import { Issues, Counters } from '../types/issues.js';
1
+ import { Issues, Counters, Rules } from '../types/issues.js';
2
2
  export declare const initIssues: () => Issues;
3
3
  export declare const initCounters: () => Counters;
4
+ export declare const defaultRules: Rules;
@@ -8,3 +8,4 @@ export const initCounters = () => ({
8
8
  processed: 0,
9
9
  total: 0,
10
10
  });
11
+ export const defaultRules = Object.fromEntries(ISSUE_TYPES.map(issueType => [issueType, 'error']));
@@ -1,6 +1,6 @@
1
1
  import { compact } from '../../util/array.js';
2
2
  import { getPackageNameFromModuleSpecifier } from '../../util/modules.js';
3
- import { isAbsolute, isInternal, join, dirname } from '../../util/path.js';
3
+ import { isInternal, dirname, toAbsolute } from '../../util/path.js';
4
4
  import { load } from '../../util/plugin.js';
5
5
  import { _resolve } from '../../util/require.js';
6
6
  import { fallback } from './fallback.js';
@@ -36,7 +36,7 @@ export const getDependenciesDeep = async (configFilePath, dependencies = new Set
36
36
  if (config.extends) {
37
37
  for (const extend of [config.extends].flat()) {
38
38
  if (isInternal(extend)) {
39
- const filePath = isAbsolute(extend) ? extend : join(dirname(configFilePath), extend);
39
+ const filePath = toAbsolute(extend, dirname(configFilePath));
40
40
  const extendConfigFilePath = _resolve(filePath);
41
41
  dependencies.add(extendConfigFilePath);
42
42
  addAll(await getDependenciesDeep(extendConfigFilePath, dependencies, options));
@@ -82,7 +82,7 @@ const getImportPluginDependencies = (settings) => {
82
82
  };
83
83
  export const getDependenciesFromSettings = (settings = {}) => {
84
84
  return compact(Object.entries(settings).reduce((packageNames, [settingKey, settings]) => {
85
- if (/^import\/(parsers|resolvers)?/.test(settingKey)) {
85
+ if (/^import\/(parsers|resolvers)?/.test(settingKey) && typeof settings !== 'string') {
86
86
  return [...packageNames, ...getImportPluginDependencies(settings)];
87
87
  }
88
88
  return packageNames;
@@ -4,7 +4,7 @@ type ParserOptions = {
4
4
  presets: string[];
5
5
  };
6
6
  };
7
- type Settings = Record<string, Record<string, unknown>>;
7
+ type Settings = Record<string, Record<string, unknown> | string>;
8
8
  type Rules = Record<string, string | number>;
9
9
  type BaseConfig = {
10
10
  extends?: string | string[];
@@ -1,17 +1,16 @@
1
- import { isAbsolute, join, dirname, isInternal } from '../../util/path.js';
1
+ import { join, isInternal, toAbsolute, dirname } from '../../util/path.js';
2
2
  import { timerify } from '../../util/Performance.js';
3
3
  import { hasDependency, load } from '../../util/plugin.js';
4
4
  export const NAME = 'Jest';
5
5
  export const ENABLERS = ['jest'];
6
6
  export const isEnabled = ({ dependencies, manifest }) => hasDependency(dependencies, ENABLERS) || Boolean(manifest.name?.startsWith('jest-presets'));
7
7
  export const CONFIG_FILE_PATTERNS = ['jest.config.{js,ts,mjs,cjs,json}', 'package.json'];
8
- const maybeJoin = (base, id) => (isAbsolute(id) ? id : join(dirname(base), id));
9
8
  const resolveExtensibleConfig = async (configFilePath) => {
10
9
  const config = await load(configFilePath);
11
10
  if (config?.preset) {
12
11
  const { preset } = config;
13
12
  if (isInternal(preset)) {
14
- const presetConfigPath = maybeJoin(configFilePath, preset);
13
+ const presetConfigPath = toAbsolute(preset, dirname(configFilePath));
15
14
  const presetConfig = await resolveExtensibleConfig(presetConfigPath);
16
15
  Object.assign(config, presetConfig);
17
16
  }
@@ -1,11 +1,11 @@
1
1
  import { OwnershipEngine } from '@snyk/github-codeowners/dist/lib/ownership/index.js';
2
2
  import chalk from 'chalk';
3
- import { isAbsolute, relative, resolve } from '../util/path.js';
3
+ import { toRelative, relative, resolve } from '../util/path.js';
4
4
  import { getTitle, logTitle, logIssueLine } from './util.js';
5
5
  const logIssueSet = (issues) => {
6
6
  issues
7
7
  .sort((a, b) => (a.owner < b.owner ? -1 : 1))
8
- .forEach(issue => console.log(chalk.cyan(issue.owner), isAbsolute(issue.symbol) ? relative(issue.symbol) : issue.symbol));
8
+ .forEach(issue => console.log(chalk.cyan(issue.owner), toRelative(issue.symbol)));
9
9
  };
10
10
  const logIssueRecord = (issues) => {
11
11
  const sortedByFilePath = issues.sort((a, b) => (a.owner < b.owner ? -1 : 1));
@@ -1,7 +1,7 @@
1
1
  import { getTitle, logTitle, logIssueLine, logIssueSet } from './util.js';
2
2
  const logIssueRecord = (issues) => {
3
3
  const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));
4
- sortedByFilePath.forEach(({ filePath, symbols, parentSymbol }) => logIssueLine({ filePath, symbols, parentSymbol }));
4
+ sortedByFilePath.forEach(logIssueLine);
5
5
  };
6
6
  export default ({ report, issues }) => {
7
7
  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
@@ -1,15 +1,17 @@
1
+ import chalk from 'chalk';
1
2
  import EasyTable from 'easy-table';
2
3
  import { relative } from '../util/path.js';
3
- import { getTitle, logTitle, logIssueSet } from './util.js';
4
+ import { getTitle, logTitle, logIssueSet, identity } from './util.js';
4
5
  const TRUNCATE_WIDTH = 40;
5
6
  const truncate = (text) => (text.length > TRUNCATE_WIDTH ? text.slice(0, TRUNCATE_WIDTH - 3) + '...' : text);
6
7
  const logIssueRecord = (issues) => {
7
8
  const table = new EasyTable();
8
9
  issues.forEach(issue => {
9
- table.cell('symbol', issue.symbols ? truncate(issue.symbols.join(', ')) : issue.symbol);
10
- issue.parentSymbol && table.cell('parentSymbol', issue.parentSymbol);
11
- issue.symbolType && table.cell('symbolType', issue.symbolType);
12
- table.cell('filePath', relative(issue.filePath));
10
+ const print = issue.severity === 'warn' ? chalk.grey : identity;
11
+ table.cell('symbol', print(issue.symbols ? truncate(issue.symbols.join(', ')) : issue.symbol));
12
+ issue.parentSymbol && table.cell('parentSymbol', print(issue.parentSymbol));
13
+ issue.symbolType && table.cell('symbolType', print(issue.symbolType));
14
+ table.cell('filePath', print(relative(issue.filePath)));
13
15
  table.newRow();
14
16
  });
15
17
  console.log(table.sort(['filePath', 'parentSymbol', 'symbol']).print().trim());
@@ -1,4 +1,6 @@
1
1
  import { ISSUE_TYPE_TITLE } from '../constants.js';
2
+ import { IssueSeverity } from '../types/issues.js';
3
+ export declare const identity: (text: string) => string;
2
4
  export declare const getTitle: (reportType: keyof typeof ISSUE_TYPE_TITLE) => string;
3
5
  export declare const logTitle: (title: string, count: number) => void;
4
6
  type LogIssueLine = {
@@ -6,7 +8,8 @@ type LogIssueLine = {
6
8
  filePath: string;
7
9
  symbols?: string[];
8
10
  parentSymbol?: string;
11
+ severity?: IssueSeverity;
9
12
  };
10
- export declare const logIssueLine: ({ owner, filePath, symbols, parentSymbol }: LogIssueLine) => void;
13
+ export declare const logIssueLine: ({ owner, filePath, symbols, parentSymbol, severity }: LogIssueLine) => void;
11
14
  export declare const logIssueSet: (issues: string[]) => void;
12
15
  export {};
@@ -1,15 +1,17 @@
1
1
  import chalk from 'chalk';
2
2
  import { ISSUE_TYPE_TITLE } from '../constants.js';
3
- import { isAbsolute, relative } from '../util/path.js';
3
+ import { toRelative, relative } from '../util/path.js';
4
+ export const identity = (text) => text;
4
5
  export const getTitle = (reportType) => {
5
6
  return ISSUE_TYPE_TITLE[reportType];
6
7
  };
7
8
  export const logTitle = (title, count) => console.log(`${chalk.bold.yellow.underline(title)} (${count})`);
8
- export const logIssueLine = ({ owner, filePath, symbols, parentSymbol }) => {
9
+ export const logIssueLine = ({ owner, filePath, symbols, parentSymbol, severity }) => {
9
10
  const symbol = symbols ? `: ${symbols.join(', ')}` : '';
10
11
  const parent = parentSymbol ? ` (${parentSymbol})` : '';
11
- console.log(`${owner ? `${chalk.cyan(owner)} ` : ''}${relative(filePath)}${symbol}${parent}`);
12
+ const print = severity === 'warn' ? chalk.grey : identity;
13
+ console.log(`${owner ? `${chalk.cyan(owner)} ` : ''}${print(`${relative(filePath)}${symbol}${parent}`)}`);
12
14
  };
13
15
  export const logIssueSet = (issues) => {
14
- issues.sort().forEach(value => console.log(isAbsolute(value) ? relative(value) : value));
16
+ issues.sort().forEach(value => console.log(toRelative(value)));
15
17
  };
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { ConfigurationValidator } from '../ConfigurationValidator.js';
3
3
  import * as Plugins from '../plugins/index.js';
4
+ import { Rules } from './issues.js';
4
5
  import type { SyncCompilers, AsyncCompilers } from '../types/compilers.js';
5
6
  export type RawConfiguration = z.infer<typeof ConfigurationValidator>;
6
7
  type NormalizedGlob = string[];
@@ -22,6 +23,7 @@ interface BaseWorkspaceConfiguration {
22
23
  export interface WorkspaceConfiguration extends BaseWorkspaceConfiguration, Partial<PluginsConfiguration> {
23
24
  }
24
25
  export interface Configuration {
26
+ rules: Rules;
25
27
  include: string[];
26
28
  exclude: string[];
27
29
  ignore: NormalizedGlob;