knip 2.35.0 → 2.37.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 (36) hide show
  1. package/README.md +1 -1
  2. package/dist/ConfigurationChief.d.ts +5 -4
  3. package/dist/ProjectPrincipal.d.ts +6 -1
  4. package/dist/ProjectPrincipal.js +7 -5
  5. package/dist/WorkspaceWorker.d.ts +1 -1
  6. package/dist/index.js +27 -4
  7. package/dist/plugins/_template/index.js +2 -1
  8. package/dist/plugins/eslint/helpers.d.ts +2 -7
  9. package/dist/plugins/prettier/index.js +2 -2
  10. package/dist/plugins/typescript/index.js +3 -3
  11. package/dist/plugins/vite/index.js +2 -12
  12. package/dist/plugins/vitest/helpers.d.ts +2 -2
  13. package/dist/plugins/vitest/index.d.ts +2 -2
  14. package/dist/plugins/vitest/index.js +30 -13
  15. package/dist/plugins/vitest/types.d.ts +14 -3
  16. package/dist/reporters/index.d.ts +1 -0
  17. package/dist/reporters/index.js +2 -0
  18. package/dist/reporters/jsonExt.d.ts +3 -0
  19. package/dist/reporters/jsonExt.js +73 -0
  20. package/dist/types/config.d.ts +1 -0
  21. package/dist/types/issues.d.ts +3 -0
  22. package/dist/types/package-json.d.ts +41 -0
  23. package/dist/types/plugins.d.ts +2 -4
  24. package/dist/types/util.d.ts +16 -0
  25. package/dist/types/util.js +1 -0
  26. package/dist/typescript/ast-helpers.d.ts +1 -0
  27. package/dist/typescript/ast-helpers.js +3 -0
  28. package/dist/typescript/getImportsAndExports.js +2 -2
  29. package/dist/util/cli-arguments.d.ts +1 -1
  30. package/dist/util/cli-arguments.js +1 -1
  31. package/dist/version.d.ts +1 -1
  32. package/dist/version.js +1 -1
  33. package/package.json +17 -17
  34. package/schema.json +1 -1
  35. package/dist/plugins/vite/types.d.ts +0 -13
  36. /package/dist/{plugins/vite/types.js → types/package-json.js} +0 -0
package/README.md CHANGED
@@ -704,7 +704,7 @@ $ npx knip --help
704
704
  -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
705
705
  --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
706
706
  --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
707
- --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)
707
+ --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)
708
708
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
709
709
  --no-config-hints Suppress configuration hints
710
710
  --no-exit-code Always exit with code zero (0)
@@ -1,7 +1,8 @@
1
+ /// <reference types="npmcli__package-json" />
1
2
  import { createPkgGraph } from '@pnpm/workspace.pkgs-graph';
2
3
  import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
3
4
  import type { Configuration, WorkspaceConfiguration } from './types/config.js';
