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/README.md +23 -79
- package/dist/cli.js +9 -484
- package/dist/cli.js.map +1 -1
- package/dist/compact.d.ts +7 -0
- package/dist/compact.d.ts.map +1 -0
- package/dist/compact.js +101 -0
- package/dist/compact.js.map +1 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +94 -0
- package/dist/constants.js.map +1 -0
- package/dist/core.d.ts +50 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +107 -0
- package/dist/core.js.map +1 -0
- package/dist/eslint.d.ts +3 -7
- package/dist/eslint.d.ts.map +1 -1
- package/dist/eslint.js +141 -10
- package/dist/eslint.js.map +1 -1
- package/dist/index.d.ts +4 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +462 -143
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +3 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +105 -0
- package/dist/init.js.map +1 -0
- package/dist/lintmax-types.d.ts +59 -0
- package/dist/lintmax-types.d.ts.map +1 -0
- package/dist/lintmax-types.js +1 -0
- package/dist/lintmax-types.js.map +1 -0
- package/dist/normalize.d.ts +39 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +138 -0
- package/dist/normalize.js.map +1 -0
- package/dist/pipeline.d.ts +5 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +200 -0
- package/dist/pipeline.js.map +1 -0
- package/oxlintrc.json +0 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,91 +1,28 @@
|
|
|
1
|
-
import { file,
|
|
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
|
|
4
|
-
|
|
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 =
|
|
10
|
+
const trimmed = normalizeIgnorePattern({ pattern });
|
|
56
11
|
return trimmed.startsWith('**/') ? `!!${trimmed}` : `!!**/${trimmed}`;
|
|
57
|
-
},
|
|
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(
|
|
14
|
+
return fromFileUrl(import.meta.resolve('@biomejs/biome/configuration_schema.json'));
|
|
70
15
|
}
|
|
71
|
-
catch {
|
|
72
|
-
|
|
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
|
|
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(
|
|
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
|
-
},
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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 = [...
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 ({
|
|
191
|
-
const base = await
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
},
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
238
|
-
|
|
553
|
+
options: biomeOptions,
|
|
554
|
+
sharedIgnorePatterns
|
|
239
555
|
}), oxlintConfig = await createOxlintConfig({
|
|
240
|
-
|
|
241
|
-
|
|
556
|
+
options: oxlintOptions,
|
|
557
|
+
sharedIgnorePatterns
|
|
242
558
|
}), eslintConfig = eslintOptions
|
|
243
|
-
? `import { eslint } from 'lintmax/eslint'\
|
|
244
|
-
: "export { default } from 'lintmax/eslint'\n", runtimeConfig = { compact: options?.compact
|
|
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.
|
|
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) =>
|
|
250
|
-
|
|
565
|
+
}, defineConfig = (options) => {
|
|
566
|
+
validateSyncOptions({ options });
|
|
567
|
+
return options;
|
|
568
|
+
};
|
|
569
|
+
export { defineConfig, sync };
|
|
251
570
|
//# sourceMappingURL=index.js.map
|