knip 6.35.1 → 6.37.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 (109) hide show
  1. package/dist/ConfigurationChief.d.ts +20 -2
  2. package/dist/ConfigurationChief.js +15 -1
  3. package/dist/DependencyDeputy.d.ts +0 -2
  4. package/dist/DependencyDeputy.js +1 -17
  5. package/dist/WorkspaceWorker.js +7 -6
  6. package/dist/binaries/bash-parser.js +1 -1
  7. package/dist/binaries/command.js +21 -10
  8. package/dist/binaries/fallback.js +5 -4
  9. package/dist/binaries/plugins.js +4 -4
  10. package/dist/binaries/resolvers/bun.js +2 -2
  11. package/dist/binaries/resolvers/find.js +5 -4
  12. package/dist/binaries/resolvers/index.d.ts +2 -1
  13. package/dist/binaries/resolvers/index.js +3 -2
  14. package/dist/binaries/resolvers/npm.js +46 -4
  15. package/dist/binaries/resolvers/npx.js +8 -4
  16. package/dist/binaries/resolvers/nub.js +3 -2
  17. package/dist/binaries/resolvers/pnpm.js +17 -10
  18. package/dist/binaries/resolvers/yarn.js +1 -1
  19. package/dist/binaries/util.d.ts +4 -1
  20. package/dist/binaries/util.js +2 -0
  21. package/dist/compilers/compilers.js +1 -1
  22. package/dist/compilers/scss.js +2 -2
  23. package/dist/graph-explorer/cache.d.ts +3 -0
  24. package/dist/graph-explorer/cache.js +6 -0
  25. package/dist/graph-explorer/explorer.d.ts +1 -0
  26. package/dist/graph-explorer/explorer.js +2 -0
  27. package/dist/graph-explorer/operations/build-exports-tree.js +14 -2
  28. package/dist/graph-explorer/operations/get-ambiguous-star-export.d.ts +8 -0
  29. package/dist/graph-explorer/operations/get-ambiguous-star-export.js +11 -0
  30. package/dist/graph-explorer/operations/get-contention.js +265 -122
  31. package/dist/graph-explorer/operations/resolve-export-origins.d.ts +32 -0
  32. package/dist/graph-explorer/operations/resolve-export-origins.js +255 -0
  33. package/dist/graph-explorer/utils.js +8 -0
  34. package/dist/manifest/index.js +1 -3
  35. package/dist/plugins/ava/index.js +1 -1
  36. package/dist/plugins/eslint/helpers.d.ts +2 -1
  37. package/dist/plugins/eslint/helpers.js +59 -14
  38. package/dist/plugins/eslint/index.js +2 -2
  39. package/dist/plugins/eslint/resolveFromAST.js +17 -11
  40. package/dist/plugins/eslint/types.d.ts +3 -3
  41. package/dist/plugins/github-actions/index.js +1 -1
  42. package/dist/plugins/graphql-codegen/index.js +15 -4
  43. package/dist/plugins/graphql-codegen/types.d.ts +11 -1
  44. package/dist/plugins/graphql-codegen/types.js +6 -0
  45. package/dist/plugins/husky/index.js +1 -1
  46. package/dist/plugins/index.d.ts +2 -0
  47. package/dist/plugins/index.js +4 -0
  48. package/dist/plugins/lefthook/index.js +1 -1
  49. package/dist/plugins/mise/scripts.js +5 -5
  50. package/dist/plugins/next/index.js +39 -5
  51. package/dist/plugins/node/index.js +6 -0
  52. package/dist/plugins/tailwind/compiler.js +2 -2
  53. package/dist/plugins/tailwind/index.js +7 -1
  54. package/dist/plugins/taskfile/index.js +1 -1
  55. package/dist/plugins/tauri/index.js +1 -1
  56. package/dist/plugins/textlint/helpers.d.ts +4 -0
  57. package/dist/plugins/textlint/helpers.js +39 -0
  58. package/dist/plugins/textlint/index.d.ts +3 -0
  59. package/dist/plugins/textlint/index.js +52 -0
  60. package/dist/plugins/textlint/types.d.ts +5 -0
  61. package/dist/plugins/textlint/types.js +1 -0
  62. package/dist/plugins/travis/index.js +1 -1
  63. package/dist/plugins/typedoc/index.js +3 -2
  64. package/dist/plugins/typedoc/types.d.ts +2 -2
  65. package/dist/plugins/varlock/index.d.ts +3 -0
  66. package/dist/plugins/varlock/index.js +31 -0
  67. package/dist/plugins/varlock/parse.d.ts +11 -0
  68. package/dist/plugins/varlock/parse.js +185 -0
  69. package/dist/plugins/varlock/scan.d.ts +2 -0
  70. package/dist/plugins/varlock/scan.js +78 -0
  71. package/dist/plugins/vite-plus/resolveFromAST.js +1 -1
  72. package/dist/plugins/vitest/index.js +4 -2
  73. package/dist/reporters/trace.js +30 -1
  74. package/dist/schema/configuration.d.ts +33 -0
  75. package/dist/schema/configuration.js +2 -0
  76. package/dist/schema/plugins.d.ts +10 -0
  77. package/dist/schema/plugins.js +2 -0
  78. package/dist/session/build-maps.js +9 -1
  79. package/dist/session/file-descriptor.js +0 -2
  80. package/dist/session/index.d.ts +1 -1
  81. package/dist/session/types.d.ts +18 -0
  82. package/dist/types/PluginNames.d.ts +2 -2
  83. package/dist/types/PluginNames.js +2 -0
  84. package/dist/types/config.d.ts +5 -1
  85. package/dist/types/module-graph.d.ts +3 -0
  86. package/dist/types/package-json.d.ts +1 -1
  87. package/dist/typescript/get-imports-and-exports.js +43 -9
  88. package/dist/typescript/visitors/exports.js +9 -4
  89. package/dist/typescript/visitors/walk.d.ts +1 -1
  90. package/dist/typescript/visitors/walk.js +4 -1
  91. package/dist/util/array.d.ts +1 -1
  92. package/dist/util/array.js +5 -1
  93. package/dist/util/create-input-handler.js +3 -3
  94. package/dist/util/create-options.d.ts +28 -2
  95. package/dist/util/file-entry-cache.js +1 -1
  96. package/dist/util/glob-core.d.ts +1 -0
  97. package/dist/util/glob-core.js +11 -3
  98. package/dist/util/jiti.js +1 -0
  99. package/dist/util/modules.d.ts +2 -0
  100. package/dist/util/modules.js +3 -1
  101. package/dist/util/package-json.d.ts +185 -183
  102. package/dist/util/parse-args.d.ts +2 -0
  103. package/dist/util/parse-args.js +6 -1
  104. package/dist/util/url.d.ts +1 -0
  105. package/dist/util/url.js +2 -0
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/package.json +5 -5
  109. package/schema.json +20 -0
