knip 5.22.3 → 5.23.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.
@@ -1,4 +1,4 @@
1
- import type { ConfigurationHint, Issue, Rules } from './types/issues.js';
1
+ import type { ConfigurationHint, Issue, Rules, TagHint } from './types/issues.js';
2
2
  type Filters = Partial<{
3
3
  dir: string;
4
4
  }>;
@@ -15,6 +15,7 @@ export declare class IssueCollector {
15
15
  private counters;
16
16
  private referencedFiles;
17
17
  private configurationHints;
18
+ private tagHints;
18
19
  private ignorePatterns;
19
20
  private isMatch;
20
21
  constructor({ cwd, rules, filters }: IssueCollectorOptions);
@@ -26,10 +27,12 @@ export declare class IssueCollector {
26
27
  addFilesIssues(filePaths: string[]): void;
27
28
  addIssue(issue: Issue): Issue | undefined;
28
29
  addConfigurationHint(issue: ConfigurationHint): void;
30
+ addTagHint(issue: TagHint): void;
29
31
  purge(): import("./types/issues.js").IssueSet;
30
32
  getIssues(): {
31
33
  issues: import("./types/issues.js").Issues;
32
34
  counters: import("./types/issues.js").Counters;
35
+ tagHints: Set<TagHint>;
33
36
  configurationHints: Set<ConfigurationHint>;
34
37
  };
35
38
  }
@@ -2,7 +2,7 @@ import picomatch from 'picomatch';
2
2
  import { initCounters, initIssues } from './issues/initializers.js';
3
3
  import { timerify } from './util/Performance.js';
4
4
  import { relative } from './util/path.js';
5
- const hasHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
5
+ const hasConfigurationHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
6
6
  const isMatch = timerify(picomatch.isMatch, 'isMatch');
7
7
  export class IssueCollector {
8
8
  cwd;
@@ -12,6 +12,7 @@ export class IssueCollector {
12
12
  counters = initCounters();
13
13
  referencedFiles = new Set();
14
14
  configurationHints = new Set();
15
+ tagHints = new Set();
15
16
  ignorePatterns = new Set();
16
17
  isMatch;
17
18
  constructor({ cwd, rules, filters }) {
@@ -59,10 +60,13 @@ export class IssueCollector {
59
60
  return issue;
60
61
  }
61
62
  addConfigurationHint(issue) {
62
- if (!hasHint(this.configurationHints, issue)) {
63
+ if (!hasConfigurationHint(this.configurationHints, issue)) {
63
64
  this.configurationHints.add(issue);
64
65
  }
65
66
  }
67
+ addTagHint(issue) {
68
+ this.tagHints.add(issue);
69
+ }
66
70
  purge() {
67
71
  const unusedFiles = this.issues.files;
68
72
  this.issues = initIssues();
@@ -73,6 +77,7 @@ export class IssueCollector {
73
77
  return {
74
78
  issues: this.issues,
75
79
  counters: this.counters,
80
+ tagHints: this.tagHints,
76
81
  configurationHints: this.configurationHints,
77
82
  };
78
83
  }
@@ -32,7 +32,7 @@ export declare class WorkspaceWorker {
32
32
  isStrict: boolean;
33
33
  rootIgnore: Configuration['ignore'];
34
34
  negatedWorkspacePatterns: string[];
35
- enabledPluginsMap: Record<"angular" | "astro" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "drizzle" | "eleventy" | "eslint" | "gatsby" | "githubActions" | "graphqlCodegen" | "husky" | "jest" | "lefthook" | "linthtml" | "lintStaged" | "lockfileLint" | "lostPixel" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nodeTestRunner" | "npmPackageJsonLint" | "nx" | "nyc" | "oclif" | "playwright" | "playwrightCt" | "postcss" | "prettier" | "releaseIt" | "remark" | "remix" | "rollup" | "semanticRelease" | "sentry" | "simpleGitHooks" | "sizeLimit" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vercelOg" | "vite" | "vitest" | "vue" | "webdriverIo" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie", boolean>;
35
+ enabledPluginsMap: Record<"astro" | "angular" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "eleventy" | "eslint" | "gatsby" | "husky" | "jest" | "lefthook" | "linthtml" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nx" | "nyc" | "oclif" | "playwright" | "postcss" | "prettier" | "remark" | "remix" | "rollup" | "sentry" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vue" | "vite" | "vitest" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie" | "drizzle" | "githubActions" | "graphqlCodegen" | "lintStaged" | "lockfileLint" | "lostPixel" | "nodeTestRunner" | "npmPackageJsonLint" | "playwrightCt" | "releaseIt" | "semanticRelease" | "simpleGitHooks" | "sizeLimit" | "vercelOg" | "webdriverIo", boolean>;
36
36
  enabledPlugins: PluginName[];
37
37
  enabledPluginsInAncestors: string[];
38
38
  cache: CacheConsultant<CacheItem>;
@@ -52,7 +52,7 @@ export declare class WorkspaceWorker {
52
52
  entryFilePatterns: Set<string>;
53
53
  productionEntryFilePatterns: Set<string>;
54
54
  referencedDependencies: ReferencedDependencies;
55
- enabledPlugins: ("angular" | "astro" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "drizzle" | "eleventy" | "eslint" | "gatsby" | "githubActions" | "graphqlCodegen" | "husky" | "jest" | "lefthook" | "linthtml" | "lintStaged" | "lockfileLint" | "lostPixel" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nodeTestRunner" | "npmPackageJsonLint" | "nx" | "nyc" | "oclif" | "playwright" | "playwrightCt" | "postcss" | "prettier" | "releaseIt" | "remark" | "remix" | "rollup" | "semanticRelease" | "sentry" | "simpleGitHooks" | "sizeLimit" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vercelOg" | "vite" | "vitest" | "vue" | "webdriverIo" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie")[];
55
+ enabledPlugins: ("astro" | "angular" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "eleventy" | "eslint" | "gatsby" | "husky" | "jest" | "lefthook" | "linthtml" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nx" | "nyc" | "oclif" | "playwright" | "postcss" | "prettier" | "remark" | "remix" | "rollup" | "sentry" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vue" | "vite" | "vitest" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie" | "drizzle" | "githubActions" | "graphqlCodegen" | "lintStaged" | "lockfileLint" | "lostPixel" | "nodeTestRunner" | "npmPackageJsonLint" | "playwrightCt" | "releaseIt" | "semanticRelease" | "simpleGitHooks" | "sizeLimit" | "vercelOg" | "webdriverIo")[];
56
56
  }>;
57
57
  onDispose(): void;
58
58
  }
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ if (isVersion) {
21
21
  const isShowProgress = isNoProgress === false && process.stdout.isTTY && typeof process.stdout.cursorTo === 'function';
22
22
  const run = async () => {
23
23
  try {
24
- const { report, issues, counters, rules, configurationHints } = await main({
24
+ const { report, issues, counters, rules, tagHints, configurationHints } = await main({
25
25
  cwd,
26
26
  tsConfigFile: tsConfig,
27
27
  gitignore: !isNoGitIgnore,
@@ -44,6 +44,7 @@ const run = async () => {
44
44
  report,
45
45
  issues,
46
46
  counters,
47
+ tagHints,
47
48
  configurationHints,
48
49
  noConfigHints,
49
50
  cwd,
package/dist/index.d.ts CHANGED
@@ -6,5 +6,6 @@ export declare const main: (unresolvedConfiguration: CommandLineOptions) => Prom
6
6
  issues: import("./types/issues.js").Issues;
7
7
  counters: import("./types/issues.js").Counters;
8
8
  rules: import("./types/issues.js").Rules;
9
+ tagHints: Set<import("./types/issues.js").TagHint>;
9
10
  configurationHints: Set<import("./types/issues.js").ConfigurationHint>;
10
11
  }>;
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import { getIsIdentifierReferencedHandler } from './util/is-identifier-reference
22
22
  import { getEntryPathFromManifest, getPackageNameFromModuleSpecifier } from './util/modules.js';
23
23
  import { dirname, join, toPosix } from './util/path.js';
24
24
  import { findMatch } from './util/regex.js';
25
- import { getShouldIgnoreHandler } from './util/tag.js';
25
+ import { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from './util/tag.js';
26
26
  import { augmentWorkspace, getToSourcePathHandler } from './util/to-source-path.js';
27
27
  import { createAndPrintTrace, printTrace } from './util/trace.js';
28
28
  import { loadTSConfig } from './util/tsconfig-loader.js';
@@ -285,12 +285,15 @@ export const main = async (unresolvedConfiguration) => {
285
285
  if (isIsolateWorkspaces)
286
286
  for (const principal of principals)
287
287
  factory.deletePrincipal(principal);
288
- const shouldIgnore = getShouldIgnoreHandler(tags, isProduction);
288
+ const shouldIgnore = getShouldIgnoreHandler(isProduction);
289
+ const shouldIgnoreTags = getShouldIgnoreTagHandler(tags);
289
290
  const isIdentifierReferenced = getIsIdentifierReferencedHandler(graph, entryPaths);
290
- const isExportedItemReferenced = (exportedItem) => exportedItem.refs > 0 &&
291
- (typeof chief.config.ignoreExportsUsedInFile === 'object'
292
- ? exportedItem.type !== 'unknown' && !!chief.config.ignoreExportsUsedInFile[exportedItem.type]
293
- : chief.config.ignoreExportsUsedInFile);
291
+ const ignoreExportsUsedInFile = chief.config.ignoreExportsUsedInFile;
292
+ const isExportedItemReferenced = (exportedItem) => exportedItem.refs[1] ||
293
+ (exportedItem.refs[0] > 0 &&
294
+ (typeof ignoreExportsUsedInFile === 'object'
295
+ ? exportedItem.type !== 'unknown' && !!ignoreExportsUsedInFile[exportedItem.type]
296
+ : ignoreExportsUsedInFile));
294
297
  const findUnusedExports = async () => {
295
298
  if (isReportValues || isReportTypes) {
296
299
  streamer.cast('Connecting the dots...');
@@ -313,7 +316,8 @@ export const main = async (unresolvedConfiguration) => {
313
316
  continue;
314
317
  if (shouldIgnore(exportedItem.jsDocTags))
315
318
  continue;
316
- if (importsForExport) {
319
+ const isIgnored = shouldIgnoreTags(exportedItem.jsDocTags);
320
+ if (!isIgnored && importsForExport) {
317
321
  const { isReferenced, reExportingEntryFile, traceNode } = isIdentifierReferenced(filePath, identifier, isIncludeEntryExports);
318
322
  if (reExportingEntryFile) {
319
323
  if (!isIncludeEntryExports) {
@@ -333,10 +337,13 @@ export const main = async (unresolvedConfiguration) => {
333
337
  continue;
334
338
  if (shouldIgnore(member.jsDocTags))
335
339
  continue;
336
- if (member.refs === 0) {
340
+ if (member.refs[0] === 0) {
337
341
  const id = `${identifier}.${member.identifier}`;
338
342
  const { isReferenced } = isIdentifierReferenced(filePath, id);
343
+ const isIgnored = shouldIgnoreTags(member.jsDocTags);
339
344
  if (!isReferenced) {
345
+ if (isIgnored)
346
+ continue;
340
347
  collector.addIssue({
341
348
  type: 'enumMembers',
342
349
  filePath,
@@ -348,12 +355,28 @@ export const main = async (unresolvedConfiguration) => {
348
355
  col: member.col,
349
356
  });
350
357
  }
358
+ else if (isIgnored) {
359
+ for (const tagName of exportedItem.jsDocTags) {
360
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
361
+ collector.addTagHint({ type: 'tag', filePath, identifier: id, tagName });
362
+ }
363
+ }
364
+ }
351
365
  }
352
366
  }
353
367
  }
354
368
  if (principal && isReportClassMembers && exportedItem.type === 'class') {
355
369
  const members = exportedItem.members.filter(member => !(findMatch(workspace.ignoreMembers, member.identifier) || shouldIgnore(member.jsDocTags)));
356
370
  for (const member of principal.findUnusedMembers(filePath, members)) {
371
+ if (shouldIgnoreTags(member.jsDocTags)) {
372
+ const identifier = `${exportedItem.identifier}.${member.identifier}`;
373
+ for (const tagName of exportedItem.jsDocTags) {
374
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
375
+ collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
376
+ }
377
+ }
378
+ continue;
379
+ }
357
380
  collector.addIssue({
358
381
  type: 'classMembers',
359
382
  filePath,
@@ -374,6 +397,8 @@ export const main = async (unresolvedConfiguration) => {
374
397
  if (hasStrictlyNsRefs && ((!report.nsTypes && isType) || !(report.nsExports || isType)))
375
398
  continue;
376
399
  if (!isExportedItemReferenced(exportedItem)) {
400
+ if (isIgnored)
401
+ continue;
377
402
  if (!isSkipLibs && principal?.hasExternalReferences(filePath, exportedItem))
378
403
  continue;
379
404
  const type = getType(hasStrictlyNsRefs, isType);
@@ -395,6 +420,13 @@ export const main = async (unresolvedConfiguration) => {
395
420
  fixer.addUnusedExportNode(filePath, exportedItem.fixes);
396
421
  }
397
422
  }
423
+ else if (isIgnored) {
424
+ for (const tagName of exportedItem.jsDocTags) {
425
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
426
+ collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
427
+ }
428
+ }
429
+ }
398
430
  }
399
431
  }
400
432
  }
@@ -523,7 +555,7 @@ export const main = async (unresolvedConfiguration) => {
523
555
  });
524
556
  }
525
557
  await findUnusedExports();
526
- const { issues, counters, configurationHints } = collector.getIssues();
558
+ const { issues, counters, tagHints, configurationHints } = collector.getIssues();
527
559
  if (isFix) {
528
560
  await fixer.fixIssues(issues);
529
561
  }
@@ -531,5 +563,5 @@ export const main = async (unresolvedConfiguration) => {
531
563
  watchReporter({ report, issues, streamer, size: analyzedFiles.size, isDebug });
532
564
  else
533
565
  streamer.clear();
534
- return { report, issues, counters, rules, configurationHints };
566
+ return { report, issues, counters, rules, tagHints, configurationHints };
535
567
  };
@@ -1,5 +1,5 @@
1
1
  declare const _default: {
2
- symbols: ({ report, issues, configurationHints, noConfigHints, isShowProgress }: import("../index.js").ReporterOptions) => void;
2
+ symbols: ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }: import("../index.js").ReporterOptions) => void;
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>;
@@ -1,3 +1,3 @@
1
1
  import type { ReporterOptions } from '../types/issues.js';
2
- declare const _default: ({ report, issues, configurationHints, noConfigHints, isShowProgress }: ReporterOptions) => void;
2
+ declare const _default: ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }: ReporterOptions) => void;
3
3
  export default _default;
@@ -20,7 +20,7 @@ const logIssueRecord = (issues) => {
20
20
  }
21
21
  console.log(table.sort(['filePath', 'parentSymbol', 'symbol']).print().trim());
22
22
  };
23
- export default ({ report, issues, configurationHints, noConfigHints, isShowProgress }) => {
23
+ export default ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }) => {
24
24
  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
25
25
  let totalIssues = 0;
26
26
  for (const [reportType, isReportType] of Object.entries(report)) {
@@ -52,13 +52,23 @@ export default ({ report, issues, configurationHints, noConfigHints, isShowProgr
52
52
  }
53
53
  }
54
54
  }
55
- if (!noConfigHints && configurationHints.size > 0) {
56
- logTitle('Configuration issues', configurationHints.size);
57
- for (const hint of configurationHints) {
58
- const { type, workspaceName, identifier } = hint;
59
- const message = `Unused item in ${type}`;
60
- const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : '';
61
- console.warn(picocolors.gray(`${message}${workspace}:`), identifier);
55
+ if (!noConfigHints) {
56
+ if (configurationHints.size > 0) {
57
+ logTitle('Configuration issues', configurationHints.size);
58
+ for (const hint of configurationHints) {
59
+ const { type, workspaceName, identifier } = hint;
60
+ const message = `Unused item in ${type}`;
61
+ const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : '';
62
+ console.warn(picocolors.gray(`${message}${workspace}:`), identifier);
63
+ }
64
+ }
65
+ if (tagHints.size > 0) {
66
+ logTitle('Tag issues', tagHints.size);
67
+ for (const hint of tagHints) {
68
+ const { filePath, identifier, tagName } = hint;
69
+ const message = `Unused tag in ${toRelative(filePath)}:`;
70
+ console.warn(picocolors.gray(message), `${identifier} → ${tagName}`);
71
+ }
62
72
  }
63
73
  }
64
74
  if (totalIssues === 0 && isShowProgress) {
@@ -33,7 +33,7 @@ export interface Export {
33
33
  type: SymbolType;
34
34
  members: ExportMember[];
35
35
  jsDocTags: Tags;
36
- refs: number;
36
+ refs: [number, boolean];
37
37
  fixes: Fixes;
38
38
  symbol?: ts.Symbol;
39
39
  isReExport: boolean;
@@ -44,7 +44,7 @@ export type ExportMember = {
44
44
  line: number;
45
45
  col: number;
46
46
  type: SymbolType;
47
- refs: number;
47
+ refs: [number, boolean];
48
48
  fix: Fix;
49
49
  symbol?: ts.Symbol;
50
50
  jsDocTags: Tags;
@@ -56,6 +56,7 @@ export type ReporterOptions = {
56
56
  report: Report;
57
57
  issues: Issues;
58
58
  counters: Counters;
59
+ tagHints: TagHints;
59
60
  configurationHints: ConfigurationHints;
60
61
  noConfigHints: boolean;
61
62
  cwd: string;
@@ -74,3 +75,11 @@ export type ConfigurationHint = {
74
75
  identifier: string | RegExp;
75
76
  workspaceName?: string;
76
77
  };
78
+ type TagHints = Set<TagHint>;
79
+ export type TagHint = {
80
+ type: 'tag';
81
+ filePath: string;
82
+ identifier: string;
83
+ tagName: string;
84
+ };
85
+ export {};
@@ -23,3 +23,4 @@ export declare const isConsiderReferencedNS: (node: ts.Identifier) => boolean;
23
23
  export declare const isTopLevel: (node: ts.Node) => boolean;
24
24
  export declare const getTypeName: (node: ts.Identifier) => ts.QualifiedName | undefined;
25
25
  export declare const isImportSpecifier: (node: ts.Node) => boolean;
26
+ export declare const isReferencedInExportedType: (node: ts.Node, symbol: ts.Symbol) => any;
@@ -151,3 +151,12 @@ export const isImportSpecifier = (node) => ts.isImportSpecifier(node.parent) ||
151
151
  ts.isImportEqualsDeclaration(node.parent) ||
152
152
  ts.isImportClause(node.parent) ||
153
153
  ts.isNamespaceImport(node.parent);
154
+ const isExported = (node) => {
155
+ if (node.modifiers?.find(mod => mod.kind === ts.SyntaxKind.ExportKeyword))
156
+ return true;
157
+ return node.parent ? isExported(node.parent) : false;
158
+ };
159
+ export const isReferencedInExportedType = (node, symbol) => symbol.exportSymbol &&
160
+ !(node.transformFlags & ts.TransformFlags.ContainsTypeScript) &&
161
+ Boolean(node.parent.transformFlags & ts.TransformFlags.ContainsTypeScript) &&
162
+ isExported(node.parent);
@@ -5,8 +5,9 @@ import { timerify } from '../util/Performance.js';
5
5
  import { addNsValue, addValue, createImports } from '../util/dependency-graph.js';
6
6
  import { isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.js';
7
7
  import { extname, isInNodeModules } from '../util/path.js';
8
+ import { isIdChar } from '../util/regex.js';
8
9
  import { shouldIgnore } from '../util/tag.js';
9
- import { getAccessMembers, getDestructuredIds, getJSDocTags, getLineAndCharacterOfPosition, getTypeName, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, } from './ast-helpers.js';
10
+ import { getAccessMembers, getDestructuredIds, getJSDocTags, getLineAndCharacterOfPosition, getTypeName, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, isReferencedInExportedType, } from './ast-helpers.js';
10
11
  import getDynamicImportVisitors from './visitors/dynamic-imports/index.js';
11
12
  import getExportVisitors from './visitors/exports/index.js';
12
13
  import { getImportsFromPragmas } from './visitors/helpers.js';
@@ -28,12 +29,13 @@ const createMember = (node, member, pos) => {
28
29
  line: line + 1,
29
30
  col: character + 1,
30
31
  fix: member.fix,
31
- refs: 0,
32
+ refs: [0, false],
32
33
  jsDocTags: getJSDocTags(member.node),
33
34
  };
34
35
  };
36
+ const isType = (item) => item.type === 'type' || item.type === 'interface' || item.type === 'member';
35
37
  const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) => {
36
- const { skipTypeOnly, tags } = options;
38
+ const { skipTypeOnly, tags, ignoreExportsUsedInFile } = options;
37
39
  const internalImports = new Map();
38
40
  const externalImports = new Set();
39
41
  const unresolvedImports = new Set();
@@ -44,6 +46,7 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) =
44
46
  const scripts = new Set();
45
47
  const traceRefs = new Set();
46
48
  const importedInternalSymbols = new Map();
49
+ const referencedSymbolsInExportedTypes = new Set();
47
50
  const visitors = getVisitors(sourceFile);
48
51
  const addInternalImport = (options) => {
49
52
  const { identifier, symbol, filePath, namespace, specifier, isReExport } = options;
@@ -186,7 +189,7 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) =
186
189
  line: line + 1,
187
190
  col: character + 1,
188
191
  fixes: fix ? [fix] : [],
189
- refs: 0,
192
+ refs: [0, false],
190
193
  isReExport,
191
194
  });
192
195
  }
@@ -282,6 +285,9 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) =
282
285
  }
283
286
  }
284
287
  }
288
+ if (ignoreExportsUsedInFile && !isTopLevel && isReferencedInExportedType(node, symbol)) {
289
+ referencedSymbolsInExportedTypes.add(symbol.exportSymbol);
290
+ }
285
291
  }
286
292
  }
287
293
  if (isTopLevel &&
@@ -304,34 +310,40 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) =
304
310
  return;
305
311
  const symbols = new Set();
306
312
  let index = 0;
307
- while (index < sourceFile.text.length && (index = sourceFile.text.indexOf(item.identifier, index)) !== -1) {
308
- const isDeclaration = index === item.pos || index === item.pos + 1;
309
- if (!isDeclaration) {
310
- const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, index));
311
- if (symbol) {
312
- if (item.symbol === symbol) {
313
- item.refs = 1;
314
- break;
315
- }
316
- const declaration = symbol.declarations?.[0];
317
- if (declaration) {
318
- if (item.symbol === declaration.name?.flowNode?.node?.symbol) {
319
- item.refs = 1;
320
- break;
313
+ const text = sourceFile.text;
314
+ const id = item.identifier;
315
+ while (index < text.length && (index = text.indexOf(id, index)) !== -1) {
316
+ if (!isIdChar(text.charAt(index - 1)) && !isIdChar(text.charAt(index + id.length))) {
317
+ const isDeclaration = index === item.pos || index === item.pos + 1;
318
+ if (!isDeclaration) {
319
+ const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, index));
320
+ if (symbol) {
321
+ const isInExportedType = referencedSymbolsInExportedTypes.has(symbol);
322
+ if (item.symbol === symbol) {
323
+ item.refs = [1, isInExportedType];
324
+ if (isInExportedType || isType(item))
325
+ break;
321
326
  }
322
- if (ts.isImportSpecifier(declaration) && symbols.has(symbol)) {
323
- item.refs = 1;
324
- break;
327
+ const declaration = symbol.declarations?.[0];
328
+ if (declaration) {
329
+ if (item.symbol === declaration.name?.flowNode?.node?.symbol) {
330
+ item.refs = [1, isInExportedType];
331
+ break;
332
+ }
333
+ if (ts.isImportSpecifier(declaration) && symbols.has(symbol)) {
334
+ item.refs = [1, isInExportedType];
335
+ break;
336
+ }
325
337
  }
338
+ symbols.add(symbol);
326
339
  }
327
- symbols.add(symbol);
328
340
  }
329
341
  }
330
- index += item.identifier.length;
342
+ index += id.length;
331
343
  }
332
344
  };
333
345
  for (const item of exports.values()) {
334
- if (options.ignoreExportsUsedInFile)
346
+ if (ignoreExportsUsedInFile)
335
347
  setRefs(item);
336
348
  for (const member of item.members) {
337
349
  setRefs(member);
@@ -1,2 +1,3 @@
1
1
  export declare const toRegexOrString: (value: string | RegExp) => string | RegExp;
2
2
  export declare const findMatch: (haystack: undefined | (string | RegExp)[], needle: string) => string | RegExp | undefined;
3
+ export declare const isIdChar: (text: string) => boolean;
@@ -1,2 +1,4 @@
1
1
  export const toRegexOrString = (value) => typeof value === 'string' && /[*+\\(|{^$]/.test(value) ? new RegExp(value) : value;
2
2
  export const findMatch = (haystack, needle) => haystack?.find(n => (typeof n === 'string' ? n === needle : n.test(needle)));
3
+ const idCharMatch = /[a-zA-Z0-9$_]/;
4
+ export const isIdChar = (text) => idCharMatch.test(text);
@@ -1,4 +1,5 @@
1
1
  import type { Tags } from '../types/cli.js';
2
2
  export declare const splitTags: (rawTags: string[]) => Tags;
3
3
  export declare const shouldIgnore: (jsDocTags: Set<string>, tags: Tags) => boolean;
4
- export declare const getShouldIgnoreHandler: (tags: Tags, isProduction: boolean) => (jsDocTags: Set<string>) => boolean;
4
+ export declare const getShouldIgnoreHandler: (isProduction: boolean) => (jsDocTags: Set<string>) => boolean;
5
+ export declare const getShouldIgnoreTagHandler: (tags: Tags) => (jsDocTags: Set<string>) => boolean;
package/dist/util/tag.js CHANGED
@@ -16,8 +16,8 @@ export const shouldIgnore = (jsDocTags, tags) => {
16
16
  return true;
17
17
  return false;
18
18
  };
19
- export const getShouldIgnoreHandler = (tags, isProduction) => (jsDocTags) => jsDocTags.has('@public') ||
19
+ export const getShouldIgnoreHandler = (isProduction) => (jsDocTags) => jsDocTags.has('@public') ||
20
20
  jsDocTags.has('@beta') ||
21
21
  jsDocTags.has('@alias') ||
22
- shouldIgnore(jsDocTags, tags) ||
23
22
  (isProduction && jsDocTags.has('@internal'));
23
+ export const getShouldIgnoreTagHandler = (tags) => (jsDocTags) => shouldIgnore(jsDocTags, tags);
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.22.3";
1
+ export declare const version = "5.23.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.22.3';
1
+ export const version = '5.23.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.22.3",
3
+ "version": "5.23.0",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {