knip 5.61.2 → 5.62.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.
@@ -16,7 +16,7 @@ import { defaultRules } from './util/issue-initializers.js';
16
16
  import { _load } from './util/loader.js';
17
17
  import mapWorkspaces from './util/map-workspaces.js';
18
18
  import { getKeysByValue } from './util/object.js';
19
- import { join, relative } from './util/path.js';
19
+ import { isAbsolute, join, relative } from './util/path.js';
20
20
  import { normalizePluginConfig } from './util/plugin.js';
21
21
  import { toRegexOrString } from './util/regex.js';
22
22
  import { ELLIPSIS } from './util/string.js';
@@ -100,7 +100,7 @@ export class ConfigurationChief {
100
100
  this.manifest.workspaces = pnpmWorkspaces;
101
101
  }
102
102
  for (const configPath of rawConfigArg ? [rawConfigArg] : KNIP_CONFIG_LOCATIONS) {
103
- this.resolvedConfigFilePath = findFile(this.cwd, configPath);
103
+ this.resolvedConfigFilePath = isAbsolute(configPath) ? configPath : findFile(this.cwd, configPath);
104
104
  if (this.resolvedConfigFilePath)
105
105
  break;
106
106
  }
@@ -1,5 +1,5 @@
1
1
  import { isBuiltin } from 'node:module';
2
- import { PackageJsonPeeker } from './PackageJsonPeeker.js';
2
+ import { PackagePeeker } from './PackagePeeker.js';
3
3
  import { DT_SCOPE, IGNORED_DEPENDENCIES, IGNORED_GLOBAL_BINARIES, IGNORED_RUNTIME_DEPENDENCIES, IGNORE_DEFINITELY_TYPED, ROOT_WORKSPACE_NAME, } from './constants.js';
4
4
  import { getDependencyMetaData } from './manifest/index.js';
5
5
  import { getDefinitelyTypedFor, getPackageFromDefinitelyTyped, getPackageNameFromModuleSpecifier, isDefinitelyTyped, } from './util/modules.js';
