oxlint-plugin-vize 0.443.0 → 0.457.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/dist/index.mjs CHANGED
@@ -1,855 +1,4 @@
1
- import { a as validateWorkaroundAuthority, c as formatBlockLabel, d as isPatinaFile, f as isScriptLikeFile, i as resolveWorkaroundSource, l as getDiagnosticBlock, m as loadBinding, o as compareLineColumn, r as readOriginalWorkaroundSource, s as extractSfcBlocks, t as isLocationBridgeProgram } from "./workaround-CiQUFJw0.mjs";
2
- import { definePlugin, defineRule } from "@oxlint/plugins";
3
- import fs from "node:fs";
4
- //#region src/plugin-option-guards.ts
5
- function optionField(value) {
6
- return value === void 0 || value === "always" || value === "never" || value === "any";
7
- }
8
- function optionalBooleanField(value) {
9
- return value === void 0 || typeof value === "boolean";
10
- }
11
- function isString(value) {
12
- return typeof value === "string";
13
- }
14
- function isRecord$1(value) {
15
- return typeof value === "object" && value !== null && !Array.isArray(value);
16
- }
17
- function hasOnlyKeys(value, keys) {
18
- return Object.keys(value).every((key) => keys.includes(key));
19
- }
20
- //#endregion
21
- //#region src/rule-options.ts
22
- function getRuleOptions(ruleName, options) {
23
- const firstOption = options[0];
24
- switch (ruleName) {
25
- case "vue/component-name-in-template-casing":
26
- if (isComponentNameInTemplateCasingOption(firstOption)) return { componentNameInTemplateCasing: firstOption };
27
- break;
28
- case "script/custom-event-name-casing":
29
- if (isCustomEventNameCasingOption(firstOption)) return { customEventNameCasing: firstOption };
30
- break;
31
- case "vue/no-mutating-props":
32
- if (isNoMutatingPropsOption(firstOption)) return { noMutatingProps: firstOption };
33
- break;
34
- case "vue/sfc-element-order":
35
- if (isSfcElementOrderOption(firstOption)) return { sfcElementOrder: firstOption };
36
- break;
37
- case "vue/html-self-closing":
38
- if (isHtmlSelfClosingOption(firstOption)) return { htmlSelfClosing: firstOption };
39
- break;
40
- case "vue/v-on-event-hyphenation":
41
- if (isHyphenationStyle(firstOption)) return { vOnEventHyphenation: firstOption };
42
- break;
43
- case "vue/attribute-hyphenation":
44
- if (isHyphenationStyle(firstOption)) return { attributeHyphenation: firstOption };
45
- break;
46
- }
47
- }
48
- function isComponentNameInTemplateCasingOption(value) {
49
- return value === "PascalCase" || value === "kebab-case";
50
- }
51
- function isCustomEventNameCasingOption(value) {
52
- return value === "camelCase" || value === "kebab-case";
53
- }
54
- function isNoMutatingPropsOption(value) {
55
- if (!isRecord$1(value)) return false;
56
- return hasOnlyKeys(value, ["shallowOnly"]) && optionalBooleanField(value.shallowOnly);
57
- }
58
- function isSfcElementOrderOption(value) {
59
- if (!isRecord$1(value)) return false;
60
- return hasOnlyKeys(value, ["order"]) && (value.order === void 0 || Array.isArray(value.order) && value.order.every(isSfcElementOrderGroup));
61
- }
62
- function isSfcElementOrderGroup(value) {
63
- return typeof value === "string" || Array.isArray(value) && value.every(isString);
64
- }
65
- function isHtmlSelfClosingOption(value) {
66
- if (!isRecord$1(value)) return false;
67
- return hasOnlyKeys(value, [
68
- "html",
69
- "svg",
70
- "math"
71
- ]) && optionField(value.svg) && optionField(value.math) && (value.html === void 0 || isRecord$1(value.html) && hasOnlyKeys(value.html, [
72
- "void",
73
- "normal",
74
- "component"
75
- ]) && optionField(value.html.void) && optionField(value.html.normal) && optionField(value.html.component));
76
- }
77
- function isHyphenationStyle(value) {
78
- return value === "always" || value === "never";
79
- }
80
- //#endregion
81
- //#region src/rule-selection.ts
82
- const selectionCache = /* @__PURE__ */ new WeakMap();
83
- /** A selection is a batching hint, never a second reporting/severity gate. */
84
- function parseRuleSelection(rules) {
85
- if (rules === void 0) return;
86
- const revision = JSON.stringify(rules);
87
- const cached = selectionCache.get(rules);
88
- if (cached?.revision === revision) return cached.selection;
89
- const optionsByRule = /* @__PURE__ */ new Map();
90
- if (Array.isArray(rules)) {
91
- for (const name of rules) if (typeof name === "string" && name.length > 0) optionsByRule.set(normalizeRuleName(name), void 0);
92
- } else if (typeof rules === "object" && rules !== null) {
93
- const usesPluginIds = Object.keys(rules).some((id) => id.startsWith("vize/"));
94
- for (const [id, entry] of Object.entries(rules)) {
95
- if (!id.startsWith("vize/") && (usesPluginIds || !/^(vue|script|style|css|type|nuxt|ecosystem)\//u.test(id))) continue;
96
- const severity = Array.isArray(entry) ? entry[0] : entry;
97
- if (severity !== "error" && severity !== "warn" && severity !== 1 && severity !== 2) continue;
98
- const name = normalizeRuleName(id);
99
- const options = Array.isArray(entry) ? getRuleOptions(name, entry.slice(1)) : void 0;
100
- optionsByRule.set(name, options);
101
- }
102
- } else return;
103
- const selection = createRuleSelection(optionsByRule);
104
- selectionCache.set(rules, {
105
- revision,
106
- selection
107
- });
108
- return selection;
109
- }
110
- function createRuleSelection(optionsByRule) {
111
- const names = [...optionsByRule.keys()].sort();
112
- const options = {};
113
- for (const name of names) Object.assign(options, optionsByRule.get(name));
114
- return {
115
- names,
116
- options,
117
- optionsByRule,
118
- cacheKey: JSON.stringify([names, ruleOptionsKey(options)])
119
- };
120
- }
121
- function normalizeRuleName(name) {
122
- return name.startsWith("vize/") ? name.slice(5) : name;
123
- }
124
- function ruleOptionsKey(options) {
125
- if (options === void 0) return "{}";
126
- return JSON.stringify(options ?? {}, (_key, value) => {
127
- if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
128
- return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
129
- });
130
- }
131
- function selectVizeRuleConfig(rules) {
132
- return Object.fromEntries(Object.entries(rules).filter(([id]) => id.startsWith("vize/")));
133
- }
134
- //#endregion
135
- //#region src/settings.ts
136
- const HELP_LEVELS = new Set([
137
- "none",
138
- "short",
139
- "full"
140
- ]);
141
- const TYPE_AWARE_RULE_PREFIX$1 = "type/";
142
- const PRESET_ALIASES = new Map([
143
- ["generalrecommended", "general-recommended"],
144
- ["happypath", "general-recommended"],
145
- ["happy", "general-recommended"],
146
- ["default", "general-recommended"],
147
- ["recommended", "general-recommended"],
148
- ["essential", "essential"],
149
- ["ecosystem", "ecosystem"],
150
- ["eco", "ecosystem"],
151
- ["incremental", "incremental"],
152
- ["all", "incremental"],
153
- ["opinionated", "opinionated"],
154
- ["opnionated", "opinionated"],
155
- ["strict", "opinionated"],
156
- ["nuxt", "nuxt"]
157
- ]);
158
- function getVizeSettings(context) {
159
- const settings = context.settings;
160
- return parseVizeSettings(settings.vize ?? settings.patina);
161
- }
162
- function parseVizeSettings(vize) {
163
- if (typeof vize !== "object" || vize === null || Array.isArray(vize)) return {};
164
- const vizeRecord = vize;
165
- const locale = vizeRecord.locale;
166
- const helpLevel = vizeRecord.helpLevel;
167
- const preset = vizeRecord.preset;
168
- const showHelp = vizeRecord.showHelp;
169
- const typeAware = vizeRecord.typeAware;
170
- const corsaPath = vizeRecord.corsaPath;
171
- const resolved = {};
172
- if (Array.isArray(vizeRecord.rules) || typeof vizeRecord.rules === "object" && vizeRecord.rules !== null) resolved.rules = vizeRecord.rules;
173
- if (typeof locale === "string") resolved.locale = locale;
174
- if (typeof preset === "string") {
175
- const normalizedPreset = normalizePreset(preset);
176
- if (normalizedPreset) resolved.preset = normalizedPreset;
177
- }
178
- if (typeof typeAware === "boolean") resolved.typeAware = typeAware;
179
- if (typeof corsaPath === "string") resolved.corsaPath = corsaPath;
180
- if (typeof helpLevel === "string" && HELP_LEVELS.has(helpLevel)) {
181
- resolved.helpLevel = helpLevel;
182
- return resolved;
183
- }
184
- if (typeof showHelp === "boolean") resolved.helpLevel = showHelp ? "full" : "none";
185
- return resolved;
186
- }
187
- function getActivePreset(settings) {
188
- return settings.preset ?? "incremental";
189
- }
190
- function isIncrementalPreset(settings) {
191
- return getActivePreset(settings) === "incremental";
192
- }
193
- function getCacheKey(filename, settings) {
194
- const selection = parseRuleSelection(settings.rules);
195
- return JSON.stringify([
196
- filename,
197
- settings.locale ?? "",
198
- settings.helpLevel ?? "",
199
- getActivePreset(settings),
200
- settings.typeAware ? "type-aware" : "",
201
- settings.corsaPath ?? "",
202
- selection?.cacheKey
203
- ]);
204
- }
205
- function isTypeAwareRuleName(ruleName) {
206
- return ruleName.startsWith(TYPE_AWARE_RULE_PREFIX$1);
207
- }
208
- function normalizePreset(value) {
209
- return PRESET_ALIASES.get(value.replaceAll(/[-_\s]/gu, "").toLowerCase());
210
- }
211
- if (import.meta.vitest) {
212
- const { describe, expect, it } = import.meta.vitest;
213
- describe("parseVizeSettings", () => {
214
- it("reads locale, help level, preset, and type-aware settings", () => {
215
- expect(parseVizeSettings({
216
- locale: "ja",
217
- helpLevel: "short",
218
- preset: "essential",
219
- typeAware: true,
220
- corsaPath: "./node_modules/@typescript/typescript-linux-x64/lib/tsc"
221
- })).toEqual({
222
- locale: "ja",
223
- helpLevel: "short",
224
- preset: "essential",
225
- typeAware: true,
226
- corsaPath: "./node_modules/@typescript/typescript-linux-x64/lib/tsc"
227
- });
228
- });
229
- it("normalizes preset aliases", () => {
230
- expect(parseVizeSettings({ preset: "recommended" })).toEqual({ preset: "general-recommended" });
231
- expect(parseVizeSettings({ preset: "incremental" })).toEqual({ preset: "incremental" });
232
- expect(parseVizeSettings({ preset: "all" })).toEqual({ preset: "incremental" });
233
- expect(parseVizeSettings({ preset: "ecosystem" })).toEqual({ preset: "ecosystem" });
234
- expect(parseVizeSettings({ preset: "strict" })).toEqual({ preset: "opinionated" });
235
- expect(parseVizeSettings({ preset: "Opnionated" })).toEqual({ preset: "opinionated" });
236
- });
237
- it("falls back to showHelp for compatibility", () => {
238
- expect(parseVizeSettings({
239
- locale: "ja",
240
- showHelp: false,
241
- preset: "Nuxt"
242
- })).toEqual({
243
- locale: "ja",
244
- helpLevel: "none",
245
- preset: "nuxt"
246
- });
247
- });
248
- it("ignores invalid values", () => {
249
- expect(parseVizeSettings({
250
- locale: 1,
251
- showHelp: "no",
252
- helpLevel: "verbose",
253
- preset: "wide",
254
- typeAware: "yes",
255
- corsaPath: false
256
- })).toEqual({});
257
- });
258
- it("ignores non-object vize settings", () => {
259
- expect(parseVizeSettings(null)).toEqual({});
260
- expect(parseVizeSettings("ja")).toEqual({});
261
- });
262
- });
263
- }
264
- //#endregion
265
- //#region src/binding.ts
266
- function lintPatina(source, filename, settings, enabledRules, ruleOptions) {
267
- return loadBinding().lintPatinaSfc(source, {
268
- filename,
269
- locale: settings.locale,
270
- helpLevel: settings.helpLevel,
271
- preset: getActivePreset(settings),
272
- enabledRules: enabledRules ? [...enabledRules] : void 0,
273
- typeAware: settings.typeAware,
274
- corsaPath: settings.corsaPath,
275
- ...ruleOptions
276
- });
277
- }
278
- function getPatinaRules() {
279
- return loadBinding().getPatinaRules();
280
- }
281
- //#endregion
282
- //#region src/script-map.ts
283
- function createSingleScriptMap(source, extractedScript, sfcBlocks = extractSfcBlocks(source)) {
284
- if (!extractedScript) return null;
285
- const blocks = sfcBlocks.filter((block) => block.kind === "script" || block.kind === "script-setup");
286
- let match = null;
287
- for (const block of blocks) {
288
- const skipped = countSkippedPrefix(block.content, extractedScript);
289
- if (skipped === null) continue;
290
- if (match !== null) return null;
291
- match = {
292
- block,
293
- scriptStart: advancePosition(block.contentStart, block.content.slice(0, skipped))
294
- };
295
- }
296
- return match;
297
- }
298
- /**
299
- * Oxlint trims the leading newline (and any indentation on that line) from a
300
- * `.vue` script block before handing the program to JS plugins, so the
301
- * extracted text starts partway into `block.content`. Returns how many
302
- * characters were dropped, or `null` when the extracted text is not this
303
- * block's body.
304
- */
305
- function countSkippedPrefix(content, extractedScript) {
306
- const skipped = content.length - extractedScript.length;
307
- if (skipped < 0 || !content.endsWith(extractedScript)) return null;
308
- return /^\s*$/u.test(content.slice(0, skipped)) ? skipped : null;
309
- }
310
- function advancePosition(from, text) {
311
- let { line, column } = from;
312
- for (const character of text) {
313
- if (character === "\n") {
314
- line += 1;
315
- column = 1;
316
- continue;
317
- }
318
- if (character !== "\r") column += 1;
319
- }
320
- return {
321
- line,
322
- column
323
- };
324
- }
325
- function mapToScriptLoc(diagnostic, scriptMap) {
326
- if (!scriptMap) return null;
327
- const { block, scriptStart } = scriptMap;
328
- if (compareLineColumn(diagnostic.location.start, scriptStart) < 0 || compareLineColumn(diagnostic.location.end, block.contentEnd) > 0) return null;
329
- return {
330
- start: toScriptPosition(diagnostic.location.start, scriptStart),
331
- end: toScriptPosition(diagnostic.location.end, scriptStart)
332
- };
333
- }
334
- /**
335
- * Patina reports 1-based SFC line/column; Oxlint expects 1-based lines and
336
- * 0-based columns relative to the extracted program.
337
- */
338
- function toScriptPosition(position, scriptStart) {
339
- if (position.line === scriptStart.line) return {
340
- line: 1,
341
- column: position.column - scriptStart.column
342
- };
343
- return {
344
- line: position.line - scriptStart.line + 1,
345
- column: position.column - 1
346
- };
347
- }
348
- //#endregion
349
- //#region src/file-state.ts
350
- const FILE_STATE_CACHE_CAPACITY = 128;
351
- const fileStateCache = /* @__PURE__ */ new Map();
352
- const EMPTY_DIAGNOSTICS = [];
353
- const TYPE_AWARE_RUNTIME_RULE = "type/corsa-runtime";
354
- let fileStateCacheClock = 0;
355
- function getFileState(context) {
356
- const settings = getVizeSettings(context);
357
- const physicalSource = fs.readFileSync(context.physicalFilename, "utf8");
358
- const cacheKey = getCacheKey(context.physicalFilename, settings);
359
- const cached = fileStateCache.get(cacheKey);
360
- if (cached && cached.state.revision.physicalSource === physicalSource) {
361
- validateWorkaroundAuthority(cached.state, context.physicalFilename);
362
- if (cached.state.extractedScript !== context.sourceCode.text) {
363
- cached.state.extractedScript = context.sourceCode.text;
364
- cached.state.scriptMap = void 0;
365
- }
366
- cached.lastUsed = nextFileStateCacheClock();
367
- return cached.state;
368
- }
369
- const resolvedSource = resolveWorkaroundSource(physicalSource, context.physicalFilename, readOriginalWorkaroundSource);
370
- const state = {
371
- revision: { physicalSource },
372
- filename: resolvedSource.filename,
373
- source: resolvedSource.source,
374
- extractedScript: context.sourceCode.text,
375
- usesOriginalLocations: resolvedSource.usesOriginalLocations || isScriptLikeFile(resolvedSource.filename),
376
- reportedDiagnostics: /* @__PURE__ */ new Set(),
377
- sfcBlocks: void 0,
378
- scriptMap: void 0,
379
- allDiagnosticsByRule: null,
380
- allDiagnosticsIncludesTypeAware: false,
381
- partialDiagnosticsByRule: /* @__PURE__ */ new Map(),
382
- requestedRules: /* @__PURE__ */ new Set(),
383
- reportedTypeAwareRuntimeDiagnostic: false,
384
- ruleSelection: parseRuleSelection(settings.rules),
385
- selectedDiagnosticsByRule: null
386
- };
387
- fileStateCache.set(cacheKey, {
388
- state,
389
- lastUsed: nextFileStateCacheClock()
390
- });
391
- evictLeastRecentlyUsedFileState();
392
- return state;
393
- }
394
- function getScriptMap(state) {
395
- if (state.scriptMap !== void 0) return state.scriptMap;
396
- state.scriptMap = createSingleScriptMap(state.source, state.extractedScript, getSfcBlocks(state));
397
- return state.scriptMap;
398
- }
399
- function getSfcBlocks(state) {
400
- if (state.sfcBlocks !== void 0) return state.sfcBlocks;
401
- state.sfcBlocks = extractSfcBlocks(state.source);
402
- return state.sfcBlocks;
403
- }
404
- function markDiagnosticAsReported(state, diagnostic) {
405
- const key = diagnosticIdentity(diagnostic);
406
- if (state.reportedDiagnostics.has(key)) return false;
407
- state.reportedDiagnostics.add(key);
408
- return true;
409
- }
410
- function diagnosticIdentity(diagnostic) {
411
- const { start, end } = diagnostic.location;
412
- return [
413
- diagnostic.rule,
414
- diagnostic.severity,
415
- diagnostic.message,
416
- start.line,
417
- start.column,
418
- start.offset,
419
- end.line,
420
- end.column,
421
- end.offset
422
- ].join("\0");
423
- }
424
- function nextFileStateCacheClock() {
425
- fileStateCacheClock += 1;
426
- return fileStateCacheClock;
427
- }
428
- function evictLeastRecentlyUsedFileState() {
429
- if (fileStateCache.size <= FILE_STATE_CACHE_CAPACITY) return;
430
- let oldestKey;
431
- let oldestUse = Number.POSITIVE_INFINITY;
432
- for (const [key, cached] of fileStateCache) if (cached.lastUsed < oldestUse) {
433
- oldestKey = key;
434
- oldestUse = cached.lastUsed;
435
- }
436
- if (oldestKey !== void 0) fileStateCache.delete(oldestKey);
437
- }
438
- function indexDiagnosticsByRule(diagnostics) {
439
- const grouped = /* @__PURE__ */ new Map();
440
- for (const diagnostic of diagnostics) {
441
- const existing = grouped.get(diagnostic.rule);
442
- if (existing) {
443
- existing.push(diagnostic);
444
- continue;
445
- }
446
- grouped.set(diagnostic.rule, [diagnostic]);
447
- }
448
- return grouped;
449
- }
450
- function diagnosticsForRule(state, diagnosticsByRule, ruleName) {
451
- const directDiagnostics = diagnosticsByRule.get(ruleName);
452
- if (directDiagnostics && directDiagnostics.length > 0) return directDiagnostics;
453
- if (!isTypeAwareRuleName(ruleName) || state.reportedTypeAwareRuntimeDiagnostic) return EMPTY_DIAGNOSTICS;
454
- const runtimeDiagnostics = diagnosticsByRule.get(TYPE_AWARE_RUNTIME_RULE);
455
- if (!runtimeDiagnostics || runtimeDiagnostics.length === 0) return EMPTY_DIAGNOSTICS;
456
- state.reportedTypeAwareRuntimeDiagnostic = true;
457
- return runtimeDiagnostics;
458
- }
459
- //#endregion
460
- //#region src/active-rule-collection.ts
461
- const programs = /* @__PURE__ */ new WeakMap();
462
- const results = /* @__PURE__ */ new WeakMap();
463
- function collectActiveRule(context, program, ruleName, options) {
464
- const state = getFileState(context);
465
- let collection = programs.get(program);
466
- if (!collection || collection.selection) {
467
- collection = {
468
- state,
469
- optionsByRule: /* @__PURE__ */ new Map()
470
- };
471
- programs.set(program, collection);
472
- }
473
- if (collection.state !== state) {
474
- programs.delete(program);
475
- throw new Error("Vize source or configuration changed during rule collection");
476
- }
477
- collection.optionsByRule.set(ruleName, options === void 0 ? void 0 : structuredClone(options));
478
- }
479
- function getActiveRuleDiagnostics(context, program, ruleName) {
480
- const collection = programs.get(program);
481
- if (!collection?.optionsByRule.has(ruleName)) return void 0;
482
- if (getFileState(context) !== collection.state) {
483
- programs.delete(program);
484
- throw new Error("Vize source or configuration changed before rule execution");
485
- }
486
- if (!collection.selection) {
487
- collection.selection = createRuleSelection(collection.optionsByRule);
488
- collection.remaining = new Set(collection.selection.names);
489
- }
490
- const { state, selection } = collection;
491
- let cached = results.get(state);
492
- if (cached?.key !== selection.cacheKey) {
493
- const settings = getVizeSettings(context);
494
- const diagnostics = indexDiagnosticsByRule(lintPatina(state.source, state.filename, {
495
- ...settings,
496
- typeAware: settings.typeAware === true || selection.names.some(isTypeAwareRuleName)
497
- }, selection.names, selection.options).diagnostics);
498
- if (cached) {
499
- state.reportedDiagnostics.clear();
500
- state.reportedTypeAwareRuntimeDiagnostic = false;
501
- }
502
- cached = {
503
- key: selection.cacheKey,
504
- diagnostics
505
- };
506
- results.set(state, cached);
507
- }
508
- const diagnostics = diagnosticsForRule(state, cached.diagnostics, ruleName);
509
- collection.remaining.delete(ruleName);
510
- if (collection.remaining.size === 0) programs.delete(program);
511
- return {
512
- state,
513
- diagnostics
514
- };
515
- }
516
- //#endregion
517
- //#region src/format.ts
518
- const helpTextCache = /* @__PURE__ */ new Map();
519
- function formatPatinaMessage(diagnostic, options) {
520
- const sections = [];
521
- const summaryLine = createSummaryLine(diagnostic.message);
522
- const details = createDetailsBody(diagnostic.message, summaryLine);
523
- const summary = options.hasMappedLocation ? summaryLine : `${summaryLine} (${formatInlineLocation(options.blockLabel, diagnostic)})`;
524
- if (details) sections.push(formatSection("Details", details));
525
- const helpText = resolveHelpText(diagnostic.help, options.helpLevel);
526
- if (helpText) sections.push(formatSection("Help", helpText));
527
- if (sections.length === 0) return summary;
528
- return `${summary}\n${sections.join("\n")}`;
529
- }
530
- function createDetailsBody(message, summary) {
531
- if (message === summary) return null;
532
- if (message.startsWith(summary)) return message.slice(summary.length).trim() || null;
533
- return message;
534
- }
535
- function formatSection(title, body) {
536
- return ` ${title}:\n${indentBlock(body, " ")}`;
537
- }
538
- function formatInlineLocation(blockLabel, diagnostic) {
539
- const { line, column } = diagnostic.location.start;
540
- return `at ${blockLabel}:${line}:${column}`;
541
- }
542
- function resolveHelpText(help, helpLevel) {
543
- if (help == null || helpLevel === "none") return null;
544
- const plainText = formatHelpText(help);
545
- if (helpLevel === "short") return firstMeaningfulLine(plainText);
546
- return plainText;
547
- }
548
- function formatHelpText(help) {
549
- const cached = helpTextCache.get(help);
550
- if (cached) return cached;
551
- const plainText = help.replace(/\r\n?/gu, "\n").replace(/^\s*```[^\n]*$/gmu, "").replace(/\*\*(.*?)\*\*/gu, "$1").replace(/__(.*?)__/gu, "$1").replace(/`([^`\n]+)`/gu, "$1").replace(/^\s*[-*]\s+/gmu, "- ").replace(/\n{3,}/gu, "\n\n").trim();
552
- helpTextCache.set(help, plainText);
553
- return plainText;
554
- }
555
- function firstMeaningfulLine(text) {
556
- for (const line of text.split("\n")) {
557
- const trimmed = line.trim();
558
- if (trimmed) return trimmed;
559
- }
560
- return null;
561
- }
562
- function indentBlock(text, indent) {
563
- return text.split("\n").map((line) => `${indent}${line}`).join("\n");
564
- }
565
- function createSummaryLine(message) {
566
- const firstSentenceEnd = message.indexOf(". ");
567
- if (firstSentenceEnd === -1) return message;
568
- return message.slice(0, firstSentenceEnd + 1);
569
- }
570
- //#endregion
571
- //#region src/plugin.ts
572
- function createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel, program) {
573
- const loc = state.usesOriginalLocations ? createOriginalSfcLoc(diagnostic) : mapToScriptLoc(diagnostic, scriptMap);
574
- const block = loc === null ? getDiagnosticBlock(diagnostic, getSfcBlocks(state)) : null;
575
- const message = formatPatinaMessage(diagnostic, {
576
- hasMappedLocation: loc !== null,
577
- blockLabel: formatBlockLabel(block),
578
- helpLevel
579
- });
580
- if (loc == null && state.extractedScript.length === 0) return {
581
- node: program,
582
- message
583
- };
584
- const fallbackColumn = state.extractedScript.length === 0 ? 0 : 1;
585
- return {
586
- loc: loc ?? {
587
- start: {
588
- line: 1,
589
- column: fallbackColumn
590
- },
591
- end: {
592
- line: 1,
593
- column: fallbackColumn
594
- }
595
- },
596
- message
597
- };
598
- }
599
- function shouldReportForCurrentProgram(diagnostic, state, scriptMap) {
600
- if (state.usesOriginalLocations) return isScriptLikeFile(state.filename) || isLocationBridgeProgram(state.extractedScript);
601
- if (scriptMap == null) return true;
602
- const block = getDiagnosticBlock(diagnostic, getSfcBlocks(state));
603
- return !isScriptBlock(block) || block === scriptMap.block;
604
- }
605
- function isScriptBlock(block) {
606
- return block?.kind === "script" || block?.kind === "script-setup";
607
- }
608
- function createOriginalSfcLoc(diagnostic) {
609
- return {
610
- start: {
611
- line: diagnostic.location.start.line,
612
- column: Math.max(0, diagnostic.location.start.column - 1)
613
- },
614
- end: {
615
- line: diagnostic.location.end.line,
616
- column: Math.max(0, diagnostic.location.end.column - 1)
617
- }
618
- };
619
- }
620
- function createPatinaRule(ruleMeta) {
621
- return defineRule({
622
- meta: {
623
- type: ruleMeta.defaultSeverity === "error" ? "problem" : "suggestion",
624
- docs: { description: ruleMeta.description },
625
- schema: ruleOptionsSchema(ruleMeta.name)
626
- },
627
- createOnce(context) {
628
- return {
629
- Program(program) {
630
- if (!isPatinaFile(context.filename)) return;
631
- const settings = getVizeSettings(context);
632
- const activePreset = getActivePreset(settings);
633
- if (ruleMeta.presets.length > 0 && !isIncrementalPreset(settings) && !ruleMeta.presets.includes(activePreset)) return;
634
- collectActiveRule(context, program, ruleMeta.name, getRuleOptions(ruleMeta.name, contextOptions(context)));
635
- },
636
- "Program:exit"(program) {
637
- const collected = getActiveRuleDiagnostics(context, program, ruleMeta.name);
638
- if (!collected) return;
639
- const { state } = collected;
640
- const helpLevel = getVizeSettings(context).helpLevel ?? "full";
641
- const scriptMap = getScriptMap(state);
642
- const diagnostics = collected.diagnostics.filter((diagnostic) => shouldReportForCurrentProgram(diagnostic, state, scriptMap));
643
- if (diagnostics.length === 0) return;
644
- for (const diagnostic of diagnostics) {
645
- if (!markDiagnosticAsReported(state, diagnostic)) continue;
646
- context.report(createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel, program));
647
- }
648
- }
649
- };
650
- }
651
- });
652
- }
653
- function ruleOptionsSchema(ruleName) {
654
- switch (ruleName) {
655
- case "vue/component-name-in-template-casing": return [{ enum: ["PascalCase", "kebab-case"] }];
656
- case "script/custom-event-name-casing": return [{ enum: ["camelCase", "kebab-case"] }];
657
- case "vue/no-mutating-props": return [noMutatingPropsSchema()];
658
- case "vue/sfc-element-order": return [sfcElementOrderSchema()];
659
- case "vue/html-self-closing": return [htmlSelfClosingSchema()];
660
- case "vue/v-on-event-hyphenation":
661
- case "vue/attribute-hyphenation": return [{ enum: ["always", "never"] }];
662
- default: return [];
663
- }
664
- }
665
- function noMutatingPropsSchema() {
666
- return {
667
- type: "object",
668
- properties: { shallowOnly: { type: "boolean" } },
669
- additionalProperties: false
670
- };
671
- }
672
- function sfcElementOrderSchema() {
673
- const selector = { type: "string" };
674
- return {
675
- type: "object",
676
- properties: { order: {
677
- type: "array",
678
- items: { oneOf: [selector, {
679
- type: "array",
680
- items: selector
681
- }] }
682
- } },
683
- additionalProperties: false
684
- };
685
- }
686
- function htmlSelfClosingSchema() {
687
- const style = { enum: [
688
- "always",
689
- "never",
690
- "any"
691
- ] };
692
- return {
693
- type: "object",
694
- properties: {
695
- html: {
696
- type: "object",
697
- properties: {
698
- void: style,
699
- normal: style,
700
- component: style
701
- },
702
- additionalProperties: false
703
- },
704
- svg: style,
705
- math: style
706
- },
707
- additionalProperties: false
708
- };
709
- }
710
- function contextOptions(context) {
711
- const options = context.options;
712
- return Array.isArray(options) ? options : [];
713
- }
714
- var plugin_default = definePlugin({
715
- meta: { name: "vize" },
716
- rules: Object.fromEntries(getPatinaRules().map((ruleMeta) => [ruleMeta.name, createPatinaRule(ruleMeta)]))
717
- });
718
- //#endregion
719
- //#region src/configs.ts
720
- const TYPE_AWARE_RULE_PREFIX = "type/";
721
- function createVizeRuleConfig(options = {}) {
722
- const presets = resolveRuleConfigPresets(options);
723
- const includeTypeAware = options.includeTypeAware ?? false;
724
- const rules = {};
725
- for (const ruleMeta of getPatinaRules()) {
726
- if (!matchesPresets(ruleMeta, presets)) continue;
727
- if (!includeTypeAware && isTypeAwareRule(ruleMeta)) continue;
728
- rules[`vize/${ruleMeta.name}`] = toOxlintSeverity(ruleMeta.defaultSeverity);
729
- }
730
- return rules;
731
- }
732
- const configs = {
733
- all: createVizeRuleConfig({ preset: "all" }),
734
- ecosystem: createVizeRuleConfig({ preset: "ecosystem" }),
735
- ecosystemWithTypeAware: createVizeRuleConfig({
736
- includeTypeAware: true,
737
- preset: "ecosystem"
738
- }),
739
- essential: createVizeRuleConfig({ preset: "essential" }),
740
- happyPath: createVizeRuleConfig({ preset: "happy-path" }),
741
- happyPathWithTypeAware: createVizeRuleConfig({
742
- includeTypeAware: true,
743
- preset: "happy-path"
744
- }),
745
- nuxt: createVizeRuleConfig({ preset: "nuxt" }),
746
- opinionated: createVizeRuleConfig({ preset: "opinionated" }),
747
- opinionatedWithTypeAware: createVizeRuleConfig({
748
- includeTypeAware: true,
749
- preset: "opinionated"
750
- }),
751
- recommended: createVizeRuleConfig({ preset: "general-recommended" }),
752
- recommendedWithTypeAware: createVizeRuleConfig({
753
- includeTypeAware: true,
754
- preset: "general-recommended"
755
- })
756
- };
757
- function normalizeRuleConfigPreset(preset) {
758
- return preset === "happy-path" ? "general-recommended" : preset;
759
- }
760
- function resolveRuleConfigPresets(options) {
761
- const requestedPresets = [...toPresetArray$1((options.preset === void 0 && options.presets === void 0 ? "general-recommended" : options.preset) ?? []), ...options.presets ?? []].map(normalizeRuleConfigPreset);
762
- return [...new Set(requestedPresets)];
763
- }
764
- function toPresetArray$1(preset) {
765
- return Array.isArray(preset) ? preset : [preset];
766
- }
767
- function matchesPresets(ruleMeta, presets) {
768
- return presets.includes("all") || presets.some((preset) => preset !== "all" && ruleMeta.presets.includes(preset));
769
- }
770
- function isTypeAwareRule(ruleMeta) {
771
- return ruleMeta.name.startsWith(TYPE_AWARE_RULE_PREFIX);
772
- }
773
- function toOxlintSeverity(severity) {
774
- return severity === "warning" ? "warn" : severity;
775
- }
776
- if (import.meta.vitest) {
777
- const { describe, expect, it } = import.meta.vitest;
778
- describe("createVizeRuleConfig", () => {
779
- it("normalizes Vize warning severity to Oxlint's warn", () => {
780
- expect(configs.recommended["vize/vue/no-multi-spaces"]).toBe("warn");
781
- });
782
- it("accepts the CLI happy-path preset alias", () => {
783
- expect(createVizeRuleConfig({ preset: "happy-path" })).toEqual(configs.recommended);
784
- expect(configs.happyPath).toEqual(configs.recommended);
785
- });
786
- it("unions multiple presets", () => {
787
- const config = createVizeRuleConfig({ presets: ["happy-path", "ecosystem"] });
788
- expect(config["vize/script/valid-define-props"]).toBe("error");
789
- expect(config["vize/ecosystem/router-link-require-to"]).toBe("error");
790
- expect(config["vize/script/no-options-api"]).toBeUndefined();
791
- });
792
- it("accepts multiple presets through the preset option", () => {
793
- expect(createVizeRuleConfig({ preset: ["happy-path", "ecosystem"] })).toEqual(createVizeRuleConfig({ presets: ["happy-path", "ecosystem"] }));
794
- });
795
- it("filters rules by preset", () => {
796
- expect(configs.essential["vize/vue/require-v-for-key"]).toBe("error");
797
- expect(configs.essential["vize/vue/require-scoped-style"]).toBeUndefined();
798
- });
799
- it("enables script correctness rules in essential and happy-path", () => {
800
- expect(configs.essential["vize/script/valid-define-props"]).toBe("error");
801
- expect(configs.essential["vize/script/no-import-compiler-macros"]).toBe("error");
802
- expect(configs.recommended["vize/script/valid-define-props"]).toBe("error");
803
- expect(configs.recommended["vize/script/no-import-compiler-macros"]).toBe("error");
804
- });
805
- it("keeps low-noise happy-path script warnings out of essential", () => {
806
- expect(configs.recommended["vize/script/no-duplicate-attr-inheritance"]).toBe("warn");
807
- expect(configs.recommended["vize/script/no-reactive-destructure"]).toBeUndefined();
808
- expect(configs.essential["vize/script/no-duplicate-attr-inheritance"]).toBeUndefined();
809
- expect(configs.recommended["vize/script/no-unused-emit-declarations"]).toBeUndefined();
810
- expect(configs.essential["vize/script/no-unused-emit-declarations"]).toBeUndefined();
811
- });
812
- it("keeps opinionated script style out of happy-path", () => {
813
- expect(configs.recommended["vize/script/no-options-api"]).toBeUndefined();
814
- expect(configs.recommended["vize/script/define-props-declaration"]).toBeUndefined();
815
- expect(configs.opinionated["vize/script/no-options-api"]).toBe("error");
816
- });
817
- it("skips unstable type-aware rules by default", () => {
818
- expect(configs.opinionated["vize/type/require-typed-props"]).toBeUndefined();
819
- expect(configs.opinionatedWithTypeAware["vize/type/require-typed-props"]).toBe("warn");
820
- });
821
- it("keeps Options API allowed in the Nuxt preset", () => {
822
- expect(configs.nuxt["vize/script/no-options-api"]).toBeUndefined();
823
- expect(configs.opinionated["vize/script/no-options-api"]).toBe("error");
824
- });
825
- it("enables Nuxt framework rules only in the Nuxt and all presets", () => {
826
- for (const ruleName of [
827
- "prefer-import-meta",
828
- "no-page-meta-runtime-values",
829
- "no-nuxt-config-test-key",
830
- "nuxt-config-keys-order"
831
- ]) {
832
- const ruleId = `vize/nuxt/${ruleName}`;
833
- expect(configs.nuxt[ruleId]).toBe("error");
834
- expect(configs.all[ruleId]).toBe("error");
835
- expect(configs.ecosystem[ruleId]).toBeUndefined();
836
- expect(configs.opinionated[ruleId]).toBeUndefined();
837
- expect(configs.recommended[ruleId]).toBeUndefined();
838
- }
839
- });
840
- it("keeps ecosystem rules out of non-ecosystem presets until explicitly selected", () => {
841
- expect(configs.recommended["vize/ecosystem/router-link-require-to"]).toBeUndefined();
842
- expect(configs.nuxt["vize/ecosystem/nuxt-prefer-nuxt-link"]).toBe("warn");
843
- expect(configs.ecosystem["vize/ecosystem/nuxt-prefer-nuxt-link"]).toBeUndefined();
844
- expect(configs.all["vize/ecosystem/nuxt-prefer-nuxt-link"]).toBe("warn");
845
- expect(configs.ecosystem["vize/ecosystem/router-link-require-to"]).toBe("error");
846
- expect(configs.ecosystem["vize/ecosystem/vue-i18n-no-missing-key"]).toBe("warn");
847
- expect(configs.ecosystem["vize/ecosystem/void-link-require-href"]).toBe("error");
848
- expect(configs.all["vize/ecosystem/router-link-require-to"]).toBe("error");
849
- });
850
- });
851
- }
852
- //#endregion
1
+ import { a as getPatinaRules, n as createVizeRuleConfig, r as plugin_default, t as configs, u as selectVizeRuleConfig } from "./configs-DY0BrbH2.mjs";
853
2
  //#region src/vite-plus.ts
854
3
  /**
855
4
  * Bare specifier Oxlint uses to load this bridge as a JS plugin.