knip 6.32.2 → 6.33.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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/DependencyDeputy.d.ts +1 -2
  3. package/dist/DependencyDeputy.js +1 -2
  4. package/dist/ProjectPrincipal.js +1 -0
  5. package/dist/binaries/resolvers/bun.js +1 -0
  6. package/dist/binaries/resolvers/pnpm.js +10 -0
  7. package/dist/config.d.ts +2 -0
  8. package/dist/config.js +1 -0
  9. package/dist/constants.js +1 -0
  10. package/dist/graph/analyze.js +1 -1
  11. package/dist/graph/build.js +0 -1
  12. package/dist/graph-explorer/explorer.d.ts +1 -0
  13. package/dist/graph-explorer/explorer.js +2 -0
  14. package/dist/graph-explorer/operations/is-enumerated.d.ts +2 -0
  15. package/dist/graph-explorer/operations/is-enumerated.js +33 -0
  16. package/dist/manifest/helpers.d.ts +2 -3
  17. package/dist/manifest/helpers.js +6 -42
  18. package/dist/manifest/index.d.ts +1 -2
  19. package/dist/manifest/index.js +2 -2
  20. package/dist/plugins/dotenv/index.js +1 -8
  21. package/dist/plugins/eslint/helpers.d.ts +2 -1
  22. package/dist/plugins/eslint/helpers.js +1 -0
  23. package/dist/plugins/eslint/resolveFromAST.d.ts +1 -0
  24. package/dist/plugins/eslint/resolveFromAST.js +5 -2
  25. package/dist/plugins/eslint/types.d.ts +1 -1
  26. package/dist/plugins/lefthook/index.js +10 -2
  27. package/dist/plugins/moonrepo/index.js +3 -3
  28. package/dist/plugins/nuxt/index.js +25 -3
  29. package/dist/plugins/nuxt/types.d.ts +5 -1
  30. package/dist/plugins/oxlint/index.js +6 -2
  31. package/dist/plugins/oxlint/types.d.ts +2 -0
  32. package/dist/plugins/tsup/index.js +1 -1
  33. package/dist/plugins/typescript/index.js +22 -5
  34. package/dist/plugins/vitest/index.js +31 -20
  35. package/dist/plugins/vitest/types.d.ts +2 -1
  36. package/dist/plugins/vitest/visitors/mock.d.ts +2 -0
  37. package/dist/plugins/vitest/visitors/mock.js +50 -0
  38. package/dist/plugins/webdriver-io/index.js +1 -1
  39. package/dist/plugins/webpack/index.js +15 -14
  40. package/dist/types/config.d.ts +1 -0
  41. package/dist/types/tsconfig-json.d.ts +6 -0
  42. package/dist/typescript/get-imports-and-exports.js +3 -0
  43. package/dist/typescript/visitors/calls.js +1 -1
  44. package/dist/typescript/visitors/members.js +56 -8
  45. package/dist/typescript/visitors/walk.d.ts +1 -0
  46. package/dist/typescript/visitors/walk.js +0 -1
  47. package/dist/util/glob-core.js +9 -1
  48. package/dist/util/load-config.js +6 -1
  49. package/dist/util/resolve.d.ts +1 -0
  50. package/dist/util/resolve.js +11 -0
  51. package/dist/util/scripts.d.ts +1 -0
  52. package/dist/util/scripts.js +1 -0
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/package.json +13 -9
@@ -6,15 +6,16 @@ import { isAbsolute, isInternal, join, toAbsolute } from '../../util/path.js';
6
6
  import { hasDependency } from '../../util/plugin.js';
7
7
  import { getIndexHtmlEntries } from '../vite/helpers.js';
8
8
  import { getAliasInputs, getEnvSpecifier, getExternalReporters } from './helpers.js';
9
+ import { createVitestMockVisitor } from './visitors/mock.js';
9
10
  const title = 'Vitest';
10
11
  const enablers = ['vitest', 'vite-plus'];
11
12
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
12
13
  const config = ['vitest.config.{js,mjs,ts,cjs,mts,cts}', 'vitest.{workspace,projects}.{js,mjs,ts,cjs,mts,cts,json}'];
13
14
  const mocks = ['**/__mocks__/**/*.?(c|m)[jt]s?(x)'];
14
- const entry = ['**/*.{bench,test,test-d,spec,spec-d}.?(c|m)[jt]s?(x)', ...mocks];
15
+ const testEntry = ['**/*.{bench,test,test-d,spec,spec-d}.?(c|m)[jt]s?(x)'];
16
+ const entry = [...testEntry, ...mocks];
15
17
  const benchmark = ['**/*.bench.?(c|m)[jt]s?(x)'];
16
18
  const findConfigDependencies = (localConfig, options, vitestRoot) => {
17
- const { configFileDir: dir } = options;
18
19
  const testConfig = localConfig.test;
19
20
  if (!testConfig)
20
21
  return [];
@@ -24,7 +25,9 @@ const findConfigDependencies = (localConfig, options, vitestRoot) => {
24
25
  ? [toDeferResolve(env)]
25
26
  : [toDependency(getEnvSpecifier(env))]
26
27
  : [];
27
- const reporters = getExternalReporters(testConfig.reporters);
28
+ const reporters = getExternalReporters(testConfig.reporters).map(specifier => isInternal(specifier) || isAbsolute(specifier)
29
+ ? { ...toDeferResolve(specifier), dir: vitestRoot }
30
+ : toDependency(specifier));
28
31
  const hasCoverage = testConfig.coverage && (testConfig.coverage.enabled !== false || testConfig.coverage.provider);
29
32
  const coverage = hasCoverage ? [`@vitest/coverage-${testConfig.coverage?.provider ?? 'v8'}`] : [];
30
33
  const setupFiles = [testConfig.setupFiles ?? []]
@@ -34,7 +37,9 @@ const findConfigDependencies = (localConfig, options, vitestRoot) => {
34
37
  ...toDeferResolve(specifier),
35
38
  dir: vitestRoot,
36
39
  }));
37
- const globalSetup = [testConfig.globalSetup ?? []].flat().map(specifier => ({ ...toDeferResolve(specifier), dir }));
40
+ const globalSetup = [testConfig.globalSetup ?? []]
41
+ .flat()
42
+ .map(specifier => ({ ...toDeferResolve(specifier), dir: vitestRoot }));
38
43
  const workspaceDependencies = [];
