knip 6.44.0 → 6.44.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.
package/README.md CHANGED
@@ -50,8 +50,7 @@ following projects:
50
50
  [2]: https://img.shields.io/npm/v/knip?color=f56e0f
51
51
  [3]: https://img.shields.io/badge/dynamic/json?url=https://npm-gapless.webpro.nl/downloads/range/last-month/knip&query=$.totalCompact&label=downloads&suffix=/month&color=f56e0f
52
52
  [4]: https://github.com/webpro-nl/knip
53
- [5]:
54
- https://img.shields.io/github/stars/webpro-nl/knip?style=flat&color=f56e0f
53
+ [5]: https://img.shields.io/github/stars/webpro-nl/knip?style=flat&color=f56e0f
55
54
  [6]: https://knip.dev
56
55
  [7]: https://www.npmx.dev/package/@knip/create-config
57
56
  [8]: https://www.npmx.dev/package/@knip/language-server
@@ -69,7 +68,6 @@ following projects:
69
68
  [20]: https://github.com/pnpm/pnpm/tree/main/deps/graph-sequencer
70
69
  [21]: https://github.com/pnpm/pnpm/blob/main/LICENSE
71
70
  [22]: https://github.com/jaredwray/cacheable/tree/main/packages/file-entry-cache
72
- [23]:
73
- https://github.com/jaredwray/cacheable/blob/main/packages/file-entry-cache/LICENSE
71
+ [23]: https://github.com/jaredwray/cacheable/blob/main/packages/file-entry-cache/LICENSE
74
72
  [24]: https://github.com/npm/json-parse-even-better-errors
75
73
  [25]: https://github.com/npm/json-parse-even-better-errors/blob/main/LICENSE.md
@@ -1,5 +1,5 @@
1
1
  import { isBuiltin } from 'node:module';
2
- import { DT_SCOPE, IGNORE_DEFINITELY_TYPED, IGNORED_DEPENDENCIES, IGNORED_RUNTIME_DEPENDENCIES, ROOT_WORKSPACE_NAME, } from './constants.js';
2
+ import { DT_SCOPE, IGNORE_DEFINITELY_TYPED, IGNORED_DEPENDENCIES, IGNORED_RUNTIME_DEPENDENCIES, PROTOCOL_CLOUDFLARE, ROOT_WORKSPACE_NAME, } from './constants.js';
3
3
  import { getDependencyMetaData } from './manifest/index.js';
4
4
  import { PackagePeeker } from './PackagePeeker.js';
5
5
  import { getDefinitelyTypedFor, getPackageFromDefinitelyTyped, getPackageNameFromModuleSpecifier, isDefinitelyTyped, } from './util/modules.js';
