knip 3.1.0 → 3.3.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.
@@ -172,7 +172,8 @@ export class ProjectPrincipal {
172
172
  }
173
173
  getHasReferences(filePath, exportedItem) {
174
174
  const hasReferences = { external: false, internal: false };
175
- const symbolReferences = this.findReferences(filePath, exportedItem.pos).flatMap(f => f.references);
175
+ const pos = exportedItem.posDecl ?? exportedItem.pos;
176
+ const symbolReferences = this.findReferences(filePath, pos).flatMap(f => f.references);
176
177
  for (const reference of symbolReferences) {
177
178
  if (reference.fileName === filePath) {
178
179
  if (!reference.isDefinition) {
@@ -15,6 +15,9 @@ const findPrettierDependencies = async (configFilePath, { manifest, isProduction
15
15
  const localConfig = configFilePath.endsWith('package.json')
16
16
  ? manifest.prettier
17
17
  : await load(configFilePath);
18
+ if (typeof localConfig === 'string') {
19
+ return [localConfig];
20
+ }
18
21
  return localConfig && Array.isArray(localConfig.plugins)
19
22
  ? localConfig.plugins.filter((plugin) => typeof plugin === 'string')
20
23
  : [];
@@ -1,5 +1,5 @@
1
1
  import { compact } from '../../util/array.js';
2
- import { dirname, join, relative } from '../../util/path.js';
2
+ import { dirname, isAbsolute, join, relative } from '../../util/path.js';
3
3
  import { timerify } from '../../util/Performance.js';
4
4
  import { hasDependency, load, tryResolve } from '../../util/plugin.js';
5
5
  import { toEntryPattern } from '../../util/protocols.js';
@@ -14,7 +14,7 @@ export const CONFIG_FILE_PATTERNS = [
14
14
  export const ENTRY_FILE_PATTERNS = ['**/*.{test,spec}.?(c|m)[jt]s?(x)'];
15
15
  const resolveEntry = (containingFilePath, specifier) => {
16
16
  const dir = dirname(containingFilePath);
17
- const resolvedPath = tryResolve(join(dir, specifier), containingFilePath);
17
+ const resolvedPath = isAbsolute(specifier) ? specifier : tryResolve(join(dir, specifier), containingFilePath);
18
18
  if (resolvedPath)
19
19
  return toEntryPattern(relative(dir, resolvedPath));
20
20
  return specifier;
@@ -47,9 +47,11 @@ export const findVitestDependencies = async (configFilePath, localConfig, option
47
47
  }
48
48
  return Array.from(dependencies);
49
49
  }
50
+ const entry = localConfig.build?.lib?.entry ?? [];
51
+ const dependencies = (typeof entry === 'string' ? [entry] : Object.values(entry)).map(specifier => resolveEntry(configFilePath, specifier));
50
52
  if (!localConfig.test)
51
- return [];
52
- return findConfigDependencies(configFilePath, localConfig, options);
53
+ return dependencies;
54
+ return [...dependencies, ...findConfigDependencies(configFilePath, localConfig, options)];
53
55
  };
54
56
  const findVitestWorkspaceDependencies = async (configFilePath, options) => {
55
57
  const localConfig = await load(configFilePath);
@@ -12,7 +12,14 @@ interface VitestConfig {
12
12
  };
13
13
  }
14
14
  export interface ViteConfig extends VitestConfig {
15
- plugins: unknown[];
15
+ plugins?: unknown[];
16
+ build?: {
17
+ lib?: {
18
+ entry: string | string[] | {
19
+ [entryAlias: string]: string;
20
+ };
21
+ };
22
+ };
16
23
  }
17
24
  export type COMMAND = 'dev' | 'serve' | 'build';
18
25
  export type MODE = 'development' | 'production';
@@ -5,6 +5,7 @@ type Identifier = string;
5
5
  export type ExportItem = {
6
6
  node: ts.Node;
7
7
  pos: number;
8
+ posDecl?: number;
8
9
  type: SymbolType;
9
10
  members?: ExportItemMember[];
10
11
  jsDocTags?: Set<string>;
@@ -12,6 +12,7 @@ export interface BoundSourceFile extends ts.SourceFile {
12
12
  symbol?: SymbolWithExports;
13
13
  resolvedModules?: ts.ModeAwareCache<ts.ResolvedModuleWithFailedLookupLocations>;
14
14
  locals?: SymbolTable;
15
+ getNamedDeclarations?(): Map<string, readonly ts.Declaration[]>;
15
16
  scriptKind?: ts.ScriptKind;
16
17
  pragmas?: Map<string, PragmaMap | PragmaMap[]>;
17
18
  }
@@ -1,10 +1,4 @@
1
1
  import ts from 'typescript';
2
- interface ValidImportTypeNode extends ts.ImportTypeNode {
3
- argument: ts.LiteralTypeNode & {
4
- literal: ts.StringLiteral;
5
- };
6
- }
7
- export declare function isValidImportTypeNode(node: ts.Node): node is ValidImportTypeNode;
8
2
  export declare function isGetOrSetAccessorDeclaration(node: ts.Node): node is ts.AccessorDeclaration;
9
3
  export declare function isPrivateMember(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.SetAccessorDeclaration | ts.GetAccessorDeclaration): boolean;
10
4
  export declare function isDefaultImport(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration | ts.ExportDeclaration): boolean;
@@ -24,4 +18,3 @@ export declare const getLineAndCharacterOfPosition: (node: ts.Node, pos: number)
24
18
  col: number;
25
19
  pos: number;
26
20
  };
27
- export {};
@@ -1,7 +1,4 @@
1
1
  import ts from 'typescript';
2
- export function isValidImportTypeNode(node) {
3
- return ts.isImportTypeNode(node);
4
- }
5
2
  export function isGetOrSetAccessorDeclaration(node) {
6
3
  return node.kind === ts.SyntaxKind.SetAccessor || node.kind === ts.SyntaxKind.GetAccessor;
7
4
  }
@@ -93,7 +93,7 @@ export const getImportsAndExports = (sourceFile, getResolvedModule, options) =>
93
93
  }
94
94
  }
95
95
  };
96
- const addExport = ({ node, identifier, type, pos, members = [] }) => {
96
+ const addExport = ({ node, identifier, type, pos, posDecl, members = [] }) => {
97
97
  if (options.skipExports)
98
98
  return;
99
99
  const jsDocTags = getJSDocTags(node);
@@ -104,7 +104,7 @@ export const getImportsAndExports = (sourceFile, getResolvedModule, options) =>
104
104
  exports.set(identifier, { ...item, members: crew, jsDocTags: tags });
105
105
  }
106
106
  else {
107
- exports.set(identifier, { node, type, members, jsDocTags, pos });
107
+ exports.set(identifier, { node, type, members, jsDocTags, pos, posDecl: posDecl ?? pos });
108
108
  }
109
109
  if (!jsDocTags.has('@alias')) {
110
110
  if (ts.isExportAssignment(node))
@@ -5,12 +5,20 @@ export default visit(() => true, node => {
5
5
  if (ts.isExportDeclaration(node)) {
6
6
  if (node.exportClause && ts.isNamedExports(node.exportClause)) {
7
7
  const type = node.isTypeOnly ? SymbolType.TYPE : SymbolType.UNKNOWN;
8
+ const sourceFile = node.getSourceFile();
9
+ const declarations = sourceFile.getNamedDeclarations?.();
8
10
  return node.exportClause.elements.map(element => {
11
+ const identifier = String(element.name.escapedText);
12
+ const declaration = declarations?.get(identifier)?.find((d) => d !== element);
13
+ const pos = element.name.pos;
14
+ const name = ts.getNameOfDeclaration(declaration);
15
+ const posDecl = name?.pos ?? declaration?.pos ?? pos;
9
16
  return {
10
17
  node: element,
11
- identifier: element.name.getText(),
18
+ identifier,
12
19
  type,
13
- pos: element.name.flowNode?.node?.pos ?? element.name.pos,
20
+ pos,
21
+ posDecl,
14
22
  };
15
23
  });
16
24
  }
@@ -1,11 +1,43 @@
1
1
  import ts from 'typescript';
2
- import { isValidImportTypeNode } from '../../ast-helpers.js';
3
2
  import { importVisitor as visit } from '../index.js';
3
+ const extractImportSpecifiers = (node) => {
4
+ const importSpecifiers = [];
5
+ function visit(node) {
6
+ if (ts.isJSDocTypeExpression(node)) {
7
+ const typeNode = node.type;
8
+ if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments) {
9
+ typeNode.typeArguments.forEach(arg => {
10
+ if (ts.isImportTypeNode(arg)) {
11
+ const importClause = arg.argument;
12
+ if (ts.isLiteralTypeNode(importClause) && ts.isStringLiteral(importClause.literal)) {
13
+ importSpecifiers.push(importClause.literal.text);
14
+ }
15
+ }
16
+ });
17
+ }
18
+ }
19
+ if (ts.isJSDocTypeTag(node)) {
20
+ const typeNode = node.typeExpression?.type;
21
+ if (ts.isImportTypeNode(typeNode)) {
22
+ const importClause = typeNode.argument;
23
+ if (ts.isLiteralTypeNode(importClause) && ts.isStringLiteral(importClause.literal)) {
24
+ importSpecifiers.push(importClause.literal.text);
25
+ }
26
+ }
27
+ }
28
+ ts.forEachChild(node, visit);
29
+ }
30
+ visit(node);
31
+ return importSpecifiers;
32
+ };
4
33
  export default visit(() => true, node => {
5
- if ('jsDoc' in node) {
6
- const type = ts.getJSDocType(node);
7
- if (type && isValidImportTypeNode(type)) {
8
- return { specifier: type.argument.literal.text };
34
+ if ('jsDoc' in node && node.jsDoc) {
35
+ const jsDoc = node.jsDoc;
36
+ if (jsDoc.length > 0 && jsDoc[0].parent.parent === node.parent) {
37
+ return jsDoc
38
+ .flatMap(jsDoc => (jsDoc.tags ?? []).flatMap(extractImportSpecifiers))
39
+ .map(specifier => ({ specifier }));
9
40
  }
10
41
  }
42
+ return [];
11
43
  });
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "3.1.0";
1
+ export declare const version = "3.3.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '3.1.0';
1
+ export const version = '3.3.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "3.1.0",
3
+ "version": "3.3.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": {
@@ -25,7 +25,7 @@
25
25
  "knip:production": "node ./dist/cli.js --directory ../.. --production --strict",
26
26
  "lint": "eslint scripts src test",
27
27
  "lint:fix": "eslint scripts src test --fix",
28
- "format": "prettier scripts src test schema.json --with-node-modules --write",
28
+ "format": "prettier scripts src test schema.json schema-jsonc.json --with-node-modules --write",
29
29
  "pretest": "node rmdir.js tmp && swc src -d tmp/src && swc test -d tmp/test",
30
30
  "test": "node --no-warnings --test tmp",
31
31
  "coverage": "c8 --reporter html node --no-warnings --loader tsx --test test/*.test.ts test/*/*.test.ts",
@@ -35,11 +35,12 @@
35
35
  "qa": "npm run lint && npm run build && npm run knip && npm run knip:production && npm test",
36
36
  "release": "release-it",
37
37
  "create-plugin": "tsx --no-warnings ./scripts/create-new-plugin.ts",
38
- "postcreate-plugin": "prettier schema.json src/ConfigurationValidator.ts --write --config .prettierrc --log-level silent"
38
+ "postcreate-plugin": "prettier schema.json schema-jsonc.json src/ConfigurationValidator.ts --write --config .prettierrc --log-level silent"
39
39
  },
40
40
  "files": [
41
41
  "dist",
42
- "schema.json"
42
+ "schema.json",
43
+ "schema-jsonc.json"
43
44
  ],
44
45
  "dependencies": {
45
46
  "@ericcornelissen/bash-parser": "0.5.2",
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Knip configuration for JSONC",
4
+ "description": "See https://github.com/webpro/knip",
5
+ "allOf": [
6
+ {
7
+ "$ref": "https://unpkg.com/knip@3/schema.json"
8
+ }
9
+ ],
10
+ "allowTrailingCommas": true
11
+ }
package/schema.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Knip configuration",
3
+ "title": "Knip configuration for JSON",
4
4
  "description": "See https://github.com/webpro/knip",
5
5
  "type": "object",
6
6
  "allOf": [