39
44
  if (testConfig.workspace !== undefined) {
40
45
  for (const workspaceConfig of testConfig.workspace) {
@@ -51,7 +56,7 @@ const findConfigDependencies = (localConfig, options, vitestRoot) => {
51
56
  }
52
57
  return [
53
58
  ...environments,
54
- ...reporters.map(id => toDependency(id)),
59
+ ...reporters,
55
60
  ...coverage.map(id => toDependency(id)),
56
61
  ...setupFiles,
57
62
  ...snapshotSerializers,
@@ -72,12 +77,14 @@ const getConfigs = async (localConfig) => {
72
77
  if (typeof config === 'function') {
73
78
  for (const command of ['serve', 'build']) {
74
79
  for (const mode of ['development', 'production']) {
75
- const cfg = await config({ command, mode, ssrBuild: undefined });
76
- configs.push(cfg);
77
- if (cfg.test?.projects) {
78
- for (const project of cfg.test.projects) {
79
- if (typeof project !== 'string') {
80
- configs.push(project);
80
+ for (const ssrBuild of command === 'build' ? [undefined, false, true] : [undefined]) {
81
+ const cfg = await config({ command, mode, ssrBuild });
82
+ configs.push(cfg);
83
+ if (cfg.test?.projects) {
84
+ for (const project of cfg.test.projects) {
85
+ if (typeof project !== 'string') {
86
+ configs.push(project);
87
+ }
81
88
  }
82
89
  }
83
90
  }
@@ -133,22 +140,23 @@ export const resolveConfig = async (localConfig, options) => {
133
140
  for (const entry of await getIndexHtmlEntries(viteRoot))
134
141
  inputs.add(entry);
135
142
  }
136
- const dir = toAbsolute(cfg.test?.root ?? '.', options.cwd);
143
+ const vitestRoot = toAbsolute(cfg.test?.root ?? '.', options.cwd);
144
+ const dir = cfg.test?.dir ? toAbsolute(cfg.test.dir, vitestRoot) : vitestRoot;
137
145
  if (cfg.test) {
138
146
  if (cfg.test?.include) {
139
147
  for (const dependency of cfg.test.include)
140
148
  dependency[0] !== '!' && inputs.add(toEntry(join(dir, dependency)));
141
- if (!options.config.entry)
142
- for (const dependency of mocks)
143
- inputs.add(toEntry(join(dir, dependency)));
144
149
  const benchmarkInclude = cfg.test.benchmark?.include ?? benchmark;
145
150
  for (const dependency of benchmarkInclude)
146
151
  dependency[0] !== '!' && inputs.add(toEntry(join(dir, dependency)));
147
152
  }
148
153
  else {
149
- for (const dependency of options.config.entry ?? entry)
154
+ for (const dependency of options.config.entry ?? testEntry)
150
155
  inputs.add(toEntry(join(dir, dependency)));
151
156
  }
157
+ if (!options.config.entry)
158
+ for (const dependency of mocks)
159
+ inputs.add(toEntry(join(vitestRoot, dependency)));
152
160
  if (cfg.test.alias)
153
161
  addAliases(cfg.test.alias);
154
162
  }
@@ -174,12 +182,11 @@ export const resolveConfig = async (localConfig, options) => {
174
182
  inputs.add(toEntry(`src/**/*${ext}`));
175
183
  }
176
184
  }
177
- for (const dependency of findConfigDependencies(cfg, options, dir))
185
+ for (const dependency of findConfigDependencies(cfg, options, vitestRoot))
178
186
  inputs.add(dependency);
179
187
  const _entry = cfg.build?.lib?.entry ?? [];
180
- const deps = (typeof _entry === 'string' ? [_entry] : Object.values(_entry))
181
- .map(specifier => join(dir, specifier))
182
- .map(id => toEntry(id));
188
+ const entries = typeof _entry === 'string' ? [_entry] : Array.isArray(_entry) ? _entry : Object.values(_entry).flat();
189
+ const deps = entries.map(specifier => join(vitestRoot, specifier)).map(id => toEntry(id));
183
190
  for (const dependency of deps)
184
191
  inputs.add(dependency);
185
192
  }
@@ -211,6 +218,9 @@ const args = {
211
218
  return inputs;
212
219
  },
213
220
  };
221
+ const registerVisitors = ({ ctx, registerVisitor }) => {
222
+ registerVisitor(createVitestMockVisitor(ctx));
223
+ };
214
224
  const plugin = {
215
225
  title,
216
226
  enablers,
@@ -219,5 +229,6 @@ const plugin = {
219
229
  entry,
220
230
  resolveConfig,
221
231
  args,
232
+ registerVisitors,
222
233
  };
223
234
  export default plugin;
@@ -17,6 +17,7 @@ interface VitestConfig {
17
17
  provider: string;
18
18
  };
19
19
  root?: string;
20
+ dir?: string;
20
21
  environment?: string;
21
22
  globalSetup?: string | string[];
22
23
  reporters?: (string | [string, unknown] | unknown)[];
@@ -42,7 +43,7 @@ export interface ViteConfig extends VitestConfig {
42
43
  build?: {
43
44
  lib?: {
44
45
  entry: string | string[] | {
45
- [entryAlias: string]: string;
46
+ [entryAlias: string]: string | string[];
46
47
  };
47
48
  };
48
49
  };
@@ -0,0 +1,2 @@
1
+ import type { PluginVisitorContext, PluginVisitorObject } from '../../../types/config.ts';
2
+ export declare function createVitestMockVisitor(ctx: PluginVisitorContext): PluginVisitorObject;
@@ -0,0 +1,50 @@
1
+ import { IMPORT_FLAGS } from '../../../constants.js';
2
+ import { getStringValue, isStringLiteral } from '../../../typescript/ast-nodes.js';
3
+ import { isShadowed } from '../../../typescript/visitors/walk.js';
4
+ export function createVitestMockVisitor(ctx) {
5
+ const viNames = new Set();
6
+ return {
7
+ Program(node) {
8
+ viNames.clear();
9
+ for (const statement of node.body) {
10
+ if (statement.type !== 'ImportDeclaration' ||
11
+ !isStringLiteral(statement.source) ||
12
+ getStringValue(statement.source) !== 'vitest')
13
+ continue;
14
+ for (const specifier of statement.specifiers ?? []) {
15
+ if (specifier.type === 'ImportSpecifier' &&
16
+ specifier.imported.type === 'Identifier' &&
17
+ specifier.imported.name === 'vi') {
18
+ viNames.add(specifier.local.name);
19
+ }
20
+ }
21
+ }
22
+ },
23
+ CallExpression(node) {
24
+ const argument = node.arguments[0];
25
+ const factory = node.arguments[1];
26
+ const factoryBody = factory?.type === 'ArrowFunctionExpression' &&
27
+ factory.body.type === 'ParenthesizedExpression' &&
28
+ factory.body.expression.type === 'ObjectExpression'
29
+ ? factory.body.expression
30
+ : undefined;
31
+ if (argument?.type !== 'ImportExpression' ||
32
+ !isStringLiteral(argument.source) ||
33
+ factory?.type !== 'ArrowFunctionExpression' ||
34
+ !factoryBody ||
35
+ factory.async ||
36
+ factory.params.length > 0 ||
37
+ factoryBody.properties.some(property => property.type === 'SpreadElement') ||
38
+ node.callee.type !== 'MemberExpression' ||
39
+ node.callee.computed ||
40
+ node.callee.object.type !== 'Identifier' ||
41
+ !viNames.has(node.callee.object.name) ||
42
+ isShadowed(node.callee.object.name, node.callee.object.start) ||
43
+ node.callee.property.type !== 'Identifier' ||
44
+ node.callee.property.name !== 'mock')
45
+ return;
46
+ ctx.markImportExpressionHandled(argument.start);
47
+ ctx.addImport(getStringValue(argument.source), argument.source.start, IMPORT_FLAGS.SIDE_EFFECTS);
48
+ },
49
+ };
50
+ }
@@ -3,7 +3,7 @@ import { hasDependency } from '../../util/plugin.js';
3
3
  const title = 'WebdriverIO';
4
4
  const enablers = ['@wdio/cli'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
- const config = ['wdio.conf.{js,ts}'];
6
+ const config = ['wdio.conf.{js,cjs,mjs,ts,cts,mts}'];
7
7
  const resolveConfig = async (config) => {
8
8
  const cfg = config?.config;
9
9
  if (!cfg)
@@ -105,20 +105,21 @@ export const findWebpackDependenciesFromConfig = async (config, options) => {
105
105
  }
106
106
  }
107
107
  }
108
- if (typeof opts.entry === 'string')
109
- entries.push(opts.entry);
110
- else if (Array.isArray(opts.entry))
111
- entries.push(...opts.entry);
112
- else if (typeof opts.entry === 'object') {
113
- for (const entry of Object.values(opts.entry)) {
114
- if (typeof entry === 'string')
115
- entries.push(entry);
116
- else if (Array.isArray(entry))
117
- entries.push(...entry);
118
- else if (typeof entry === 'function')
119
- entries.push(entry());
120
- else if (entry && typeof entry === 'object' && 'filename' in entry)
121
- entries.push(entry['filename']);
108
+ const entry = typeof opts.entry === 'function' ? await opts.entry() : opts.entry;
109
+ if (typeof entry === 'string')
110
+ entries.push(entry);
111
+ else if (Array.isArray(entry))
112
+ entries.push(...entry);
113
+ else if (typeof entry === 'object') {
114
+ for (const item of Object.values(entry)) {
115
+ if (typeof item === 'string')
116
+ entries.push(item);
117
+ else if (Array.isArray(item))
118
+ entries.push(...item);
119
+ else if (typeof item === 'function')
120
+ entries.push(item());
121
+ else if (item && typeof item === 'object' && 'import' in item)
122
+ entries.push(...[item.import].flat());
122
123
  }
123
124
  }
124
125
  if (entries.length === 0 && opts.context)
@@ -138,6 +138,7 @@ export type PluginVisitorContext = {
138
138
  sourceText: string;
139
139
  addScript: (script: string) => void;
140
140
  addImport: (specifier: string, pos: number, modifiers: number) => void;
141
+ markImportExpressionHandled: (pos: number) => void;
141
142
  addImportGlob: (patterns: string[], options?: {
142
143
  base?: string;
143
144
  filter?: RegExp;
@@ -8,7 +8,13 @@ export interface TsConfigJson {
8
8
  }>;
9
9
  [key: string]: unknown;
10
10
  };
11
+ contentMappers?: {
12
+ package: string;
13
+ }[];
11
14
  references?: Array<{
12
15
  path: string;
13
16
  }>;
14
17
  }
18
+ export interface ContentMapperManifest {
19
+ exec?: string[];
20
+ }
@@ -25,6 +25,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
25
25
  const specifierExportNames = new Set();
26
26
  const scripts = new Set();
27
27
  const importGlobs = [];
28
+ const handledImportExpressions = new Set();
28
29
  const importAliases = new Map();
29
30
  const addImportAlias = (aliasName, id, importFilePath) => {
30
31
  const aliases = importAliases.get(aliasName);
@@ -278,6 +279,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
278
279
  pluginCtx.sourceText = sourceText;
279
280
  pluginCtx.addScript = (s) => scripts.add(s);
280
281
  pluginCtx.addImport = (spec, pos, mod) => addImport(spec, undefined, undefined, undefined, pos, mod);
282
+ pluginCtx.markImportExpressionHandled = (pos) => handledImportExpressions.add(pos);
281
283
  pluginCtx.addImportGlob = (patterns, opts) => importGlobs.push({ patterns, base: opts?.base, filter: opts?.filter });
282
284
  pluginCtx.markExportRegistered = (name) => registeredCustomElements.add(name);
283
285
  }
@@ -311,6 +313,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
311
313
  resolveModule,
312
314
  programFiles,
313
315
  entryFiles,
316
+ handledImportExpressions,
314
317
  visitor,
315
318
  getJSDocTags,
316
319
  });
@@ -202,7 +202,7 @@ export function handleCallExpression(node, s) {
202
202
  addValue(internalImport.import, OPAQUE, s.filePath);
203
203
  else {
204
204
  internalImport.refs.add(arg.name);
205
- (internalImport.enumerated ??= new Set()).add(arg.name);
205
+ (internalImport.enumerated ??= new Set()).add(_import.importedName);
206
206
  }
207
207
  }
208
208
  }
@@ -2,6 +2,12 @@ import { OPAQUE } from '../../constants.js';
2
2
  import { addValue } from '../../util/module-graph.js';
3
3
  import { getStringValue, isStringLiteral } from '../ast-nodes.js';
4
4
  import { isShadowed } from './walk.js';
5
+ function markWholeObjectRead(internalImport, id) {
6
+ (internalImport.enumerated ??= new Set()).add(id);
7
+ }
8
+ function isNumericKey(value) {
9
+ return value != null && Number.isFinite(Number(value)) && String(Number(value)) === value;
10
+ }
5
11
  export function handleMemberExpression(node, s) {
6
12
  if (node.object.type === 'MemberExpression' && node.object.object.type === 'MemberExpression') {
7
13
  s.chainedMemberExprs.add(node.object);
@@ -18,12 +24,21 @@ export function handleMemberExpression(node, s) {
18
24
  memberName = node.property.name;
19
25
  }
20
26
  else if (node.computed && isStringLiteral(node.property)) {
21
- memberName = getStringValue(node.property);
27
+ const value = getStringValue(node.property);
28
+ if (!_import.isNamespace && isNumericKey(value)) {
29
+ markWholeObjectRead(internalImport, _import.importedName);
30
+ return;
31
+ }
32
+ memberName = value;
22
33
  }
23
34
  else if (node.computed && _import.isNamespace) {
24
35
  addValue(internalImport.import, OPAQUE, s.filePath);
25
36
  return;
26
37
  }
38
+ else if (node.computed) {
39
+ markWholeObjectRead(internalImport, _import.importedName);
40
+ return;
41
+ }
27
42
  if (memberName) {
28
43
  if (_import.isDynamicImport) {
29
44
  addValue(internalImport.import, memberName, s.filePath);
@@ -44,18 +59,51 @@ export function handleMemberExpression(node, s) {
44
59
  if (aliases) {
45
60
  s.accessedAliases.add(localName);
46
61
  let memberName;
62
+ let isWholeRead = false;
47
63
  if (node.computed === false && node.property.type === 'Identifier') {
48
64
  memberName = node.property.name;
49
65
  }
50
66
  else if (node.computed && isStringLiteral(node.property)) {
51
- memberName = getStringValue(node.property);
67
+ const value = getStringValue(node.property);
68
+ if (isNumericKey(value))
69
+ isWholeRead = true;
70
+ else
71
+ memberName = value;
52
72
  }
53
- if (memberName) {
54
- for (const alias of aliases) {
55
- const internalImport = s.internal.get(alias.filePath);
56
- if (internalImport) {
57
- s.addNsMemberRefs(internalImport, alias.id, memberName);
58
- }
73
+ else if (node.computed) {
74
+ isWholeRead = true;
75
+ }
76
+ for (const alias of aliases) {
77
+ const internalImport = s.internal.get(alias.filePath);
78
+ if (!internalImport)
79
+ continue;
80
+ if (isWholeRead) {
81
+ const origin = s.localImportMap.get(alias.id);
82
+ markWholeObjectRead(internalImport, origin?.importedName ?? alias.id);
83
+ }
84
+ else if (memberName) {
85
+ s.addNsMemberRefs(internalImport, alias.id, memberName);
86
+ }
87
+ }
88
+ }
89
+ }
90
+ if (node.computed &&
91
+ node.object.type === 'MemberExpression' &&
92
+ !node.object.computed &&
93
+ node.object.object.type === 'Identifier' &&
94
+ node.object.property.type === 'Identifier') {
95
+ const rootName = node.object.object.name;
96
+ if (!isShadowed(rootName, node.object.object.start)) {
97
+ const _import = s.localImportMap.get(rootName);
98
+ if (_import?.isNamespace) {
99
+ const internalImport = s.internal.get(_import.filePath);
100
+ if (internalImport) {
101
+ const mid = node.object.property.name;
102
+ const value = isStringLiteral(node.property) ? getStringValue(node.property) : undefined;
103
+ if (value !== undefined && !isNumericKey(value))
104
+ s.addNsMemberRefs(internalImport, rootName, `${mid}.${value}`);
105
+ else
106
+ markWholeObjectRead(internalImport, mid);
59
107
  }
60
108
  }
61
109
  }
@@ -43,6 +43,7 @@ interface WalkContext {
43
43
  resolveModule: ResolveModule;
44
44
  programFiles: Set<string>;
45
45
  entryFiles: Set<string>;
46
+ handledImportExpressions: Set<number>;
46
47
  visitor: Visitor;
47
48
  getJSDocTags: (nodeStart: number) => Set<string>;
48
49
  }
@@ -726,7 +726,6 @@ function walkAST(program, sourceText, filePath, hasModuleSyntax, ctx) {
726
726
  sourceText,
727
727
  isJS,
728
728
  isModuleFile: isExternalModule(program, hasModuleSyntax),
729
- handledImportExpressions: new Set(),
730
729
  bareExprRefs: new Set(),
731
730
  accessedAliases: new Set(),
732
731
  nsContainers: new Map(),
@@ -91,9 +91,14 @@ export const findAndParseGitignores = async (cwd, workspaceDirs) => {
91
91
  }
92
92
  return deepFilterMatcher;
93
93
  };
94
+ const seenGitignoreFiles = new Set();
94
95
  const addFile = (filePath, baseDir) => {
96
+ const absPath = toPosix(filePath);
97
+ if (seenGitignoreFiles.has(absPath))
98
+ return;
99
+ seenGitignoreFiles.add(absPath);
95
100
  gitignoreFiles.push(relative(cwd, filePath));
96
- const dir = baseDir ?? dirname(toPosix(filePath));
101
+ const dir = baseDir ?? dirname(absPath);
97
102
  const base = relative(cwd, dir);
98
103
  const ancestor = base.startsWith('..') ? `${relative(dir, cwd)}/` : undefined;
99
104
  const ignoresForDir = new Set(base === '' ? GLOBAL_IGNORE_PATTERNS : []);
@@ -154,6 +159,9 @@ export const findAndParseGitignores = async (cwd, workspaceDirs) => {
154
159
  if (isFile(excludePath))
155
160
  addFile(excludePath, cwd);
156
161
  }
162
+ const rootGitignorePath = join(cwd, '.gitignore');
163
+ if (isFile(rootGitignorePath))
164
+ addFile(rootGitignorePath);
157
165
  let isRelevantDir;
158
166
  if (workspaceDirs && workspaceDirs.size > 0) {
159
167
  const relevantAncestors = new Set();
@@ -13,12 +13,17 @@ const unwrapFunction = async (maybeFunction, options) => {
13
13
  }
14
14
  return maybeFunction;
15
15
  };
16
+ const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
16
17
  export async function loadResolvedConfigFile(configPath, options) {
17
18
  const loadedValue = await _load(configPath);
19
+ let config;
18
20
  try {
19
- return await unwrapFunction(loadedValue, options);
21
+ config = await unwrapFunction(loadedValue, options);
20
22
  }
21
23
  catch (_error) {
22
24
  throw new ConfigurationError(`Error running the function from ${configPath}`);
23
25
  }
26
+ if (!isObject(config))
27
+ throw new ConfigurationError(`Expected an object as configuration from ${configPath}`);
28
+ return config;
24
29
  }
@@ -1,4 +1,5 @@
1
1
  export declare const extensionAlias: Record<string, string[]>;
2
+ export declare const resolvePackageManifestPath: (packageName: string, baseDir: string) => string | undefined;
2
3
  export declare const _resolveModuleSync: (specifier: string, basePath: string) => string | undefined;
3
4
  declare const resolveDeclarationSync: (specifier: string, containingFile: string) => {
4
5
  path: string;
@@ -23,6 +23,17 @@ const declarationResolver = new ResolverFactory({
23
23
  nodePath: false,
24
24
  });
25
25
  resolverInstances.push(declarationResolver);
26
+ const packageManifestResolver = new ResolverFactory({
27
+ extensions: ['.json'],
28
+ exportsFields: [],
29
+ nodePath: false,
30
+ });
31
+ resolverInstances.push(packageManifestResolver);
32
+ export const resolvePackageManifestPath = (packageName, baseDir) => {
33
+ const resolved = packageManifestResolver.sync(baseDir, `${packageName}/package.json`);
34
+ if (resolved.path)
35
+ return toPosix(resolved.path);
36
+ };
26
37
  const createSyncModuleResolver = (extensions, tsConfigFile) => {
27
38
  const baseOptions = {
28
39
  extensions,
@@ -3,5 +3,6 @@ export interface ScriptCommand {
3
3
  binary: string;
4
4
  args: string[];
5
5
  }
6
+ export declare const toShellCommand: (argv: string[]) => string;
6
7
  export declare function walkCommands(node: Node): Generator<Command>;
7
8
  export declare const getScriptCommands: (script: string) => ScriptCommand[];
@@ -1,6 +1,7 @@
1
1
  import { parse } from 'unbash';
2
2
  import { extractBinary } from './modules.js';
3
3
  const spawningBinaries = new Set(['c8', 'cross-env', 'retry-cli']);
4
+ export const toShellCommand = (argv) => argv.map(arg => `'${arg.replaceAll("'", `'\\''`)}'`).join(' ');
4
5
  export function* walkCommands(node) {
5
6
  switch (node.type) {
6
7
  case 'Command':
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.32.2";
1
+ export declare const version = "6.33.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.32.2';
1
+ export const version = '6.33.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.32.2",
3
+ "version": "6.33.0",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",
@@ -71,6 +71,10 @@
71
71
  "types": "./dist/types.d.ts",
72
72
  "default": "./dist/index.js"
73
73
  },
74
+ "./config": {
75
+ "types": "./dist/config.d.ts",
76
+ "default": "./dist/config.js"
77
+ },
74
78
  "./session": {
75
79
  "types": "./dist/session/index.d.ts",
76
80
  "default": "./dist/session/index.js"
@@ -78,27 +82,27 @@
78
82
  },
79
83
  "dependencies": {
80
84
  "fdir": "^6.5.0",
81
- "formatly": "^0.3.0",
82
- "get-tsconfig": "4.14.1",
85
+ "formatly": "^0.7.0",
86
+ "get-tsconfig": "4.14.3",
83
87
  "jiti": "^2.7.0",
84
- "oxc-parser": "^0.143.0",
88
+ "oxc-parser": "^0.147.0",
85
89
  "oxc-resolver": "11.24.2",
86
- "picomatch": "^4.0.5",
87
- "smol-toml": "^1.7.1",
90
+ "picomatch": "^4.0.7",
91
+ "smol-toml": "^1.8.0",
88
92
  "strip-json-comments": "5.0.3",
89
93
  "tinyglobby": "^0.2.17",
90
- "unbash": "^4.0.9",
94
+ "unbash": "^4.0.10",
91
95
  "yaml": "^2.9.0",
92
96
  "zod": "^4.4.3"
93
97
  },
94
98
  "devDependencies": {
95
99
  "@jest/types": "^30.4.1",
96
- "@types/bun": "^1.3.14",
100
+ "@types/bun": "^1.4.0",
97
101
  "@types/picomatch": "^4.0.3",
98
102
  "@types/webpack": "^5.28.5",
99
103
  "codeclimate-types": "^0.3.1",
100
104
  "prettier": "^3.9.6",
101
- "tsx": "^4.23.11",
105
+ "tsx": "^4.23.12",
102
106
  "typescript": "7.0.2"
103
107
  },
104
108
  "engines": {