knip 6.0.4 → 6.0.6

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.
@@ -60,7 +60,7 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
60
60
  if (isIgnored &&
61
61
  (isReferenced || isReferencedInUsedExport(exportedItem, filePath, isIncludeEntryExports))) {
62
62
  for (const tagName of exportedItem.jsDocTags) {
63
- if (options.tags[1].includes(tagName.replace(/^@/, ''))) {
63
+ if (options.tags[1].includes(tagName)) {
64
64
  collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
65
65
  }
66
66
  }
@@ -114,7 +114,7 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
114
114
  }
115
115
  else if (isIgnored) {
116
116
  for (const tagName of exportedItem.jsDocTags) {
117
- if (options.tags[1].includes(tagName.replace(/^@/, ''))) {
117
+ if (options.tags[1].includes(tagName)) {
118
118
  collector.addTagHint({ type: 'tag', filePath, identifier: id, tagName });
119
119
  }
120
120
  }
@@ -310,14 +310,18 @@ export async function build({ chief, collector, counselor, deputy, principal, is
310
310
  if (!isIgnored)
311
311
  pp.addEntryPath(filePath, { skipExportsAnalysis: true });
312
312
  }
313
+ const wsDependencies = deputy.getDependencies(workspace.name);
313
314
  for (const _import of file.imports.imports) {
314
- if (_import.filePath) {
315
- const packageName = getPackageNameFromModuleSpecifier(_import.specifier);
316
- if (packageName && isInternalWorkspace(packageName)) {
317
- file.imports.external.add({ ..._import, specifier: packageName });
318
- if (!isGitIgnored(_import.filePath)) {
319
- pp.addProgramPath(_import.filePath);
320
- }
315
+ if (!_import.filePath)
316
+ continue;
317
+ const packageName = getPackageNameFromModuleSpecifier(_import.specifier);
318
+ if (!packageName)
319
+ continue;
320
+ const isWorkspace = isInternalWorkspace(packageName);
321
+ if (isWorkspace || wsDependencies.has(packageName)) {
322
+ file.imports.external.add({ ..._import, specifier: packageName });
323
+ if (isWorkspace && !isGitIgnored(_import.filePath)) {
324
+ pp.addProgramPath(_import.filePath);
321
325
  }
322
326
  }
323
327
  }
@@ -45,7 +45,7 @@ const resolveConfig = async (localConfig, options) => {
45
45
  commands = [target.options.command];
46
46
  else if (target.options?.commands)
47
47
  commands = target.options.commands.map(commandConfig => typeof commandConfig === 'string' ? commandConfig : commandConfig.command);
48
- const cwd = target.options?.cwd ? join(options.cwd, target.options.cwd) : undefined;
48
+ const cwd = target.options?.cwd ? join(options.cwd, target.options.cwd) : options.cwd;
49
49
  return options.getInputsFromScripts(commands, { cwd });
50
50
  });
51
51
  const configInputs = targets.flatMap(target => {
@@ -47,7 +47,7 @@ export interface ExportMember extends Position {
47
47
  readonly identifier: Identifier;
48
48
  readonly type: SymbolType;
49
49
  readonly fix: Fix;
50
- readonly jsDocTags: Tags;
50
+ jsDocTags: Tags;
51
51
  readonly flags: number;
52
52
  hasRefsInFile: boolean;
53
53
  }
@@ -11,7 +11,7 @@ import { walkAST } from "./visitors/walk.js";
11
11
  const jsDocImportRe = /import\(\s*['"]([^'"]+)['"]\s*\)(?:\.(\w+))?/g;
12
12
  const jsDocImportTagRe = /@import\s+(?:\{[^}]*\}|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/g;
13
13
  const jsxImportSourceRe = /@jsxImportSource\s+(\S+)/;
14
- const referenceTypesRe = /\/\s*<reference\s+types\s*=\s*"([^"]+)"\s*\/>/;
14
+ const referenceTypesRe = /\s*<reference\s+types\s*=\s*"([^"]+)"\s*\/>/;
15
15
  const envRe = /@(?:vitest|jest)-environment\s+(\S+)/g;
16
16
  const getImportsAndExports = (filePath, sourceText, resolveModule, options, ignoreExportsUsedInFile, skipExportsForFile, visitor, pluginCtx, cachedParseResult) => {
17
17
  const skipExports = skipExportsForFile || !options.isReportExports;
@@ -303,7 +303,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
303
303
  addImport(id, undefined, undefined, undefined, comment.start + results.index, modifiers);
304
304
  }
305
305
  if (comment.type === 'Line') {
306
- const refMatch = ('/' + comment.value).match(referenceTypesRe);
306
+ const refMatch = comment.value.match(referenceTypesRe);
307
307
  if (refMatch) {
308
308
  addImport(refMatch[1], undefined, undefined, undefined, comment.start, IMPORT_FLAGS.TYPE_ONLY);
309
309
  }
@@ -4,14 +4,28 @@ import { DEFAULT_EXTENSIONS, DTS_EXTENSIONS } from "../constants.js";
4
4
  import { sanitizeSpecifier } from "../util/modules.js";
5
5
  import { timerify } from "../util/Performance.js";
6
6
  import { dirname, extname, isAbsolute, isInNodeModules, join } from "../util/path.js";
7
- import { _createSyncModuleResolver, _resolveModuleSync, convertPathsToAlias } from "../util/resolve.js";
7
+ import { _createSyncModuleResolver, _resolveModuleSync } from "../util/resolve.js";
8
+ function compilePathMappings(paths) {
9
+ if (!paths)
10
+ return undefined;
11
+ const mappings = [];
12
+ for (const key in paths) {
13
+ const starIdx = key.indexOf('*');
14
+ if (starIdx >= 0) {
15
+ mappings.push({ prefix: key.slice(0, starIdx), wildcard: true, values: paths[key] });
16
+ }
17
+ else {
18
+ mappings.push({ prefix: key, wildcard: false, values: paths[key] });
19
+ }
20
+ }
21
+ return mappings.length > 0 ? mappings : undefined;
22
+ }
8
23
  export function createCustomModuleResolver(compilerOptions, customCompilerExtensions, toSourceFilePath) {
9
24
  const customCompilerExtensionsSet = new Set(customCompilerExtensions);
10
25
  const hasCustomExts = customCompilerExtensionsSet.size > 0;
11
26
  const extensions = [...DEFAULT_EXTENSIONS, ...customCompilerExtensions, ...DTS_EXTENSIONS];
12
- const alias = convertPathsToAlias(compilerOptions.paths);
13
27
  const resolveSync = hasCustomExts ? _createSyncModuleResolver(extensions) : _resolveModuleSync;
14
- const resolveWithAlias = alias ? _createSyncModuleResolver(extensions, alias) : undefined;
28
+ const pathMappings = compilePathMappings(compilerOptions.paths);
15
29
  const rootDirs = compilerOptions.rootDirs;
16
30
  function toSourcePath(resolvedFileName) {
17
31
  if (!hasCustomExts || !customCompilerExtensionsSet.has(extname(resolvedFileName))) {
@@ -27,24 +41,27 @@ export function createCustomModuleResolver(compilerOptions, customCompilerExtens
27
41
  };
28
42
  }
29
43
  function resolveModuleName(name, containingFile) {
30
- const sanitizedSpecifier = sanitizeSpecifier(name);
31
- if (isBuiltin(sanitizedSpecifier))
44
+ const specifier = sanitizeSpecifier(name);
45
+ if (isBuiltin(specifier))
32
46
  return undefined;
33
- const resolvedFileName = resolveSync(sanitizedSpecifier, containingFile);
47
+ const resolvedFileName = resolveSync(specifier, containingFile);
34
48
  if (resolvedFileName)
35
49
  return toResult(resolvedFileName);
36
- if (resolveWithAlias) {
37
- const aliasResolved = resolveWithAlias(sanitizedSpecifier, containingFile);
38
- if (aliasResolved)
39
- return toResult(aliasResolved);
40
- }
41
- const candidate = isAbsolute(sanitizedSpecifier)
42
- ? sanitizedSpecifier
43
- : join(dirname(containingFile), sanitizedSpecifier);
44
- if (existsSync(candidate)) {
45
- return { resolvedFileName: candidate, isExternalLibraryImport: false };
50
+ if (pathMappings) {
51
+ for (const { prefix, wildcard, values } of pathMappings) {
52
+ if (wildcard ? specifier.startsWith(prefix) : specifier === prefix) {
53
+ const captured = wildcard ? specifier.slice(prefix.length) : '';
54
+ for (const value of values) {
55
+ const starIdx = value.indexOf('*');
56
+ const mapped = starIdx >= 0 ? value.slice(0, starIdx) + captured + value.slice(starIdx + 1) : value;
57
+ const resolved = resolveSync(mapped, containingFile);
58
+ if (resolved)
59
+ return toResult(resolved);
60
+ }
61
+ }
62
+ }
46
63
  }
47
- if (rootDirs && !isAbsolute(sanitizedSpecifier)) {
64
+ if (rootDirs && !isAbsolute(specifier)) {
48
65
  const containingDir = dirname(containingFile);
49
66
  for (const srcRoot of rootDirs) {
50
67
  if (!containingDir.startsWith(srcRoot))
@@ -53,13 +70,17 @@ export function createCustomModuleResolver(compilerOptions, customCompilerExtens
53
70
  for (const targetRoot of rootDirs) {
54
71
  if (targetRoot === srcRoot)
55
72
  continue;
56
- const mapped = join(targetRoot, relPath, sanitizedSpecifier);
73
+ const mapped = join(targetRoot, relPath, specifier);
57
74
  const resolved = resolveSync(mapped, containingFile);
58
75
  if (resolved)
59
76
  return toResult(resolved);
60
77
  }
61
78
  }
62
79
  }
80
+ const candidate = isAbsolute(specifier) ? specifier : join(dirname(containingFile), specifier);
81
+ if (existsSync(candidate)) {
82
+ return { resolvedFileName: candidate, isExternalLibraryImport: false };
83
+ }
63
84
  }
64
85
  return timerify(resolveModuleName);
65
86
  }
@@ -1,6 +1,7 @@
1
1
  import { parseSync, rawTransferSupported, } from 'oxc-parser';
2
2
  import { DEFAULT_EXTENSIONS, FIX_FLAGS, SYMBOL_TYPE } from "../../constants.js";
3
3
  import { extname } from "../../util/path.js";
4
+ import { EMPTY_TAGS } from "./jsdoc.js";
4
5
  const defaultParseOptions = {
5
6
  sourceType: 'unambiguous',
6
7
  experimentalRawTransfer: rawTransferSupported(),
@@ -57,6 +58,18 @@ export function extractNamespaceMembers(decl, options, lineStarts, getJSDocTags,
57
58
  const members = [];
58
59
  const addMember = (name, pos, stmtStart, stmtEnd) => {
59
60
  const fullName = prefix ? `${prefix}.${name}` : name;
61
+ const tags = getJSDocTags(stmtStart);
62
+ const existing = members.find(m => m.identifier === fullName);
63
+ if (existing) {
64
+ if (tags.size) {
65
+ if (existing.jsDocTags === EMPTY_TAGS)
66
+ existing.jsDocTags = new Set(tags);
67
+ else
68
+ for (const t of tags)
69
+ existing.jsDocTags.add(t);
70
+ }
71
+ return;
72
+ }
60
73
  const { line, col } = getLineAndCol(lineStarts, pos);
61
74
  const fix = options.isFixExports
62
75
  ? [stmtStart, stmtEnd, FIX_FLAGS.OBJECT_BINDING | FIX_FLAGS.WITH_NEWLINE]
@@ -69,7 +82,7 @@ export function extractNamespaceMembers(decl, options, lineStarts, getJSDocTags,
69
82
  col,
70
83
  fix,
71
84
  hasRefsInFile: false,
72
- jsDocTags: getJSDocTags(stmtStart),
85
+ jsDocTags: tags,
73
86
  flags: 0,
74
87
  });
75
88
  };
@@ -150,7 +150,7 @@ const _addParamShadows = (params, body) => {
150
150
  if (!body || !params)
151
151
  return;
152
152
  const range = [body.start, body.end];
153
- const items = Array.isArray(params) ? params : params.items ?? params;
153
+ const items = Array.isArray(params) ? params : (params.items ?? params);
154
154
  for (const param of items)
155
155
  _collectBindingNames(param, range);
156
156
  };
@@ -1,5 +1,4 @@
1
1
  export declare const _resolveModuleSync: (specifier: string, basePath: string) => string | undefined;
2
- export declare const _createSyncModuleResolver: (extensions: string[], alias?: Record<string, string[]>) => (specifier: string, basePath: string) => string | undefined;
3
- export declare function convertPathsToAlias(paths: Record<string, string[]> | undefined): Record<string, string[]> | undefined;
2
+ export declare const _createSyncModuleResolver: (extensions: string[]) => (specifier: string, basePath: string) => string | undefined;
4
3
  export declare function clearResolverCache(): void;
5
4
  export declare const _resolveSync: (specifier: string, baseDir: string) => string | undefined;
@@ -34,18 +34,7 @@ const createSyncModuleResolver = (extensions, alias) => {
34
34
  };
35
35
  const resolveModuleSync = createSyncModuleResolver([...DEFAULT_EXTENSIONS, ...DTS_EXTENSIONS, '.json', '.jsonc']);
36
36
  export const _resolveModuleSync = timerify(resolveModuleSync, 'resolveModuleSync');
37
- export const _createSyncModuleResolver = (extensions, alias) => timerify(createSyncModuleResolver(extensions, alias), 'resolveModuleSync');
38
- export function convertPathsToAlias(paths) {
39
- if (!paths)
40
- return undefined;
41
- const alias = {};
42
- for (const key in paths) {
43
- const stripWildcard = key.endsWith('/*');
44
- const aliasKey = stripWildcard ? key.slice(0, -2) : key;
45
- alias[aliasKey] = stripWildcard ? paths[key].map(v => (v.endsWith('/*') ? v.slice(0, -2) : v)) : paths[key];
46
- }
47
- return alias;
48
- }
37
+ export const _createSyncModuleResolver = (extensions) => timerify(createSyncModuleResolver(extensions), 'resolveModuleSync');
49
38
  const createSyncResolver = (extensions) => {
50
39
  const resolver = new ResolverFactory({
51
40
  extensions,
package/dist/util/tag.js CHANGED
@@ -4,11 +4,11 @@ export const splitTags = (rawTags) => {
4
4
  return tags.reduce(([incl, excl], tag) => {
5
5
  const match = tag.match(/[a-zA-Z]+/);
6
6
  if (match)
7
- (tag.startsWith('-') ? excl : incl).push(match[0]);
7
+ (tag.startsWith('-') ? excl : incl).push(`@${match[0]}`);
8
8
  return [incl, excl];
9
9
  }, [[], []]);
10
10
  };
11
- const hasTag = (tags, jsDocTags) => tags.some(tag => jsDocTags.has(`@${tag}`));
11
+ const hasTag = (tags, jsDocTags) => tags.some(tag => jsDocTags.has(tag));
12
12
  export const shouldIgnore = (jsDocTags, tags) => {
13
13
  const [includeJSDocTags, excludeJSDocTags] = tags;
14
14
  if (includeJSDocTags.length > 0 && !hasTag(includeJSDocTags, jsDocTags))
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.0.4";
1
+ export declare const version = "6.0.6";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.0.4';
1
+ export const version = '6.0.6';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.0.4",
3
+ "version": "6.0.6",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",