4
- import type { PackageJsonWithPlugins } from './types/plugins.js';
5
+ import type { PackageJson } from '@npmcli/package-json';
5
6
  type ConfigurationManagerOptions = {
6
7
  cwd: string;
7
8
  isProduction: boolean;
@@ -13,14 +14,14 @@ export type Workspace = {
13
14
  ancestors: string[];
14
15
  config: WorkspaceConfiguration;
15
16
  manifestPath: string;
16
- manifest: PackageJsonWithPlugins;
17
+ manifest: PackageJson;
17
18
  };
18
19
  export declare class ConfigurationChief {
19
20
  cwd: string;
20
21
  isProduction: boolean;
21
22
  config: Configuration;
22
23
  manifestPath?: string;
23
- manifest?: PackageJsonWithPlugins;
24
+ manifest?: PackageJson;
24
25
  ignoredWorkspacePatterns: string[];
25
26
  manifestWorkspaces: Map<string, string>;
26
27
  additionalWorkspaceNames: Set<string>;
@@ -29,7 +30,7 @@ export declare class ConfigurationChief {
29
30
  availableWorkspaceDirs: string[];
30
31
  availableWorkspaceManifests: {
31
32
  dir: string;
32
- manifest: PackageJsonWithPlugins;
33
+ manifest: PackageJson;
33
34
  }[];
34
35
  workspacesGraph: ReturnType<typeof createPkgGraph> | undefined;
35
36
  enabledWorkspaces: Workspace[];
@@ -58,7 +58,12 @@ export declare class ProjectPrincipal {
58
58
  external: boolean;
59
59
  internal: boolean;
60
60
  };
61
- findUnusedMembers(filePath: string, members: ExportItemMember[]): string[];
61
+ findUnusedMembers(filePath: string, members: ExportItemMember[]): ExportItemMember[];
62
62
  private findReferences;
63
+ getPos(node: ts.Node, pos: number): {
64
+ pos: number;
65
+ line: number;
66
+ col: number;
67
+ };
63
68
  }
64
69
  export {};
@@ -2,7 +2,7 @@ import { isGitIgnoredSync } from 'globby';
2
2
  import ts from 'typescript';
3
3
  import { DEFAULT_EXTENSIONS } from './constants.js';
4
4
  import { IGNORED_FILE_EXTENSIONS } from './constants.js';
5
- import { getJSDocTags, isInModuleBlock } from './typescript/ast-helpers.js';
5
+ import { getJSDocTags, getLineAndCharacterOfPosition, isInModuleBlock } from './typescript/ast-helpers.js';
6
6
  import { createHosts } from './typescript/createHosts.js';
7
7
  import { getImportsAndExports } from './typescript/getImportsAndExports.js';
8
8
  import { SourceFileManager } from './typescript/SourceFileManager.js';
@@ -176,8 +176,7 @@ export class ProjectPrincipal {
176
176
  return hasReferences;
177
177
  }
178
178
  findUnusedMembers(filePath, members) {
179
- return members
180
- .filter(member => {
179
+ return members.filter(member => {
181
180
  if (getJSDocTags(member.node).has('@public'))
182
181
  return false;
183
182
  const referencedSymbols = this.findReferences(filePath, member.pos);
@@ -188,8 +187,7 @@ export class ProjectPrincipal {
188
187
  const internalRefs = files.filter(f => f === filePath);
189
188
  const externalRefs = files.filter(f => f !== filePath);
190
189
  return externalRefs.length === 0 && internalRefs.length === 0;
191
- })
192
- .map(member => member.identifier);
190
+ });
193
191
  }
194
192
  findReferences(filePath, pos) {
195
193
  try {
@@ -199,4 +197,8 @@ export class ProjectPrincipal {
199
197
  return [];
200
198
  }
201
199
  }
200
+ getPos(node, pos) {
201
+ const { line, character } = getLineAndCharacterOfPosition(node, pos);
202
+ return { pos, line: line + 1, col: character + 1 };
203
+ }
202
204
  }
@@ -1,5 +1,5 @@
1
1
  import type { Configuration, PluginName, WorkspaceConfiguration } from './types/config.js';
2
- import type { PackageJsonWithPlugins } from './types/plugins.js';
2
+ import type { PackageJsonWithPlugins } from './types/package-json.js';
3
3
  import type { InstalledBinaries, HostDependencies } from './types/workspace.js';
4
4
  type WorkspaceManagerOptions = {
5
5
  name: string;
package/dist/index.js CHANGED
@@ -304,12 +304,24 @@ export const main = async (unresolvedConfiguration) => {
304
304
  if (isProduction)
305
305
  continue;
306
306
  principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
307
- collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
307
+ collector.addIssue({
308
+ type: 'enumMembers',
309
+ filePath,
310
+ symbol: member.identifier,
311
+ parentSymbol: symbol,
312
+ ...principal.getPos(member.node, member.pos),
313
+ });
308
314
  });
309
315
  }
310
316
  if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
311
317
  principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
312
- collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
318
+ collector.addIssue({
319
+ type: 'classMembers',
320
+ filePath,
321
+ symbol: member.identifier,
322
+ parentSymbol: symbol,
323
+ ...principal.getPos(member.node, member.pos),
324
+ });
313
325
  });
314
326
  }
315
327
  continue;
@@ -321,11 +333,22 @@ export const main = async (unresolvedConfiguration) => {
321
333
  if (isProduction)
322
334
  continue;
323
335
  const type = isStar ? 'nsTypes' : 'types';
324
- collector.addIssue({ type, filePath, symbol, symbolType: exportedItem.type });
336
+ collector.addIssue({
337
+ type,
338
+ filePath,
339
+ symbol,
340
+ symbolType: exportedItem.type,
341
+ ...principal.getPos(exportedItem.node, exportedItem.pos),
342
+ });
325
343
  }
326
344
  else {
327
345
  const type = isStar ? 'nsExports' : 'exports';
328
- collector.addIssue({ type, filePath, symbol });
346
+ collector.addIssue({
347
+ type,
348
+ filePath,
349
+ symbol,
350
+ ...principal.getPos(exportedItem.node, exportedItem.pos),
351
+ });
329
352
  }
330
353
  }
331
354
  }