@@ -176,12 +176,12 @@ export class DependencyDeputy {
176
176
  for (const [workspace, { manifestPath: filePath, manifestStr }] of this._manifests.entries()) {
177
177
  const referencedDependencies = this.referencedDependencies.get(workspace);
178
178
  const hasTypesIncluded = this.getHasTypesIncluded(workspace);
179
- const peeker = new PackageJsonPeeker(manifestStr);
180
- const peerDepRecs = {};
179
+ const peeker = new PackagePeeker(manifestStr);
180
+ const peerDepCount = {};
181
181
  const isReferencedDependency = (dependency, isPeerDep) => {
182
182
  if (referencedDependencies?.has(dependency))
183
183
  return true;
184
- if (isPeerDep && peerDepRecs[dependency])
184
+ if (isPeerDep && peerDepCount[dependency])
185
185
  return false;
186
186
  const [scope, typedDependency] = dependency.split('/');
187
187
  if (scope === DT_SCOPE) {
@@ -202,10 +202,10 @@ export class DependencyDeputy {
202
202
  }
203
203
  const hostDependencies = this.getHostDependenciesFor(workspace, dependency);
204
204
  for (const { name } of hostDependencies) {
205
- if (!peerDepRecs[name])
206
- peerDepRecs[name] = 1;
205
+ if (!peerDepCount[name])
206
+ peerDepCount[name] = 1;
207
207
  else
208
- peerDepRecs[name]++;
208
+ peerDepCount[name]++;
209
209
  }
210
210
  return hostDependencies.some(hostDependency => (isPeerDep === false || !hostDependency.isPeerOptional) && isReferencedDependency(hostDependency.name, true));
211
211
  };
@@ -1,4 +1,4 @@
1
- export declare class PackageJsonPeeker {
1
+ export declare class PackagePeeker {
2
2
  private manifestStr;
3
3
  private lines;
4
4
  private sections;
@@ -1,4 +1,4 @@
1
- export class PackageJsonPeeker {
1
+ export class PackagePeeker {
2
2
  manifestStr;
3
3
  lines = [];
4
4
  sections = {};
@@ -50,7 +50,7 @@ export declare class ProjectPrincipal {
50
50
  getUsedResolvedFiles(): string[];
51
51
  private getProgramSourceFiles;
52
52
  getUnreferencedFiles(): string[];
53
- analyzeSourceFile(filePath: string, options: Omit<GetImportsAndExportsOptions, 'skipExports'>, isGitIgnored: (filePath: string) => boolean, isInternalWorkspace: (packageName: string) => boolean, getPrincipalByFilePath: (filePath: string) => undefined | ProjectPrincipal): FileNode;
53
+ analyzeSourceFile(filePath: string, options: Omit<GetImportsAndExportsOptions, 'skipExports'>): FileNode;
54
54
  invalidateFile(filePath: string): void;
55
55
  findUnusedMembers(filePath: string, members: ExportMember[]): ExportMember[];
56
56
  hasExternalReferences(filePath: string, exportedItem: Export): boolean;
@@ -1,14 +1,13 @@
1
1
  import ts from 'typescript';
2
2
  import { CacheConsultant } from './CacheConsultant.js';
3
3
  import { getCompilerExtensions } from './compilers/index.js';
4
- import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, PUBLIC_TAG } from './constants.js';
4
+ import { ANONYMOUS, DEFAULT_EXTENSIONS, PUBLIC_TAG } from './constants.js';
5
5
  import { SourceFileManager } from './typescript/SourceFileManager.js';
6
6
  import { createHosts } from './typescript/create-hosts.js';
7
7
  import { _getImportsAndExports } from './typescript/get-imports-and-exports.js';
8
8
  import { timerify } from './util/Performance.js';
9
9
  import { compact } from './util/array.js';
10
- import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from './util/modules.js';
11
- import { dirname, extname, isInNodeModules, join, toAbsolute } from './util/path.js';
10
+ import { extname, isInNodeModules, toAbsolute } from './util/path.js';
12
11
  const baseCompilerOptions = {
13
12
  allowJs: true,
14
13
  allowSyntheticDefaultImports: true,
@@ -156,7 +155,7 @@ export class ProjectPrincipal {
156
155
  const sourceFiles = this.getProgramSourceFiles();
157
156
  return Array.from(this.projectPaths).filter(filePath => !sourceFiles.has(filePath));
158
157
  }
159
- analyzeSourceFile(filePath, options, isGitIgnored, isInternalWorkspace, getPrincipalByFilePath) {
158
+ analyzeSourceFile(filePath, options) {
160
159
  const fd = this.cache.getFileDescriptor(filePath);
161
160
  if (!fd.changed && fd.meta?.data)
162
161
  return fd.meta.data;
@@ -168,43 +167,7 @@ export class ProjectPrincipal {
168
167
  throw new Error(`Unable to find ${filePath}`);
169
168
  const skipExports = this.skipExportsAnalysis.has(filePath);
170
169
  const resolve = (specifier) => this.backend.resolveModuleNames([specifier], sourceFile.fileName)[0];
171
- const { imports, ...rest } = _getImportsAndExports(sourceFile, resolve, typeChecker, { ...options, skipExports });
172
- const { internal, resolved, specifiers, unresolved, external } = imports;
173
- const unresolvedImports = new Set();
174
- for (const [specifier, specifierFilePath] of specifiers) {
175
- const packageName = getPackageNameFromModuleSpecifier(specifier);
176
- if (packageName && isInternalWorkspace(packageName)) {
177
- external.add(packageName);
178
- const principal = getPrincipalByFilePath(specifierFilePath);
179
- if (principal && !isGitIgnored(specifierFilePath))
180
- principal.addNonEntryPath(specifierFilePath);
181
- }
182
- }
183
- for (const filePath of resolved) {
184
- const isIgnored = isGitIgnored(filePath);
185
- if (!isIgnored)
186
- this.addEntryPath(filePath, { skipExportsAnalysis: true });
187
- }
188
- for (const unresolvedImport of unresolved) {
189
- const { specifier } = unresolvedImport;
190
- if (specifier.startsWith('http'))
191
- continue;
192
- const sanitizedSpecifier = sanitizeSpecifier(specifier);
193
- if (isStartsLikePackageName(sanitizedSpecifier)) {
194
- external.add(sanitizedSpecifier);
195
- }
196
- else {
197
- const isIgnored = isGitIgnored(join(dirname(filePath), sanitizedSpecifier));
198
- if (!isIgnored) {
199
- const ext = extname(sanitizedSpecifier);
200
- const hasIgnoredExtension = FOREIGN_FILE_EXTENSIONS.has(ext);
201
- if (!ext || (ext !== '.json' && !hasIgnoredExtension)) {
202
- unresolvedImports.add(unresolvedImport);
203
- }
204
- }
205
- }
206
- }
207
- return { imports: { internal, unresolved: unresolvedImports, external }, ...rest };
170
+ return _getImportsAndExports(sourceFile, resolve, typeChecker, { ...options, skipExports });
208
171
  }
209
172
  invalidateFile(filePath) {
210
173
  this.backend.fileManager.snapshotCache.delete(filePath);
@@ -29,6 +29,7 @@ type WorkspaceManagerOptions = {
29
29
  type CacheItem = {
30
30
  resolveConfig?: Input[];
31
31
  resolveFromAST?: Input[];
32
+ configFile?: Input;
32
33
  };
33
34
  export declare class WorkspaceWorker {
34
35
  name: string;
@@ -262,6 +262,8 @@ export class WorkspaceWorker {
262
262
  if (data.resolveFromAST)
263
263
  for (const id of data.resolveFromAST)
264
264
  addInput(id, configFilePath);
265
+ if (data.configFile)
266
+ addInput(data.configFile);
265
267
  continue;
266
268
  }
267
269
  const resolveOpts = {
@@ -301,15 +303,17 @@ export class WorkspaceWorker {
301
303
  cache.resolveFromAST = inputs;
302
304
  }
303
305
  }
304
- if (basename(configFilePath) !== 'package.json') {
306
+ if (!isManifest) {
305
307
  addInput(toEntry(configFilePath));
306
308
  addInput(toConfig(pluginName, configFilePath));
309
+ cache.configFile = toEntry(configFilePath);
310
+ if (fd?.changed && fd.meta && !seen.get(key)?.has(configFilePath)) {
311
+ fd.meta.data = cache;
312
+ }
307
313
  if (!seen.has(key))
308
314
  seen.set(key, new Set());
309
315
  seen.get(key)?.add(configFilePath);
310
316
  }
311
- if (!isManifest && fd?.changed && fd.meta)
312
- fd.meta.data = cache;
313
317
  }
314
318
  if (plugin.resolve) {
315
319
  const dependencies = (await plugin.resolve(options)) ?? [];
@@ -3,7 +3,21 @@ import { isFile } from '../../util/fs.js';
3
3
  import { toEntry } from '../../util/input.js';
4
4
  import { isAbsolute, join } from '../../util/path.js';
5
5
  import { resolveX } from './bunx.js';
6
- const commands = ['add', 'create', 'init', 'install', 'link', 'pm', 'remove', 'run', 'test', 'update', 'upgrade', 'x'];
6
+ const commands = [
7
+ 'add',
8
+ 'audit',
9
+ 'create',
10
+ 'init',
11
+ 'install',
12
+ 'link',
13
+ 'pm',
14
+ 'remove',
15
+ 'run',
16
+ 'test',
17
+ 'update',
18
+ 'upgrade',
19
+ 'x',
20
+ ];
7
21
  export const resolve = (_binary, args, options) => {
8
22
  const parsed = parseArgs(args, { string: ['cwd'] });
9
23
  const [command, script] = parsed._;
@@ -1,17 +1,18 @@
1
1
  import { WorkspaceWorker } from '../WorkspaceWorker.js';
2
2
  import { _getInputsFromScripts } from '../binaries/index.js';
3
3
  import { getCompilerExtensions, getIncludedCompilers } from '../compilers/index.js';
4
- import { DEFAULT_EXTENSIONS } from '../constants.js';
4
+ import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from '../constants.js';
5
5
  import { perfObserver } from '../util/Performance.js';
6
6
  import { debugLog, debugLogArray } from '../util/debug.js';
7
7
  import { getReferencedInputsHandler } from '../util/get-referenced-inputs.js';
8
8
  import { _glob, negate } from '../util/glob.js';
9
9
  import { isAlias, isConfig, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isIgnore, isProductionEntry, isProject, toProductionEntry, } from '../util/input.js';
10
+ import { loadTSConfig } from '../util/load-tsconfig.js';
10
11
  import { getOrCreateFileNode, updateImportMap } from '../util/module-graph.js';
12
+ import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.js';
11
13
  import { getEntryPathsFromManifest } from '../util/package-json.js';
12
- import { dirname, isAbsolute, join, relative, toRelative } from '../util/path.js';
14
+ import { dirname, extname, isAbsolute, join, relative, toRelative } from '../util/path.js';
13
15
  import { augmentWorkspace, getToSourcePathHandler, getToSourcePathsHandler } from '../util/to-source-path.js';
14
- import { loadTSConfig } from '../util/tsconfig-loader.js';
15
16
  export async function build({ cacheLocation, chief, collector, cwd, deputy, factory, gitignore, isCache, isFixExports, isFixTypes, isGitIgnored, isIsolateWorkspaces, isProduction, isSkipLibs, isStrict, isWatch, report, streamer, tags, tsConfigFile, workspaces, }) {
16
17
  const configFilesMap = new Map();
17
18
  const enabledPluginsStore = new Map();
@@ -250,37 +251,68 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
250
251
  analyzedFiles.add(filePath);
251
252
  const workspace = chief.findWorkspaceByFilePath(filePath);
252
253
  if (workspace) {
253
- const { imports, exports, duplicates, scripts, traceRefs } = principal.analyzeSourceFile(filePath, {
254
+ const file = principal.analyzeSourceFile(filePath, {
254
255
  skipTypeOnly: isStrict,
255
256
  isFixExports,
256
257
  isFixTypes,
257
258
  ignoreExportsUsedInFile: chief.config.ignoreExportsUsedInFile,
258
259
  isReportClassMembers,
259
260
  tags,
260
- }, isGitIgnored, isInternalWorkspace, getPrincipalByFilePath);
261
- const node = getOrCreateFileNode(graph, filePath);
262
- node.imports = imports;
263
- node.exports = exports;
264
- node.duplicates = duplicates;
265
- node.scripts = scripts;
266
- node.traceRefs = traceRefs;
267
- updateImportMap(node, imports.internal, graph);
268
- node.internalImportCache = imports.internal;
269
- graph.set(filePath, node);
270
- if (scripts && scripts.size > 0) {
261
+ });
262
+ const _unresolved = new Set();
263
+ for (const unresolvedImport of file.imports.unresolved) {
264
+ const { specifier } = unresolvedImport;
265
+ if (specifier.startsWith('http'))
266
+ continue;
267
+ const sanitizedSpecifier = sanitizeSpecifier(specifier);
268
+ if (isStartsLikePackageName(sanitizedSpecifier)) {
269
+ file.imports.external.add(sanitizedSpecifier);
270
+ }
271
+ else {
272
+ const isIgnored = isGitIgnored(join(dirname(filePath), sanitizedSpecifier));
273
+ if (!isIgnored) {
274
+ const ext = extname(sanitizedSpecifier);
275
+ const hasIgnoredExtension = FOREIGN_FILE_EXTENSIONS.has(ext);
276
+ if (!ext || (ext !== '.json' && !hasIgnoredExtension))
277
+ _unresolved.add(unresolvedImport);
278
+ }
279
+ }
280
+ }
281
+ for (const filePath of file.imports.resolved) {
282
+ const isIgnored = isGitIgnored(filePath);
283
+ if (!isIgnored)
284
+ principal.addEntryPath(filePath, { skipExportsAnalysis: true });
285
+ }
286
+ for (const [specifier, specifierFilePath] of file.imports.specifiers) {
287
+ const packageName = getPackageNameFromModuleSpecifier(specifier);
288
+ if (packageName && isInternalWorkspace(packageName)) {
289
+ file.imports.external.add(packageName);
290
+ const principal = getPrincipalByFilePath(specifierFilePath);
291
+ if (principal && !isGitIgnored(specifierFilePath)) {
292
+ principal.addNonEntryPath(specifierFilePath);
293
+ }
294
+ }
295
+ }
296
+ if (file.scripts && file.scripts.size > 0) {
271
297
  const dependencies = deputy.getDependencies(workspace.name);
272
298
  const manifestScriptNames = new Set(Object.keys(chief.getManifestForWorkspace(workspace.name)?.scripts ?? {}));
273
299
  const dir = dirname(filePath);
274
300
  const options = { cwd: dir, rootCwd: cwd, containingFilePath: filePath, dependencies, manifestScriptNames };
275
- const inputs = _getInputsFromScripts(scripts, options);
301
+ const inputs = _getInputsFromScripts(file.scripts, options);
276
302
  for (const input of inputs) {
277
303
  input.containingFilePath ??= filePath;
278
304
  input.dir ??= dir;
279
305
  const specifierFilePath = getReferencedInternalFilePath(input, workspace);
280
306
  if (specifierFilePath)
281
- analyzeSourceFile(specifierFilePath, principal);
307
+ principal.addEntryPath(specifierFilePath, { skipExportsAnalysis: true });
282
308
  }
283
309
  }
310
+ const node = getOrCreateFileNode(graph, filePath);
311
+ file.imports.unresolved = _unresolved;
312
+ Object.assign(node, file);
313
+ updateImportMap(node, file.imports.internal, graph);
314
+ node.internalImportCache = file.imports.internal;
315
+ graph.set(filePath, node);
284
316
  }
285
317
  };
286
318
  for (let i = 0; i < principals.length; ++i) {
@@ -18,7 +18,7 @@ const resolveConfig = async (config, options) => {
18
18
  for (const [targetName, target] of Object.entries(project.architect)) {
19
19
  const { options: opts, configurations: configs } = target;
20
20
  const [packageName] = typeof target.builder === 'string' ? target.builder.split(':') : [];
21
- if (typeof packageName === 'string')
21
+ if (packageName)
22
22
  inputs.add(toDependency(packageName));
23
23
  if (opts) {
24
24
  if ('tsConfig' in opts && typeof opts.tsConfig === 'string') {
@@ -55,7 +55,7 @@ const resolveConfig = async (config, options) => {
55
55
  }));
56
56
  }
57
57
  }
58
- if (target.builder === '@angular-devkit/build-angular:karma' && opts) {
58
+ if (target.builder && isAngularBuilderRefWithName({ builderRef: target.builder, name: 'karma' }) && opts) {
59
59
  const karmaBuilderOptions = opts;
60
60
  const testFilePatterns = karmaBuilderOptions.include ?? ['**/*.spec.ts'];
61
61
  for (const testFilePattern of testFilePatterns) {
@@ -103,6 +103,10 @@ const entriesByOption = (opts) => new Map(Object.entries({
103
103
  ? [opts.ssr.entry]
104
104
  : [],
105
105
  }));
106
+ const isAngularBuilderRefWithName = ({ builderRef, name }) => {
107
+ const [pkg, builderName] = builderRef.split(':');
108
+ return (pkg === '@angular-devkit/build-angular' || pkg === '@angular/build') && builderName === name;
109
+ };
106
110
  const PRODUCTION_CONFIG_NAME = 'production';
107
111
  const BUILD_TARGET_NAME = 'build';
108
112
  export default {
@@ -14,14 +14,10 @@ const production = [
14
14
  ];
15
15
  const resolveFromAST = sourceFile => {
16
16
  const srcDir = getSrcDir(sourceFile);
17
+ const setSrcDir = (entry) => entry.replace(/^`src\//, `${srcDir}/`);
17
18
  return [
18
- ...[`${srcDir}/content/config.ts`, `${srcDir}/content.config.ts`].map(path => toEntry(path)),
19
- ...[
20
- `${srcDir}/pages/**/*.{astro,mdx,js,ts}`,
21
- `${srcDir}/content/**/*.mdx`,
22
- `${srcDir}/middleware.{js,ts}`,
23
- `${srcDir}/actions/index.{js,ts}`,
24
- ].map(path => toProductionEntry(path)),
19
+ ...entry.map(setSrcDir).map(path => toEntry(path)),
20
+ ...production.map(setSrcDir).map(path => toProductionEntry(path)),
25
21
  ];
26
22
  };
27
23
  const resolve = options => {
@@ -20,6 +20,5 @@ export const getSrcDir = (sourceFile) => {
20
20
  });
21
21
  return result;
22
22
  }
23
- const foundValue = visit(sourceFile);
24
- return foundValue ?? srcDir;
23
+ return visit(sourceFile) ?? srcDir;
25
24
  };
@@ -1,14 +1,22 @@
1
+ import { arrayify } from '../../util/array.js';
1
2
  import { toConfig } from '../../util/input.js';
3
+ import { join } from '../../util/path.js';
2
4
  import { hasDependency } from '../../util/plugin.js';
3
5
  const title = 'Biome';
4
6
  const enablers = ['@biomejs/biome'];
5
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
8
  const config = ['biome.json', 'biome.jsonc'];
9
+ const isRootConfigReference = (specifier) => specifier === '//';
7
10
  const resolveExtends = (extendsArray, options) => {
8
- return extendsArray.map(specifier => toConfig('biome', specifier, { containingFilePath: options.configFilePath }));
11
+ return extendsArray.map(specifier => {
12
+ if (isRootConfigReference(specifier)) {
13
+ return toConfig('biome', join(options.rootCwd, 'biome'), { containingFilePath: options.configFilePath });
14
+ }
15
+ return toConfig('biome', specifier, { containingFilePath: options.configFilePath });
16
+ });
9
17
  };
10
18
  const resolveConfig = (config, options) => {
11
- return [...resolveExtends(config.extends || [], options)];
19
+ return [...resolveExtends(arrayify(config.extends), options)];
12
20
  };
13
21
  export default {
14
22
  title,
@@ -8,7 +8,8 @@ export const getInputs = (config, options) => {
8
8
  if (extname(configFileName) === '.json' || !/eslint\.config/.test(configFileName)) {
9
9
  return getInputsDeprecated(config, options);
10
10
  }
11
- const dependencies = config.flatMap(config => config.settings ? getDependenciesFromSettings(config.settings).filter(id => id !== '@typescript-eslint/parser') : []);
11
+ const dependencies = config.flatMap(config => config.settings ? getDependenciesFromSettings(config.settings) : []);
12
+ dependencies.push('eslint-import-resolver-typescript');
12
13
  return compact(dependencies).map(id => toDeferResolve(id, { optional: true }));
13
14
  };
14
15
  const getInputsDeprecated = (config, options) => {
@@ -1,5 +1,5 @@
1
1
  import { toEntry } from '../../util/input.js';
2
- import { join } from '../../util/path.js';
2
+ import { isAbsolute, join } from '../../util/path.js';
3
3
  import { hasDependency } from '../../util/plugin.js';
4
4
  import { configFiles, inputsFromFrameworks, inputsFromPlugins, loadConfig } from './helpers.js';
5
5
  const title = 'Karma';
@@ -19,12 +19,14 @@ const resolveConfig = async (localConfig, options) => {
19
19
  if (config.files) {
20
20
  for (const fileOrPatternObj of config.files) {
21
21
  const fileOrPattern = typeof fileOrPatternObj === 'string' ? fileOrPatternObj : fileOrPatternObj.pattern;
22
- inputs.add(toEntry(join(options.configFileDir, basePath, fileOrPattern)));
22
+ const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);
23
+ inputs.add(toEntry(absPath));
23
24
  }
24
25
  }
25
26
  if (config.exclude) {
26
27
  for (const fileOrPattern of config.exclude) {
27
- inputs.add(toEntry(`!${join(options.configFileDir, basePath, fileOrPattern)}`));
28
+ const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);
29
+ inputs.add(toEntry(`!${absPath}`));
28
30
  }
29
31
  }
30
32
  return Array.from(inputs);
@@ -7,7 +7,7 @@ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const config = [
8
8
  'package.json',
9
9
  'postcss.config.json',
10
- ...toLilconfig('postcss', { configDir: false, additionalExtensions: ['ts', 'mts', 'cts', 'yaml', 'yml'] }),
10
+ ...toLilconfig('postcss', { configDir: false, additionalExtensions: ['mts', 'cts', 'yaml', 'yml'] }),
11
11
  ];
12
12
  const resolveConfig = config => {
13
13
  const plugins = config.plugins
@@ -6,7 +6,7 @@ const enablers = ['size-limit'];
6
6
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
7
7
  const config = [
8
8
  'package.json',
9
- ...toLilconfig('size-limit', { configDir: false, additionalExtensions: ['ts', 'mts', 'cts'], rcSuffix: '' }),
9
+ ...toLilconfig('size-limit', { configDir: false, additionalExtensions: ['mts', 'cts'], rcSuffix: '' }),
10
10
  ];
11
11
  const resolve = options => {
12
12
  const allDeps = [
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_EXTENSIONS } from '../../constants.js';
2
- import { toAlias, toDeferResolve, toDependency, toEntry } from '../../util/input.js';
2
+ import { _glob } from '../../util/glob.js';
3
+ import { toAlias, toConfig, toDeferResolve, toDependency, toEntry } from '../../util/input.js';
3
4
  import { join, toPosix } from '../../util/path.js';
4
5
  import { hasDependency } from '../../util/plugin.js';
5
6
  import { getEnvPackageName, getExternalReporters } from './helpers.js';
@@ -28,11 +29,20 @@ const findConfigDependencies = (localConfig, options) => {
28
29
  workspaceDependencies.push(...findConfigDependencies(workspaceConfig, options));
29
30
  }
30
31
  }
32
+ const projectsDependencies = [];
33
+ if (testConfig.projects !== undefined) {
34
+ for (const projectConfig of testConfig.projects) {
35
+ if (typeof projectConfig !== 'string') {
36
+ projectsDependencies.push(...findConfigDependencies(projectConfig, options));
37
+ }
38
+ }
39
+ }
31
40
  return [
32
41
  ...[...environments, ...reporters, ...coverage].map(id => toDependency(id)),
33
42
  ...setupFiles,
34
43
  ...globalSetup,
35
44
  ...workspaceDependencies,
45
+ ...projectsDependencies,
36
46
  ];
37
47
  };
38
48
  const getConfigs = async (localConfig) => {
@@ -44,11 +54,25 @@ const getConfigs = async (localConfig) => {
44
54
  for (const mode of ['development', 'production']) {
45
55
  const cfg = await config({ command, mode, ssrBuild: undefined });
46
56
  configs.push(cfg);
57
+ if (cfg.test?.projects) {
58
+ for (const project of cfg.test.projects) {
59
+ if (typeof project !== 'string') {
60
+ configs.push(project);
61
+ }
62
+ }
63
+ }
47
64
  }
48
65
  }
49
66
  }
50
67
  else {
51
68
  configs.push(config);
69
+ if (config.test?.projects) {
70
+ for (const project of config.test.projects) {
71
+ if (typeof project !== 'string') {
72
+ configs.push(project);
73
+ }
74
+ }
75
+ }
52
76
  }
53
77
  }
54
78
  }
@@ -58,6 +82,22 @@ export const resolveConfig = async (localConfig, options) => {
58
82
  const inputs = new Set();
59
83
  inputs.add(toEntry(join(options.cwd, 'src/vite-env.d.ts')));
60
84
  const configs = await getConfigs(localConfig);
85
+ for (const cfg of configs) {
86
+ if (cfg.test?.projects) {
87
+ for (const project of cfg.test.projects) {
88
+ if (typeof project === 'string') {
89
+ const projectFiles = await _glob({
90
+ cwd: options.cwd,
91
+ patterns: [project],
92
+ gitignore: false,
93
+ });
94
+ for (const projectFile of projectFiles) {
95
+ inputs.add(toConfig('vitest', projectFile, { containingFilePath: options.configFilePath }));
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
61
101
  const addStar = (value) => (value.endsWith('*') ? value : join(value, '*').replace(/\/\*\*$/, '/*'));
62
102
  const addAliases = (aliasOptions) => {
63
103
  for (const [alias, value] of Object.entries(aliasOptions)) {
@@ -23,6 +23,11 @@ interface VitestConfig {
23
23
  workspace: never;
24
24
  };
25
25
  })[];
26
+ projects?: (string | (ViteConfig & {
27
+ test: VitestConfig['test'] & {
28
+ projects: never;
29
+ };
30
+ }))[];
26
31
  alias?: AliasOptions;
27
32
  };
28
33
  }
@@ -55,6 +55,8 @@ export type FileNode = {
55
55
  internal: ImportMap;
56
56
  external: Set<string>;
57
57
  unresolved: Set<UnresolvedImport>;
58
+ resolved: Set<FilePath>;
59
+ specifiers: Set<[string, FilePath]>;
58
60
  };
59
61
  exports: ExportMap;
60
62
  duplicates: Iterable<Array<IssueSymbol>>;
@@ -149,7 +149,7 @@ const getMemberStringLiterals = (typeChecker, node) => {
149
149
  return type.types.map(type => type.value);
150
150
  }
151
151
  if (ts.isPropertyAccessExpression(node)) {
152
- return [node.name.escapedText];
152
+ return [node.name.getText()];
153
153
  }
154
154
  };
155
155
  export const getAccessMembers = (typeChecker, node) => {
@@ -1,18 +1,5 @@
1
1
  import ts from 'typescript';
2
2
  import type { GetImportsAndExportsOptions } from '../types/config.js';
3
- import type { IssueSymbol } from '../types/issues.js';
4
- import type { ExportMap, ImportMap, UnresolvedImport } from '../types/module-graph.js';
3
+ import type { FileNode } from '../types/module-graph.js';
5
4
  import type { BoundSourceFile } from './SourceFile.js';
6
- export declare const _getImportsAndExports: (sourceFile: BoundSourceFile, resolveModule: (specifier: string) => ts.ResolvedModuleFull | undefined, typeChecker: ts.TypeChecker, options: GetImportsAndExportsOptions) => {
7
- imports: {
8
- internal: ImportMap;
9
- external: Set<string>;
10
- resolved: Set<string>;
11
- specifiers: Set<[string, string]>;
12
- unresolved: Set<UnresolvedImport>;
13
- };
14
- exports: ExportMap;
15
- duplicates: IssueSymbol[][];
16
- scripts: Set<string>;
17
- traceRefs: Set<string>;
18
- };
5
+ export declare const _getImportsAndExports: (sourceFile: BoundSourceFile, resolveModule: (specifier: string) => ts.ResolvedModuleFull | undefined, typeChecker: ts.TypeChecker, options: GetImportsAndExportsOptions) => FileNode;
@@ -35,6 +35,8 @@ const createFileNode = () => ({
35
35
  internal: new Map(),
36
36
  external: new Set(),
37
37
  unresolved: new Set(),
38
+ resolved: new Set(),
39
+ specifiers: new Set(),
38
40
  },
39
41
  exports: new Map(),
40
42
  duplicates: new Set(),
@@ -23,7 +23,7 @@ const toConfigMap = (defaultExtensions, builderConfig) => (moduleName, options)
23
23
  return [...baseFiles, rcFiles, ...configFiles, ...configDirFiles];
24
24
  };
25
25
  export const toCosmiconfig = toConfigMap(['json', 'yaml', 'yml', 'js', 'ts', 'cjs', 'mjs'], { configDir: true });
26
- export const toLilconfig = toConfigMap(['json', 'js', 'cjs', 'mjs'], { configDir: true });
26
+ export const toLilconfig = toConfigMap(['json', 'ts', 'js', 'cjs', 'mjs'], { configDir: true });
27
27
  export const toUnconfig = toConfigMap(['json', 'ts', 'mts', 'cts', 'js', 'mjs', 'cjs'], {
28
28
  configDir: false,
29
29
  rcPrefix: '',
@@ -16,6 +16,6 @@ const createSyncResolver = (extensions) => {
16
16
  catch (_error) { }
17
17
  };
18
18
  };
19
- const resolveSync = createSyncResolver([...DEFAULT_EXTENSIONS, '.json']);
19
+ const resolveSync = createSyncResolver([...DEFAULT_EXTENSIONS, '.json', '.jsonc']);
20
20
  export const _resolveSync = timerify(resolveSync);
21
21
  export const _createSyncResolver = extensions => timerify(createSyncResolver(extensions));
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.61.2";
1
+ export declare const version = "5.62.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.61.2';
1
+ export const version = '5.62.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.61.2",
3
+ "version": "5.62.0",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {