oxlint-plugin-vize 0.429.0 → 0.429.2

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 CHANGED
@@ -6,7 +6,7 @@ This package lets Oxlint execute Patina through Vize's native binding while stil
6
6
 
7
7
  > [!IMPORTANT]
8
8
  > `oxlint-plugin-vize` is a terminal-first Vue SFC linting package.
9
- > Until upstream Vue support in Oxlint matures, prefer `oxlint-vize -f stylish` for day-to-day output and treat machine-readable / full-fidelity original-SFC reporting as best-effort.
9
+ > Until upstream Vue support in Oxlint matures, use `oxlint-vize` for original SFC locations in terminal and JSON output. Direct `oxlint` still has JS plugin location limits.
10
10
 
11
11
  ## Main Features
12
12
 
@@ -15,7 +15,7 @@ This package lets Oxlint execute Patina through Vize's native binding while stil
15
15
  - Ships preset rule maps for JS/TS Oxlint configs: `configs.recommended`, `configs.happyPath`, `configs.essential`, `configs.ecosystem`, `configs.opinionated`, `configs.nuxt`, `configs.all`, and type-aware opt-in variants.
16
16
  - Ships `createVizeLintConfig()`, `flatConfigs`, and `defineVizeLintConfig()` for the Vite+ `lint` block in `vite.config.ts`, which is the only Oxlint configuration `vp lint` and `vp check` read.
17
17
  - Supports runtime settings through `settings.vize`, including `locale`, `preset`, and `helpLevel`.
18
- - Provides the `oxlint-vize` CLI wrapper, which runs Oxlint with a scriptless-SFC workaround and rewrites temporary paths back to the original `.vue` files.
18
+ - Provides the `oxlint-vize` CLI wrapper, which runs Oxlint with a temporary location bridge for Vue and HTML files and reports their original paths and Patina locations.
19
19
  - Resolves Vize native bindings through platform-specific optional dependencies, so published installs do not need a separate `@vizejs/native` package.
20
20
  - Caches file contents and native rule results for the lifetime of the Oxlint process, reducing duplicate work when several Vize rules are enabled for the same file.
21
21
 
@@ -229,20 +229,18 @@ For day-to-day terminal runs, the recommended command today is:
229
229
  vp exec oxlint-vize -c .oxlintrc.json -f stylish src