@@ -11,7 +11,8 @@ export const PROJECT_FILE_PATTERNS = [];
11
11
  const findPluginDependencies = async (configFilePath, options) => {
12
12
  const { manifest, config, isProduction } = options;
13
13
  const localConfig = configFilePath.endsWith('package.json')
14
- ? manifest.plugin
14
+ ?
15
+ manifest.plugin
15
16
  : await load(configFilePath);
16
17
  if (!localConfig)
17
18
  return [];
@@ -1,12 +1,7 @@
1
- /// <reference types="npmcli__package-json" />
2
- import type { ESLintConfig } from './types.js';
3
- import type { PackageJson } from '@npmcli/package-json';
4
- type Manifest = PackageJson & {
5
- eslintConfig?: ESLintConfig;
6
- };
1
+ import type { PackageJsonWithPlugins } from '../../types/package-json.js';
7
2
  type GetDependenciesDeep = (configFilePath: string, options: {
8
3
  cwd: string;
9
- manifest: Manifest;
4
+ manifest: PackageJsonWithPlugins;
10
5
  }, dependencies?: Set<string>) => Promise<Set<string>>;
11
6
  export declare const getDependenciesDeep: GetDependenciesDeep;
12
7
  export declare const resolvePluginSpecifier: (specifier: string) => string;
@@ -5,8 +5,8 @@ export const ENABLERS = ['prettier'];
5
5
  export const isEnabled = ({ dependencies, config }) => hasDependency(dependencies, ENABLERS) || 'prettier' in config;
6
6
  export const CONFIG_FILE_PATTERNS = [
7
7
  '.prettierrc',
8
- '.prettierrc.{json,js,cjs,yml,yaml}',
9
- 'prettier.config.{js,cjs}',
8
+ '.prettierrc.{json,js,cjs,mjs,yml,yaml}',
9
+ 'prettier.config.{js,cjs,mjs}',
10
10
  'package.json',
11
11
  ];
12
12
  const findPrettierDependencies = async (configFilePath, { manifest, isProduction }) => {
@@ -25,19 +25,19 @@ const resolveExtensibleConfig = async (configFilePath) => {
25
25
  };
26
26
  export const findTypeScriptDependencies = async (configFilePath, options) => {
27
27
  const { isProduction } = options;
28
- if (isProduction)
29
- return [];
30
28
  const { compilerOptions } = await loadTSConfig(configFilePath);
31
29
  const localConfig = await resolveExtensibleConfig(configFilePath);
32
30
  if (!compilerOptions || !localConfig)
33
31
  return [];
32
+ const jsx = compilerOptions?.jsxImportSource ? [compilerOptions.jsxImportSource] : [];
33
+ if (isProduction)
34
+ return [...jsx];
34
35
  const extend = localConfig.extends ? [localConfig.extends].flat().filter(extend => !isInternal(extend)) : [];
35
36
  const types = compilerOptions.types ?? [];
36
37
  const plugins = Array.isArray(compilerOptions?.plugins)
37
38
  ? compilerOptions.plugins.map(plugin => (typeof plugin === 'object' && 'name' in plugin ? plugin.name : ''))
38
39
  : [];
39
40
  const importHelpers = compilerOptions?.importHelpers ? ['tslib'] : [];
40
- const jsx = compilerOptions?.jsxImportSource ? [compilerOptions.jsxImportSource] : [];
41
41
  return compact([...extend, ...types, ...plugins, ...importHelpers, ...jsx]);
42
42
  };
43
43
  export const findDependencies = timerify(findTypeScriptDependencies);
@@ -1,6 +1,6 @@
1
1
  import { timerify } from '../../util/Performance.js';
2
2
  import { hasDependency, load } from '../../util/plugin.js';
3
- import { findVitestDeps } from '../vitest/index.js';
3
+ import { findVitestDependencies } from '../vitest/index.js';
4
4
  export const NAME = 'Vite';
5
5
  export const ENABLERS = ['vite'];
6
6
  export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
@@ -9,16 +9,6 @@ const findViteDependencies = async (configFilePath, options) => {
9
9
  const localConfig = await load(configFilePath);
10
10
  if (!localConfig)
11
11
  return [];
12
- if (typeof localConfig === 'function') {
13
- const dependencies = new Set();
14
- for (const command of ['dev', 'serve', 'build']) {
15
- for (const mode of ['development', 'production']) {
16
- const config = await localConfig({ command, mode, ssrBuild: undefined });
17
- findVitestDeps(config, options).forEach(dependency => dependencies.add(dependency));
18
- }
19
- }
20
- return Array.from(dependencies);
21
- }
22
- return findVitestDeps(localConfig, options);
12
+ return findVitestDependencies(localConfig, options);
23
13
  };
24
14
  export const findDependencies = timerify(findViteDependencies);
@@ -1,6 +1,6 @@
1
- import type { VitestConfig } from './types.js';
1
+ import type { ViteConfig } from './types.js';
2
2
  type BuiltinEnvironment = 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime';
3
3
  type VitestEnvironment = BuiltinEnvironment | (string & Record<never, never>);
4
4
  export declare const getEnvPackageName: (env: VitestEnvironment) => any;
5
- export declare const getExternalReporters: (reporters?: VitestConfig['test']['reporters']) => unknown[];
5
+ export declare const getExternalReporters: (reporters?: ViteConfig['test']['reporters']) => unknown[];
6
6
  export {};
@@ -1,9 +1,9 @@
1
- import type { VitestConfigOrFn } from './types.js';
1
+ import type { ViteConfigOrFn } from './types.js';
2
2
  import type { IsPluginEnabledCallback, GenericPluginCallback, GenericPluginCallbackOptions } from '../../types/plugins.js';
3
3
  export declare const NAME = "Vitest";
4
4
  export declare const ENABLERS: string[];
5
5
  export declare const isEnabled: IsPluginEnabledCallback;
6
6
  export declare const CONFIG_FILE_PATTERNS: string[];
7
7
  export declare const ENTRY_FILE_PATTERNS: string[];
8
- export declare const findVitestDeps: (localConfig: VitestConfigOrFn, options: GenericPluginCallbackOptions) => any[];
8
+ export declare const findVitestDependencies: (localConfig: ViteConfigOrFn, options: GenericPluginCallbackOptions) => Promise<any[]>;
9
9
  export declare const findDependencies: GenericPluginCallback;
@@ -1,4 +1,3 @@
1
- import { compact } from '../../util/array.js';
2
1
  import { timerify } from '../../util/Performance.js';
3
2
  import { hasDependency, load } from '../../util/plugin.js';
4
3
  import { toEntryPattern } from '../../util/protocols.js';
@@ -8,14 +7,11 @@ export const ENABLERS = ['vitest'];
8
7
  export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
9
8
  export const CONFIG_FILE_PATTERNS = ['vitest.config.ts', 'vitest.{workspace,projects}.{ts,js,json}'];
10
9
  export const ENTRY_FILE_PATTERNS = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];
11
- export const findVitestDeps = (localConfig, options) => {
12
- const { isProduction } = options;
13
- localConfig = typeof localConfig === 'function' ? localConfig() : localConfig;
14
- if (!localConfig || !localConfig.test)
15
- return [];
10
+ const findConfigDependencies = (localConfig, options) => {
11
+ const { isProduction, config } = options;
16
12
  const testConfig = localConfig.test;
17
- const entryPatterns = (options.config?.entry ?? testConfig.include ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);
18
- if (isProduction)
13
+ const entryPatterns = (config?.entry ?? testConfig?.include ?? ENTRY_FILE_PATTERNS).map(toEntryPattern);
14
+ if (!testConfig || isProduction)
19
15
  return entryPatterns;
20
16
  const environments = testConfig.environment ? [getEnvPackageName(testConfig.environment)] : [];
21
17
  const reporters = getExternalReporters(testConfig.reporters);
@@ -24,10 +20,31 @@ export const findVitestDeps = (localConfig, options) => {
24
20
  const globalSetup = testConfig.globalSetup ? [testConfig.globalSetup].flat() : [];
25
21
  return [...entryPatterns, ...environments, ...reporters, ...coverage, ...setupFiles, ...globalSetup];
26
22
  };
27
- const findVitestDependencies = async (configFilePath, options) => {
23
+ export const findVitestDependencies = async (localConfig, options) => {
24
+ if (!localConfig)
25
+ return [];
26
+ if (typeof localConfig === 'function') {
27
+ const dependencies = new Set();
28
+ for (const command of ['dev', 'serve', 'build']) {
29
+ for (const mode of ['development', 'production']) {
30
+ const config = await localConfig({ command, mode, ssrBuild: undefined });
31
+ findConfigDependencies(config, options).forEach(dependency => dependencies.add(dependency));
32
+ }
33
+ }
34
+ return Array.from(dependencies);
35
+ }
36
+ if (!localConfig.test)
37
+ return [];
38
+ return findConfigDependencies(localConfig, options);
39
+ };
40
+ const findVitestWorkspaceDependencies = async (configFilePath, options) => {
28
41
  const localConfig = await load(configFilePath);
29
- return compact([localConfig]
30
- .flat()
31
- .flatMap(config => (!config || typeof config === 'string' ? [] : findVitestDeps(config, options))));
42
+ const dependencies = new Set();
43
+ for (const config of [localConfig].flat()) {
44
+ if (config && typeof config !== 'string') {
45
+ (await findVitestDependencies(config, options)).forEach(dependency => dependencies.add(dependency));
46
+ }
47
+ }
48
+ return Array.from(dependencies);
32
49
  };
33
- export const findDependencies = timerify(findVitestDependencies);
50
+ export const findDependencies = timerify(findVitestWorkspaceDependencies);
@@ -1,4 +1,4 @@
1
- export interface VitestConfig {
1
+ interface VitestConfig {
2
2
  test: {
3
3
  include: string[];
4
4
  coverage?: {
@@ -10,5 +10,16 @@ export interface VitestConfig {
10
10
  setupFiles?: string | string[];
11
11
  };
12
12
  }
13
- export type VitestConfigOrFn = VitestConfig | (() => VitestConfig);
14
- export type VitestWorkspaceConfig = (string | VitestConfig)[];
13
+ export interface ViteConfig extends VitestConfig {
14
+ plugins: unknown[];
15
+ }
16
+ export type COMMAND = 'dev' | 'serve' | 'build';
17
+ export type MODE = 'development' | 'production';
18
+ interface Options {
19
+ command: COMMAND;
20
+ mode: MODE;
21
+ ssrBuild?: boolean | undefined;
22
+ }
23
+ export type ViteConfigOrFn = ViteConfig | ((options: Options) => ViteConfig) | ((options: Options) => Promise<ViteConfig>);
24
+ export type VitestWorkspaceConfig = (string | ViteConfig)[];
25
+ export {};
@@ -3,5 +3,6 @@ declare const _default: {
3
3
  compact: ({ report, issues, isShowProgress }: import("../index.js").ReporterOptions) => void;
4
4
  codeowners: ({ report, issues, isShowProgress, options }: import("../index.js").ReporterOptions) => void;
5
5
  json: ({ report, issues, options }: import("../index.js").ReporterOptions) => Promise<void>;
6
+ jsonExt: ({ report, issues, options }: import("../index.js").ReporterOptions) => Promise<void>;
6
7
  };
7
8
  export default _default;
@@ -1,10 +1,12 @@
1
1
  import codeowners from './codeowners.js';
2
2
  import compact from './compact.js';
3
3
  import json from './json.js';
4
+ import jsonExt from './jsonExt.js';
4
5
  import symbols from './symbols.js';
5
6
  export default {
6
7
  symbols,
7
8
  compact,
8
9
  codeowners,
9
10
  json,
11
+ jsonExt,
10
12
  };
@@ -0,0 +1,3 @@
1
+ import type { ReporterOptions } from '../types/issues.js';
2
+ declare const _default: ({ report, issues, options }: ReporterOptions) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,73 @@
1
+ import { OwnershipEngine } from '@snyk/github-codeowners/dist/lib/ownership/index.js';
2
+ import { isFile } from '../util/fs.js';
3
+ import { relative, resolve } from '../util/path.js';
4
+ const mergeTypes = (type) => type === 'exports' || type === 'nsExports' ? 'exports' : type === 'types' || type === 'nsTypes' ? 'types' : type;
5
+ export default async ({ report, issues, options }) => {
6
+ let opts = {};
7
+ try {
8
+ opts = options ? JSON.parse(options) : opts;
9
+ }
10
+ catch (error) {
11
+ console.error(error);
12
+ }
13
+ const json = {};
14
+ const codeownersFilePath = resolve(opts.codeowners ?? '.github/CODEOWNERS');
15
+ const codeownersEngine = isFile(codeownersFilePath) && OwnershipEngine.FromCodeownersFile(codeownersFilePath);
16
+ const flatten = (issues) => Object.values(issues).flatMap(Object.values);
17
+ const initRow = (filePath) => {
18
+ const file = relative(filePath);
19
+ const row = {
20
+ file,
21
+ ...(codeownersEngine && { owners: codeownersEngine.calcFileOwnership(file) }),
22
+ ...(report.files && { files: false }),
23
+ ...(report.dependencies && { dependencies: [] }),
24
+ ...(report.devDependencies && { devDependencies: [] }),
25
+ ...(report.optionalPeerDependencies && { optionalPeerDependencies: [] }),
26
+ ...(report.unlisted && { unlisted: [] }),
27
+ ...(report.binaries && { binaries: [] }),
28
+ ...(report.unresolved && { unresolved: [] }),
29
+ ...((report.exports || report.nsExports) && { exports: [] }),
30
+ ...((report.types || report.nsTypes) && { types: [] }),
31
+ ...(report.enumMembers && { enumMembers: {} }),
32
+ ...(report.classMembers && { classMembers: {} }),
33
+ ...(report.duplicates && { duplicates: [] }),
34
+ };
35
+ return row;
36
+ };
37
+ for (const [reportType, isReportType] of Object.entries(report)) {
38
+ if (isReportType) {
39
+ if (reportType === 'files') {
40
+ Array.from(issues[reportType]).forEach(filePath => {
41
+ json[filePath] = json[filePath] ?? initRow(filePath);
42
+ json[filePath][reportType] = true;
43
+ });
44
+ }
45
+ else {
46
+ const type = mergeTypes(reportType);
47
+ flatten(issues[reportType]).forEach(issue => {
48
+ const { filePath, symbol, symbols, parentSymbol } = issue;
49
+ json[filePath] = json[filePath] ?? initRow(filePath);
50
+ if (type === 'duplicates') {
51
+ symbols && json[filePath][type]?.push(symbols.map(symbol => ({ name: symbol })));
52
+ }
53
+ else if (type === 'enumMembers' || type === 'classMembers') {
54
+ const item = json[filePath][type];
55
+ if (parentSymbol && item) {
56
+ item[parentSymbol] = item[parentSymbol] ?? [];
57
+ item[parentSymbol].push({ name: issue.symbol, line: issue.line, col: issue.col, pos: issue.pos });
58
+ }
59
+ }
60
+ else {
61
+ if (type === 'exports' || type === 'types') {
62
+ json[filePath][type]?.push({ name: issue.symbol, line: issue.line, col: issue.col, pos: issue.pos });
63
+ }
64
+ else {
65
+ json[filePath][type]?.push({ name: symbol });
66
+ }
67
+ }
68
+ });
69
+ }
70
+ }
71
+ }
72
+ console.log(JSON.stringify(Object.values(json)));
73
+ };
@@ -7,6 +7,7 @@ export type RawConfiguration = z.infer<typeof ConfigurationValidator>;
7
7
  export type RawPluginConfiguration = z.infer<typeof pluginSchema>;
8
8
  type NormalizedGlob = string[];
9
9
  export type PluginName = keyof typeof Plugins;
10
+ export type PluginMap = typeof Plugins;
10
11
  export type EnsuredPluginConfiguration = {
11
12
  config: NormalizedGlob | null;
12
13
  entry: NormalizedGlob | null;
@@ -15,6 +15,9 @@ export type Issue = {
15
15
  symbolType?: SymbolType;
16
16
  parentSymbol?: string;
17
17
  severity?: IssueSeverity;
18
+ pos?: number;
19
+ line?: number;
20
+ col?: number;
18
21
  };
19
22
  export type IssueSet = Set<string>;
20
23
  export type IssueRecords = Record<string, Record<string, Issue>>;
@@ -0,0 +1,41 @@
1
+ import type { PluginMap } from './config.js';
2
+ import type { LiteralUnion, MergeUnion } from './util.js';
3
+ type Dependency = Record<string, string>;
4
+ type ExportCondition = LiteralUnion<'import' | 'require' | 'node' | 'node-addons' | 'deno' | 'browser' | 'electron' | 'react-native' | 'default', string>;
5
+ type Exports = null | string | string[] | {
6
+ [key in ExportCondition]: Exports;
7
+ } | {
8
+ [key: string]: Exports;
9
+ };
10
+ type ExtractKeys<T, K extends string> = T extends {
11
+ PACKAGE_JSON_PATH: infer P;
12
+ } ? P extends `${infer First}.${infer Second}` ? {
13
+ [K1 in First]?: {
14
+ [K2 in Second]?: unknown;
15
+ };
16
+ } : {
17
+ [P in T['PACKAGE_JSON_PATH'] & string]?: unknown;
18
+ } : Record<K, unknown>;
19
+ type PluginKeys = {
20
+ [K in keyof PluginMap]: ExtractKeys<PluginMap[K], K>;
21
+ };
22
+ type Plugins = MergeUnion<PluginKeys[keyof PluginMap]>;
23
+ export type PackageJsonWithPlugins = {
24
+ name?: string;
25
+ main?: string;
26
+ bin?: string | Record<string, string>;
27
+ version?: string;
28
+ workspaces?: string[] | {
29
+ packages?: string[];
30
+ };
31
+ exports?: Exports;
32
+ scripts?: Record<string, string>;
33
+ dependencies?: Dependency;
34
+ devDependencies?: Dependency;
35
+ peerDependencies?: Dependency;
36
+ optionalDependencies?: Dependency;
37
+ peerDependenciesMeta?: Record<string, {
38
+ optional: true;
39
+ }>;
40
+ } & Plugins;
41
+ export {};
@@ -1,10 +1,8 @@
1
- /// <reference types="npmcli__package-json" />
2
1
  import type { EnsuredPluginConfiguration, WorkspaceConfiguration } from './config.js';
3
- import type { PackageJson } from '@npmcli/package-json';
4
- export type PackageJsonWithPlugins = PackageJson & Record<string, unknown>;
2
+ import type { PackageJsonWithPlugins } from './package-json.js';
5
3
  type IsPluginEnabledCallbackOptions = {
6
4
  cwd: string;
7
- manifest: PackageJson;
5
+ manifest: PackageJsonWithPlugins;
8
6
  dependencies: Set<string>;
9
7
  config: WorkspaceConfiguration;
10
8
  };
@@ -0,0 +1,16 @@
1
+ type Primitive = null | undefined | string | number | boolean | symbol | bigint;
2
+ export type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
3
+ type CommonKeys<T extends object> = keyof T;
4
+ type AllKeys<T> = T extends unknown ? keyof T : never;
5
+ type Subtract<A, C> = A extends C ? never : A;
6
+ type NonCommonKeys<T extends object> = Subtract<AllKeys<T>, CommonKeys<T>>;
7
+ type PickType<T, K extends AllKeys<T>> = T extends {
8
+ [k in K]?: unknown;
9
+ } ? T[K] : undefined;
10
+ type PickTypeOf<T, K extends string | number | symbol> = K extends AllKeys<T> ? PickType<T, K> : never;
11
+ export type MergeUnion<T extends object> = {
12
+ [k in CommonKeys<T>]: PickTypeOf<T, k>;
13
+ } & {
14
+ [k in NonCommonKeys<T>]?: PickTypeOf<T, k>;
15
+ };
16
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -25,4 +25,5 @@ export declare function findDescendants<T>(node: ts.Node | undefined, callback:
25
25
  export declare const isDeclarationFileExtension: (extension: string) => boolean;
26
26
  export declare const isInModuleBlock: (node: ts.Node) => boolean;
27
27
  export declare const getJSDocTags: (node: ts.Node) => Set<string>;
28
+ export declare const getLineAndCharacterOfPosition: (node: ts.Node, pos: number) => ts.LineAndCharacter;
28
29
  export {};
@@ -110,3 +110,6 @@ export const getJSDocTags = (node) => {
110
110
  }
111
111
  return tags;
112
112
  };
113
+ export const getLineAndCharacterOfPosition = (node, pos) => {
114
+ return node.getSourceFile().getLineAndCharacterOfPosition(pos);
115
+ };
@@ -95,10 +95,10 @@ export const getImportsAndExports = (sourceFile, options) => {
95
95
  const item = exports.get(identifier);
96
96
  const crew = [...item.members, ...members];
97
97
  const tags = new Set([...item.jsDocTags, ...jsDocTags]);
98
- exports.set(identifier, { ...item, node, type, pos, members: crew, jsDocTags: tags });
98
+ exports.set(identifier, { ...item, members: crew, jsDocTags: tags });
99
99
  }
100
100
  else {
101
- exports.set(identifier, { node, type, pos, members, jsDocTags });
101
+ exports.set(identifier, { node, type, members, jsDocTags, pos });
102
102
  }
103
103
  if (!jsDocTags.has('@alias')) {
104
104
  if (ts.isExportAssignment(node))
@@ -1,4 +1,4 @@
1
- export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n -W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)\n --directory [dir] Run process from a different directory (default: cwd)\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted,unresolved\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --include-entry-exports Include entry files when reporting unused exports\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n -d, --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure count and running time of expensive functions and display stats table\n -h, --help Print this help text\n -V, --version Print version\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore documentation and bug reports: https://github.com/webpro/knip";
1
+ export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n -W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)\n --directory [dir] Run process from a different directory (default: cwd)\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted,unresolved\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --include-entry-exports Include entry files when reporting unused exports\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n -d, --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure count and running time of expensive functions and display stats table\n -h, --help Print this help text\n -V, --version Print version\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore documentation and bug reports: https://github.com/webpro/knip";
2
2
  declare const _default: {
3
3
  config: string | undefined;
4
4
  debug: boolean | undefined;
@@ -20,7 +20,7 @@ Options:
20
20
  -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
21
21
  --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
22
22
  --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
23
- --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)
23
+ --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)
24
24
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
25
25
  --no-config-hints Suppress configuration hints
26
26
  --no-exit-code Always exit with code zero (0)
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.35.0";
1
+ export declare const version = "2.37.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.35.0';
1
+ export const version = '2.37.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.35.0",
3
+ "version": "2.37.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",
@@ -46,7 +46,7 @@
46
46
  "@npmcli/map-workspaces": "^3.0.4",
47
47
  "@pkgjs/parseargs": "0.11.0",
48
48
  "@pnpm/logger": "5.0.0",
49
- "@pnpm/workspace.pkgs-graph": "2.0.7",
49
+ "@pnpm/workspace.pkgs-graph": "2.0.8",
50
50
  "@snyk/github-codeowners": "^1.1.0",
51
51
  "chalk": "^5.2.0",
52
52
  "easy-table": "^1.2.0",
@@ -61,26 +61,26 @@
61
61
  "summary": "^2.1.0",
62
62
  "typescript": "^5.0.2",
63
63
  "zod": "3.22.4",
64
- "zod-validation-error": "^1.5.0"
64
+ "zod-validation-error": "2.0.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@jest/types": "29.6.3",
68
68
  "@npmcli/package-json": "5.0.0",
69
69
  "@release-it/bumper": "5.1.0",
70
70
  "@swc/cli": "0.1.62",
71
- "@swc/core": "1.3.92",
72
- "@types/eslint": "8.44.4",
73
- "@types/js-yaml": "4.0.7",
74
- "@types/micromatch": "4.0.3",
75
- "@types/minimist": "1.2.3",
76
- "@types/node": "20.8.4",
77
- "@types/npmcli__map-workspaces": "3.0.2",
78
- "@types/pkgjs__parseargs": "0.10.1",
79
- "@types/webpack": "5.28.3",
80
- "@typescript-eslint/eslint-plugin": "6.7.5",
81
- "@typescript-eslint/parser": "6.7.5",
71
+ "@swc/core": "1.3.94",
72
+ "@types/eslint": "8.44.6",
73
+ "@types/js-yaml": "4.0.8",
74
+ "@types/micromatch": "4.0.4",
75
+ "@types/minimist": "1.2.4",
76
+ "@types/node": "20.8.7",
77
+ "@types/npmcli__map-workspaces": "3.0.3",
78
+ "@types/pkgjs__parseargs": "0.10.2",
79
+ "@types/webpack": "5.28.4",
80
+ "@typescript-eslint/eslint-plugin": "6.8.0",
81
+ "@typescript-eslint/parser": "6.8.0",
82
82
  "c8": "8.0.1",
83
- "eslint": "8.51.0",
83
+ "eslint": "8.52.0",
84
84
  "eslint-import-resolver-typescript": "3.6.1",
85
85
  "eslint-plugin-import": "2.28.1",
86
86
  "eslint-plugin-n": "16.2.0",
@@ -89,8 +89,8 @@
89
89
  "release-it": "16.2.1",
90
90
  "remark-cli": "12.0.0",
91
91
  "remark-preset-webpro": "1.0.0",
92
- "tsx": "3.13.0",
93
- "type-fest": "4.4.0"
92
+ "tsx": "3.14.0",
93
+ "type-fest": "4.5.0"
94
94
  },
95
95
  "engines": {
96
96
  "node": ">=16.17.0 <17 || >=18.6.0"
package/schema.json CHANGED
@@ -133,7 +133,7 @@
133
133
  }
134
134
  },
135
135
  "globPatterns": {
136
- "description": "Use file paths and glob patterns to match files. Knip uses fast-glob and and minimatch (https://github.com/micromatch/micromatch#matching-features)",
136
+ "description": "Use file paths and glob patterns to match files. Knip uses fast-glob and minimatch (https://github.com/micromatch/micromatch#matching-features)",
137
137
  "anyOf": [
138
138
  {
139
139
  "type": "string"
@@ -1,13 +0,0 @@
1
- import type { VitestConfig } from '../vitest/types.js';
2
- interface Config extends VitestConfig {
3
- plugins: unknown[];
4
- }
5
- export type COMMAND = 'dev' | 'serve' | 'build';
6
- export type MODE = 'development' | 'production';
7
- interface Options {
8
- command: COMMAND;
9
- mode: MODE;
10
- ssrBuild?: boolean | undefined;
11
- }
12
- export type ViteConfig = Config | ((options: Options) => Config) | ((options: Options) => Promise<Config>);
13
- export {};