oxlint-plugin-vize 0.303.0 → 0.306.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 CHANGED
@@ -13,6 +13,7 @@ This package lets Oxlint execute Patina through Vize's native binding while stil
13
13
  - Runs Vize Patina rules inside Oxlint as `vize/*` diagnostics, so Vue-specific findings can live beside Oxlint core rules in one command.
14
14
  - Keeps Oxlint's existing rules and built-in `vue` plugin active. The bridge adds Vize rules; it does not replace `eqeqeq`, `no-console`, or your existing `vue/*` setup.
15
15
  - Ships preset rule maps for JS/TS Oxlint configs: `configs.recommended`, `configs.essential`, `configs.ecosystem`, `configs.opinionated`, `configs.nuxt`, `configs.all`, and type-aware opt-in variants.
16
+ - Ships `createVizeLintConfig()` for the Vite+ `lint` block in `vite.config.ts`, which is the only Oxlint configuration `vp lint` and `vp check` read.
16
17
  - Supports runtime settings through `settings.vize`, including `locale`, `preset`, and `helpLevel`.
17
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
19
  - Resolves Vize native bindings through platform-specific optional dependencies, so published installs do not need a separate `@vizejs/native` package.
@@ -46,6 +47,67 @@ vp install -D oxlint oxlint-plugin-vize
46
47
 
47
48
  ## Usage
48
49
 
50
+ Which file you configure depends on which command you run.
51
+
52
+ | Command | Reads |
53
+ | ----------------------- | ------------------------------------ |
54
+ | `vp lint`, `vp check` | the `lint` block in `vite.config.ts` |
55
+ | `oxlint`, `oxlint-vize` | `.oxlintrc.json` (or `-c <path>`) |
56
+
57
+ > [!IMPORTANT]
58
+ > Vite+ never reads `.oxlintrc.json`. A `.oxlintrc.json` carrying `jsPlugins` and `vize/*` rules
59
+ > looks configured, but `vp lint` ignores the file, so Oxlint never sees a `vize/*` rule id and
60
+ > reports **zero** Vize diagnostics while exiting `0`. `vp lint --init` does not migrate an existing
61
+ > `.oxlintrc.json` either: it writes a fresh `lint` block and leaves the old file in place.
62
+ > If you use `vp lint`, configure the `lint` block.
63
+
64
+ ### With `vp lint` (Vite+)
65
+
66
+ `createVizeLintConfig()` returns the whole `lint` block, so the `jsPlugins` entry cannot go missing:
67
+
68
+ ```ts
69
+ // vite.config.ts
70
+ import { defineConfig } from "vite-plus";
71
+ import { createVizeLintConfig } from "oxlint-plugin-vize";
72
+
73
+ export default defineConfig({
74
+ lint: createVizeLintConfig({
75
+ preset: "essential",
76
+ rules: {
77
+ "no-console": "warn",
78
+ },
79
+ settings: {
80
+ helpLevel: "short",
81
+ },
82
+ }),
83
+ });
84
+ ```
85
+
86
+ `preset` drives both the emitted rule map and `settings.vize.preset`, so the two can never disagree.
87
+ That matters because the bridge silently drops any `vize/*` rule outside the active preset: listing
88
+ `vize/ecosystem/router-link-require-to` while the active preset is `general-recommended` reports
89
+ nothing at all. `createVizeLintConfig` throws for that case, and for unknown `vize/*` ids, instead of
90
+ leaving you with a config that looks enabled and reports nothing. Use `preset: "incremental"` when
91
+ you want only the rules you list, and `preset: "all"` for every bundle at once.
92
+
93
+ The emitted block always enables Oxlint's built-in `vue` plugin. Pass `plugins` to keep the rest of
94
+ your project's plugin list; they are merged with `vue`, never replaced, because narrowing the list
95
+ would silently drop everything those plugins report. A `create-vue` project keeps its generated set
96
+ like this:
97
+
98
+ ```ts
99
+ export default defineConfig({
100
+ lint: {
101
+ ...createVizeLintConfig({
102
+ plugins: ["eslint", "typescript", "unicorn", "oxc"],
103
+ }),
104
+ ignorePatterns: ["dist/**"],
105
+ },
106
+ });
107
+ ```
108
+
109
+ ### With `oxlint` or `oxlint-vize`
110
+
49
111
  Enable Oxlint's built-in `vue` plugin as well as this JS plugin:
50
112
 