230
230
  ```
231
231
 
232
- `oxlint-vize` is a thin wrapper around `oxlint`. Until upstream JS plugin coverage improves, it appends a temporary `<script setup>` block only for scriptless `.vue` files so Oxlint's JS plugin pipeline still invokes Vize, then rewrites reported paths back to the original files.
233
- `stylish` is currently the most usable compromise for mixed Oxlint + Vize output because the Patina summary can inline the original SFC location even though Oxlint still anchors JS plugin diagnostics to the extracted script program.
232
+ `oxlint-vize` appends a temporary `<script setup>` location bridge for Vue and HTML files, including files with existing scripts. This lets Patina report the original SFC line and column through Oxlint's JS plugin pipeline. The wrapper rewrites temporary paths back to the original files; `-f json` retains those original positions for machine-readable diagnostics.
234
233
 
235
234
  ## Limitations
236
235
 
237
- - Raw `oxlint` still misses files without `<script>` or `<script setup>`. The temporary `oxlint-vize` wrapper works around this by generating a transient script block for scriptless `.vue` files before invoking `oxlint`.
238
- - Oxlint JS plugins only accept ranges inside the extracted Vue script program. For template diagnostics, Vize now inlines the original SFC block and `line:column` into the summary, while the formatter anchor still points at the script block.
239
- - Formatter parity is not there yet. `stylish` is recommended for human-readable terminal output, while `json` and other machine-readable outputs are best treated as debugging aids for original template/style positions.
236
+ - Raw `oxlint` still misses files without `<script>` or `<script setup>`. It also anchors template diagnostics to an extracted script instead of the original template location. Use `oxlint-vize` when linting Vue and HTML files.
237
+ - The wrapper's temporary files are not copied back when Oxlint fix flags are used; it warns when `--fix` or `--fix-suggestions` is passed.
240
238
  - Oxlint core rules that need JavaScript bindings extracted from Vue templates, such as template-aware unused-variable checks, still depend on upstream work in [Oxc's Better Vue Support](https://github.com/oxc-project/oxc/issues/15761).
241
- - Vize's own SFC diagnostics can run through the plugin, but precise original-SFC ranges across all Oxlint formatters depend on the JS plugin reporting work tracked in [oxc-project/oxc#20465](https://github.com/oxc-project/oxc/issues/20465).
239
+ - Direct `oxlint` original-SFC ranges depend on the JS plugin reporting work tracked in [oxc-project/oxc#20465](https://github.com/oxc-project/oxc/issues/20465).
242
240
  - Type-aware Vize rules are experimental and excluded from the default exported configs. Opt into them explicitly with `configs.recommendedWithTypeAware`, `configs.opinionatedWithTypeAware`, or `createVizeRuleConfig({ includeTypeAware: true, preset: ... })`, and use `settings.vize.typeAware: true` when you want the shared full-file pass to run them eagerly.
243
241
 
244
242
  ## Current expectations
245
243
 
246
244
  - This release is meant for terminal-first workflows.
247
- - It is not yet a promise of precise original-SFC spans across every Oxlint formatter.
248
- - Once Oxlint can preserve original Vue positions for JS plugins reliably, Vize can improve formatter parity and machine-readable reporting without relying on summary fallbacks.
245
+ - The `oxlint-vize` wrapper reports original Patina positions in `stylish` and `json`; direct `oxlint` remains limited by its extracted-script JS plugin input.
246
+ - Once Oxlint preserves original Vue positions for JS plugins, the temporary location bridge can be removed.
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as hasVueLikeExtension, n as hasScriptLikeBlock, s as isStandaloneHtmlFile, t as appendScriptlessWorkaround } from "./workaround-DRe3y2CB.mjs";
1
+ import { i as hasVueLikeExtension, s as isStandaloneHtmlFile, t as appendScriptlessWorkaround } from "./workaround-CcnA_oWy.mjs";
2
2
  import { spawn, spawnSync } from "node:child_process";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs";
@@ -200,7 +200,6 @@ function prepareScriptlessWorkaroundFiles(cwd, filenames) {
200
200
  for (const filename of filenames) {
201
201
  const source = fs.readFileSync(filename, "utf8");
202
202
  const isStandaloneHtml = isStandaloneHtmlFile(filename);
203
- if (!isStandaloneHtml && hasScriptLikeBlock(source)) continue;
204
203
  const relativeFilename = path.relative(cwd, filename);
205
204
  if (tempDir == null) {
206
205
  const created = createWorkaroundTempDir(nodeModulesDir, tempRoot);
package/dist/index.mjs CHANGED
@@ -1,9 +1,26 @@
1
- import { a as isPatinaFile, c as compareLineColumn, d as getDiagnosticBlock, l as extractSfcBlocks, o as isScriptLikeFile, r as resolveWorkaroundSource, u as formatBlockLabel } from "./workaround-DRe3y2CB.mjs";
1
+ import { a as isPatinaFile, c as compareLineColumn, d as getDiagnosticBlock, l as extractSfcBlocks, n as isLocationBridgeProgram, o as isScriptLikeFile, r as resolveWorkaroundSource, u as formatBlockLabel } from "./workaround-CcnA_oWy.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { definePlugin, defineRule } from "@oxlint/plugins";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import path from "node:path";
6
6
  import fs from "node:fs";
7
+ //#region src/plugin-option-guards.ts
8
+ function optionField(value) {
9
+ return value === void 0 || value === "always" || value === "never" || value === "any";
10
+ }
11
+ function optionalBooleanField(value) {
12
+ return value === void 0 || typeof value === "boolean";
13
+ }
14
+ function isString(value) {
15
+ return typeof value === "string";
16
+ }
17
+ function isRecord$1(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ function hasOnlyKeys(value, keys) {
21
+ return Object.keys(value).every((key) => keys.includes(key));
22
+ }
23
+ //#endregion
7
24
  //#region src/native.ts
8
25
  const require = createRequire(import.meta.url);
9
26
  const FALLBACK_BINDING_PACKAGE = "@vizejs/native";
@@ -553,29 +570,36 @@ function createSummaryLine(message) {
553
570
  }
554
571
  //#endregion
555
572
  //#region src/plugin.ts
556
- function createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel) {
573
+ function createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel, program) {
557
574
  const loc = state.usesOriginalLocations ? createOriginalSfcLoc(diagnostic) : mapToScriptLoc(diagnostic, scriptMap);
558
575
  const block = loc === null ? getDiagnosticBlock(diagnostic, getSfcBlocks(state)) : null;
576
+ const message = formatPatinaMessage(diagnostic, {
577
+ hasMappedLocation: loc !== null,
578
+ blockLabel: formatBlockLabel(block),
579
+ helpLevel
580
+ });
581
+ if (loc == null && state.extractedScript.length === 0) return {
582
+ node: program,
583
+ message
584
+ };
585
+ const fallbackColumn = state.extractedScript.length === 0 ? 0 : 1;
559
586
  return {
560
587
  loc: loc ?? {
561
588
  start: {
562
589
  line: 1,
563
- column: 1
590
+ column: fallbackColumn
564
591
  },
565
592
  end: {
566
593
  line: 1,
567
- column: 1
594
+ column: fallbackColumn
568
595
  }
569
596
  },
570
- message: formatPatinaMessage(diagnostic, {
571
- hasMappedLocation: loc !== null,
572
- blockLabel: formatBlockLabel(block),
573
- helpLevel
574
- })
597
+ message
575
598
  };
576
599
  }
577
600
  function shouldReportForCurrentProgram(diagnostic, state, scriptMap) {
578
- if (state.usesOriginalLocations || scriptMap == null) return true;
601
+ if (state.usesOriginalLocations) return isScriptLikeFile(state.filename) || isLocationBridgeProgram(state.extractedScript);
602
+ if (scriptMap == null) return true;
579
603
  const block = getDiagnosticBlock(diagnostic, getSfcBlocks(state));
580
604
  return !isScriptBlock(block) || block === scriptMap.block;
581
605
  }
@@ -602,7 +626,7 @@ function createPatinaRule(ruleMeta) {
602
626
  schema: ruleOptionsSchema(ruleMeta.name)
603
627
  },
604
628
  createOnce(context) {
605
- return { Program() {
629
+ return { Program(program) {
606
630
  if (!isPatinaFile(context.filename)) return;
607
631
  const settings = getVizeSettings(context);
608
632
  const activePreset = getActivePreset(settings);
@@ -615,7 +639,7 @@ function createPatinaRule(ruleMeta) {
615
639
  if (diagnostics.length === 0) return;
616
640
  for (const diagnostic of diagnostics) {
617
641
  if (!markDiagnosticAsReported(state, diagnostic)) continue;
618
- context.report(createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel));
642
+ context.report(createOxlintDiagnostic(diagnostic, state, scriptMap, helpLevel, program));
619
643
  }
620
644
  } };
621
645
  }
@@ -740,21 +764,6 @@ function isHtmlSelfClosingOption(value) {
740
764
  function isHyphenationStyle(value) {
741
765
  return value === "always" || value === "never";
742
766
  }
743
- function optionField(value) {
744
- return value === void 0 || value === "always" || value === "never" || value === "any";
745
- }
746
- function optionalBooleanField(value) {
747
- return value === void 0 || typeof value === "boolean";
748
- }
749
- function isString(value) {
750
- return typeof value === "string";
751
- }
752
- function isRecord$1(value) {
753
- return typeof value === "object" && value !== null && !Array.isArray(value);
754
- }
755
- function hasOnlyKeys(value, keys) {
756
- return Object.keys(value).every((key) => keys.includes(key));
757
- }
758
767
  var plugin_default = definePlugin({
759
768
  meta: { name: "vize" },
760
769
  rules: Object.fromEntries(getPatinaRules().map((ruleMeta) => [ruleMeta.name, createPatinaRule(ruleMeta)]))
@@ -141,12 +141,16 @@ function isStandaloneHtmlFile(filename) {
141
141
  const SCRIPTLESS_WORKAROUND_MARKER = "oxlint-plugin-vize-scriptless";
142
142
  const SCRIPTLESS_WORKAROUND_OPEN_TAG_PREFIX = `<script setup lang="ts" data-${SCRIPTLESS_WORKAROUND_MARKER}="`;