@@ -127,7 +127,7 @@ export class DependencyDeputy {
127
127
  maybeAddReferencedExternalDependency(workspace, packageName, { specifier, isDevOnly, isTypeOnly, isResolved, isPublishedType, isCaught }) {
128
128
  if (!this.isReportDependencies)
129
129
  return true;
130
- if (specifier.startsWith('node:') || isBuiltin(specifier))
130
+ if (specifier.startsWith('node:') || specifier.startsWith(PROTOCOL_CLOUDFLARE) || isBuiltin(specifier))
131
131
  return true;
132
132
  if (IGNORED_RUNTIME_DEPENDENCIES.has(packageName))
133
133
  return true;
@@ -2,6 +2,7 @@ import type { ParseResult } from 'oxc-parser';
2
2
  import { CacheConsultant } from './CacheConsultant.ts';
3
3
  import type { Compilers } from './compilers/types.ts';
4
4
  import type { GetImportsAndExportsOptions, IgnoreExportsUsedInFile, PluginVisitorContext, PluginVisitorObject } from './types/config.ts';
5
+ import type { ConfigurationFiles } from './session/configuration.ts';
5
6
  import type { FileNode } from './types/module-graph.ts';
6
7
  import type { Paths } from './types/project.ts';
7
8
  import type { ResolveGlobPattern } from './typescript/resolve-module-names.ts';
@@ -39,7 +40,7 @@ export declare class ProjectPrincipal {
39
40
  addCompilers(workspaceName: string, compilers: Compilers): void;
40
41
  addPaths(paths: Paths, basePath: string, scope: string): void;
41
42
  addRootDirs(rootDirs: string[] | undefined, scope: string): void;
42
- collectConfigurationImports(paths: Set<string>): void;
43
+ collectConfigurationImports(configurationFiles: ConfigurationFiles): void;
43
44
  init(): void;
44
45
  private hasAcceptedExtension;
45
46
  addEntryPath(filePath: string, options?: {
@@ -100,8 +100,8 @@ export class ProjectPrincipal {
100
100
  const scoped = this.rootDirs.get(scope) ?? [];
101
101
  this.rootDirs.set(scope, compact([...scoped, ...rootDirs]));
102
102
  }
103
- collectConfigurationImports(paths) {
104
- for (const filePath of paths) {
103
+ collectConfigurationImports(configurationFiles) {
104
+ for (const filePath of configurationFiles.paths) {
105
105
  if (!DEFAULT_EXTENSIONS.has(extname(filePath)))
106
106
  continue;
107
107
  const source = this.fileManager.readRawFile(filePath);
@@ -111,7 +111,7 @@ export class ProjectPrincipal {
111
111
  for (const specifier of extractSpecifiers(_parseFile(filePath, source), source, filePath)) {
112
112
  const resolved = this.resolveSpecifier(specifier, filePath);
113
113
  if (resolved && !isInNodeModules(resolved))
114
- paths.add(resolved);
114
+ configurationFiles.addImport(filePath, resolved);
115
115
  }
116
116
  }
117
117
  catch { }
@@ -56,7 +56,12 @@ const commands = new Set([
56
56
  ]);
57
57
  export const resolve = (_binary, words, options) => {
58
58
  const binary = toCommandBinary(_binary, options);
59
- const parsed = parseArgs(words, { string: ['cwd'] });
59
+ const parsed = parseArgs(words, {
60
+ string: ['cwd'],
61
+ boolean: ['bun', 'hot', 'if-present', 'silent', 'smol', 'watch'],
62
+ alias: { bun: 'b' },
63
+ isStorePositions: true,
64
+ });
60
65
  const [command, script] = parsed._;
61
66
  if (command === 'x') {
62
67
  const wordsForX = words.filter(word => word.value !== 'x');
@@ -81,7 +86,7 @@ export const resolve = (_binary, words, options) => {
81
86
  const dir = parsed.cwd ? join(cwd, parsed.cwd) : undefined;
82
87
  const opts = dir ? { cwd: dir } : {};
83
88
  if (command !== 'run')
84
- return [binary, ...fromArgs(words, opts)];
89
+ return [binary, ...fromArgs(dir ? words : words.slice(parsed.positionalIndices[0]), opts)];
85
90
  const input = toBinary(filePath, { optional: true });
86
91
  if (dir)
87
92
  input.dir = dir;
@@ -11,6 +11,7 @@ export declare const BETA_TAG = "@beta";
11
11
  export declare const ALIAS_TAG = "@alias";
12
12
  export declare const DT_SCOPE = "@types";
13
13
  export declare const PROTOCOL_VIRTUAL = "virtual:";
14
+ export declare const PROTOCOL_CLOUDFLARE = "cloudflare:";
14
15
  export declare const IGNORED_GLOBAL_BINARIES: Set<string>;
15
16
  export declare const IGNORED_DEPENDENCIES: Set<string>;
16
17
  export declare const IGNORED_RUNTIME_DEPENDENCIES: Set<string>;
package/dist/constants.js CHANGED
@@ -20,6 +20,7 @@ export const BETA_TAG = '@beta';
20
20
  export const ALIAS_TAG = '@alias';
21
21
  export const DT_SCOPE = '@types';
22
22
  export const PROTOCOL_VIRTUAL = 'virtual:';
23
+ export const PROTOCOL_CLOUDFLARE = 'cloudflare:';
23
24
  export const IGNORED_GLOBAL_BINARIES = new Set([
24
25
  'amplify',
25
26
  'aws',
@@ -5,7 +5,12 @@ import { api, resolveName } from './helpers.js';
5
5
  const title = 'Babel';
6
6
  const enablers = [/^@babel\//];
7
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
- const config = ['babel.config.{json,js,cjs,mjs,cts,ts}', '.babelrc.{json,js,cjs,mjs,cts}', '.babelrc', 'package.json'];
8
+ const config = [
9
+ 'babel.config.{json,js,cjs,mjs,cts,ts,mts}',
10
+ '.babelrc.{json,js,cjs,mjs,cts}',
11
+ '.babelrc',
12
+ 'package.json',
13
+ ];
9
14
  const getName = (value) => [Array.isArray(value) ? value[0] : value].filter(name => typeof name === 'string');
10
15
  export const getDependenciesFromConfig = (config) => {
11
16
  const presets = config.presets?.flatMap(getName).map(name => resolveName(name, 'preset')) ?? [];
@@ -5,10 +5,17 @@ const enablers = ['cspell'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
6
  const config = [
7
7
  'package.json',
8
- 'cspell.config.{js,cjs,mjs,ts,mts,json,yaml,yml}',
9
- 'cspell.{json,yaml,yml}',
8
+ '{.,}cspell.config.{js,cjs,mjs,ts,cts,mts,json,jsonc,yaml,yml,toml}',
9
+ '{.,}cspell.{jsonc,yaml,yml}',
10
10
  '.c{s,S}pell.json',
11
11
  'c{s,S}pell.json',
12
+ '.vscode/c{s,S}pell.json',
13
+ '.vscode/.cspell.json',
14
+ '.config/{.,}cspell.config.{js,cjs,mjs,ts,cts,mts,json,jsonc,yaml,yml,toml}',
15
+ '.config/.c{s,S}pell.json',
16
+ '.config/c{s,S}pell.json',
17
+ '.config/{.,}cspell.jsonc',
18
+ '.config/cspell.{yaml,yml}',
12
19
  ];
13
20
  const resolveConfig = config => {
14
21
  return [config?.import ?? []].flat().map(id => toDeferResolve(id));
@@ -8,7 +8,7 @@ const title = 'Eleventy';
8
8
  const enablers = ['@11ty/eleventy'];
9
9
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
10
10
  const config = ['.eleventy.js', 'eleventy.config.{js,cjs,mjs}'];
11
- const production = ['posts/**/*.11tydata.js', '_data/**/*.{js,cjs,mjs}'];
11
+ const production = ['posts/**/*.11tydata.{js,cjs,mjs}', '_data/**/*.{js,cjs,mjs}'];
12
12
  const resolveConfig = async (localConfig, options) => {
13
13
  const { configFileDir } = options;
14
14
  const dummyUserConfig = new DummyEleventyConfig();
@@ -38,7 +38,7 @@ const resolveConfig = async (localConfig, options) => {
38
38
  .concat([
39
39
  join(inputDir, dataDir, '**/*.{js,cjs,mjs}'),
40
40
  join(inputDir, `**/*.{${typeof templateFormats === 'string' ? templateFormats : templateFormats.join(',')}}`),
41
- join(inputDir, '**/*.11tydata.js'),
41
+ join(inputDir, '**/*.11tydata.{js,cjs,mjs}'),
42
42
  ...copiedEntries,
43
43
  ].map(id => toProductionEntry(id)));
44
44
  };
@@ -4,7 +4,7 @@ import { hasDependency } from '../../util/plugin.js';
4
4
  const title = 'Mocha';
5
5
  const enablers = ['mocha'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
- const config = ['.mocharc.{js,cjs,json,jsonc,yml,yaml}', 'package.json'];
7
+ const config = ['.mocharc.{js,cjs,mjs,json,jsonc,yml,yaml}', 'package.json'];
8
8
  const entry = ['**/test/*.{js,cjs,mjs}'];
9
9
  const defaultExtensions = ['js', 'cjs', 'mjs'];
10
10
  const builtInReporters = new Set([
@@ -7,7 +7,7 @@ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const packageJsonPath = 'npmpackagejsonlint';
8
8
  const config = ['package.json', ...toCosmiconfig('npmpackagejsonlint')];
9
9
  const resolveConfig = localConfig => {
10
- return localConfig?.extends ? [localConfig.extends].map(id => toDependency(id)) : [];
10
+ return localConfig?.extends ? [localConfig.extends].flat().map(id => toDependency(id)) : [];
11
11
  };
12
12
  const plugin = {
13
13
  title,
@@ -1,3 +1,3 @@
1
1
  export type NpmPkgJsonLintConfig = {
2
- extends?: string;
2
+ extends?: string | string[];
3
3
  };
@@ -5,7 +5,7 @@ import { hasDependency } from '../../util/plugin.js';
5
5
  const title = 'Relay';
6
6
  const enablers = ['vite-plugin-relay', '@swc/plugin-relay', 'babel-plugin-relay'];
7
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
- const config = ['relay.config.json', 'relay.config.js'];
8
+ const config = ['package.json', 'relay.config.json', 'relay.config.js'];
9
9
  const resolveConfig = async (config) => {
10
10
  const projects = 'projects' in config ? Object.values(config.projects) : [config];
11
11
  return projects.map(project => {
@@ -3,7 +3,7 @@ import { hasDependency } from '../../util/plugin.js';
3
3
  const title = 'Release It!';
4
4
  const enablers = ['release-it'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
- const config = ['.release-it.{json,js,cjs,ts,yml,yaml,toml}', 'package.json'];
6
+ const config = ['.release-it.{json,jsonc,json5,js,mjs,cjs,ts,mts,cts,yml,yaml,toml}', 'package.json'];
7
7
  const resolveConfig = (config, options) => {
8
8
  const plugins = config.plugins ? Object.keys(config.plugins) : [];
9
9
  const scripts = config.hooks ? Object.values(config.hooks).flat() : [];
@@ -1,6 +1,7 @@
1
- import { toProductionEntry } from '../../util/input.js';
1
+ import { toDeferResolve, toProductionEntry } from '../../util/input.js';
2
2
  import { hasDependency } from '../../util/plugin.js';
3
3
  import { resolveConfig as resolveRspackConfig } from '../rspack/index.js';
4
+ import { getSwcPluginSpecifiers } from '../webpack/index.js';
4
5
  const title = 'Rsbuild';
5
6
  const enablers = ['@rsbuild/core'];
6
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
@@ -17,6 +18,22 @@ const getConfigs = async (configOrFn) => {
17
18
  configs.push(await configOrFn(params));
18
19
  return configs;
19
20
  };
21
+ const getAliases = (chain) => {
22
+ const aliases = {};
23
+ for (const item of [chain].flat()) {
24
+ if (typeof item === 'function')
25
+ continue;
26
+ for (const key in item) {
27
+ const targets = [];
28
+ for (const target of [item[key]].flat())
29
+ if (typeof target === 'string')
30
+ targets.push(target);
31
+ if (targets.length > 0)
32
+ aliases[key] = targets;
33
+ }
34
+ }
35
+ return aliases;
36
+ };
20
37
  const resolveConfig = async (configOrFn, options) => {
21
38
  const entries = new Set();
22
39
  const inputs = [];
@@ -48,6 +65,17 @@ const resolveConfig = async (configOrFn, options) => {
48
65
  };
49
66
  const checkConfig = async (localConfig, config) => {
50
67
  checkSource(localConfig.source);
68
+ const alias = localConfig.resolve?.alias;
69
+ if (alias) {
70
+ for (const input of await resolveRspackConfig({ resolve: { alias: getAliases(alias) } }, options)) {
71
+ inputs.push(input);
72
+ }
73
+ }
74
+ const swc = localConfig.tools?.swc;
75
+ if (swc && typeof swc === 'object') {
76
+ for (const specifier of getSwcPluginSpecifiers(swc))
77
+ inputs.push(toDeferResolve(specifier));
78
+ }
51
79
  if (!localConfig.tools?.rspack)
52
80
  return;
53
81
  const target = localConfig.output?.target ?? config.output?.target ?? 'web';
@@ -1,4 +1,5 @@
1
1
  import type { Configuration } from 'webpack';
2
+ import type { SWCConfig } from '../swc/types.ts';
2
3
  type EntryDescription = Record<string, unknown>;
3
4
  type Entry = Record<string, string | string[] | (EntryDescription & {
4
5
  html?: boolean;
@@ -12,18 +13,25 @@ type RspackConfigUtils = {
12
13
  isWebWorker: boolean;
13
14
  };
14
15
  type RspackConfig = Configuration | ((config: Configuration, utils: RspackConfigUtils) => Configuration | void | Promise<Configuration | void>);
16
+ type AliasObject = Record<string, string | false | (string | false)[]>;
17
+ type Alias = AliasObject | ((aliases: AliasObject) => AliasObject | void);
18
+ export type AliasChain = Alias | Alias[];
15
19
  export type RsbuildConfig = {
16
20
  plugins?: unknown[];
17
21
  mode?: 'development' | 'production' | 'none';
18
22
  output?: {
19
23
  target?: RspackConfigUtils['target'];
20
24
  };
25
+ resolve?: {
26
+ alias?: AliasChain;
27
+ };
21
28
  source?: {
22
29
  entry?: Entry;
23
30
  preEntry?: string | string[];
24
31
  };
25
32
  tools?: {
26
33
  rspack?: RspackConfig | RspackConfig[];
34
+ swc?: SWCConfig | ((config: SWCConfig) => SWCConfig | void);
27
35
  };
28
36
  environments?: {
29
37
  [k: string]: Omit<RsbuildConfig, 'environments'>;
@@ -1,9 +1,10 @@
1
1
  import { toDependency } from '../../util/input.js';
2
2
  import { hasDependency } from '../../util/plugin.js';
3
+ import { toCosmiconfig } from '../../util/plugin-config.js';
3
4
  const title = 'SVGR';
4
5
  const enablers = ['@svgr/cli', '@svgr/core'];
5
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
- const config = ['.svgrrc', '.svgrrc.{yaml,yml,json,js}', 'svgr.config.{js,cjs}', 'package.json'];
7
+ const config = ['package.json', ...toCosmiconfig('svgr')];
7
8
  const resolveConfig = async (config) => {
8
9
  const inputs = [];
9
10
  if (config.plugins) {
@@ -6,9 +6,14 @@ const enablers = ['@swc/core'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const config = ['.swcrc'];
8
8
  const resolveConfig = async (config) => {
9
- const inputs = config?.jsc?.experimental?.plugins ?? [];
10
- const externalHelpers = config.jsc?.externalHelpers ? ['@swc/helpers'] : [];
11
- return compact([...inputs.map(([id]) => id), ...externalHelpers]).map(id => toDependency(id));
9
+ const dependencies = [];
10
+ for (const item of [config].flat()) {
11
+ for (const [id] of item?.jsc?.experimental?.plugins ?? [])
12
+ dependencies.push(id);
13
+ if (item?.jsc?.externalHelpers)
14
+ dependencies.push('@swc/helpers');
15
+ }
16
+ return compact(dependencies).map(id => toDependency(id));
12
17
  };
13
18
  const plugin = {
14
19
  title,
@@ -1,5 +1,7 @@
1
1
  import type { Plugin, ResolveConfig } from '../../types/config.ts';
2
+ import type { SWCConfig } from '../swc/types.ts';
2
3
  import type { WebpackConfig } from './types.ts';
4
+ export declare const getSwcPluginSpecifiers: (options: SWCConfig) => string[];
3
5
  export declare const findWebpackDependenciesFromConfig: ResolveConfig<WebpackConfig>;
4
6
  declare const plugin: Plugin;
5
7
  export default plugin;
@@ -59,10 +59,8 @@ const resolveRuleSetDependencies = (rule) => {
59
59
  });
60
60
  };
61
61
  const isSwcLoader = (loader) => loader === 'builtin:swc-loader' || loader.endsWith('/swc-loader') || loader === 'swc-loader';
62
- const resolveSwcPluginDependencies = (use) => {
63
- if (!hasLoaderOptions(use) || !isSwcLoader(use.loader))
64
- return [];
65
- const plugins = use.options.jsc?.experimental?.plugins;
62
+ export const getSwcPluginSpecifiers = (options) => {
63
+ const plugins = options.jsc?.experimental?.plugins;
66
64
  if (!Array.isArray(plugins))
67
65
  return [];
68
66
  return plugins.flatMap(plugin => {
@@ -73,6 +71,11 @@ const resolveSwcPluginDependencies = (use) => {
73
71
  return [];
74
72
  });
75
73
  };
74
+ const resolveSwcPluginDependencies = (use) => {
75
+ if (!hasLoaderOptions(use) || !isSwcLoader(use.loader))
76
+ return [];
77
+ return getSwcPluginSpecifiers(use.options);
78
+ };
76
79
  const resolveUseItem = (use) => {
77
80
  if (!use)
78
81
  return [];
@@ -5,8 +5,11 @@ export declare class SessionConfigurationChangedError extends Error {
5
5
  }
6
6
  export declare class ConfigurationFiles {
7
7
  paths: Set<string>;
8
+ private importers;
8
9
  private matchers;
9
10
  constructor(options: MainOptions);
10
11
  addPatterns(dir: string, patterns: string[]): void;
12
+ addImport(importer: string, imported: string): void;
13
+ getAffected(changes: WatchChange[], cwd: string): Set<string>;
11
14
  getChange(changes: WatchChange[], cwd: string): SessionConfigurationChangedError | undefined;
12
15
  }
@@ -8,6 +8,7 @@ export class SessionConfigurationChangedError extends Error {
8
8
  }
9
9
  export class ConfigurationFiles {
10
10
  paths = new Set();
11
+ importers = new Map();
11
12
  matchers = [];
12
13
  constructor(options) {
13
14
  const manifests = [];
@@ -32,6 +33,24 @@ export class ConfigurationFiles {
32
33
  if (included.length > 0)
33
34
  this.matchers.push({ dir, isMatch: picomatch(included, { dot: true, ignore: excluded }) });
34
35
  }
36
+ addImport(importer, imported) {
37
+ this.paths.add(imported);
38
+ const importers = this.importers.get(imported);
39
+ if (importers)
40
+ importers.add(importer);
41
+ else
42
+ this.importers.set(imported, new Set([importer]));
43
+ }
44
+ getAffected(changes, cwd) {
45
+ const affected = new Set();
46
+ for (const change of changes)
47
+ affected.add(toAbsolute(toPosix(change.filePath), cwd));
48
+ for (const filePath of affected) {
49
+ for (const importer of this.importers.get(filePath) ?? [])
50
+ affected.add(importer);
51
+ }
52
+ return affected;
53
+ }
35
54
  getChange(changes, cwd) {
36
55
  for (const change of changes) {
37
56
  const filePath = toAbsolute(toPosix(change.filePath), cwd);
@@ -49,9 +49,10 @@ export const createSession = async (options) => {
49
49
  clearModuleResolutionCaches();
50
50
  clearGlobCache();
51
51
  clearGitignoreCache();
52
- if (current.session.configurationFiles)
53
- unload(current.session.configurationFiles.paths);
54
- const nextOptions = await createOptions(current.options.createdFrom);
52
+ const { configurationFiles } = current.session;
53
+ if (configurationFiles)
54
+ unload(configurationFiles.getAffected(changes, current.options.cwd));
55
+ const nextOptions = await createOptions(current.options.createdFrom, true);
55
56
  current = await initialize({ ...nextOptions, isCache: false });
56
57
  Object.assign(results, current.results);
57
58
  return { duration: performance.now() - startTime, mem: process.memoryUsage().heapUsed };
@@ -1,5 +1,6 @@
1
- import type { ArrayExpression, Expression, ImportExpression, VariableDeclarator } from 'oxc-parser';
1
+ import type { ArrayExpression, CallExpression, Expression, ImportExpression, VariableDeclarator } from 'oxc-parser';
2
2
  import type { WalkState } from './walk.ts';
3
3
  export declare function getPromiseAllElements(expression: Expression): ArrayExpression['elements'] | undefined;
4
4
  export declare function handleVariableDeclarator(node: VariableDeclarator, s: WalkState): void;
5
+ export declare function handleImportThen(node: CallExpression, s: WalkState): void;
5
6
  export declare function handleImportExpression(node: ImportExpression, s: WalkState): void;
@@ -25,46 +25,8 @@ export function handleVariableDeclarator(node, s) {
25
25
  importExpr = init;
26
26
  }
27
27
  if (importExpr && isStringLiteral(importExpr.source)) {
28
- s.handledImportExpressions.add(importExpr.start);
29
- const specifier = getStringValue(importExpr.source);
30
28
  const caughtFlag = init.type === 'AwaitExpression' && s.caughtTryBlockEnds.length > 0 ? IMPORT_FLAGS.CAUGHT : IMPORT_FLAGS.NONE;
31
- if (node.id.type === 'ObjectPattern') {
32
- for (const prop of node.id.properties) {
33
- if (prop.type === 'Property') {
34
- const importedName = prop.key?.type === 'Identifier'
35
- ? prop.key.name
36
- : prop.key?.type === 'Literal' && typeof prop.key.value === 'string'
37
- ? prop.key.value
38
- : undefined;
39
- const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;
40
- const alias = localName !== importedName ? localName : undefined;
41
- s.addImport(specifier, importedName, alias, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
42
- }
43
- else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {
44
- s.addImport(specifier, IMPORT_STAR, prop.argument.name, undefined, prop.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
45
- }
46
- }
47
- }
48
- else if (node.id.type === 'Identifier') {
49
- if (init.type === 'AwaitExpression') {
50
- s.addImport(specifier, 'default', node.id.name, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
51
- const resolved = s.resolveModule(specifier, s.filePath);
52
- if (resolved && !resolved.isExternalLibraryImport && !isInNodeModules(resolved.resolvedFileName)) {
53
- s.localImportMap.set(node.id.name, {
54
- importedName: IMPORT_STAR,
55
- filePath: resolved.resolvedFileName,
56
- isNamespace: true,
57
- isDynamicImport: true,
58
- });
59
- }
60
- }
61
- else {
62
- s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | IMPORT_FLAGS.OPAQUE | caughtFlag);
63
- }
64
- }
65
- else {
66
- s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | IMPORT_FLAGS.OPAQUE | caughtFlag);
67
- }
29
+ handleDynamicImportBinding(node.id, importExpr, init.type === 'AwaitExpression', caughtFlag, s);
68
30
  return;
69
31
  }
70
32
  if (init.type === 'CallExpression' &&
@@ -268,6 +230,62 @@ export function handleVariableDeclarator(node, s) {
268
230
  }
269
231
  }
270
232
  }
233
+ function handleDynamicImportBinding(id, importExpr, isAwaited, caughtFlag, s) {
234
+ s.handledImportExpressions.add(importExpr.start);
235
+ const specifier = getStringValue(importExpr.source);
236
+ if (id.type === 'ObjectPattern') {
237
+ for (const prop of id.properties) {
238
+ if (prop.type === 'Property') {
239
+ const importedName = prop.key?.type === 'Identifier'
240
+ ? prop.key.name
241
+ : prop.key?.type === 'Literal' && typeof prop.key.value === 'string'
242
+ ? prop.key.value
243
+ : undefined;
244
+ const localName = prop.value?.type === 'Identifier' ? prop.value.name : importedName;
245
+ const alias = localName !== importedName ? localName : undefined;
246
+ s.addImport(specifier, importedName, alias, undefined, prop.key?.start ?? prop.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
247
+ }
248
+ else if (prop.type === 'RestElement' && prop.argument?.type === 'Identifier') {
249
+ s.addImport(specifier, IMPORT_STAR, prop.argument.name, undefined, prop.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
250
+ }
251
+ }
252
+ }
253
+ else if (id.type === 'Identifier') {
254
+ if (isAwaited) {
255
+ s.addImport(specifier, 'default', id.name, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | caughtFlag);
256
+ const resolved = s.resolveModule(specifier, s.filePath);
257
+ if (resolved && !resolved.isExternalLibraryImport && !isInNodeModules(resolved.resolvedFileName)) {
258
+ s.localImportMap.set(id.name, {
259
+ importedName: IMPORT_STAR,
260
+ filePath: resolved.resolvedFileName,
261
+ isNamespace: true,
262
+ isDynamicImport: true,
263
+ });
264
+ }
265
+ }
266
+ else {
267
+ s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | IMPORT_FLAGS.OPAQUE | caughtFlag);
268
+ }
269
+ }
270
+ else {
271
+ s.addImport(specifier, undefined, undefined, undefined, importExpr.source.start, IMPORT_FLAGS.DYNAMIC | IMPORT_FLAGS.OPAQUE | caughtFlag);
272
+ }
273
+ }
274
+ export function handleImportThen(node, s) {
275
+ const callee = node.callee;
276
+ if (callee.type !== 'MemberExpression' || callee.computed || callee.property.name !== 'then')
277
+ return;
278
+ if (callee.object.type !== 'ImportExpression' || !isStringLiteral(callee.object.source))
279
+ return;
280
+ const fn = node.arguments[0];
281
+ if (fn?.type !== 'ArrowFunctionExpression' && fn?.type !== 'FunctionExpression')
282
+ return;
283
+ const param = fn.params[0];
284
+ if (param?.type !== 'Identifier' && param?.type !== 'ObjectPattern')
285
+ return;
286
+ const caughtFlag = s.caughtImportExpressions.has(callee.object.start) ? IMPORT_FLAGS.CAUGHT : IMPORT_FLAGS.NONE;
287
+ handleDynamicImportBinding(param, callee.object, true, caughtFlag, s);
288
+ }
271
289
  export function handleImportExpression(node, s) {
272
290
  if (s.handledImportExpressions.has(node.start))
273
291
  return;
@@ -7,7 +7,7 @@ import { collectAugmentationRefs, getLineAndCol, getStringValue, isStringLiteral
7
7
  import { EMPTY_TAGS } from './jsdoc.js';
8
8
  import { handleCallExpression, handleNewExpression, trackCustomElementRegistry } from './calls.js';
9
9
  import { handleExportAssignment, handleExportDefault, handleExportNamed, handleExpressionStatement, } from './exports.js';
10
- import { getPromiseAllElements, handleImportExpression, handleVariableDeclarator } from './imports.js';
10
+ import { getPromiseAllElements, handleImportExpression, handleImportThen, handleVariableDeclarator, } from './imports.js';
11
11
  import { handleJSXMemberExpression, handleMemberExpression } from './members.js';
12
12
  let state;
13
13
  const _getFix = (start, end, flags) => state.options.isFixExports ? [start, end, flags ?? FIX_FLAGS.NONE] : undefined;
@@ -419,6 +419,7 @@ const coreVisitorObject = {
419
419
  }
420
420
  },
421
421
  CallExpression(node) {
422
+ handleImportThen(node, state);
422
423
  handleCallExpression(node, state);
423
424
  },
424
425
  NewExpression(node) {
@@ -3,7 +3,7 @@ import { type ParsedCLIArgs } from './cli-arguments.ts';
3
3
  interface CreateOptions extends Partial<Options> {
4
4
  args?: ParsedCLIArgs;
5
5
  }
6
- export declare const createOptions: (options: CreateOptions) => Promise<{
6
+ export declare const createOptions: (options: CreateOptions, isReload?: boolean) => Promise<{
7
7
  createdFrom: CreateOptions;
8
8
  cacheLocation: string;
9
9
  catalog: import("../CatalogCounselor.ts").CatalogContainer[];
@@ -8,12 +8,12 @@ import { findFile, loadJSON } from './fs.js';
8
8
  import { getIncludedIssueTypes, shorthandCycles, shorthandDeps, shorthandExports, shorthandFiles, } from './get-included-issue-types.js';
9
9
  import { defaultRules } from './issue-initializers.js';
10
10
  import { loadResolvedConfigFile } from './load-config.js';
11
- import { _load } from './loader.js';
11
+ import { _load, unload } from './loader.js';
12
12
  import { logWarning } from './log.js';
13
13
  import { getKeysByValue } from './object.js';
14
14
  import { isAbsolute, isInternal, join, resolveCwd, toAbsolute } from './path.js';
15
15
  import { splitTags } from './tag.js';
16
- export const createOptions = async (options) => {
16
+ export const createOptions = async (options, isReload = false) => {
17
17
  const { args = {} } = options;
18
18
  const cwd = resolveCwd(options.cwd ?? args.directory ?? '.');
19
19
  const manifestPath = findFile(cwd, 'package.json');
@@ -32,6 +32,8 @@ export const createOptions = async (options) => {
32
32
  if (args.config && !configFilePath && !manifest.knip) {
33
33
  throw new ConfigurationError(`Unable to find ${args.config} or package.json#knip`);
34
34
  }
35
+ if (options.isSession && !isReload && configFilePath)
36
+ unload([configFilePath]);
35
37
  const loadedConfig = Object.assign({}, manifest.knip, configFilePath ? await loadResolvedConfigFile(configFilePath, args) : {});
36
38
  const validIssueTypes = new Set(ISSUE_TYPES);
37
39
  for (const key of ['rules', 'include', 'exclude']) {
@@ -6,26 +6,15 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
- import { readFileSync } from 'node:fs';
10
9
  import { createRequire } from 'node:module';
11
10
  import { pathToFileURL } from 'node:url';
12
11
  import { LoaderError } from './errors.js';
13
- import { findFile, loadFile, loadJSON, loadJSONC, loadTOML, loadYAML, parseJSONC, parseYAML } from './fs.js';
12
+ import { loadFile, loadJSON, loadJSONC, loadTOML, loadYAML, parseJSONC, parseYAML } from './fs.js';
14
13
  import { jiti } from './jiti.js';
15
14
  import { timerify } from './Performance.js';
16
- import { dirname, extname, isInternal, toNative } from './path.js';
17
- const unloaded = new Map();
18
- const isNativeModule = (filePath) => {
19
- const ext = extname(filePath);
20
- if (ext !== '.js')
21
- return ext === '.mjs';
22
- for (let dir = dirname(filePath); dir !== dirname(dir); dir = dirname(dir)) {
23
- const manifestPath = findFile(dir, 'package.json');
24
- if (manifestPath)
25
- return JSON.parse(readFileSync(manifestPath, 'utf8')).type === 'module';
26
- }
27
- return false;
28
- };
15
+ import { extname, isInternal, toNative } from './path.js';
16
+ const versions = new Map();
17
+ const isReloadable = (ext) => ext === '.js' || ext === '.mjs' || ext === '.cjs';
29
18
  const reload = async (filePath, version) => {
30
19
  let imported;
31
20
  try {
@@ -67,8 +56,9 @@ const load = async (filePath) => {
67
56
  if (ext === '.toml') {
68
57
  return await loadTOML(filePath);
69
58
  }
70
- const version = unloaded.get(filePath);
71
- if (version && isNativeModule(filePath))
59
+ const version = versions.get(filePath) ?? 0;
60
+ versions.set(filePath, version);
61
+ if (version && isReloadable(ext))
72
62
  return await reload(filePath, version);
73
63
  return await jiti.import(filePath, { default: true });
74
64
  }
@@ -82,6 +72,8 @@ export const unload = (filePaths) => {
82
72
  for (const filePath of filePaths) {
83
73
  delete cache[filePath];
84
74
  delete cache[toNative(filePath)];
85
- unloaded.set(filePath, (unloaded.get(filePath) ?? 0) + 1);
75
+ const version = versions.get(filePath);
76
+ if (version !== undefined)
77
+ versions.set(filePath, version + 1);
86
78
  }
87
79
  };
@@ -1,5 +1,5 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { DT_SCOPE, PROTOCOL_VIRTUAL } from '../constants.js';
2
+ import { DT_SCOPE, PROTOCOL_CLOUDFLARE, PROTOCOL_VIRTUAL } from '../constants.js';
3
3
  import { isAbsolute, isInNodeModules, join, toPosix } from './path.js';
4
4
  export const getPackageNameFromModuleSpecifier = (specifier) => {
5
5
  if (!isStartsLikePackageName(specifier) && !isTildePackageName(specifier))
@@ -89,6 +89,7 @@ const CHAR_HASH = 35;
89
89
  const CHAR_QUESTION = 63;
90
90
  export const sanitizeSpecifier = (specifier) => {
91
91
  if (specifier.startsWith('node:') ||
92
+ specifier.startsWith(PROTOCOL_CLOUDFLARE) ||
92
93
  isAbsolute(specifier) ||
93
94
  specifier.charCodeAt(0) === CHAR_COLON ||
94
95
  specifier.startsWith(PROTOCOL_VIRTUAL)) {
@@ -16,7 +16,7 @@ export const getSessionHandler = async (options, { analyzedFiles, analyzeSourceF
16
16
  const deleted = new Set();
17
17
  const modified = new Set();
18
18
  if (configurationFiles)
19
- principal.collectConfigurationImports(configurationFiles.paths);
19
+ principal.collectConfigurationImports(configurationFiles);
20
20
  const processFileChanges = async (changes) => {
21
21
  const startTime = performance.now();
22
22
  let hasRelevantChanges = false;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.44.0";
1
+ export declare const version = "6.44.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.44.0';
1
+ export const version = '6.44.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.44.0",
3
+ "version": "6.44.1",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",