@@ -0,0 +1,255 @@
1
+ import { isBuiltin } from 'node:module';
2
+ import { IMPORT_FLAGS, IMPORT_STAR } from '../../constants.js';
3
+ import { getCachedExportTable, setCachedExportTable } from '../cache.js';
4
+ import { forEachAliasReExport, forEachNamespaceReExport, forEachPassThroughReExport, getStarReExportSources, } from '../visitors.js';
5
+ const originKey = (origin) => origin.filePath + '\0' + origin.identifier;
6
+ const isExplicit = (info, name) => info.node?.exports.has(name) || info.terminals.has(name);
7
+ export const createExportResolver = (graph) => {
8
+ const infos = new Map();
9
+ const getInfo = (filePath) => {
10
+ const cached = infos.get(filePath);
11
+ if (cached)
12
+ return cached;
13
+ const node = graph.get(filePath);
14
+ const info = { node, references: new Map(), terminals: new Map(), stars: [] };
15
+ infos.set(filePath, info);
16
+ if (!node)
17
+ return info;
18
+ const add = (map, name, origin) => {
19
+ const list = map.get(name);
20
+ if (list)
21
+ list.push(origin);
22
+ else
23
+ map.set(name, [origin]);
24
+ };
25
+ for (const [sourcePath, imports] of node.imports.internal) {
26
+ if (getStarReExportSources(imports))
27
+ info.stars.push(sourcePath);
28
+ forEachNamespaceReExport(imports, name => {
29
+ const exp = node.exports.get(name);
30
+ if (!exp || exp.isBindingReExport)
31
+ add(info.terminals, name, { filePath: sourcePath, identifier: IMPORT_STAR });
32
+ });
33
+ forEachPassThroughReExport(imports, name => {
34
+ if (node.exports.get(name)?.isBindingReExport)
35
+ add(info.references, name, { filePath: sourcePath, identifier: name });
36
+ });
37
+ forEachAliasReExport(imports, (identifier, alias) => {
38
+ if (node.exports.get(alias)?.isBindingReExport)
39
+ add(info.references, alias, { filePath: sourcePath, identifier });
40
+ });
41
+ }
42
+ const addExternal = (record) => {
43
+ if (!(record.modifiers & IMPORT_FLAGS.RE_EXPORT) || !record.identifier)
44
+ return;
45
+ const name = record.alias ?? record.identifier;
46
+ const exp = node.exports.get(name);
47
+ if ((!exp && !(record.identifier === IMPORT_STAR && record.alias)) ||
48
+ (record.isTypeOnly && exp && !exp.isBindingReExport) ||
49
+ info.references.has(name))
50
+ return;
51
+ add(info.terminals, name, { filePath: record.specifier, identifier: record.identifier });
52
+ };
53
+ for (const record of node.imports.external)
54
+ addExternal(record);
55
+ for (const record of node.imports.imports)
56
+ if (isBuiltin(record.specifier))
57
+ addExternal(record);
58
+ return info;
59
+ };
60
+ const build = (filePath, identifier) => {
61
+ const states = new Map();
62
+ const discovery = [];
63
+ const updates = [];
64
+ const request = (path, name) => {
65
+ let state = states.get(path);
66
+ if (!state) {
67
+ state = {
68
+ filePath: path,
69
+ info: getInfo(path),
70
+ names: new Set(),
71
+ all: false,
72
+ entries: new Map(),
73
+ dependents: new Map(),
74
+ pending: new Set(),
75
+ };
76
+ states.set(path, state);
77
+ }
78
+ if (state.all || (name !== undefined && state.names.has(name)))
79
+ return state;
80
+ if (name === undefined)
81
+ state.all = true;
82
+ else
83
+ state.names.add(name);
84
+ discovery.push([state, name]);
85
+ return state;
86
+ };
87
+ const merge = (state, name, origins) => {
88
+ let entry = state.entries.get(name);
89
+ let changed = false;
90
+ if (!entry) {
91
+ entry = { origins, hasExplicitExport: !!isExplicit(state.info, name) };
92
+ state.entries.set(name, entry);
93
+ changed = origins.size > 0;
94
+ }
95
+ else if (entry.origins.size === 0) {
96
+ entry.origins = origins;
97
+ changed = origins.size > 0;
98
+ }
99
+ else {
100
+ let merged;
101
+ for (const [key, origin] of origins) {
102
+ if (!entry.origins.has(key))
103
+ (merged ??= new Map(entry.origins)).set(key, origin);
104
+ }
105
+ if (merged) {
106
+ entry.origins = merged;
107
+ changed = true;
108
+ }
109
+ }
110
+ if (changed) {
111
+ if (state.pending.size === 0)
112
+ updates.push(state);
113
+ state.pending.add(name);
114
+ }
115
+ };
116
+ const connect = (source, target, sourceName, targetName) => {
117
+ let edge = source.dependents.get(target);
118
+ if (!edge)
119
+ source.dependents.set(target, (edge = { star: false, aliases: new Map() }));
120
+ if (sourceName === undefined || targetName === undefined)
121
+ edge.star = true;
122
+ else {
123
+ let aliases = edge.aliases.get(sourceName);
124
+ if (!aliases)
125
+ edge.aliases.set(sourceName, (aliases = new Set()));
126
+ aliases.add(targetName);
127
+ }
128
+ };
129
+ request(filePath, identifier);
130
+ for (let index = 0; index < discovery.length; index++) {
131
+ const [state, name] = discovery[index];
132
+ const cached = getCachedExportTable(graph, state.filePath);
133
+ if (name === undefined && cached?.complete) {
134
+ for (const [id, entry] of cached.entries)
135
+ merge(state, id, entry.origins);
136
+ continue;
137
+ }
138
+ const seed = (id) => {
139
+ const entry = cached?.entries.get(id);
140
+ if (entry) {
141
+ merge(state, id, entry.origins);
142
+ return;
143
+ }
144
+ const terminals = state.info.terminals.get(id);
145
+ const references = state.info.references.get(id);
146
+ const exp = state.info.node?.exports.get(id);
147
+ if (terminals || exp) {
148
+ const origins = new Map();
149
+ if (terminals)
150
+ for (const origin of terminals)
151
+ origins.set(originKey(origin), origin);
152
+ else if (exp && !exp.isBindingReExport) {
153
+ const origin = { filePath: state.filePath, identifier: exp.binding };
154
+ origins.set(originKey(origin), origin);
155
+ }
156
+ merge(state, id, origins);
157
+ }
158
+ if (references)
159
+ for (const source of references)
160
+ connect(request(source.filePath, source.identifier), state, source.identifier, id);
161
+ };
162
+ if (name === undefined) {
163
+ if (state.info.node)
164
+ for (const id of state.info.node.exports.keys())
165
+ seed(id);
166
+ for (const id of state.info.terminals.keys())
167
+ if (!state.info.node?.exports.has(id))
168
+ seed(id);
169
+ for (const source of state.info.stars)
170
+ connect(request(source), state);
171
+ }
172
+ else {
173
+ seed(name);
174
+ if (cached?.complete || cached?.entries.has(name) || isExplicit(state.info, name) || name === 'default')
175
+ continue;
176
+ for (const source of state.info.stars)
177
+ connect(request(source, name), state);
178
+ }
179
+ }
180
+ for (let index = 0; index < updates.length; index++) {
181
+ const state = updates[index];
182
+ const names = state.pending;
183
+ state.pending = new Set();
184
+ for (const [target, edge] of state.dependents) {
185
+ for (const name of names) {
186
+ const entry = state.entries.get(name);
187
+ if (!entry)
188
+ continue;
189
+ if (edge.star &&
190
+ name !== 'default' &&
191
+ !isExplicit(target.info, name) &&
192
+ (target.all || target.names.has(name)))
193
+ merge(target, name, entry.origins);
194
+ const aliases = edge.aliases.get(name);
195
+ if (aliases)
196
+ for (const alias of aliases)
197
+ merge(target, alias, entry.origins);
198
+ }
199
+ }
200
+ }
201
+ for (const state of states.values()) {
202
+ const cached = getCachedExportTable(graph, state.filePath);
203
+ if (cached && !state.all) {
204
+ for (const [name, entry] of state.entries)
205
+ cached.entries.set(name, entry);
206
+ }
207
+ else
208
+ setCachedExportTable(graph, state.filePath, { entries: state.entries, complete: state.all });
209
+ }
210
+ };
211
+ const getTable = (filePath) => {
212
+ if (!getCachedExportTable(graph, filePath)?.complete)
213
+ build(filePath);
214
+ return getCachedExportTable(graph, filePath)?.entries ?? new Map();
215
+ };
216
+ const resolve = (filePath, identifier) => {
217
+ const cached = getCachedExportTable(graph, filePath);
218
+ if (!cached?.complete && !cached?.entries.has(identifier))
219
+ build(filePath, identifier);
220
+ return getCachedExportTable(graph, filePath)?.entries.get(identifier);
221
+ };
222
+ const getSources = (filePath, identifier) => {
223
+ const info = getInfo(filePath);
224
+ if (isExplicit(info, identifier))
225
+ return info.references.get(identifier) ?? [];
226
+ return identifier === 'default' ? [] : info.stars.map(source => ({ filePath: source, identifier }));
227
+ };
228
+ const reachesOrigin = (source, origin, site) => {
229
+ const visited = new Set([originKey(site)]);
230
+ const queue = [source];
231
+ const key = originKey(origin);
232
+ for (let index = 0; index < queue.length; index++) {
233
+ const current = queue[index];
234
+ const currentKey = originKey(current);
235
+ if (visited.has(currentKey))
236
+ continue;
237
+ visited.add(currentKey);
238
+ if (current.identifier === IMPORT_STAR && currentKey === key)
239
+ return true;
240
+ const info = getInfo(current.filePath);
241
+ const exp = info.node?.exports.get(current.identifier);
242
+ if (info.terminals.get(current.identifier)?.some(value => originKey(value) === key))
243
+ return true;
244
+ if (exp && !exp.isBindingReExport && originKey({ filePath: current.filePath, identifier: exp.binding }) === key)
245
+ return true;
246
+ queue.push(...getSources(current.filePath, current.identifier));
247
+ }
248
+ return false;
249
+ };
250
+ return { getTable, resolve, getInfo, reachesOrigin };
251
+ };
252
+ export const resolveExportOrigins = (graph, filePath, identifier) => {
253
+ const entry = createExportResolver(graph).resolve(filePath, identifier);
254
+ return { origins: entry ? [...entry.origins.values()] : [], hasExplicitExport: entry?.hasExplicitExport ?? false };
255
+ };
@@ -27,6 +27,14 @@ export const getExportedIdentifiers = (graph, filePath, visited = new Set()) =>
27
27
  continue;
