lintmax 0.0.10 → 0.0.12

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.

Potentially problematic release.


This version of lintmax might be problematic. Click here for more details.

package/dist/index.js CHANGED
@@ -1,91 +1,28 @@
1
- import { file, spawnSync, write } from 'bun';
1
+ import { file, write } from 'bun';
2
+ import { BIOME_IGNORE_PATTERNS, BIOME_PATTERN_RULE_OVERRIDES, BIOME_RULES_OFF, DEFAULT_SHARED_IGNORE_PATTERNS, OXLINT_PATTERN_RULE_OVERRIDES, SHARED_OVERRIDE_SYMBOL_KEY } from './constants.js';
3
+ import { cacheDir, ensureDirectory, readRequiredJson } from './core.js';
4
+ import { assertJsonSerializable, assertObject, assertOptionalString, findUnknownRules, normalizeIgnorePattern, normalizeObjectListInput, normalizePathListInput, normalizeRulesOffInput, normalizeTailwindOption, stripPluginNamespace } from './normalize.js';
2
5
  import { dirnamePath, fromFileUrl, joinPath } from './path.js';
3
- const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta.resolve('../package.json'))), biomeRulesOff = [
4
- 'noBarrelFile',
5
- 'noConditionalExpect',
6
- 'noConsole',
7
- 'noDefaultExport',
8
- 'noExcessiveCognitiveComplexity',
9
- 'noExcessiveLinesPerFile',
10
- 'noExcessiveLinesPerFunction',
11
- 'noExportedImports',
12
- 'noImplicitBoolean',
13
- 'noJsxLiterals',
14
- 'noJsxPropsBind',
15
- 'noMagicNumbers',
16
- 'noNestedTernary',
17
- 'noNodejsModules',
18
- 'noProcessGlobal',
19
- 'noReactSpecificProps',
20
- 'noSecrets',
21
- 'noSolidDestructuredProps',
22
- 'noTernary',
23
- 'noUndeclaredDependencies',
24
- 'noUnresolvedImports',
25
- 'useBlockStatements',
26
- 'useComponentExportOnlyModules',
27
- 'useDestructuring',
28
- 'useExplicitType',
29
- 'useImportExtensions',
30
- 'useNamingConvention',
31
- 'useQwikValidLexicalScope',
32
- 'useSingleVarDeclarator',
33
- 'useSolidForComponent',
34
- 'useSortedClasses'
35
- ], biomeIgnorePatterns = [
36
- '!!**/.build',
37
- '!!**/.cache',
38
- '!!**/.next',
39
- '!!**/.output',
40
- '!!**/.turbo',
41
- '!!**/.venv',
42
- '!!**/.wxt',
43
- '!!**/_generated',
44
- '!!**/Android',
45
- '!!**/Darwin',
46
- '!!**/dist',
47
- '!!**/maestro',
48
- '!!**/module_bindings',
49
- '!!**/playwright-report',
50
- '!!**/test-results',
51
- '!!**/*.xcassets'
52
- ], normalizeBiomeIgnorePattern = ({ pattern }) => {
6
+ const SHARED_OVERRIDE_KEYS = ['biome', 'eslint', 'oxlint'];
7
+ const pkgRoot = dirnamePath(fromFileUrl(import.meta.resolve('../package.json'))), normalizeBiomeIgnorePattern = ({ pattern }) => {
53
8
  if (pattern.startsWith('!!'))
54
9
  return pattern;
55
- const trimmed = pattern.startsWith('./') ? pattern.slice(2) : pattern;
10
+ const trimmed = normalizeIgnorePattern({ pattern });
56
11
  return trimmed.startsWith('**/') ? `!!${trimmed}` : `!!**/${trimmed}`;
57
- }, decodeBytes = (bytes) => decoder.decode(bytes ?? new Uint8Array()), ensureDirectory = ({ directory }) => {
58
- const result = spawnSync({
59
- cmd: ['mkdir', '-p', directory],
60
- stderr: 'pipe',
61
- stdout: 'pipe'
62
- });
63
- if (result.exitCode === 0)
64
- return;
65
- const stderr = decodeBytes(result.stderr).trim();
66
- throw new Error(stderr.length > 0 ? stderr : `Failed to create directory: ${directory}`);
67
- }, resolveBundledModule = ({ specifier }) => {
12
+ }, resolveSchemaPath = async ({ cwd }) => {
68
13
  try {
69
- return fromFileUrl(import.meta.resolve(specifier));
14
+ return fromFileUrl(import.meta.resolve('@biomejs/biome/configuration_schema.json'));
70
15
  }
71
- catch {
72
- return null;
16
+ catch (error) {
17
+ if (!(error instanceof Error))
18
+ throw error;
73
19
  }
74
- }, readJsonFile = async ({ filePath }) => {
75
- const text = await file(filePath).text();
76
- return JSON.parse(text);
77
- }, resolveSchemaPath = async ({ cwd }) => {
78
- const bundled = resolveBundledModule({
79
- specifier: '@biomejs/biome/configuration_schema.json'
80
- });
81
- if (bundled)
82
- return bundled;
83
20
  const consumerCandidate = joinPath(cwd, 'node_modules', '@biomejs', 'biome', 'configuration_schema.json');
84
21
  if (await file(consumerCandidate).exists())
85
22
  return consumerCandidate;
86
23
  throw new Error('Cannot find module @biomejs/biome/configuration_schema.json');
87
24
  }, resolveBiomeSchema = async ({ cwd }) => {
88
- const schemaPath = await resolveSchemaPath({ cwd }), schema = await readJsonFile({ filePath: schemaPath }), rulesProps = schema.$defs.Rules?.properties ?? {}, categories = Object.keys(rulesProps).filter(k => k !== 'recommended'), ruleMap = new Map();
25
+ const schemaPath = await resolveSchemaPath({ cwd }), schema = await readRequiredJson({ path: schemaPath }), rulesProps = schema.$defs.Rules?.properties ?? {}, categories = Object.keys(rulesProps).filter(k => k !== 'recommended'), ruleMap = new Map();
89
26
  for (const cat of categories) {
90
27
  const key = cat.charAt(0).toUpperCase() + cat.slice(1), props = schema.$defs[key]?.properties;
91
28
  if (props)
@@ -97,8 +34,23 @@ const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta
97
34
  }, extractRuleNames = (rules) => {
98
35
  const names = [];
99
36
  for (const key of Object.keys(rules))
100
- names.push(key.includes('/') ? key.slice(key.indexOf('/') + 1) : key);
37
+ names.push(stripPluginNamespace({ rule: key }));
101
38
  return names;
39
+ }, assertKnownBiomeRuleNames = ({ categoryMap, label, rules }) => {
40
+ const unknown = findUnknownRules({
41
+ knownRules: new Set(categoryMap.keys()),
42
+ normalizeRule: (rule) => stripPluginNamespace({ rule }),
43
+ rules
44
+ });
45
+ if (unknown.length > 0)
46
+ throw new Error(`${label} contains unknown biome rules: ${unknown.join(', ')}`);
47
+ }, assertKnownOxlintRuleNames = ({ label, rules, knownRules }) => {
48
+ const unknown = findUnknownRules({
49
+ knownRules,
50
+ rules
51
+ });
52
+ if (unknown.length > 0)
53
+ throw new Error(`${label} contains unknown oxlint rules: ${unknown.join(', ')}`);
102
54
  }, groupByCategory = ({ categoryMap, ruleNames }) => {
103
55
  const result = {};
104
56
  for (const rule of ruleNames) {
@@ -109,15 +61,336 @@ const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta
109
61
  }
110
62
  }
111
63
  return result;
112
- }, createBiomeConfig = async ({ cwd, globalIgnorePatterns, options }) => {
113
- const { categories, ruleMap } = await resolveBiomeSchema({ cwd }), allRulesOff = [...biomeRulesOff];
114
- if (options?.rules)
115
- for (const key of Object.keys(options.rules)) {
116
- const ruleName = key.includes('/') ? key.slice(key.indexOf('/') + 1) : key;
64
+ }, normalizeSharedOverrideItem = ({ index, item, label }) => {
65
+ const files = normalizePathListInput({
66
+ label: `${label}[${index}].files`,
67
+ value: item.files
68
+ }), biomeRules = normalizeRulesOffInput({ label: `${label}[${index}].biome`, value: item.biome }), eslintRules = normalizeRulesOffInput({ label: `${label}[${index}].eslint`, value: item.eslint }), oxlintRules = normalizeRulesOffInput({ label: `${label}[${index}].oxlint`, value: item.oxlint });
69
+ if (!(biomeRules || eslintRules || oxlintRules))
70
+ throw new Error(`${label}[${index}] must define at least one action: biome, eslint, or oxlint`);
71
+ return {
72
+ biomeRules,
73
+ eslintRules,
74
+ files,
75
+ oxlintRules
76
+ };
77
+ }, normalizeSharedOverrides = ({ label, value }) => {
78
+ const out = [];
79
+ if (value === undefined)
80
+ return out;
81
+ const obj = assertObject({ label, value }), entries = [];
82
+ for (const [pattern, rawOverride] of Object.entries(obj)) {
83
+ const override = assertObject({
84
+ label: `${label}.${pattern}`,
85
+ value: rawOverride
86
+ });
87
+ for (const key of Object.keys(override))
88
+ if (!SHARED_OVERRIDE_KEYS.includes(key))
89
+ throw new Error(`${label}.${pattern}.${key} is not supported. Use biome, eslint, or oxlint.`);
90
+ entries.push({
91
+ ...override,
92
+ files: [pattern]
93
+ });
94
+ }
95
+ for (const [i, item] of entries.entries())
96
+ out.push(normalizeSharedOverrideItem({
97
+ index: i,
98
+ item,
99
+ label
100
+ }));
101
+ return out;
102
+ }, collectSharedRuleOverrides = ({ ruleKey, sharedOverrides }) => {
103
+ const out = [];
104
+ for (const override of sharedOverrides) {
105
+ const rules = override[ruleKey];
106
+ if (rules)
107
+ out.push({ files: override.files, rules });
108
+ }
109
+ return out;
110
+ }, parseLinterOffOverrides = ({ assertKnownRules, fileKey, label, requireOff = false, value }) => {
111
+ const out = [];
112
+ for (const [i, override] of normalizeObjectListInput({
113
+ label,
114
+ value
115
+ }).entries()) {
116
+ const itemLabel = requireOff ? `${label}[${i}]` : label;
117
+ if (requireOff && override[fileKey] === undefined)
118
+ throw new Error(`${label}[${i}].${fileKey} is required`);
119
+ if (requireOff && override.off === undefined)
120
+ throw new Error(`${label}[${i}].off is required`);
121
+ const normalizedOverrideRules = normalizeRulesOffInput({
122
+ label: `${itemLabel}.off`,
123
+ value: override.off
124
+ });
125
+ if (normalizedOverrideRules) {
126
+ assertKnownRules?.(normalizedOverrideRules);
127
+ out.push({
128
+ files: normalizePathListInput({
129
+ label: `${itemLabel}.${fileKey}`,
130
+ value: override[fileKey]
131
+ }),
132
+ rules: normalizedOverrideRules
133
+ });
134
+ }
135
+ }
136
+ return out;
137
+ }, parseTopLevelSyncScalars = ({ options }) => {
138
+ if (options.compact !== undefined && typeof options.compact !== 'boolean')
139
+ throw new Error('compact must be a boolean');
140
+ const tailwind = normalizeTailwindOption({
141
+ label: 'tailwind',
142
+ value: options.tailwind
143
+ });
144
+ assertOptionalString({
145
+ label: 'tsconfigRootDir',
146
+ value: options.tsconfigRootDir
147
+ });
148
+ const sharedOverrides = options.overrides === undefined
149
+ ? []
150
+ : normalizeSharedOverrides({
151
+ label: 'overrides',
152
+ value: options.overrides
153
+ }), topLevelIgnores = options.ignores === undefined
154
+ ? []
155
+ : normalizePathListInput({
156
+ label: 'ignores',
157
+ value: options.ignores
158
+ });
159
+ return {
160
+ sharedOverrides,
161
+ tailwind,
162
+ topLevelIgnores,
163
+ tsconfigRootDir: options.tsconfigRootDir
164
+ };
165
+ }, parseLinterSyncCommon = ({ linter, value }) => {
166
+ const parsed = {};
167
+ if (value.ignores !== undefined)
168
+ parsed.ignores = normalizePathListInput({
169
+ label: `${linter}.ignores`,
170
+ value: value.ignores
171
+ });
172
+ const normalizedOffRules = normalizeRulesOffInput({
173
+ label: `${linter}.off`,
174
+ value: value.off
175
+ });
176
+ if (normalizedOffRules)
177
+ parsed.off = Object.keys(normalizedOffRules);
178
+ return parsed;
179
+ }, validateBiomeSyncConfig = ({ biome }) => {
180
+ const biomeValue = assertObject({ label: 'biome config', value: biome }), parsedCommon = parseLinterSyncCommon({
181
+ linter: 'biome',
182
+ value: biomeValue
183
+ }), parsedOverrides = parseLinterOffOverrides({
184
+ fileKey: 'includes',
185
+ label: 'biome.overrides',
186
+ requireOff: true,
187
+ value: biomeValue.overrides
188
+ }).map(override => ({
189
+ includes: override.files,
190
+ off: Object.keys(override.rules)
191
+ })), parsed = {
192
+ ...parsedCommon
193
+ };
194
+ if (parsedOverrides.length > 0)
195
+ parsed.overrides = parsedOverrides;
196
+ return parsed;
197
+ }, validateEslintSyncConfig = ({ eslint }) => {
198
+ const eslintValue = assertObject({ label: 'eslint config', value: eslint }), parsedCommon = parseLinterSyncCommon({
199
+ linter: 'eslint',
200
+ value: eslintValue
201
+ }), parsed = {
202
+ ...parsedCommon
203
+ };
204
+ if (eslintValue.tailwind !== undefined)
205
+ throw new Error('eslint.tailwind is not supported in sync config. Use top-level tailwind.');
206
+ if (eslintValue.tsconfigRootDir !== undefined)
207
+ throw new Error('eslint.tsconfigRootDir is not supported in sync config. Use top-level tsconfigRootDir.');
208
+ if (eslintValue.append !== undefined) {
209
+ const appendEntries = normalizeObjectListInput({
210
+ label: 'eslint.append',
211
+ value: eslintValue.append
212
+ });
213
+ for (const [i, item] of appendEntries.entries())
214
+ assertJsonSerializable({
215
+ label: `eslint.append[${i}]`,
216
+ value: item
217
+ });
218
+ parsed.append = appendEntries;
219
+ }
220
+ return parsed;
221
+ }, validateOxlintSyncConfig = ({ oxlint }) => {
222
+ const oxlintValue = assertObject({ label: 'oxlint config', value: oxlint }), parsedCommon = parseLinterSyncCommon({
223
+ linter: 'oxlint',
224
+ value: oxlintValue
225
+ }), parsedOverrides = parseLinterOffOverrides({
226
+ fileKey: 'files',
227
+ label: 'oxlint.overrides',
228
+ requireOff: true,
229
+ value: oxlintValue.overrides
230
+ }).map(override => ({
231
+ files: override.files,
232
+ off: Object.keys(override.rules)
233
+ })), parsed = {
234
+ ...parsedCommon
235
+ };
236
+ if (parsedOverrides.length > 0)
237
+ parsed.overrides = parsedOverrides;
238
+ return parsed;
239
+ }, validateSyncOptions = ({ options }) => {
240
+ if (!options)
241
+ return {
242
+ sharedOverrides: [],
243
+ topLevelIgnores: []
244
+ };
245
+ const parsedTopLevelScalars = parseTopLevelSyncScalars({ options }), parsed = {
246
+ ...parsedTopLevelScalars
247
+ };
248
+ if (options.biome)
249
+ parsed.biome = validateBiomeSyncConfig({ biome: options.biome });
250
+ if (options.eslint)
251
+ parsed.eslint = validateEslintSyncConfig({ eslint: options.eslint });
252
+ if (options.oxlint)
253
+ parsed.oxlint = validateOxlintSyncConfig({ oxlint: options.oxlint });
254
+ return parsed;
255
+ }, mergeUniquePatterns = ({ into, patterns }) => {
256
+ if (!patterns)
257
+ return;
258
+ for (const raw of patterns) {
259
+ const pattern = normalizeIgnorePattern({ pattern: raw });
260
+ if (pattern.length > 0 && !into.includes(pattern))
261
+ into.push(pattern);
262
+ }
263
+ }, mergeIgnorePatternGroups = ({ groups }) => {
264
+ const merged = [];
265
+ for (const group of groups)
266
+ mergeUniquePatterns({ into: merged, patterns: group });
267
+ return merged;
268
+ }, buildUserIgnorePatterns = ({ topLevelIgnores }) => mergeIgnorePatternGroups({
269
+ groups: [topLevelIgnores]
270
+ }), buildLinterOptionsWithSharedOverrides = ({ createEmpty, ruleKey, sharedOverrides, source, mapSharedOverride }) => {
271
+ if (!(source || sharedOverrides.length > 0))
272
+ return source;
273
+ const next = source ? { ...source } : createEmpty();
274
+ next.off = source?.off;
275
+ const sharedRuleOverrides = collectSharedRuleOverrides({
276
+ ruleKey,
277
+ sharedOverrides
278
+ }).map(mapSharedOverride), localOverrides = source?.overrides ?? [], mergedOverrides = [...sharedRuleOverrides, ...localOverrides];
279
+ if (mergedOverrides.length > 0)
280
+ next.overrides = mergedOverrides;
281
+ return next;
282
+ }, buildBiomeOptions = ({ biomeSource, sharedOverrides }) => buildLinterOptionsWithSharedOverrides({
283
+ createEmpty: () => ({}),
284
+ mapSharedOverride: override => ({
285
+ includes: override.files,
286
+ off: Object.keys(override.rules)
287
+ }),
288
+ ruleKey: 'biomeRules',
289
+ sharedOverrides,
290
+ source: biomeSource
291
+ }), buildOxlintOptions = ({ oxlintSource, sharedOverrides }) => buildLinterOptionsWithSharedOverrides({
292
+ createEmpty: () => ({}),
293
+ mapSharedOverride: override => ({
294
+ files: override.files,
295
+ off: Object.keys(override.rules)
296
+ }),
297
+ ruleKey: 'oxlintRules',
298
+ sharedOverrides,
299
+ source: oxlintSource
300
+ }), buildEslintIgnorePatterns = ({ eslintSource, userIgnorePatterns }) => mergeIgnorePatternGroups({
301
+ groups: [
302
+ userIgnorePatterns,
303
+ eslintSource?.ignores === undefined
304
+ ? undefined
305
+ : normalizePathListInput({
306
+ label: 'eslint.ignores',
307
+ value: eslintSource.ignores
308
+ })
309
+ ]
310
+ }), buildEslintOptions = ({ eslintSource, sharedOverrides, tailwind, tsconfigRootDir, userIgnorePatterns }) => {
311
+ const eslintIgnores = buildEslintIgnorePatterns({
312
+ eslintSource,
313
+ userIgnorePatterns
314
+ }), sharedRuleOverrides = collectSharedRuleOverrides({
315
+ ruleKey: 'eslintRules',
316
+ sharedOverrides
317
+ }).map(override => ({
318
+ files: override.files,
319
+ rules: override.rules
320
+ })), sharedOverrideAppendIndexes = sharedRuleOverrides.map((_, index) => index);
321
+ if (eslintSource || sharedOverrides.length > 0 || tailwind !== undefined || tsconfigRootDir !== undefined) {
322
+ const eslintOptions = {
323
+ off: eslintSource?.off
324
+ };
325
+ if (eslintIgnores.length > 0)
326
+ eslintOptions.ignores = eslintIgnores;
327
+ const appendEntries = (eslintSource?.append ?? []).map(entry => entry), mergedAppend = [...sharedRuleOverrides, ...appendEntries];
328
+ if (mergedAppend.length > 0)
329
+ eslintOptions.append = mergedAppend;
330
+ const mergedTailwind = normalizeTailwindOption({
331
+ label: 'tailwind',
332
+ value: tailwind
333
+ });
334
+ eslintOptions.tailwind = mergedTailwind ?? true;
335
+ eslintOptions.tsconfigRootDir = tsconfigRootDir;
336
+ return {
337
+ eslintOptions,
338
+ sharedOverrideAppendIndexes
339
+ };
340
+ }
341
+ if (eslintIgnores.length === 0)
342
+ return {
343
+ eslintOptions: undefined,
344
+ sharedOverrideAppendIndexes
345
+ };
346
+ return {
347
+ eslintOptions: {
348
+ ignores: eslintIgnores,
349
+ tailwind: true
350
+ },
351
+ sharedOverrideAppendIndexes
352
+ };
353
+ }, normalizeLinterOffOverrides = ({ assertKnownRules, fileKey, label, value }) => parseLinterOffOverrides({
354
+ assertKnownRules,
355
+ fileKey,
356
+ label,
357
+ value
358
+ }), normalizeKnownOffRules = ({ assertKnownRules, label, value }) => {
359
+ const normalizedRules = normalizeRulesOffInput({
360
+ label,
361
+ value
362
+ });
363
+ if (!normalizedRules)
364
+ return;
365
+ assertKnownRules?.(normalizedRules);
366
+ return normalizedRules;
367
+ }, createBiomeConfig = async ({ cwd, options, sharedIgnorePatterns }) => {
368
+ const { categories, ruleMap } = await resolveBiomeSchema({ cwd }), allRulesOff = [...BIOME_RULES_OFF], normalizedRules = normalizeKnownOffRules({
369
+ assertKnownRules: rules => assertKnownBiomeRuleNames({
370
+ categoryMap: ruleMap,
371
+ label: 'biome.off',
372
+ rules
373
+ }),
374
+ label: 'biome.off',
375
+ value: options?.off
376
+ });
377
+ if (normalizedRules)
378
+ for (const key of Object.keys(normalizedRules)) {
379
+ const ruleName = stripPluginNamespace({ rule: key });
117
380
  if (!allRulesOff.includes(ruleName))
118
381
  allRulesOff.push(ruleName);
119
382
  }
120
- const ignorePatterns = [...biomeIgnorePatterns], mergedIgnorePatterns = [...(globalIgnorePatterns ?? []), ...(options?.ignorePatterns ?? [])];
383
+ const ignorePatterns = [...BIOME_IGNORE_PATTERNS], mergedIgnorePatterns = mergeIgnorePatternGroups({
384
+ groups: [
385
+ sharedIgnorePatterns,
386
+ options?.ignores === undefined
387
+ ? undefined
388
+ : normalizePathListInput({
389
+ label: 'biome.ignores',
390
+ value: options.ignores
391
+ })
392
+ ]
393
+ });
121
394
  for (const pattern of mergedIgnorePatterns) {
122
395
  const negated = normalizeBiomeIgnorePattern({ pattern });
123
396
  if (!ignorePatterns.includes(negated))
@@ -135,23 +408,35 @@ const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta
135
408
  }
136
409
  }
137
410
  ];
138
- if (options?.overrides)
139
- for (const override of options.overrides)
140
- if (override.disableLinter)
141
- overrides.push({
142
- includes: override.includes,
143
- linter: { enabled: false }
144
- });
145
- else if (override.rules)
146
- overrides.push({
147
- includes: override.includes,
148
- linter: {
149
- rules: groupByCategory({
150
- categoryMap: ruleMap,
151
- ruleNames: extractRuleNames(override.rules)
152
- })
153
- }
154
- });
411
+ for (const override of BIOME_PATTERN_RULE_OVERRIDES)
412
+ overrides.push({
413
+ includes: [...override.includes],
414
+ linter: {
415
+ rules: groupByCategory({
416
+ categoryMap: ruleMap,
417
+ ruleNames: override.rules
418
+ })
419
+ }
420
+ });
421
+ for (const override of normalizeLinterOffOverrides({
422
+ assertKnownRules: rules => assertKnownBiomeRuleNames({
423
+ categoryMap: ruleMap,
424
+ label: 'biome.overrides.off',
425
+ rules
426
+ }),
427
+ fileKey: 'includes',
428
+ label: 'biome.overrides',
429
+ value: options?.overrides
430
+ }))
431
+ overrides.push({
432
+ includes: override.files,
433
+ linter: {
434
+ rules: groupByCategory({
435
+ categoryMap: ruleMap,
436
+ ruleNames: extractRuleNames(override.rules)
437
+ })
438
+ }
439
+ });
155
440
  return {
156
441
  $schema: 'https://biomejs.dev/schemas/latest/schema.json',
157
442
  assist: { actions: { source: { organizeImports: 'off' } } },
@@ -187,8 +472,28 @@ const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta
187
472
  },
188
473
  overrides
189
474
  };
190
- }, createOxlintConfig = async ({ globalIgnorePatterns, options }) => {
191
- const base = await readJsonFile({ filePath: joinPath(pkgRoot, 'oxlintrc.json') }), mergedIgnorePatterns = [...(globalIgnorePatterns ?? []), ...(options?.ignorePatterns ?? [])];
475
+ }, createOxlintConfig = async ({ options, sharedIgnorePatterns }) => {
476
+ const base = await readRequiredJson({ path: joinPath(pkgRoot, 'oxlintrc.json') }), knownRules = new Set([
477
+ ...Object.keys(base.rules),
478
+ ...(base.overrides ?? []).flatMap(override => Object.keys(override.rules)),
479
+ ...OXLINT_PATTERN_RULE_OVERRIDES.flatMap(override => Object.keys(override.rules))
480
+ ]), mergedIgnorePatterns = mergeIgnorePatternGroups({
481
+ groups: [
482
+ sharedIgnorePatterns,
483
+ options?.ignores === undefined
484
+ ? undefined
485
+ : normalizePathListInput({
486
+ label: 'oxlint.ignores',
487
+ value: options.ignores
488
+ })
489
+ ]
490
+ });
491
+ base.overrides ??= [];
492
+ for (const override of OXLINT_PATTERN_RULE_OVERRIDES)
493
+ base.overrides.push({
494
+ files: [...override.files],
495
+ rules: { ...override.rules }
496
+ });
192
497
  if (mergedIgnorePatterns.length > 0) {
193
498
  base.ignorePatterns ??= [];
194
499
  for (const pattern of mergedIgnorePatterns)
@@ -197,55 +502,69 @@ const decoder = new TextDecoder(), pkgRoot = dirnamePath(fromFileUrl(import.meta
197
502
  }
198
503
  if (!options)
199
504
  return base;
200
- if (options.rules)
201
- for (const [key, value] of Object.entries(options.rules))
505
+ const normalizedRules = normalizeKnownOffRules({
506
+ assertKnownRules: rules => assertKnownOxlintRuleNames({
507
+ knownRules,
508
+ label: 'oxlint.off',
509
+ rules
510
+ }),
511
+ label: 'oxlint.off',
512
+ value: options.off
513
+ });
514
+ if (normalizedRules)
515
+ for (const [key, value] of Object.entries(normalizedRules))
202
516
  base.rules[key] = value;
203
- if (options.overrides) {
204
- base.overrides ??= [];
205
- for (const override of options.overrides)
206
- base.overrides.push({ files: override.files, rules: override.rules });
207
- }
517
+ for (const override of normalizeLinterOffOverrides({
518
+ assertKnownRules: rules => assertKnownOxlintRuleNames({
519
+ knownRules,
520
+ label: 'oxlint.overrides.off',
521
+ rules
522
+ }),
523
+ fileKey: 'files',
524
+ label: 'oxlint.overrides',
525
+ value: options.overrides
526
+ }))
527
+ base.overrides.push({
528
+ files: override.files,
529
+ rules: override.rules
530
+ });
208
531
  return base;
209
- }, warnToError = (rules) => {
210
- const result = {};
211
- for (const [key, value] of Object.entries(rules))
212
- if (value === undefined)
213
- result[key] = 'error';
214
- else if (value === 'warn' || value === 1)
215
- result[key] = 'error';
216
- else if (Array.isArray(value) && (value[0] === 'warn' || value[0] === 1))
217
- result[key] = ['error', ...value.slice(1)];
218
- else
219
- result[key] = value;
220
- return result;
221
- }, cacheDir = joinPath('node_modules', '.cache', 'lintmax'), sync = async (options) => {
222
- const cwd = process.cwd(), dir = joinPath(cwd, cacheDir), globalIgnorePatterns = options?.globalIgnorePatterns ?? [], mergedEslintIgnores = [...globalIgnorePatterns];
223
- if (options?.eslint?.ignores)
224
- for (const pattern of options.eslint.ignores)
225
- if (!mergedEslintIgnores.includes(pattern))
226
- mergedEslintIgnores.push(pattern);
227
- const eslintOptions = options?.eslint
228
- ? { ...options.eslint }
229
- : mergedEslintIgnores.length > 0
230
- ? { ignores: mergedEslintIgnores }
231
- : undefined;
232
- if (eslintOptions && mergedEslintIgnores.length > 0)
233
- eslintOptions.ignores = mergedEslintIgnores;
532
+ }, sync = async (options) => {
533
+ const { biome, eslint, oxlint, sharedOverrides, tailwind, topLevelIgnores, tsconfigRootDir } = validateSyncOptions({
534
+ options
535
+ }), cwd = process.cwd(), dir = joinPath(cwd, cacheDir), userIgnorePatterns = buildUserIgnorePatterns({ topLevelIgnores }), sharedIgnorePatterns = mergeIgnorePatternGroups({
536
+ groups: [DEFAULT_SHARED_IGNORE_PATTERNS, userIgnorePatterns]
537
+ }), biomeOptions = buildBiomeOptions({
538
+ biomeSource: biome,
539
+ sharedOverrides
540
+ }), oxlintOptions = buildOxlintOptions({
541
+ oxlintSource: oxlint,
542
+ sharedOverrides
543
+ }), { eslintOptions, sharedOverrideAppendIndexes } = buildEslintOptions({
544
+ eslintSource: eslint,
545
+ sharedOverrides,
546
+ tailwind,
547
+ tsconfigRootDir,
548
+ userIgnorePatterns
549
+ });
234
550
  ensureDirectory({ directory: dir });
235
551
  const biomeConfig = await createBiomeConfig({
236
552
  cwd,
237
- globalIgnorePatterns,
238
- options: options?.biome
553
+ options: biomeOptions,
554
+ sharedIgnorePatterns
239
555
  }), oxlintConfig = await createOxlintConfig({
240
- globalIgnorePatterns,
241
- options: options?.oxlint
556
+ options: oxlintOptions,
557
+ sharedIgnorePatterns
242
558
  }), eslintConfig = eslintOptions
243
- ? `import { eslint } from 'lintmax/eslint'\nexport default eslint(${JSON.stringify(eslintOptions)})\n`
244
- : "export { default } from 'lintmax/eslint'\n", runtimeConfig = { compact: options?.compact === true };
559
+ ? `import { eslint } from 'lintmax/eslint'\nconst options = ${JSON.stringify(eslintOptions)}\nfor (const index of ${JSON.stringify(sharedOverrideAppendIndexes)}) {\n const entry = options.append?.[index]\n if (entry && typeof entry === 'object') entry[Symbol.for(${JSON.stringify(SHARED_OVERRIDE_SYMBOL_KEY)})] = true\n}\nexport default eslint(options)\n`
560
+ : "export { default } from 'lintmax/eslint'\n", runtimeConfig = { compact: options?.compact !== false };
245
561
  await write(joinPath(dir, 'biome.json'), `${JSON.stringify(biomeConfig, null, 2)}\n`);
246
562
  await write(joinPath(dir, '.oxlintrc.json'), `${JSON.stringify(oxlintConfig, null, 2)}\n`);
247
- await write(joinPath(dir, 'eslint.config.mjs'), eslintConfig);
563
+ await write(joinPath(dir, 'eslint.generated.mjs'), eslintConfig);
248
564
  await write(joinPath(dir, 'lintmax.json'), `${JSON.stringify(runtimeConfig, null, 2)}\n`);
249
- }, defineConfig = (options) => options;
250
- export { cacheDir, defineConfig, sync, warnToError };
565
+ }, defineConfig = (options) => {
566
+ validateSyncOptions({ options });
567
+ return options;
568
+ };
569
+ export { defineConfig, sync };
251
570
  //# sourceMappingURL=index.js.map