knip 5.21.2 → 5.22.1

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,17 +1,8 @@
1
- import { type FileDescriptor } from 'file-entry-cache';
2
- interface FD<T> extends FileDescriptor {
3
- readonly meta?: {
4
- readonly size?: number;
5
- readonly mtime?: number;
6
- readonly hash?: string;
7
- data?: T;
8
- };
9
- }
1
+ import { type FileDescriptor } from './util/file-entry-cache.js';
10
2
  export declare class CacheConsultant<T> {
11
3
  private cache;
12
4
  constructor(name: string);
13
5
  static getCacheLocation(): string;
14
- getFileDescriptor(file: string): FD<T>;
6
+ getFileDescriptor(file: string): FileDescriptor<T>;
15
7
  reconcile(): void;
16
8
  }
17
- export {};
@@ -1,20 +1,19 @@
1
- import fileEntryCache, {} from 'file-entry-cache';
2
1
  import { timerify } from './util/Performance.js';
3
2
  import parsedArgValues from './util/cli-arguments.js';
3
+ import { FileEntryCache } from './util/file-entry-cache.js';
4
4
  import { cwd, join } from './util/path.js';
5
5
  import { version } from './version.js';
6
6
  const defaultCacheLocation = join(cwd, 'node_modules', '.cache', 'knip');
7
7
  const { cache: isCache = false, watch: isWatch = false } = parsedArgValues;
8
8
  const cacheLocation = parsedArgValues['cache-location'] ?? defaultCacheLocation;
9
- const create = timerify(fileEntryCache.create, 'createCache');
10
- const dummyFileDescriptor = { key: '', changed: true, notFound: true, meta: undefined };
9
+ const dummyFileDescriptor = { key: '', changed: true, notFound: true };
11
10
  const isEnabled = isCache || isWatch;