51
113
  ```json
package/dist/index.d.mts CHANGED
@@ -4,7 +4,15 @@ import * as _$_oxlint_plugins0 from "@oxlint/plugins";
4
4
  declare const _default: _$_oxlint_plugins0.Plugin;
5
5
  //#endregion
6
6
  //#region src/model.d.ts
7
+ type HelpLevel = "none" | "short" | "full";
7
8
  type PatinaPreset = "general-recommended" | "essential" | "ecosystem" | "incremental" | "opinionated" | "nuxt";
9
+ interface PatinaSettings {
10
+ locale?: string;
11
+ helpLevel?: HelpLevel;
12
+ preset?: PatinaPreset;
13
+ typeAware?: boolean;
14
+ corsaPath?: string;
15
+ }
8
16
  //#endregion
9
17
  //#region src/configs.d.ts
10
18
  type OxlintRuleSeverity = "error" | "warn";
@@ -27,4 +35,97 @@ declare const configs: {
27
35
  readonly recommendedWithTypeAware: OxlintRuleConfig;
28
36
  };
29
37
  //#endregion
30
- export { type OxlintRuleConfig, type OxlintRuleSeverity, type VizeRuleConfigOptions, configs, createVizeRuleConfig, _default as default };
38
+ //#region src/vite-plus.d.ts
39
+ /**
40
+ * Bare specifier Oxlint uses to load this bridge as a JS plugin.
41
+ *
42
+ * Vite+ hands the `lint` block to Oxlint unchanged, so the same specifier works
43
+ * in `vite.config.ts` and in a hand-written `.oxlintrc.json`.
44
+ */
45
+ declare const VIZE_JS_PLUGIN_SPECIFIER = "oxlint-plugin-vize";
46
+ /**
47
+ * Rule bundles `createVizeLintConfig` accepts.
48
+ *
49
+ * `"all"` and `"incremental"` both disable preset gating in the bridge, which is
50
+ * what makes them usable: `"all"` needs every bundle's rules to run at once, and
51
+ * `"incremental"` needs only the rules the caller lists to run.
52
+ */
53
+ type VizeLintPreset = PatinaPreset | "all";
54
+ interface VizeLintConfigOptions {
55
+ /**
56
+ * Include Vize's unstable `vize/type/*` rules in the preset rule map. Off by
57
+ * default, matching `createVizeRuleConfig`.
58
+ */
59
+ includeTypeAware?: boolean;
60
+ /**
61
+ * Built-in Oxlint plugins to keep enabled alongside `vue`.
62
+ *
63
+ * The emitted block always lists `vue`, and narrowing a project's plugin list
64
+ * would silently drop the diagnostics those plugins produce, so anything passed
65
+ * here is merged rather than replaced. A `create-vue` project, for example,
66
+ * needs `["eslint", "typescript", "unicorn", "oxc"]` carried over.
67
+ */
68
+ plugins?: readonly string[];
69
+ /**
70
+ * Rule bundle to enable. Defaults to `"general-recommended"`, the bridge's own
71
+ * default. `"incremental"` emits no preset rules, so only `rules` run.
72
+ */
73
+ preset?: VizeLintPreset;
74
+ /**
75
+ * Extra Oxlint rules merged after the preset rules. Core Oxlint rule ids pass
76
+ * through untouched; `vize/*` ids are validated against the rules the native
77
+ * bridge registers and against the resolved preset.
78
+ */
79
+ rules?: OxlintRuleConfig;
80
+ /**
81
+ * Patina runtime settings forwarded through `settings.vize`. `preset` is
82
+ * intentionally absent: it is derived from `options.preset` so the rule map and
83
+ * the bridge's runtime gate can never disagree.
84
+ */
85
+ settings?: Omit<PatinaSettings, "preset">;
86
+ }
87
+ interface VizeLintConfig {
88
+ jsPlugins: string[];
89
+ plugins: string[];
90
+ rules: OxlintRuleConfig;
91
+ settings: {
92
+ vize: PatinaSettings;
93
+ };
94
+ }
95
+ /**
96
+ * Builds the Oxlint configuration block that runs Patina through this bridge.
97
+ *
98
+ * Vite+ (`vp lint` / `vp check`) reads its Oxlint configuration from the `lint`
99
+ * key of `vite.config.ts` and never reads `.oxlintrc.json`. Wiring the bridge by
100
+ * hand therefore has a silent failure mode: a `.oxlintrc.json` carrying
101
+ * `jsPlugins` and `vize/*` rules looks configured, Vite+ ignores the file, Oxlint
102
+ * never sees a `vize/*` rule id, and `vp lint` reports zero Vize diagnostics
103
+ * while exiting `0`. Spreading this object into `lint` removes the chance to get
104
+ * that wrong:
105
+ *
106
+ * ```ts
107
+ * import { defineConfig } from "vite-plus";
108
+ * import { createVizeLintConfig } from "oxlint-plugin-vize";
109
+ *
110
+ * export default defineConfig({
111
+ * lint: createVizeLintConfig({ preset: "essential" }),
112
+ * });
113
+ * ```
114
+ *
115
+ * Both validations below exist because the bridge's normal failure modes are
116
+ * silent rather than loud:
117
+ *
118
+ * - An unknown `vize/*` id only produces Oxlint's
119
+ * `Rule '...' not found in plugin 'vize'` error when Oxlint actually reads the
120
+ * config. A typo in a config Oxlint never reads reports nothing at all.
121
+ * - A `vize/*` id that is outside the active preset is dropped by the bridge's
122
+ * runtime preset gate (`plugin.ts`), so it stays listed in `rules` and reports
123
+ * nothing. `preset: "incremental"` is the supported way to run an arbitrary
124
+ * subset.
125
+ *
126
+ * @throws {Error} When `options.rules` names a `vize/*` id the native bridge does
127
+ * not register, or one the resolved preset would silently suppress.
128
+ */
129
+ declare function createVizeLintConfig(options?: VizeLintConfigOptions): VizeLintConfig;
130
+ //#endregion
131
+ export { type OxlintRuleConfig, type OxlintRuleSeverity, VIZE_JS_PLUGIN_SPECIFIER, type VizeLintConfig, type VizeLintConfigOptions, type VizeLintPreset, type VizeRuleConfigOptions, configs, createVizeLintConfig, createVizeRuleConfig, _default as default };
package/dist/index.mjs CHANGED
@@ -623,4 +623,100 @@ if (import.meta.vitest) {
623
623
  });