28
28
  addIdentifier(identifier);
29
29
  }
30
+ for (const _import of node.imports.imports) {
31
+ if (!_import.filePath &&
32
+ _import.modifiers & IMPORT_FLAGS.RE_EXPORT &&
33
+ _import.identifier === IMPORT_STAR &&
34
+ _import.alias) {
35
+ addIdentifier(_import.alias, true);
36
+ }
37
+ }
30
38
  if (node.imports?.internal) {
31
39
  for (const [importedPath, importDetails] of node.imports.internal) {
32
40
  forEachPassThroughReExport(importDetails, (id, _sources) => {
@@ -17,10 +17,8 @@ const getMetaDataFromPackageJson = ({ dir, packageNames }) => {
17
17
  if (manifest) {
18
18
  const defaultBinaryName = packageName.replace(/^@[^/]+\//, '');
19
19
  const binaries = typeof manifest.bin === 'string' ? [defaultBinaryName] : Object.keys(manifest.bin ?? {});
20
- for (const binaryName of binaries) {
20
+ for (const binaryName of binaries)
21
21
  addBinary(binaryName, packageName);
22
- addBinary(packageName, binaryName);
23
- }
24
22
  const packagePeerDependencies = Object.keys(manifest.peerDependencies ?? {});
25
23
  for (const packagePeerDependency of packagePeerDependencies) {
26
24
  const hostDependency = {
@@ -23,7 +23,7 @@ const resolveConfig = async (localConfig, options) => {
23
23
  const nodeArgs = localConfig.nodeArguments ?? [];
24
24
  const requireArgs = (localConfig.require ?? []).map(require => `--require ${require}`);
25
25
  const fakeCommand = `node ${nodeArgs.join(' ')} ${requireArgs.join(' ')}`;
26
- return files.concat(options.getInputsFromScripts(fakeCommand, { knownBinsOnly: true }));
26
+ return files.concat(options.getInputsFromScripts(fakeCommand, { optionalBinaries: true }));
27
27
  };
28
28
  const plugin = {
29
29
  title,
@@ -1,7 +1,8 @@
1
1
  import type { PluginOptions } from '../../types/config.ts';
2
+ import type { Manifest } from '../../util/package-json.ts';
2
3
  import { type ConfigInput, type Input } from '../../util/input.ts';
3
4
  import type { ESLintConfig, ESLintConfigDeprecated, OverrideConfigDeprecated, Settings } from './types.ts';
4
5
  export declare const isFlatConfig: (fileName: string) => boolean;
5
6
  export declare const getInputs: (config: ESLintConfigDeprecated | OverrideConfigDeprecated | ESLintConfig, options: PluginOptions) => (Input | ConfigInput)[];
6
7
  export declare const getInputsFromSettings: (settings?: Settings) => Input[];
7
- export declare const resolveFormatters: (formatters: string | string[]) => Set<Input>;
8
+ export declare const resolveFormatters: (formatters: string | string[], manifest: Manifest) => Set<Input>;
@@ -16,20 +16,36 @@ export const getInputs = (config, options) => {
16
16
  return compact(dependencies).map(id => toDeferResolve(id, { optional: true }));
17
17
  };
18
18
  const getInputsDeprecated = (config, options) => {
19
- const extendsSpecifiers = config.extends ? compact([config.extends].flat().map(resolveExtendSpecifier)) : [];
19
+ const extendsList = config.extends ? [config.extends].flat() : [];
20
+ const extendsSpecifiers = compact(extendsList.map(resolveExtendSpecifier));
20
21
  if (extendsSpecifiers.some(specifier => specifier?.startsWith('eslint-plugin-prettier')))
21
22
  extendsSpecifiers.push('eslint-config-prettier');
22
- const extendConfigs = extendsSpecifiers.map(specifier => toConfig('eslint', specifier, { containingFilePath: options.configFilePath }));
23
+ const extendConfigs = [...extendsList.filter(isInternal), ...extendsSpecifiers].map(specifier => toConfig('eslint', specifier, { containingFilePath: options.configFilePath }));
23
24
  const plugins = config.plugins ? config.plugins.map(resolvePluginSpecifier) : [];
24
- const parser = config.parser ?? config.parserOptions?.parser;
25
+ const parsers = getParsers(config);
25
26
  const babelDependencies = config.parserOptions?.babelOptions
26
27
  ? getDependenciesFromConfig(config.parserOptions.babelOptions)
27
28
  : [];
28
29
  const settings = config.settings ? getDependenciesFromSettings(config.settings) : [];
29
30
  const rules = getDependenciesFromRules({});
30
31
  const overrides = config.overrides ? [config.overrides].flat().flatMap(d => getInputsDeprecated(d, options)) : [];
31
- const deferred = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(id => toDeferResolve(id));
32
- return [...extendConfigs, ...deferred, ...babelDependencies, ...overrides];
32
+ const deferred = compact([...extendsSpecifiers, ...plugins, ...settings, ...rules]).map(id => toDeferResolve(id));
33
+ return [...extendConfigs, ...deferred, ...parsers, ...babelDependencies, ...overrides];
34
+ };
35
+ const isParserObject = (value) => typeof value.parseForESLint === 'function' || typeof value.parse === 'function';
36
+ const toParser = (name) => toDeferResolve(name, name === 'espree' ? { optional: true } : {});
37
+ const getParsers = ({ parser, parserOptions }) => {
38
+ const inputs = [];
39
+ for (const value of [parser, parserOptions?.parser]) {
40
+ if (typeof value === 'string')
41
+ inputs.push(toParser(value));
42
+ else if (value && typeof value === 'object' && !isParserObject(value)) {
43
+ for (const name of Object.values(value))
44
+ if (typeof name === 'string')
45
+ inputs.push(toParser(name));
46
+ }
47
+ }
48
+ return inputs;
33
49
  };
34
50
  const isQualifiedSpecifier = (specifier) => specifier === 'eslint' ||
35
51
  /\/eslint-(config|plugin)$/.test(specifier) ||
@@ -58,10 +74,19 @@ const resolveExtendSpecifier = (specifier) => {
58
74
  return resolveSpecifier(namespace, specifier);
59
75
  };
60
76
  const getDependenciesFromRules = (rules = {}) => Object.keys(rules).flatMap(ruleKey => ruleKey.includes('/') ? [resolveSpecifier('eslint-plugin', ruleKey.split('/').slice(0, -1).join('/'))] : []);
77
+ const getResolverNames = (value) => {
78
+ if (typeof value === 'string')
79
+ return [value];
80
+ if (Array.isArray(value))
81
+ return value.flatMap(getResolverNames);
82
+ if (value && typeof value === 'object')
83
+ return Object.keys(value);
84
+ return [];
85
+ };
61
86
  const getDependenciesFromSettings = (settings = {}) => {
62
87
  return Object.entries(settings).flatMap(([settingKey, settings]) => {
63
88
  if (settingKey === 'import/resolver') {
64
- return (typeof settings === 'string' ? [settings] : Object.keys(settings))
89
+ return getResolverNames(settings)
65
90
  .filter(key => key !== 'node')
66
91
  .map(key => {
67
92
  if (isInternal(key))
@@ -82,15 +107,35 @@ const getDependenciesFromSettings = (settings = {}) => {
82
107
  };
83
108
  export const getInputsFromSettings = (settings) => compact(getDependenciesFromSettings(settings)).map(id => toDeferResolve(id, { optional: true }));
84
109
  const builtinFormatters = new Set(['html', 'json-with-metadata', 'json', 'stylish']);
85
- export const resolveFormatters = (formatters) => {
110
+ const builtinFormattersUntilV8 = new Set([
111
+ 'checkstyle',
112
+ 'compact',
113
+ 'jslint-xml',
114
+ 'junit',
115
+ 'tap',
116
+ 'unix',
117
+ 'visualstudio',
118
+ ]);
119
+ const normalizeFormatterName = (name) => {
120
+ if (!name.startsWith('@'))
121
+ return name.startsWith('eslint-formatter-') ? name : `eslint-formatter-${name}`;
122
+ const [scope, id] = name.split('/');
123
+ if (!id)
124
+ return `${scope}/eslint-formatter`;
125
+ return /^eslint-formatter(-|$)/.test(id) ? name : name.replace('/', '/eslint-formatter-');
126
+ };
127
+ export const resolveFormatters = (formatters, manifest) => {
86
128
  const inputs = new Set();
87
- for (const formatter of [formatters].flat()) {
88
- if (builtinFormatters.has(formatter))
89
- continue;
90
- else if (isInternal(formatter))
91
- inputs.add(toDeferResolve(formatter));
92
- else
93
- inputs.add(toDependency(`eslint-formatter-${formatter}`));
129
+ const isBeforeV9 = (manifest.getMajor('eslint') ?? 9) < 9;
130
+ for (const rawFormatter of [formatters].flat()) {
131
+ const formatter = rawFormatter.replace(/\\/g, '/');
132
+ if (!formatter.startsWith('@') && formatter.includes('/')) {
133
+ inputs.add(toDeferResolve(isInternal(formatter) ? formatter : `./${formatter}`));
134
+ }
135
+ else {
136
+ const isBuiltin = builtinFormatters.has(formatter) || (isBeforeV9 && builtinFormattersUntilV8.has(formatter));
137
+ inputs.add(toDependency(normalizeFormatterName(formatter), isBuiltin ? { optional: true } : {}));
138
+ }
94
139
  }
95
140
  return inputs;
96
141
  };
@@ -49,12 +49,12 @@ const args = {
49
49
  config: true,
50
50
  alias: { format: ['f'] },
51
51
  boolean: ['inspect-config'],
52
- resolveInputs: (parsed) => {
52
+ resolveInputs: (parsed, { manifest }) => {
53
53
  const inputs = [];
54
54
  if (parsed['inspect-config'])
55
55
  inputs.push(toDependency('@eslint/config-inspector', { optional: true }));
56
56
  if (parsed['format'])
57
- for (const input of resolveFormatters(parsed['format']))
57
+ for (const input of resolveFormatters(parsed['format'], manifest))
58
58
  inputs.push(input);
59
59
  return inputs;
60
60
  },
@@ -11,6 +11,21 @@ export const getInputsFromSettingsAST = (program) => {
11
11
  const dep = key === 'import/resolver' ? `eslint-import-resolver-${resolver}` : resolver;
12
12
  inputs.push(toDeferResolve(dep, { optional: true }));
13
13
  };
14
+ const addResolvers = (key, node) => {
15
+ if (node?.type === 'ArrayExpression') {
16
+ for (const element of node.elements)
17
+ addResolvers(key, element);
18
+ }
19
+ else if (node?.type === 'ObjectExpression') {
20
+ for (const prop of node.properties) {
21
+ if (prop.type === 'Property')
22
+ addResolver(key, getPropertyKey(prop));
23
+ }
24
+ }
25
+ else {
26
+ addResolver(key, getStringValue(node));
27
+ }
28
+ };
14
29
  const visitor = new Visitor({
15
30
  ObjectExpression(node) {
16
31
  const settingsNode = findProperty(node, 'settings');
@@ -20,17 +35,8 @@ export const getInputsFromSettingsAST = (program) => {
20
35
  if (prop.type !== 'Property')
21
36
  continue;
22
37
  const key = getPropertyKey(prop);
23
- if (key !== 'import/resolver' && key !== 'import/parsers')
24
- continue;
25
- if (prop.value?.type === 'ObjectExpression') {
26
- for (const p of prop.value.properties ?? []) {
27
- if (p.type === 'Property')
28
- addResolver(key, getPropertyKey(p));
29
- }
30
- }
31
- else {
32
- addResolver(key, getStringValue(prop.value));
33
- }
38
+ if (key === 'import/resolver' || key === 'import/parsers')
39
+ addResolvers(key, prop.value);
34
40
  }
35
41
  },
36
42
  });
@@ -1,14 +1,14 @@
1
1
  type ParserOptions = {
2
2
  project?: string;
3
- parser?: string;
3
+ parser?: string | Record<string, unknown>;
4
4
  babelOptions?: {
5
5
  plugins: string[];
6
6
  presets: string[];
7
7
  };
8
8
  };
9
- export type Settings = Record<string, Record<string, unknown> | string>;
9
+ export type Settings = Record<string, Record<string, unknown> | string | unknown[]>;
10
10
  type Rules = Record<string, string | number>;
11
- type BaseConfig = {
11
+ export type BaseConfig = {
12
12
  extends?: string | string[];
13
13
  parser?: string;
14
14
  parserOptions?: ParserOptions;
@@ -34,7 +34,7 @@ const resolveConfig = async (config, options) => {
34
34
  const manifest = getManifest(dir) ?? options.manifest;
35
35
  const actionPath = relative(dir, configFileDir);
36
36
  const run = step.run.replace(ACTION_PATH_VAR, actionPath ? `./${actionPath}` : '.');
37
- for (const input of getInputsFromScripts([run], { knownBinsOnly: true, manifest })) {
37
+ for (const input of getInputsFromScripts([run], { optionalBinaries: true, manifest })) {
38
38
  if (isDeferResolveEntry(input) && path && !workingDir) {
39
39
  input.specifier = relative(join(dir, path), join(rootCwd, input.specifier));
40
40
  }
@@ -1,8 +1,8 @@
1
1
  import { toDependency, toEntry, toProductionEntry } from '../../util/input.js';
2
2
  import { get } from '../../util/object.js';
3
- import { isInternal, join } from '../../util/path.js';
3
+ import { isInternal, join, normalize } from '../../util/path.js';
4
4
  import { hasDependency } from '../../util/plugin.js';
5
- import { isConfigurationOutput, isGraphqlConfigTypes, isGraphqlProjectsConfigTypes } from './types.js';
5
+ import { isConfigurationOutput, isGraphqlConfigTypes, isGraphqlProjectsConfigTypes, isNearOperationFilePreset, } from './types.js';
6
6
  const title = 'GraphQL Codegen';
7
7
  const enablers = [/^@graphql-codegen\//, 'graphql-config'];
8
8
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
@@ -24,6 +24,17 @@ const getPluginPackageName = (name) => {
24
24
  return name;
25
25
  return `@graphql-codegen/${name}`;
26
26
  };
27
+ const getOutputPattern = (output, outputConfig) => {
28
+ if (isConfigurationOutput(outputConfig) && isNearOperationFilePreset(outputConfig.preset)) {
29
+ const presetConfig = outputConfig.presetConfig ?? {};
30
+ const { folder = '', extension = '.generated.ts', fileName = '*', filePerOperation = false } = presetConfig;
31
+ const subfolder = normalize(folder)
32
+ .split('/')
33
+ .filter(segment => segment !== '' && segment !== '.' && segment !== '..');
34
+ return join(output, '**', ...subfolder, `${filePerOperation ? '*' : fileName}${extension}`);
35
+ }
36
+ return output.endsWith('/') ? `${output}**` : output;
37
+ };
27
38
  const resolveConfig = (config, options) => {
28
39
  const codegenConfigs = isGraphqlProjectsConfigTypes(config)
29
40
  ? Object.values(config.projects).flatMap(project => project.extensions?.codegen ?? [])
@@ -33,8 +44,8 @@ const resolveConfig = (config, options) => {
33
44
  const generateConfigs = codegenConfigs.filter((config) => Boolean(config?.generates));
34
45
  const generateSet = generateConfigs.flatMap(config => Object.values(config.generates));
35
46
  const outputs = generateConfigs
36
- .flatMap(config => Object.keys(config.generates))
37
- .map(output => toProductionEntry(join(options.configFileDir, output.endsWith('/') ? `${output}**` : output)));
47
+ .flatMap(config => Object.entries(config.generates))
48
+ .map(([output, outputConfig]) => toProductionEntry(join(options.configFileDir, getOutputPattern(output, outputConfig))));
38
49
  const configurationOutput = generateSet.filter(isConfigurationOutput);
39
50
  const presets = configurationOutput
40
51
  .map(configOutput => (configOutput.preset ? configOutput.preset : undefined))
@@ -12,10 +12,20 @@ type OutputPreset = {
12
12
  buildGeneratesSection: (options: unknown) => Promise<unknown>;
13
13
  prepareDocuments?: (outputFilePath: string, outputSpecificDocuments: unknown) => Promise<unknown>;
14
14
  };
15
+ export interface NearOperationFilePresetConfig {
16
+ baseTypesPath?: string;
17
+ extension?: string;
18
+ fileName?: string;
19
+ filePerOperation?: boolean;
20
+ folder?: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export declare const isNearOperationFilePreset: (preset: ConfiguredOutput['preset']) => preset is PresetNames;
15
24
  export declare function isConfigurationOutput(config: ConfiguredOutput | ConfiguredPlugin[]): config is ConfiguredOutput;
16
- interface ConfiguredOutput {
25
+ export interface ConfiguredOutput {
17
26
  plugins?: OutputConfig[];
18
27
  preset?: PresetNames | OutputPreset;
28
+ presetConfig?: NearOperationFilePresetConfig;
19
29
  }
20
30
  export interface GraphqlCodegenTypes {
21
31
  generates: {
@@ -1,3 +1,9 @@
1
+ const nearOperationFilePresetNames = new Set([
2
+ 'near-operation-file',
3
+ 'near-operation-file-preset',
4
+ '@graphql-codegen/near-operation-file-preset',
5
+ ]);
6
+ export const isNearOperationFilePreset = (preset) => typeof preset === 'string' && nearOperationFilePresetNames.has(preset);
1
7
  export function isConfigurationOutput(config) {
2
8
  return 'preset' in config || 'plugins' in config;
3
9
  }
@@ -17,7 +17,7 @@ const resolveConfig = (script, options) => {
17
17
  return [toDependency('husky'), ...options.getInputsFromScripts(scripts, { ...options })];
18
18
  }
19
19
  }
20
- return options.getInputsFromScripts(String(script), { knownBinsOnly: true });
20
+ return options.getInputsFromScripts(String(script), { optionalBinaries: true });
21
21
  };
22
22
  const plugin = {
23
23
  title,
@@ -150,6 +150,7 @@ export declare const Plugins: {
150
150
  taskfile: import("../types/config.ts").Plugin;
151
151
  tauri: import("../types/config.ts").Plugin;
152
152
  temporal: import("../types/config.ts").Plugin;
153
+ textlint: import("../types/config.ts").Plugin;
153
154
  travis: import("../types/config.ts").Plugin;
154
155
  'ts-node': import("../types/config.ts").Plugin;
155
156
  tsd: import("../types/config.ts").Plugin;
@@ -166,6 +167,7 @@ export declare const Plugins: {
166
167
  'unplugin-vue-i18n': import("../types/config.ts").Plugin;
167
168
  'unplugin-vue-markdown': import("../types/config.ts").Plugin;
168
169
  'unplugin-vue-router': import("../types/config.ts").Plugin;
170
+ varlock: import("../types/config.ts").Plugin;
169
171
  vercel: import("../types/config.ts").Plugin;
170
172
  'vercel-og': import("../types/config.ts").Plugin;
171
173
  vike: import("../types/config.ts").Plugin;
@@ -144,6 +144,7 @@ import { default as tanstackRouter } from './tanstack-router/index.js';
144
144
  import { default as taskfile } from './taskfile/index.js';
145
145
  import { default as tauri } from './tauri/index.js';
146
146
  import { default as temporal } from './temporal/index.js';
147
+ import { default as textlint } from './textlint/index.js';
147
148
  import { default as travis } from './travis/index.js';
148
149
  import { default as tsNode } from './ts-node/index.js';
149
150
  import { default as tsd } from './tsd/index.js';
@@ -160,6 +161,7 @@ import { default as unpluginVueComponents } from './unplugin-vue-components/inde
160
161
  import { default as unpluginVueI18n } from './unplugin-vue-i18n/index.js';
161
162
  import { default as unpluginVueMarkdown } from './unplugin-vue-markdown/index.js';
162
163
  import { default as unpluginVueRouter } from './unplugin-vue-router/index.js';
164
+ import { default as varlock } from './varlock/index.js';
163
165
  import { default as vercel } from './vercel/index.js';
164
166
  import { default as vercelOg } from './vercel-og/index.js';
165
167
  import { default as vike } from './vike/index.js';
@@ -328,6 +330,7 @@ export const Plugins = {
328
330
  taskfile,
329
331
  tauri,
330
332
  temporal,
333
+ textlint,
331
334
  travis,
332
335
  'ts-node': tsNode,
333
336
  tsd,
@@ -344,6 +347,7 @@ export const Plugins = {
344
347
  'unplugin-vue-i18n': unpluginVueI18n,
345
348
  'unplugin-vue-markdown': unpluginVueMarkdown,
346
349
  'unplugin-vue-router': unpluginVueRouter,
350
+ varlock,
347
351
  vercel,
348
352
  'vercel-og': vercelOg,
349
353
  vike,
@@ -21,7 +21,7 @@ const resolveConfig = async (localConfig, options) => {
21
21
  const inputs = manifest.devDependencies ? Object.keys(manifest.devDependencies).map(id => toDependency(id)) : [];
22
22
  if (extname(configFileName) !== '') {
23
23
  const scripts = findByKeyDeep(localConfig, 'run').flatMap(command => {
24
- const deps = getInputsFromScripts([command.run], { ...options, knownBinsOnly: true });
24
+ const deps = getInputsFromScripts([command.run], { ...options, optionalBinaries: true });
25
25
  const dir = command.root ?? cwd;
26
26
  return deps.flatMap(dependency => ({ ...dependency, dir }));
27
27
  });