12
11
  export class CacheConsultant {
13
12
  cache;
14
13
  constructor(name) {
15
14
  if (isCache) {
16
15
  const cacheName = `${name.replace(/[^a-z0-9]/g, '-').replace(/-*$/, '')}-${version}`;
17
- this.cache = create(cacheName, cacheLocation);
16
+ this.cache = new FileEntryCache(cacheName, cacheLocation);
18
17
  this.reconcile = timerify(this.cache.reconcile).bind(this.cache);
19
18
  this.getFileDescriptor = timerify(this.cache.getFileDescriptor).bind(this.cache);
20
19
  }
@@ -8,7 +8,6 @@ import { timerify } from './util/Performance.js';
8
8
  import { compact } from './util/array.js';
9
9
  import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from './util/modules.js';
10
10
  import { dirname, extname, isInNodeModules, join } from './util/path.js';
11
- import { _deserialize, _serialize } from './util/serialize.js';
12
11
  const baseCompilerOptions = {
13
12
  allowJs: true,
14
13
  allowSyntheticDefaultImports: true,
@@ -150,7 +149,7 @@ export class ProjectPrincipal {
150
149
  analyzeSourceFile(filePath, options, isGitIgnored, isPackageNameInternalWorkspace, getPrincipalByFilePath) {
151
150
  const fd = this.cache.getFileDescriptor(filePath);
152
151
  if (!fd.changed && fd.meta?.data)
153
- return _deserialize(fd.meta.data);
152
+ return fd.meta.data;
154
153
  const typeChecker = this.backend.typeChecker;
155
154
  if (!typeChecker)
156
155
  throw new Error('Must initialize TypeChecker before source file analysis');
@@ -246,7 +245,8 @@ export class ProjectPrincipal {
246
245
  const fd = this.cache.getFileDescriptor(filePath);
247
246
  if (!fd?.meta)
248
247
  continue;
249
- fd.meta.data = _serialize(file);
248
+ const { imported, internalImportCache, ...clone } = file;
249
+ fd.meta.data = clone;
250
250
  }
251
251
  this.cache.reconcile();
252
252
  }
@@ -16,6 +16,10 @@ type WorkspaceManagerOptions = {
16
16
  isStrict: boolean;
17
17
  };
18
18
  export type ReferencedDependencies = Set<[string, string]>;
19
+ type CacheItem = {
20
+ resolveEntryPaths?: string[];
21
+ resolveConfig?: string[];
22
+ };
19
23
  export declare class WorkspaceWorker {
20
24
  name: string;
21
25
  dir: string;
@@ -28,10 +32,10 @@ export declare class WorkspaceWorker {
28
32
  isStrict: boolean;
29
33
  rootIgnore: Configuration['ignore'];
30
34
  negatedWorkspacePatterns: string[];
31
- 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>;
32
36
  enabledPlugins: PluginName[];
33
37
  enabledPluginsInAncestors: string[];
34
- cache: CacheConsultant<unknown>;
38
+ cache: CacheConsultant<CacheItem>;
35
39
  constructor({ name, dir, cwd, config, manifest, dependencies, isProduction, isStrict, rootIgnore, negatedWorkspacePatterns, enabledPluginsInAncestors, }: WorkspaceManagerOptions);
36
40
  init(): Promise<void>;
37
41
  private determineEnabledPlugins;
@@ -48,7 +52,7 @@ export declare class WorkspaceWorker {
48
52
  entryFilePatterns: Set<string>;
49
53
  productionEntryFilePatterns: Set<string>;
50
54
  referencedDependencies: ReferencedDependencies;
51
- 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")[];
52
56
  }>;
53
57
  onDispose(): void;
54
58
  }
@@ -202,22 +202,33 @@ export class WorkspaceWorker {
202
202
  if (hasResolveEntryPaths || shouldRunConfigResolver) {
203
203
  const isManifest = basename(configFilePath) === 'package.json';
204
204
  const fd = isManifest ? undefined : this.cache.getFileDescriptor(configFilePath);
205
- const config = fd?.meta?.data && !fd.changed
206
- ? fd.meta.data
207
- : await loadConfigForPlugin(configFilePath, plugin, opts, pluginName);
208
- if (config) {
209
- if (hasResolveEntryPaths) {
210
- const dependencies = (await plugin.resolveEntryPaths?.(config, opts)) ?? [];
211
- for (const id of dependencies)
205
+ if (fd?.meta?.data && !fd.changed) {
206
+ if (fd.meta.data.resolveEntryPaths)
207
+ for (const id of fd.meta.data.resolveEntryPaths)
212
208
  configEntryPaths.push(id);
213
- }
214
- if (shouldRunConfigResolver) {
215
- const dependencies = (await plugin.resolveConfig?.(config, opts)) ?? [];
216
- for (const id of dependencies)
209
+ if (fd.meta.data.resolveConfig)
210
+ for (const id of fd.meta.data.resolveConfig)
217
211
  addDependency(id, configFilePath);
212
+ }
213
+ else {
214
+ const config = await loadConfigForPlugin(configFilePath, plugin, opts, pluginName);
215
+ const data = {};
216
+ if (config) {
217
+ if (hasResolveEntryPaths) {
218
+ const dependencies = (await plugin.resolveEntryPaths?.(config, opts)) ?? [];
219
+ for (const id of dependencies)
220
+ configEntryPaths.push(id);
221
+ data.resolveEntryPaths = dependencies;
222
+ }
223
+ if (shouldRunConfigResolver) {
224
+ const dependencies = (await plugin.resolveConfig?.(config, opts)) ?? [];
225
+ for (const id of dependencies)
226
+ addDependency(id, configFilePath);
227
+ data.resolveConfig = dependencies;
228
+ }
229
+ if (!isManifest && fd?.changed && fd.meta)
230
+ fd.meta.data = data;
218
231
  }
219
- if (!isManifest && fd?.changed && fd.meta)
220
- fd.meta.data = config;
221
232
  }
222
233
  }
223
234
  }
@@ -30,6 +30,18 @@ export default visit(() => true, node => {
30
30
  });
31
31
  }
32
32
  }
33
+ else if (ts.isObjectBindingPattern(arg.name)) {
34
+ return arg.name.elements.map(element => {
35
+ const identifier = (element.propertyName ?? element.name).getText();
36
+ const alias = element.propertyName ? element.name.getText() : undefined;
37
+ return {
38
+ identifier,
39
+ alias,
40
+ specifier,
41
+ pos: element.pos,
42
+ };
43
+ });
44
+ }
33
45
  }
34
46
  return { identifier: 'default', specifier, pos };
35
47
  }
@@ -0,0 +1,30 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import fs from 'node:fs';
3
+ type MetaData<T> = {
4
+ size: number;
5
+ mtime: number;
6
+ data?: T;
7
+ };
8
+ export type FileDescriptor<T> = {
9
+ key: string;
10
+ changed?: boolean;
11
+ notFound?: boolean;
12
+ err?: unknown;
13
+ meta?: MetaData<T>;
14
+ };
15
+ export declare class FileEntryCache<T> {
16
+ filePath: string;
17
+ cache: Map<string, MetaData<T>>;
18
+ normalizedEntries: Map<string, FileDescriptor<T>>;
19
+ constructor(cacheId: string, _path: string);
20
+ removeNotFoundFiles(): void;
21
+ getFileDescriptor(filePath: string): FileDescriptor<T>;
22
+ _getFileDescriptorUsingMtimeAndSize(filePath: string, fstat: fs.Stats): FileDescriptor<T>;
23
+ removeEntry(entryName: string): void;
24
+ _getMetaForFileUsingMtimeAndSize(cacheEntry: FileDescriptor<T>): {
25
+ size: number;
26
+ mtime: number;
27
+ };
28
+ reconcile(): void;
29
+ }
30
+ export {};
@@ -0,0 +1,109 @@
1
+ import fs from 'node:fs';
2
+ import { timerify } from './Performance.js';
3
+ import { debugLog } from './debug.js';
4
+ import { isDirectory, isFile } from './fs.js';
5
+ import { dirname, isAbsolute, resolve } from './path.js';
6
+ import { deserialize, serialize } from './serialize.js';
7
+ const cwd = process.cwd();
8
+ const createCache = (filePath) => {
9
+ try {
10
+ return deserialize(fs.readFileSync(filePath));
11
+ }
12
+ catch (_err) {
13
+ debugLog('*', `Error reading cache from ${filePath}`);
14
+ }
15
+ };
16
+ const create = timerify(createCache);
17
+ export class FileEntryCache {
18
+ filePath;
19
+ cache = new Map();
20
+ normalizedEntries = new Map();
21
+ constructor(cacheId, _path) {
22
+ this.filePath = isAbsolute(_path) ? resolve(_path, cacheId) : resolve(cwd, _path, cacheId);
23
+ if (isFile(this.filePath))
24
+ this.cache = create(this.filePath);
25
+ this.removeNotFoundFiles();
26
+ }
27
+ removeNotFoundFiles() {
28
+ for (const filePath of this.normalizedEntries.keys()) {
29
+ try {
30
+ fs.statSync(filePath);
31
+ }
32
+ catch (error) {
33
+ if (error.code === 'ENOENT')
34
+ this.cache.delete(filePath);
35
+ }
36
+ }
37
+ }
38
+ getFileDescriptor(filePath) {
39
+ let fstat;
40
+ try {
41
+ if (!isAbsolute(filePath))
42
+ filePath = resolve(filePath);
43
+ fstat = fs.statSync(filePath);
44
+ }
45
+ catch (error) {
46
+ this.removeEntry(filePath);
47
+ return { key: filePath, notFound: true, err: error };
48
+ }
49
+ return this._getFileDescriptorUsingMtimeAndSize(filePath, fstat);
50
+ }
51
+ _getFileDescriptorUsingMtimeAndSize(filePath, fstat) {
52
+ let meta = this.cache.get(filePath);
53
+ const cacheExists = Boolean(meta);
54
+ const cSize = fstat.size;
55
+ const cTime = fstat.mtime.getTime();
56
+ let isDifferentDate;
57
+ let isDifferentSize;
58
+ if (meta) {
59
+ isDifferentDate = cTime !== meta.mtime;
60
+ isDifferentSize = cSize !== meta.size;
61
+ }
62
+ else {
63
+ meta = { size: cSize, mtime: cTime };
64
+ }
65
+ const fd = {
66
+ key: filePath,
67
+ changed: !cacheExists || isDifferentDate || isDifferentSize,
68
+ meta,
69
+ };
70
+ this.normalizedEntries.set(filePath, fd);
71
+ return fd;
72
+ }
73
+ removeEntry(entryName) {
74
+ if (!isAbsolute(entryName))
75
+ entryName = resolve(cwd, entryName);
76
+ this.normalizedEntries.delete(entryName);
77
+ this.cache.delete(entryName);
78
+ }
79
+ _getMetaForFileUsingMtimeAndSize(cacheEntry) {
80
+ const stat = fs.statSync(cacheEntry.key);
81
+ const meta = Object.assign(cacheEntry.meta ?? {}, {
82
+ size: stat.size,
83
+ mtime: stat.mtime.getTime(),
84
+ });
85
+ return meta;
86
+ }
87
+ reconcile() {
88
+ this.removeNotFoundFiles();
89
+ for (const [entryName, cacheEntry] of this.normalizedEntries.entries()) {
90
+ try {
91
+ const meta = this._getMetaForFileUsingMtimeAndSize(cacheEntry);
92
+ this.cache.set(entryName, meta);
93
+ }
94
+ catch (error) {
95
+ if (error.code !== 'ENOENT')
96
+ throw error;
97
+ }
98
+ }
99
+ try {
100
+ const dir = dirname(this.filePath);
101
+ if (!isDirectory(dir))
102
+ fs.mkdirSync(dir, { recursive: true });
103
+ fs.writeFileSync(this.filePath, serialize(this.cache));
104
+ }
105
+ catch (_err) {
106
+ debugLog('*', `Error writing cache to ${this.filePath}`);
107
+ }
108
+ }
109
+ }
@@ -1,3 +1,3 @@
1
- import type { FileNode } from '../types/dependency-graph.js';
2
- export declare const _serialize: (data: FileNode) => FileNode;
3
- export declare const _deserialize: (data: FileNode) => FileNode;
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare let s: (data: any) => Buffer, d: (buffer: Buffer) => any;
3
+ export { s as serialize, d as deserialize };
@@ -1,42 +1,12 @@
1
- import { timerify } from './Performance.js';
2
- const serializeObj = (obj) => {
3
- if (!obj)
4
- return obj;
5
- if (obj instanceof Set)
6
- return Array.from(obj);
7
- if (obj instanceof Map) {
8
- const o = { _m: 1 };
9
- for (const [key, value] of obj)
10
- o[key] = serializeObj(value);
11
- return o;
12
- }
13
- if (typeof obj === 'object')
14
- for (const key in obj)
15
- obj[key] = serializeObj(obj[key]);
16
- return obj;
17
- };
18
- const deserializeObj = (obj) => {
19
- if (!obj)
20
- return obj;
21
- if (Array.isArray(obj))
22
- return new Set(obj);
23
- if (obj._m) {
24
- const map = new Map();
25
- for (const key in obj)
26
- key !== '_m' && map.set(key, deserializeObj(obj[key]));
27
- return map;
28
- }
29
- if (typeof obj === 'object')
30
- for (const key in obj)
31
- obj[key] = deserializeObj(obj[key]);
32
- return obj;
33
- };
34
- const serialize = (data) => {
35
- const clone = structuredClone(data);
36
- clone.imported = undefined;
37
- clone.internalImportCache = undefined;
38
- return serializeObj(clone);
39
- };
40
- const deserialize = (data) => deserializeObj(data);
41
- export const _serialize = timerify(serialize);
42
- export const _deserialize = timerify(deserialize);
1
+ let s, d;
2
+ if (typeof Bun !== 'undefined') {
3
+ const { serialize, deserialize } = await import('bun:jsc');
4
+ s = serialize;
5
+ d = deserialize;
6
+ }
7
+ else {
8
+ const { serialize, deserialize } = await import('node:v8');
9
+ s = serialize;
10
+ d = deserialize;
11
+ }
12
+ export { s as serialize, d as deserialize };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.21.2";
1
+ export declare const version = "5.22.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.21.2';
1
+ export const version = '5.22.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.21.2",
3
+ "version": "5.22.1",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {
@@ -61,7 +61,6 @@
61
61
  "@snyk/github-codeowners": "1.1.0",
62
62
  "easy-table": "1.2.0",
63
63
  "fast-glob": "^3.3.2",
64
- "file-entry-cache": "8.0.0",
65
64
  "jiti": "^1.21.0",
66
65
  "js-yaml": "^4.1.0",
67
66
  "minimist": "^1.2.8",
@@ -84,7 +83,6 @@
84
83
  "@jest/types": "^29.6.3",
85
84
  "@release-it/bumper": "^6.0.1",
86
85
  "@types/bun": "^1.1.4",
87
- "@types/file-entry-cache": "5.0.4",
88
86
  "@types/js-yaml": "^4.0.9",
89
87
  "@types/minimist": "^1.2.5",
90
88
  "@types/picomatch": "2.3.3",
@@ -95,7 +93,7 @@
95
93
  "playwright": "^1.44.1",
96
94
  "release-it": "^17.3.0",
97
95
  "type-fest": "^4.20.0",
98
- "typescript": "^5.4.5"
96
+ "typescript": "^5.5.2"
99
97
  },
100
98
  "engines": {
101
99
  "node": ">=18.6.0"