624
624
  }
625
625
  //#endregion
626
- export { configs, createVizeRuleConfig, plugin_default as default };
626
+ //#region src/vite-plus.ts
627
+ /**
628
+ * Bare specifier Oxlint uses to load this bridge as a JS plugin.
629
+ *
630
+ * Vite+ hands the `lint` block to Oxlint unchanged, so the same specifier works
631
+ * in `vite.config.ts` and in a hand-written `.oxlintrc.json`.
632
+ */
633
+ const VIZE_JS_PLUGIN_SPECIFIER = "oxlint-plugin-vize";
634
+ const VIZE_RULE_PREFIX = "vize/";
635
+ /**
636
+ * Builds the Oxlint configuration block that runs Patina through this bridge.
637
+ *
638
+ * Vite+ (`vp lint` / `vp check`) reads its Oxlint configuration from the `lint`
639
+ * key of `vite.config.ts` and never reads `.oxlintrc.json`. Wiring the bridge by
640
+ * hand therefore has a silent failure mode: a `.oxlintrc.json` carrying
641
+ * `jsPlugins` and `vize/*` rules looks configured, Vite+ ignores the file, Oxlint
642
+ * never sees a `vize/*` rule id, and `vp lint` reports zero Vize diagnostics
643
+ * while exiting `0`. Spreading this object into `lint` removes the chance to get
644
+ * that wrong:
645
+ *
646
+ * ```ts
647
+ * import { defineConfig } from "vite-plus";
648
+ * import { createVizeLintConfig } from "oxlint-plugin-vize";
649
+ *
650
+ * export default defineConfig({
651
+ * lint: createVizeLintConfig({ preset: "essential" }),
652
+ * });
653
+ * ```
654
+ *
655
+ * Both validations below exist because the bridge's normal failure modes are
656
+ * silent rather than loud:
657
+ *
658
+ * - An unknown `vize/*` id only produces Oxlint's
659
+ * `Rule '...' not found in plugin 'vize'` error when Oxlint actually reads the
660
+ * config. A typo in a config Oxlint never reads reports nothing at all.
661
+ * - A `vize/*` id that is outside the active preset is dropped by the bridge's
662
+ * runtime preset gate (`plugin.ts`), so it stays listed in `rules` and reports
663
+ * nothing. `preset: "incremental"` is the supported way to run an arbitrary
664
+ * subset.
665
+ *
666
+ * @throws {Error} When `options.rules` names a `vize/*` id the native bridge does
667
+ * not register, or one the resolved preset would silently suppress.
668
+ */
669
+ function createVizeLintConfig(options = {}) {
670
+ const preset = options.preset ?? "general-recommended";
671
+ const extraRules = options.rules ?? {};
672
+ assertUsableVizeRules(extraRules, preset);
673
+ return {
674
+ jsPlugins: [VIZE_JS_PLUGIN_SPECIFIER],
675
+ plugins: [...new Set(["vue", ...options.plugins ?? []])],
676
+ rules: {
677
+ ...createPresetRules(preset, options.includeTypeAware),
678
+ ...extraRules
679
+ },
680
+ settings: { vize: {
681
+ ...options.settings,
682
+ preset: toRuntimePreset(preset)
683
+ } }
684
+ };
685
+ }
686
+ function createPresetRules(preset, includeTypeAware) {
687
+ return preset === "incremental" ? {} : createVizeRuleConfig({
688
+ includeTypeAware,
689
+ preset
690
+ });
691
+ }
692
+ /**
693
+ * Maps a rule bundle to the `settings.vize.preset` the bridge must run with.
694
+ *
695
+ * `"all"` becomes `"incremental"` because the bridge gates each rule on preset
696
+ * membership; gating an all-bundles rule map by any single preset would suppress
697
+ * the rules that only belong to the other bundles.
698
+ */
699
+ function toRuntimePreset(preset) {
700
+ return preset === "all" ? "incremental" : preset;
701
+ }
702
+ function assertUsableVizeRules(rules, preset) {
703
+ const ruleMetaById = new Map(getPatinaRules().map((ruleMeta) => [`${VIZE_RULE_PREFIX}${ruleMeta.name}`, ruleMeta]));
704
+ const configuredIds = Object.keys(rules).filter((ruleId) => ruleId.startsWith(VIZE_RULE_PREFIX)).sort();
705
+ const unknownIds = configuredIds.filter((ruleId) => !ruleMetaById.has(ruleId));
706
+ if (unknownIds.length > 0) throw new Error(`Unknown Vize rule ${pluralizeIds(unknownIds)}: ${unknownIds.join(", ")}. Check the id against the rules oxlint-plugin-vize registers.`);
707
+ const runtimePreset = toRuntimePreset(preset);
708
+ const suppressedIds = configuredIds.filter((ruleId) => isSuppressedByPreset(ruleMetaById.get(ruleId), runtimePreset));
709
+ if (suppressedIds.length > 0) throw new Error(`Vize rule ${pluralizeIds(suppressedIds)} outside the "${preset}" preset: ${suppressedIds.join(", ")}. Use preset: "incremental" to run an explicit rule subset, or pick the preset that owns these rules.`);
710
+ }
711
+ /**
712
+ * Mirrors the runtime gate in `plugin.ts`: rules with no preset membership are
713
+ * never gated, and `"incremental"` skips gating entirely.
714
+ */
715
+ function isSuppressedByPreset(ruleMeta, runtimePreset) {
716
+ return ruleMeta.presets.length > 0 && runtimePreset !== "incremental" && !ruleMeta.presets.includes(runtimePreset);
717
+ }
718
+ function pluralizeIds(ids) {
719
+ return ids.length === 1 ? "id" : "ids";
720
+ }
721
+ //#endregion
722
+ export { VIZE_JS_PLUGIN_SPECIFIER, configs, createVizeLintConfig, createVizeRuleConfig, plugin_default as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-vize",
3
- "version": "0.303.0",
3
+ "version": "0.306.0",
4
4
  "description": "Oxlint JS plugin bridge for Vize Patina",
5
5
  "keywords": [
6
6
  "lint",
@@ -44,21 +44,21 @@
44
44
  "devDependencies": {
45
45
  "@tsdown/css": "0.22.0",
46
46
  "@types/node": "25.9.2",
47
- "@vizejs/native": "0.303.0",
47
+ "@vizejs/native": "0.306.0",
48
48
  "tsdown": "0.22.0",
49
49
  "typescript": "6.0.3",
50
50
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.21",
51
51
  "vite-plus": "0.1.21"
52
52
  },
53
53
  "optionalDependencies": {
54
- "@vizejs/native-darwin-arm64": "0.303.0",
55
- "@vizejs/native-darwin-x64": "0.303.0",
56
- "@vizejs/native-linux-arm64-gnu": "0.303.0",
57
- "@vizejs/native-linux-arm64-musl": "0.303.0",
58
- "@vizejs/native-linux-x64-gnu": "0.303.0",
59
- "@vizejs/native-linux-x64-musl": "0.303.0",
60
- "@vizejs/native-win32-arm64-msvc": "0.303.0",
61
- "@vizejs/native-win32-x64-msvc": "0.303.0"
54
+ "@vizejs/native-darwin-arm64": "0.306.0",
55
+ "@vizejs/native-darwin-x64": "0.306.0",
56
+ "@vizejs/native-linux-arm64-gnu": "0.306.0",
57
+ "@vizejs/native-linux-arm64-musl": "0.306.0",
58
+ "@vizejs/native-linux-x64-gnu": "0.306.0",
59
+ "@vizejs/native-linux-x64-musl": "0.306.0",
60
+ "@vizejs/native-win32-arm64-msvc": "0.306.0",
61
+ "@vizejs/native-win32-x64-msvc": "0.306.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": "^22 || >= 24"