143
143
  const SCRIPTLESS_WORKAROUND_FILENAME_ATTR = `data-${SCRIPTLESS_WORKAROUND_MARKER}="`;
144
+ const LOCATION_BRIDGE_MARKER = "/*vize-location-bridge*/";
144
145
  function hasScriptLikeBlock(source) {
145
146
  return extractSfcBlocks(source).some((block) => block.kind === "script" || block.kind === "script-setup");
146
147
  }
147
148
  function appendScriptlessWorkaround(source, filename) {
148
149
  return `${createWorkaroundScript(source, filename)}${source}`;
149
150
  }
151
+ function isLocationBridgeProgram(extractedScript) {
152
+ return extractedScript.trimEnd().endsWith(LOCATION_BRIDGE_MARKER);
153
+ }
150
154
  function resolveWorkaroundSource(source, fallbackFilename) {
151
155
  const workaroundBlock = getPrependedWorkaroundBlock(source);
152
156
  if (workaroundBlock == null) return {
@@ -167,7 +171,7 @@ function getPrependedWorkaroundBlock(source) {
167
171
  if (!source.startsWith(SCRIPTLESS_WORKAROUND_OPEN_TAG_PREFIX)) return null;
168
172
  const [firstBlock] = extractSfcBlocks(source);
169
173
  if (!firstBlock || firstBlock.kind !== "script-setup") return null;
170
- if (!isWhitespaceOnly(firstBlock.content)) return null;
174
+ if (!firstBlock.content.endsWith(LOCATION_BRIDGE_MARKER) || !isWhitespaceOnly(firstBlock.content.slice(0, -24))) return null;
171
175
  const contentStart = offsetFromLineColumn(source, firstBlock.contentStart);
172
176
  const contentEnd = offsetFromLineColumn(source, firstBlock.contentEnd);
173
177
  const encodedFilename = encodedFilenameFromWorkaroundOpenTag(source.slice(0, contentStart));
@@ -175,7 +179,7 @@ function getPrependedWorkaroundBlock(source) {
175
179
  const closeTagEnd = contentEnd + 9;
176
180
  const strippedSourceStart = sourceStartAfterSyntheticBlock(source, closeTagEnd);
177
181
  if (strippedSourceStart == null) return null;
178
- if (firstBlock.content !== createWhitespaceMirror(source.slice(strippedSourceStart))) return null;
182
+ if (firstBlock.content !== createWhitespaceMirror(source.slice(strippedSourceStart)) + LOCATION_BRIDGE_MARKER) return null;
179
183
  return {
180
184
  closeTagEnd,
181
185
  encodedFilename
@@ -194,7 +198,7 @@ function encodedFilenameFromWorkaroundOpenTag(openTag) {
194
198
  return openTag.slice(encodedFilenameStart, encodedFilenameEnd);
195
199
  }
196
200
  function createWorkaroundScript(source, filename) {
197
- return `${SCRIPTLESS_WORKAROUND_OPEN_TAG_PREFIX}${encodeWorkaroundFilename(filename)}">${createWhitespaceMirror(source)}<\/script>\n`;
201
+ return `${SCRIPTLESS_WORKAROUND_OPEN_TAG_PREFIX}${encodeWorkaroundFilename(filename)}">${createWhitespaceMirror(source)}${LOCATION_BRIDGE_MARKER}<\/script>\n`;
198
202
  }
199
203
  function createWhitespaceMirror(source) {
200
204
  return source.replaceAll(/[^\r\n]/gu, " ");
@@ -251,4 +255,4 @@ if (import.meta.vitest) {
251
255
  });
252
256
  }
253
257
  //#endregion
254
- export { isPatinaFile as a, compareLineColumn as c, getDiagnosticBlock as d, hasVueLikeExtension as i, extractSfcBlocks as l, hasScriptLikeBlock as n, isScriptLikeFile as o, resolveWorkaroundSource as r, isStandaloneHtmlFile as s, appendScriptlessWorkaround as t, formatBlockLabel as u };
258
+ export { isPatinaFile as a, compareLineColumn as c, getDiagnosticBlock as d, hasVueLikeExtension as i, extractSfcBlocks as l, isLocationBridgeProgram as n, isScriptLikeFile as o, resolveWorkaroundSource as r, isStandaloneHtmlFile as s, appendScriptlessWorkaround as t, formatBlockLabel as u };
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "devDependencies": {
13
13
  "@tsdown/css": "0.22.14",
14
14
  "@types/node": "25.9.2",
15
- "@vizejs/native": "0.429.0",
15
+ "@vizejs/native": "0.429.2",
16
16
  "tsdown": "0.22.14",
17
17
  "typescript": "6.0.3",
18
18
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
@@ -49,14 +49,14 @@
49
49
  },
50
50
  "name": "oxlint-plugin-vize",
51
51
  "optionalDependencies": {
52
- "@vizejs/native-darwin-arm64": "0.429.0",
53
- "@vizejs/native-darwin-x64": "0.429.0",
54
- "@vizejs/native-linux-arm64-gnu": "0.429.0",
55
- "@vizejs/native-linux-arm64-musl": "0.429.0",
56
- "@vizejs/native-linux-x64-gnu": "0.429.0",
57
- "@vizejs/native-linux-x64-musl": "0.429.0",
58
- "@vizejs/native-win32-arm64-msvc": "0.429.0",
59
- "@vizejs/native-win32-x64-msvc": "0.429.0"
52
+ "@vizejs/native-darwin-arm64": "0.429.2",
53
+ "@vizejs/native-darwin-x64": "0.429.2",
54
+ "@vizejs/native-linux-arm64-gnu": "0.429.2",
55
+ "@vizejs/native-linux-arm64-musl": "0.429.2",
56
+ "@vizejs/native-linux-x64-gnu": "0.429.2",
57
+ "@vizejs/native-linux-x64-musl": "0.429.2",
58
+ "@vizejs/native-win32-arm64-msvc": "0.429.2",
59
+ "@vizejs/native-win32-x64-msvc": "0.429.2"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public"
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "type": "module",
70
70
  "types": "./dist/index.d.mts",
71
- "version": "0.429.0",
71
+ "version": "0.429.2",
72
72
  "scripts": {
73
73
  "build": "vp pack",
74
74
  "check": "vp check src vite.config.ts",