oxlint-plugin-vize 0.434.0 → 0.439.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.
- package/README.md +2 -2
- package/dist/cli.mjs +529 -44
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +290 -247
- package/dist/{workaround-CcnA_oWy.mjs → workaround-r91ttnV_.mjs} +64 -28
- package/package.json +11 -11
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,8 @@ interface PatinaSettings {
|
|
|
10
10
|
preset?: PatinaPreset;
|
|
11
11
|
typeAware?: boolean;
|
|
12
12
|
corsaPath?: string;
|
|
13
|
+
/** Rules to evaluate together. Oxlint's own rules still control reporting. */
|
|
14
|
+
rules?: readonly string[] | Record<string, unknown>;
|
|
13
15
|
}
|
|
14
16
|
//#endregion
|
|
15
17
|
//#region src/configs.d.ts
|
|
@@ -91,11 +93,11 @@ interface VizeLintConfigOptions {
|
|
|
91
93
|
*/
|
|
92
94
|
rules?: OxlintRuleConfig;
|
|
93
95
|
/**
|
|
94
|
-
* Patina runtime settings forwarded through `settings.vize`. `preset`
|
|
95
|
-
*
|
|
96
|
+
* Patina runtime settings forwarded through `settings.vize`. `preset` and
|
|
97
|
+
* `rules` are derived from the selected presets and final rule map,
|
|
96
98
|
* so the rule map and the bridge's runtime gate can never disagree.
|
|
97
99
|
*/
|
|
98
|
-
settings?: Omit<PatinaSettings, "preset">;
|
|
100
|
+
settings?: Omit<PatinaSettings, "preset" | "rules">;
|
|
99
101
|
}
|
|
100
102
|
interface VizeLintConfigSettings extends Record<string, unknown> {
|
|
101
103
|
vize: PatinaSettings;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as validateWorkaroundAuthority, c as formatBlockLabel, d as isPatinaFile, f as isScriptLikeFile, i as resolveWorkaroundSource, l as getDiagnosticBlock, o as compareLineColumn, r as readOriginalWorkaroundSource, s as extractSfcBlocks, t as isLocationBridgeProgram } from "./workaround-r91ttnV_.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { definePlugin, defineRule } from "@oxlint/plugins";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
@@ -21,6 +21,66 @@ function hasOnlyKeys(value, keys) {
|
|
|
21
21
|
return Object.keys(value).every((key) => keys.includes(key));
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
24
|
+
//#region src/rule-options.ts
|
|
25
|
+
function getRuleOptions(ruleName, options) {
|
|
26
|
+
const firstOption = options[0];
|
|
27
|
+
switch (ruleName) {
|
|
28
|
+
case "vue/component-name-in-template-casing":
|
|
29
|
+
if (isComponentNameInTemplateCasingOption(firstOption)) return { componentNameInTemplateCasing: firstOption };
|
|
30
|
+
break;
|
|
31
|
+
case "script/custom-event-name-casing":
|
|
32
|
+
if (isCustomEventNameCasingOption(firstOption)) return { customEventNameCasing: firstOption };
|
|
33
|
+
break;
|
|
34
|
+
case "vue/no-mutating-props":
|
|
35
|
+
if (isNoMutatingPropsOption(firstOption)) return { noMutatingProps: firstOption };
|
|
36
|
+
break;
|
|
37
|
+
case "vue/sfc-element-order":
|
|
38
|
+
if (isSfcElementOrderOption(firstOption)) return { sfcElementOrder: firstOption };
|
|
39
|
+
break;
|
|
40
|
+
case "vue/html-self-closing":
|
|
41
|
+
if (isHtmlSelfClosingOption(firstOption)) return { htmlSelfClosing: firstOption };
|
|
42
|
+
break;
|
|
43
|
+
case "vue/v-on-event-hyphenation":
|
|
44
|
+
if (isHyphenationStyle(firstOption)) return { vOnEventHyphenation: firstOption };
|
|
45
|
+
break;
|
|
46
|
+
case "vue/attribute-hyphenation":
|
|
47
|
+
if (isHyphenationStyle(firstOption)) return { attributeHyphenation: firstOption };
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isComponentNameInTemplateCasingOption(value) {
|
|
52
|
+
return value === "PascalCase" || value === "kebab-case";
|
|
53
|
+
}
|
|
54
|
+
function isCustomEventNameCasingOption(value) {
|
|
55
|
+
return value === "camelCase" || value === "kebab-case";
|
|
56
|
+
}
|
|
57
|
+
function isNoMutatingPropsOption(value) {
|
|
58
|
+
if (!isRecord$1(value)) return false;
|
|
59
|
+
return hasOnlyKeys(value, ["shallowOnly"]) && optionalBooleanField(value.shallowOnly);
|
|
60
|
+
}
|
|
61
|
+
function isSfcElementOrderOption(value) {
|
|
62
|
+
if (!isRecord$1(value)) return false;
|
|
63
|
+
return hasOnlyKeys(value, ["order"]) && (value.order === void 0 || Array.isArray(value.order) && value.order.every(isSfcElementOrderGroup));
|
|
64
|
+
}
|
|
65
|
+
function isSfcElementOrderGroup(value) {
|
|
66
|
+
return typeof value === "string" || Array.isArray(value) && value.every(isString);
|
|
67
|
+
}
|
|
68
|
+
function isHtmlSelfClosingOption(value) {
|
|
69
|
+
if (!isRecord$1(value)) return false;
|
|
70
|
+
return hasOnlyKeys(value, [
|
|
71
|
+
"html",
|
|
72
|
+
"svg",
|
|
73
|
+
"math"
|
|
74
|
+
]) && optionField(value.svg) && optionField(value.math) && (value.html === void 0 || isRecord$1(value.html) && hasOnlyKeys(value.html, [
|
|
75
|
+
"void",
|
|
76
|
+
"normal",
|
|
77
|
+
"component"
|
|
78
|
+
]) && optionField(value.html.void) && optionField(value.html.normal) && optionField(value.html.component));
|
|
79
|
+
}
|
|
80
|
+
function isHyphenationStyle(value) {
|
|
81
|
+
return value === "always" || value === "never";
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
24
84
|
//#region src/native.ts
|
|
25
85
|
const require = createRequire(import.meta.url);
|
|
26
86
|
const FALLBACK_BINDING_PACKAGE = "@vizejs/native";
|
|
@@ -111,88 +171,58 @@ if (import.meta.vitest) {
|
|
|
111
171
|
});
|
|
112
172
|
}
|
|
113
173
|
//#endregion
|
|
114
|
-
//#region src/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
174
|
+
//#region src/rule-selection.ts
|
|
175
|
+
const selectionCache = /* @__PURE__ */ new WeakMap();
|
|
176
|
+
/** A selection is a batching hint, never a second reporting/severity gate. */
|
|
177
|
+
function parseRuleSelection(rules) {
|
|
178
|
+
if (rules === void 0) return;
|
|
179
|
+
const revision = JSON.stringify(rules);
|
|
180
|
+
const cached = selectionCache.get(rules);
|
|
181
|
+
if (cached?.revision === revision) return cached.selection;
|
|
182
|
+
const optionsByRule = /* @__PURE__ */ new Map();
|
|
183
|
+
if (Array.isArray(rules)) {
|
|
184
|
+
for (const name of rules) if (typeof name === "string" && name.length > 0) optionsByRule.set(normalizeRuleName(name), void 0);
|
|
185
|
+
} else if (typeof rules === "object" && rules !== null) {
|
|
186
|
+
const usesPluginIds = Object.keys(rules).some((id) => id.startsWith("vize/"));
|
|
187
|
+
for (const [id, entry] of Object.entries(rules)) {
|
|
188
|
+
if (!id.startsWith("vize/") && (usesPluginIds || !/^(vue|script|style|css|type|nuxt|ecosystem)\//u.test(id))) continue;
|
|
189
|
+
const severity = Array.isArray(entry) ? entry[0] : entry;
|
|
190
|
+
if (severity !== "error" && severity !== "warn" && severity !== 1 && severity !== 2) continue;
|
|
191
|
+
const name = normalizeRuleName(id);
|
|
192
|
+
const options = Array.isArray(entry) ? getRuleOptions(name, entry.slice(1)) : void 0;
|
|
193
|
+
optionsByRule.set(name, options);
|
|
194
|
+
}
|
|
195
|
+
} else return;
|
|
196
|
+
const selection = createRuleSelection(optionsByRule);
|
|
197
|
+
selectionCache.set(rules, {
|
|
198
|
+
revision,
|
|
199
|
+
selection
|
|
125
200
|
});
|
|
201
|
+
return selection;
|
|
126
202
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
//#region src/script-map.ts
|
|
132
|
-
function createSingleScriptMap(source, extractedScript, sfcBlocks = extractSfcBlocks(source)) {
|
|
133
|
-
if (!extractedScript) return null;
|
|
134
|
-
const blocks = sfcBlocks.filter((block) => block.kind === "script" || block.kind === "script-setup");
|
|
135
|
-
let match = null;
|
|
136
|
-
for (const block of blocks) {
|
|
137
|
-
const skipped = countSkippedPrefix(block.content, extractedScript);
|
|
138
|
-
if (skipped === null) continue;
|
|
139
|
-
if (match !== null) return null;
|
|
140
|
-
match = {
|
|
141
|
-
block,
|
|
142
|
-
scriptStart: advancePosition(block.contentStart, block.content.slice(0, skipped))
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
return match;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Oxlint trims the leading newline (and any indentation on that line) from a
|
|
149
|
-
* `.vue` script block before handing the program to JS plugins, so the
|
|
150
|
-
* extracted text starts partway into `block.content`. Returns how many
|
|
151
|
-
* characters were dropped, or `null` when the extracted text is not this
|
|
152
|
-
* block's body.
|
|
153
|
-
*/
|
|
154
|
-
function countSkippedPrefix(content, extractedScript) {
|
|
155
|
-
const skipped = content.length - extractedScript.length;
|
|
156
|
-
if (skipped < 0 || !content.endsWith(extractedScript)) return null;
|
|
157
|
-
return /^\s*$/u.test(content.slice(0, skipped)) ? skipped : null;
|
|
158
|
-
}
|
|
159
|
-
function advancePosition(from, text) {
|
|
160
|
-
let { line, column } = from;
|
|
161
|
-
for (const character of text) {
|
|
162
|
-
if (character === "\n") {
|
|
163
|
-
line += 1;
|
|
164
|
-
column = 1;
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (character !== "\r") column += 1;
|
|
168
|
-
}
|
|
203
|
+
function createRuleSelection(optionsByRule) {
|
|
204
|
+
const names = [...optionsByRule.keys()].sort();
|
|
205
|
+
const options = {};
|
|
206
|
+
for (const name of names) Object.assign(options, optionsByRule.get(name));
|
|
169
207
|
return {
|
|
170
|
-
|
|
171
|
-
|
|
208
|
+
names,
|
|
209
|
+
options,
|
|
210
|
+
optionsByRule,
|
|
211
|
+
cacheKey: JSON.stringify([names, ruleOptionsKey(options)])
|
|
172
212
|
};
|
|
173
213
|
}
|
|
174
|
-
function
|
|
175
|
-
|
|
176
|
-
const { block, scriptStart } = scriptMap;
|
|
177
|
-
if (compareLineColumn(diagnostic.location.start, scriptStart) < 0 || compareLineColumn(diagnostic.location.end, block.contentEnd) > 0) return null;
|
|
178
|
-
return {
|
|
179
|
-
start: toScriptPosition(diagnostic.location.start, scriptStart),
|
|
180
|
-
end: toScriptPosition(diagnostic.location.end, scriptStart)
|
|
181
|
-
};
|
|
214
|
+
function normalizeRuleName(name) {
|
|
215
|
+
return name.startsWith("vize/") ? name.slice(5) : name;
|
|
182
216
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
line: position.line - scriptStart.line + 1,
|
|
194
|
-
column: position.column - 1
|
|
195
|
-
};
|
|
217
|
+
function ruleOptionsKey(options) {
|
|
218
|
+
if (options === void 0) return "{}";
|
|
219
|
+
return JSON.stringify(options ?? {}, (_key, value) => {
|
|
220
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
221
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function selectVizeRuleConfig(rules) {
|
|
225
|
+
return Object.fromEntries(Object.entries(rules).filter(([id]) => id.startsWith("vize/")));
|
|
196
226
|
}
|
|
197
227
|
//#endregion
|
|
198
228
|
//#region src/settings.ts
|
|
@@ -232,6 +262,7 @@ function parseVizeSettings(vize) {
|
|
|
232
262
|
const typeAware = vizeRecord.typeAware;
|
|
233
263
|
const corsaPath = vizeRecord.corsaPath;
|
|
234
264
|
const resolved = {};
|
|
265
|
+
if (Array.isArray(vizeRecord.rules) || typeof vizeRecord.rules === "object" && vizeRecord.rules !== null) resolved.rules = vizeRecord.rules;
|
|
235
266
|
if (typeof locale === "string") resolved.locale = locale;
|
|
236
267
|
if (typeof preset === "string") {
|
|
237
268
|
const normalizedPreset = normalizePreset(preset);
|
|
@@ -247,20 +278,22 @@ function parseVizeSettings(vize) {
|
|
|
247
278
|
return resolved;
|
|
248
279
|
}
|
|
249
280
|
function getActivePreset(settings) {
|
|
250
|
-
return settings.preset ?? "
|
|
281
|
+
return settings.preset ?? "incremental";
|
|
251
282
|
}
|
|
252
283
|
function isIncrementalPreset(settings) {
|
|
253
284
|
return getActivePreset(settings) === "incremental";
|
|
254
285
|
}
|
|
255
286
|
function getCacheKey(filename, settings) {
|
|
256
|
-
|
|
287
|
+
const selection = parseRuleSelection(settings.rules);
|
|
288
|
+
return JSON.stringify([
|
|
257
289
|
filename,
|
|
258
290
|
settings.locale ?? "",
|
|
259
291
|
settings.helpLevel ?? "",
|
|
260
292
|
getActivePreset(settings),
|
|
261
293
|
settings.typeAware ? "type-aware" : "",
|
|
262
|
-
settings.corsaPath ?? ""
|
|
263
|
-
|
|
294
|
+
settings.corsaPath ?? "",
|
|
295
|
+
selection?.cacheKey
|
|
296
|
+
]);
|
|
264
297
|
}
|
|
265
298
|
function isTypeAwareRuleName(ruleName) {
|
|
266
299
|
return ruleName.startsWith(TYPE_AWARE_RULE_PREFIX$1);
|
|
@@ -322,6 +355,90 @@ if (import.meta.vitest) {
|
|
|
322
355
|
});
|
|
323
356
|
}
|
|
324
357
|
//#endregion
|
|
358
|
+
//#region src/binding.ts
|
|
359
|
+
function lintPatina(source, filename, settings, enabledRules, ruleOptions) {
|
|
360
|
+
return loadBinding().lintPatinaSfc(source, {
|
|
361
|
+
filename,
|
|
362
|
+
locale: settings.locale,
|
|
363
|
+
helpLevel: settings.helpLevel,
|
|
364
|
+
preset: getActivePreset(settings),
|
|
365
|
+
enabledRules: enabledRules ? [...enabledRules] : void 0,
|
|
366
|
+
typeAware: settings.typeAware,
|
|
367
|
+
corsaPath: settings.corsaPath,
|
|
368
|
+
...ruleOptions
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function getPatinaRules() {
|
|
372
|
+
return loadBinding().getPatinaRules();
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/script-map.ts
|
|
376
|
+
function createSingleScriptMap(source, extractedScript, sfcBlocks = extractSfcBlocks(source)) {
|
|
377
|
+
if (!extractedScript) return null;
|
|
378
|
+
const blocks = sfcBlocks.filter((block) => block.kind === "script" || block.kind === "script-setup");
|
|
379
|
+
let match = null;
|
|
380
|
+
for (const block of blocks) {
|
|
381
|
+
const skipped = countSkippedPrefix(block.content, extractedScript);
|
|
382
|
+
if (skipped === null) continue;
|
|
383
|
+
if (match !== null) return null;
|
|
384
|
+
match = {
|
|
385
|
+
block,
|
|
386
|
+
scriptStart: advancePosition(block.contentStart, block.content.slice(0, skipped))
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return match;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Oxlint trims the leading newline (and any indentation on that line) from a
|
|
393
|
+
* `.vue` script block before handing the program to JS plugins, so the
|
|
394
|
+
* extracted text starts partway into `block.content`. Returns how many
|
|
395
|
+
* characters were dropped, or `null` when the extracted text is not this
|
|
396
|
+
* block's body.
|
|
397
|
+
*/
|
|
398
|
+
function countSkippedPrefix(content, extractedScript) {
|
|
399
|
+
const skipped = content.length - extractedScript.length;
|
|
400
|
+
if (skipped < 0 || !content.endsWith(extractedScript)) return null;
|
|
401
|
+
return /^\s*$/u.test(content.slice(0, skipped)) ? skipped : null;
|
|
402
|
+
}
|
|
403
|
+
function advancePosition(from, text) {
|
|
404
|
+
let { line, column } = from;
|
|
405
|
+
for (const character of text) {
|
|
406
|
+
if (character === "\n") {
|
|
407
|
+
line += 1;
|
|
408
|
+
column = 1;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (character !== "\r") column += 1;
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
line,
|
|
415
|
+
column
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function mapToScriptLoc(diagnostic, scriptMap) {
|
|
419
|
+
if (!scriptMap) return null;
|
|
420
|
+
const { block, scriptStart } = scriptMap;
|
|
421
|
+
if (compareLineColumn(diagnostic.location.start, scriptStart) < 0 || compareLineColumn(diagnostic.location.end, block.contentEnd) > 0) return null;
|
|
422
|
+
return {
|
|
423
|
+
start: toScriptPosition(diagnostic.location.start, scriptStart),
|
|
424
|
+
end: toScriptPosition(diagnostic.location.end, scriptStart)
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Patina reports 1-based SFC line/column; Oxlint expects 1-based lines and
|
|
429
|
+
* 0-based columns relative to the extracted program.
|
|
430
|
+
*/
|
|
431
|
+
function toScriptPosition(position, scriptStart) {
|
|
432
|
+
if (position.line === scriptStart.line) return {
|
|
433
|
+
line: 1,
|
|
434
|
+
column: position.column - scriptStart.column
|
|
435
|
+
};
|
|
436
|
+
return {
|
|
437
|
+
line: position.line - scriptStart.line + 1,
|
|
438
|
+
column: position.column - 1
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
325
442
|
//#region src/file-state.ts
|
|
326
443
|
const FILE_STATE_CACHE_CAPACITY = 128;
|
|
327
444
|
const fileStateCache = /* @__PURE__ */ new Map();
|
|
@@ -331,10 +448,10 @@ let fileStateCacheClock = 0;
|
|
|
331
448
|
function getFileState(context) {
|
|
332
449
|
const settings = getVizeSettings(context);
|
|
333
450
|
const physicalSource = fs.readFileSync(context.physicalFilename, "utf8");
|
|
334
|
-
const
|
|
335
|
-
const cacheKey = getCacheKey(resolvedSource.filename, settings);
|
|
451
|
+
const cacheKey = getCacheKey(context.physicalFilename, settings);
|
|
336
452
|
const cached = fileStateCache.get(cacheKey);
|
|
337
453
|
if (cached && cached.state.revision.physicalSource === physicalSource) {
|
|
454
|
+
validateWorkaroundAuthority(cached.state, context.physicalFilename);
|
|
338
455
|
if (cached.state.extractedScript !== context.sourceCode.text) {
|
|
339
456
|
cached.state.extractedScript = context.sourceCode.text;
|
|
340
457
|
cached.state.scriptMap = void 0;
|
|
@@ -342,6 +459,7 @@ function getFileState(context) {
|
|
|
342
459
|
cached.lastUsed = nextFileStateCacheClock();
|
|
343
460
|
return cached.state;
|
|
344
461
|
}
|
|
462
|
+
const resolvedSource = resolveWorkaroundSource(physicalSource, context.physicalFilename, readOriginalWorkaroundSource);
|
|
345
463
|
const state = {
|
|
346
464
|
revision: { physicalSource },
|
|
347
465
|
filename: resolvedSource.filename,
|
|
@@ -355,7 +473,9 @@ function getFileState(context) {
|
|
|
355
473
|
allDiagnosticsIncludesTypeAware: false,
|
|
356
474
|
partialDiagnosticsByRule: /* @__PURE__ */ new Map(),
|
|
357
475
|
requestedRules: /* @__PURE__ */ new Set(),
|
|
358
|
-
reportedTypeAwareRuntimeDiagnostic: false
|
|
476
|
+
reportedTypeAwareRuntimeDiagnostic: false,
|
|
477
|
+
ruleSelection: parseRuleSelection(settings.rules),
|
|
478
|
+
selectedDiagnosticsByRule: null
|
|
359
479
|
};
|
|
360
480
|
fileStateCache.set(cacheKey, {
|
|
361
481
|
state,
|
|
@@ -364,91 +484,6 @@ function getFileState(context) {
|
|
|
364
484
|
evictLeastRecentlyUsedFileState();
|
|
365
485
|
return state;
|
|
366
486
|
}
|
|
367
|
-
function getDiagnosticsForRule(context, state, ruleName, ruleOptions) {
|
|
368
|
-
if (ruleOptions != null) return getConfiguredRuleDiagnostics(context, state, ruleName, ruleOptions);
|
|
369
|
-
if (state.allDiagnosticsByRule) {
|
|
370
|
-
if (isTypeAwareRuleName(ruleName) && !state.allDiagnosticsIncludesTypeAware) {
|
|
371
|
-
const settings = getVizeSettings(context);
|
|
372
|
-
state.allDiagnosticsByRule = indexDiagnosticsByRule(lintPatina(state.source, state.filename, {
|
|
373
|
-
...settings,
|
|
374
|
-
typeAware: true
|
|
375
|
-
}).diagnostics);
|
|
376
|
-
state.allDiagnosticsIncludesTypeAware = true;
|
|
377
|
-
}
|
|
378
|
-
return diagnosticsForRule(state, state.allDiagnosticsByRule, ruleName);
|
|
379
|
-
}
|
|
380
|
-
const cached = state.partialDiagnosticsByRule.get(ruleName);
|
|
381
|
-
if (cached) return cached;
|
|
382
|
-
const settings = getVizeSettings(context);
|
|
383
|
-
const ruleSettings = isTypeAwareRuleName(ruleName) ? {
|
|
384
|
-
...settings,
|
|
385
|
-
typeAware: true
|
|
386
|
-
} : settings;
|
|
387
|
-
if (isIncrementalPreset(settings)) {
|
|
388
|
-
const diagnostics = lintPatina(state.source, state.filename, ruleSettings, [ruleName]).diagnostics;
|
|
389
|
-
const ruleDiagnostics = diagnosticsForRule(state, indexDiagnosticsByRule(diagnostics), ruleName);
|
|
390
|
-
state.partialDiagnosticsByRule.set(ruleName, ruleDiagnostics);
|
|
391
|
-
return ruleDiagnostics;
|
|
392
|
-
}
|
|
393
|
-
if (state.requestedRules.size === 0) {
|
|
394
|
-
state.requestedRules.add(ruleName);
|
|
395
|
-
const diagnostics = lintPatina(state.source, state.filename, ruleSettings, [ruleName]).diagnostics;
|
|
396
|
-
const ruleDiagnostics = diagnosticsForRule(state, indexDiagnosticsByRule(diagnostics), ruleName);
|
|
397
|
-
state.partialDiagnosticsByRule.set(ruleName, ruleDiagnostics);
|
|
398
|
-
return ruleDiagnostics;
|
|
399
|
-
}
|
|
400
|
-
state.requestedRules.add(ruleName);
|
|
401
|
-
const fullPassTypeAware = settings.typeAware === true || Array.from(state.requestedRules).some(isTypeAwareRuleName);
|
|
402
|
-
const allDiagnostics = lintPatina(state.source, state.filename, {
|
|
403
|
-
...settings,
|
|
404
|
-
typeAware: fullPassTypeAware
|
|
405
|
-
}).diagnostics;
|
|
406
|
-
state.allDiagnosticsByRule = indexDiagnosticsByRule(allDiagnostics);
|
|
407
|
-
state.allDiagnosticsIncludesTypeAware = fullPassTypeAware;
|
|
408
|
-
state.partialDiagnosticsByRule.clear();
|
|
409
|
-
return diagnosticsForRule(state, state.allDiagnosticsByRule, ruleName);
|
|
410
|
-
}
|
|
411
|
-
function getConfiguredRuleDiagnostics(context, state, ruleName, ruleOptions) {
|
|
412
|
-
const cacheKey = configuredRuleCacheKey(ruleName, ruleOptions);
|
|
413
|
-
const cached = state.partialDiagnosticsByRule.get(cacheKey);
|
|
414
|
-
if (cached) return cached;
|
|
415
|
-
const settings = getVizeSettings(context);
|
|
416
|
-
const ruleSettings = isTypeAwareRuleName(ruleName) ? {
|
|
417
|
-
...settings,
|
|
418
|
-
typeAware: true
|
|
419
|
-
} : settings;
|
|
420
|
-
const diagnostics = lintPatina(state.source, state.filename, ruleSettings, [ruleName], ruleOptions).diagnostics;
|
|
421
|
-
const ruleDiagnostics = diagnosticsForRule(state, indexDiagnosticsByRule(diagnostics), ruleName);
|
|
422
|
-
state.partialDiagnosticsByRule.set(cacheKey, ruleDiagnostics);
|
|
423
|
-
return ruleDiagnostics;
|
|
424
|
-
}
|
|
425
|
-
function configuredRuleCacheKey(ruleName, ruleOptions) {
|
|
426
|
-
return [
|
|
427
|
-
ruleName,
|
|
428
|
-
ruleOptions.componentNameInTemplateCasing ?? "",
|
|
429
|
-
ruleOptions.customEventNameCasing ?? "",
|
|
430
|
-
ruleOptions.noMutatingProps == null ? "" : noMutatingPropsCacheKey(ruleOptions.noMutatingProps),
|
|
431
|
-
ruleOptions.sfcElementOrder == null ? "" : sfcElementOrderCacheKey(ruleOptions.sfcElementOrder),
|
|
432
|
-
ruleOptions.htmlSelfClosing == null ? "" : htmlSelfClosingCacheKey(ruleOptions.htmlSelfClosing),
|
|
433
|
-
ruleOptions.vOnEventHyphenation ?? "",
|
|
434
|
-
ruleOptions.attributeHyphenation ?? ""
|
|
435
|
-
].join("\0");
|
|
436
|
-
}
|
|
437
|
-
function noMutatingPropsCacheKey(options) {
|
|
438
|
-
return options.shallowOnly === true ? "1" : "0";
|
|
439
|
-
}
|
|
440
|
-
function sfcElementOrderCacheKey(options) {
|
|
441
|
-
return (options.order ?? []).map((group) => Array.isArray(group) ? group.join("") : group).join("");
|
|
442
|
-
}
|
|
443
|
-
function htmlSelfClosingCacheKey(options) {
|
|
444
|
-
return [
|
|
445
|
-
options.html?.void ?? "",
|
|
446
|
-
options.html?.normal ?? "",
|
|
447
|
-
options.html?.component ?? "",
|
|
448
|
-
options.svg ?? "",
|
|
449
|
-
options.math ?? ""
|
|
450
|
-
].join("/");
|
|
451
|
-
}
|
|
452
487
|
function getScriptMap(state) {
|
|
453
488
|
if (state.scriptMap !== void 0) return state.scriptMap;
|
|
454
489
|
state.scriptMap = createSingleScriptMap(state.source, state.extractedScript, getSfcBlocks(state));
|
|
@@ -515,6 +550,63 @@ function diagnosticsForRule(state, diagnosticsByRule, ruleName) {
|
|
|
515
550
|
return runtimeDiagnostics;
|
|
516
551
|
}
|
|
517
552
|
//#endregion
|
|
553
|
+
//#region src/active-rule-collection.ts
|
|
554
|
+
const programs = /* @__PURE__ */ new WeakMap();
|
|
555
|
+
const results = /* @__PURE__ */ new WeakMap();
|
|
556
|
+
function collectActiveRule(context, program, ruleName, options) {
|
|
557
|
+
const state = getFileState(context);
|
|
558
|
+
let collection = programs.get(program);
|
|
559
|
+
if (!collection || collection.selection) {
|
|
560
|
+
collection = {
|
|
561
|
+
state,
|
|
562
|
+
optionsByRule: /* @__PURE__ */ new Map()
|
|
563
|
+
};
|
|
564
|
+
programs.set(program, collection);
|
|
565
|
+
}
|
|
566
|
+
if (collection.state !== state) {
|
|
567
|
+
programs.delete(program);
|
|
568
|
+
throw new Error("Vize source or configuration changed during rule collection");
|
|
569
|
+
}
|
|
570
|
+
collection.optionsByRule.set(ruleName, options === void 0 ? void 0 : structuredClone(options));
|
|
571
|
+
}
|
|
572
|
+
function getActiveRuleDiagnostics(context, program, ruleName) {
|
|
573
|
+
const collection = programs.get(program);
|
|
574
|
+
if (!collection?.optionsByRule.has(ruleName)) return void 0;
|
|
575
|
+
if (getFileState(context) !== collection.state) {
|
|
576
|
+
programs.delete(program);
|
|
577
|
+
throw new Error("Vize source or configuration changed before rule execution");
|
|
578
|
+
}
|
|
579
|
+
if (!collection.selection) {
|
|
580
|
+
collection.selection = createRuleSelection(collection.optionsByRule);
|
|
581
|
+
collection.remaining = new Set(collection.selection.names);
|
|
582
|
+
}
|
|
583
|
+
const { state, selection } = collection;
|
|
584
|
+
let cached = results.get(state);
|
|
585
|
+
if (cached?.key !== selection.cacheKey) {
|
|
586
|
+
const settings = getVizeSettings(context);
|
|
587
|
+
const diagnostics = indexDiagnosticsByRule(lintPatina(state.source, state.filename, {
|
|
588
|
+
...settings,
|
|
589
|
+
typeAware: settings.typeAware === true || selection.names.some(isTypeAwareRuleName)
|
|
590
|
+
}, selection.names, selection.options).diagnostics);
|
|
591
|
+
if (cached) {
|
|
592
|
+
state.reportedDiagnostics.clear();
|
|
593
|
+
state.reportedTypeAwareRuntimeDiagnostic = false;
|
|
594
|
+
}
|
|
595
|
+
cached = {
|
|
596
|
+
key: selection.cacheKey,
|
|
597
|
+
diagnostics
|
|
598
|
+
};
|
|
599
|
+
results.set(state, cached);
|
|
600
|
+
}
|
|
601
|
+
const diagnostics = diagnosticsForRule(state, cached.diagnostics, ruleName);
|
|
602
|
+
collection.remaining.delete(ruleName);
|
|
603
|
+
if (collection.remaining.size === 0) programs.delete(program);
|
|
604
|
+
return {
|
|
605
|
+
state,
|
|
606
|
+
diagnostics
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
//#endregion
|
|
518
610
|
//#region src/format.ts
|
|
519
611
|
const helpTextCache = /* @__PURE__ */ new Map();
|
|
520
612
|
function formatPatinaMessage(diagnostic, options) {
|
|
@@ -626,22 +718,28 @@ function createPatinaRule(ruleMeta) {
|
|
|
626
718
|
schema: ruleOptionsSchema(ruleMeta.name)
|
|
627
719
|
},
|
|
628
720
|
createOnce(context) {
|
|
629
|
-
return {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
721
|
+
return {
|
|
722
|
+
Program(program) {
|
|
723
|
+
if (!isPatinaFile(context.filename)) return;
|
|
724
|
+
const settings = getVizeSettings(context);
|
|
725
|
+
const activePreset = getActivePreset(settings);
|
|
726
|
+
if (ruleMeta.presets.length > 0 && !isIncrementalPreset(settings) && !ruleMeta.presets.includes(activePreset)) return;
|
|
727
|
+
collectActiveRule(context, program, ruleMeta.name, getRuleOptions(ruleMeta.name, contextOptions(context)));
|
|
728
|
+
},
|
|
729
|
+
"Program:exit"(program) {
|
|
730
|
+
const collected = getActiveRuleDiagnostics(context, program, ruleMeta.name);
|
|
731
|
+
if (!collected) return;
|
|
732
|
+
const { state } = collected;
|
|
733
|
+
const helpLevel = getVizeSettings(context).helpLevel ?? "full";
|
|
734
|
+
const scriptMap = getScriptMap(state);
|
|
735
|
+
const diagnostics = collected.diagnostics.filter((diagnostic) => shouldReportForCurrentProgram(diagnostic, state, scriptMap));
|
|
736
|
+
if (diagnostics.length === 0) return;
|
|
737
|
+
for (const diagnostic of diagnostics) {
|
|
738
|
+
if (!markDiagnosticAsReported(state, diagnostic)) continue;
|
|
739
|
+
context.report(createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel, program));
|
|
740
|
+
}
|
|
643
741
|
}
|
|
644
|
-
}
|
|
742
|
+
};
|
|
645
743
|
}
|
|
646
744
|
});
|
|
647
745
|
}
|
|
@@ -706,64 +804,6 @@ function contextOptions(context) {
|
|
|
706
804
|
const options = context.options;
|
|
707
805
|
return Array.isArray(options) ? options : [];
|
|
708
806
|
}
|
|
709
|
-
function getRuleOptions(ruleName, options) {
|
|
710
|
-
const firstOption = options[0];
|
|
711
|
-
switch (ruleName) {
|
|
712
|
-
case "vue/component-name-in-template-casing":
|
|
713
|
-
if (isComponentNameInTemplateCasingOption(firstOption)) return { componentNameInTemplateCasing: firstOption };
|
|
714
|
-
break;
|
|
715
|
-
case "script/custom-event-name-casing":
|
|
716
|
-
if (isCustomEventNameCasingOption(firstOption)) return { customEventNameCasing: firstOption };
|
|
717
|
-
break;
|
|
718
|
-
case "vue/no-mutating-props":
|
|
719
|
-
if (isNoMutatingPropsOption(firstOption)) return { noMutatingProps: firstOption };
|
|
720
|
-
break;
|
|
721
|
-
case "vue/sfc-element-order":
|
|
722
|
-
if (isSfcElementOrderOption(firstOption)) return { sfcElementOrder: firstOption };
|
|
723
|
-
break;
|
|
724
|
-
case "vue/html-self-closing":
|
|
725
|
-
if (isHtmlSelfClosingOption(firstOption)) return { htmlSelfClosing: firstOption };
|
|
726
|
-
break;
|
|
727
|
-
case "vue/v-on-event-hyphenation":
|
|
728
|
-
if (isHyphenationStyle(firstOption)) return { vOnEventHyphenation: firstOption };
|
|
729
|
-
break;
|
|
730
|
-
case "vue/attribute-hyphenation":
|
|
731
|
-
if (isHyphenationStyle(firstOption)) return { attributeHyphenation: firstOption };
|
|
732
|
-
break;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
function isComponentNameInTemplateCasingOption(value) {
|
|
736
|
-
return value === "PascalCase" || value === "kebab-case";
|
|
737
|
-
}
|
|
738
|
-
function isCustomEventNameCasingOption(value) {
|
|
739
|
-
return value === "camelCase" || value === "kebab-case";
|
|
740
|
-
}
|
|
741
|
-
function isNoMutatingPropsOption(value) {
|
|
742
|
-
if (!isRecord$1(value)) return false;
|
|
743
|
-
return hasOnlyKeys(value, ["shallowOnly"]) && optionalBooleanField(value.shallowOnly);
|
|
744
|
-
}
|
|
745
|
-
function isSfcElementOrderOption(value) {
|
|
746
|
-
if (!isRecord$1(value)) return false;
|
|
747
|
-
return hasOnlyKeys(value, ["order"]) && (value.order === void 0 || Array.isArray(value.order) && value.order.every(isSfcElementOrderGroup));
|
|
748
|
-
}
|
|
749
|
-
function isSfcElementOrderGroup(value) {
|
|
750
|
-
return typeof value === "string" || Array.isArray(value) && value.every(isString);
|
|
751
|
-
}
|
|
752
|
-
function isHtmlSelfClosingOption(value) {
|
|
753
|
-
if (!isRecord$1(value)) return false;
|
|
754
|
-
return hasOnlyKeys(value, [
|
|
755
|
-
"html",
|
|
756
|
-
"svg",
|
|
757
|
-
"math"
|
|
758
|
-
]) && optionField(value.svg) && optionField(value.math) && (value.html === void 0 || isRecord$1(value.html) && hasOnlyKeys(value.html, [
|
|
759
|
-
"void",
|
|
760
|
-
"normal",
|
|
761
|
-
"component"
|
|
762
|
-
]) && optionField(value.html.void) && optionField(value.html.normal) && optionField(value.html.component));
|
|
763
|
-
}
|
|
764
|
-
function isHyphenationStyle(value) {
|
|
765
|
-
return value === "always" || value === "never";
|
|
766
|
-
}
|
|
767
807
|
var plugin_default = definePlugin({
|
|
768
808
|
meta: { name: "vize" },
|
|
769
809
|
rules: Object.fromEntries(getPatinaRules().map((ruleMeta) => [ruleMeta.name, createPatinaRule(ruleMeta)]))
|
|
@@ -955,16 +995,18 @@ function buildVizeLintConfig(options, buildOptions) {
|
|
|
955
995
|
const runtimePreset = buildOptions.forceIncrementalRuntime ? "incremental" : toRuntimePreset(presets);
|
|
956
996
|
const extraRules = options.rules ?? {};
|
|
957
997
|
assertUsableVizeRules(extraRules, presets, runtimePreset);
|
|
998
|
+
const rules = {
|
|
999
|
+
...createPresetRules(presets, options.includeTypeAware),
|
|
1000
|
+
...extraRules
|
|
1001
|
+
};
|
|
958
1002
|
return {
|
|
959
1003
|
jsPlugins: [VIZE_JS_PLUGIN_SPECIFIER],
|
|
960
1004
|
plugins: [...new Set(["vue", ...options.plugins ?? []])],
|
|
961
|
-
rules
|
|
962
|
-
...createPresetRules(presets, options.includeTypeAware),
|
|
963
|
-
...extraRules
|
|
964
|
-
},
|
|
1005
|
+
rules,
|
|
965
1006
|
settings: { vize: {
|
|
966
1007
|
...options.settings,
|
|
967
|
-
preset: runtimePreset
|
|
1008
|
+
preset: runtimePreset,
|
|
1009
|
+
rules: selectVizeRuleConfig(rules)
|
|
968
1010
|
} }
|
|
969
1011
|
};
|
|
970
1012
|
}
|
|
@@ -1083,6 +1125,7 @@ function defineVizeLintConfig(...entries) {
|
|
|
1083
1125
|
if (uniquePresets.length === 1) config.settings.vize.preset = uniquePresets[0];
|
|
1084
1126
|
else if (uniquePresets.length > 1) config.settings.vize.preset = "incremental";
|
|
1085
1127
|
}
|
|
1128
|
+
if (config.settings.vize.rules !== void 0 || Object.keys(config.rules).some((id) => id.startsWith("vize/"))) config.settings.vize.rules = selectVizeRuleConfig(config.rules);
|
|
1086
1129
|
return config;
|
|
1087
1130
|
}
|
|
1088
1131
|
function mergeFlatConfigEntry(target, runtimePresets, entry) {
|