oxlint-plugin-vize 0.390.0 → 0.391.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 +41 -13
- package/dist/index.d.mts +78 -22
- package/dist/index.mjs +225 -27
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -12,8 +12,8 @@ This package lets Oxlint execute Patina through Vize's native binding while stil
|
|
|
12
12
|
|
|
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
|
-
- 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.
|
|
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
|
+
- 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
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.
|
|
19
19
|
- Resolves Vize native bindings through platform-specific optional dependencies, so published installs do not need a separate `@vizejs/native` package.
|
|
@@ -63,7 +63,7 @@ Which file you configure depends on which command you run.
|
|
|
63
63
|
|
|
64
64
|
### With `vp lint` (Vite+)
|
|
65
65
|
|
|
66
|
-
`createVizeLintConfig()` returns
|
|
66
|
+
`createVizeLintConfig()` returns a complete `lint` block, so the `jsPlugins` entry cannot go missing. The default preset is `"happy-path"`/`"general-recommended"`: use it when you want a safe Vue baseline without taking a position on stronger style or framework choices.
|
|
67
67
|
|
|
68
68
|
```ts
|
|
69
69
|
// vite.config.ts
|
|
@@ -72,7 +72,7 @@ import { createVizeLintConfig } from "oxlint-plugin-vize";
|
|
|
72
72
|
|
|
73
73
|
export default defineConfig({
|
|
74
74
|
lint: createVizeLintConfig({
|
|
75
|
-
preset: "
|
|
75
|
+
preset: "happy-path",
|
|
76
76
|
rules: {
|
|
77
77
|
"no-console": "warn",
|
|
78
78
|
},
|
|
@@ -83,12 +83,18 @@ export default defineConfig({
|
|
|
83
83
|
});
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
`preset` drives both the emitted rule map and `settings.vize.preset
|
|
87
|
-
That matters because the bridge silently drops any `vize/*`
|
|
88
|
-
`vize/ecosystem/router-link-require-to` while the active
|
|
89
|
-
nothing at all. `createVizeLintConfig` throws for that case,
|
|
90
|
-
leaving you with a config that looks enabled and reports
|
|
91
|
-
|
|
86
|
+
`preset` drives both the emitted rule map and `settings.vize.preset` when a single bundle is
|
|
87
|
+
selected, so the two can never disagree. That matters because the bridge silently drops any `vize/*`
|
|
88
|
+
rule outside the active preset: listing `vize/ecosystem/router-link-require-to` while the active
|
|
89
|
+
preset is `general-recommended` reports nothing at all. `createVizeLintConfig` throws for that case,
|
|
90
|
+
and for unknown `vize/*` ids, instead of leaving you with a config that looks enabled and reports
|
|
91
|
+
nothing.
|
|
92
|
+
|
|
93
|
+
Use `preset: "incremental"` when you want only the rules you list, `preset: "all"` for every bundle
|
|
94
|
+
at once, and `preset: ["happy-path", "ecosystem"]` or `presets: ["happy-path", "ecosystem"]` when
|
|
95
|
+
you want several bundles without taking on all of them. Multiple-bundle configs emit
|
|
96
|
+
`settings.vize.preset: "incremental"` so the runtime gate cannot suppress one selected bundle's
|
|
97
|
+
rules while another bundle is active.
|
|
92
98
|
|
|
93
99
|
The emitted block always enables Oxlint's built-in `vue` plugin. Pass `plugins` to keep the rest of
|
|
94
100
|
your project's plugin list; they are merged with `vue`, never replaced, because narrowing the list
|
|
@@ -106,6 +112,27 @@ export default defineConfig({
|
|
|
106
112
|
});
|
|
107
113
|
```
|
|
108
114
|
|
|
115
|
+
For Flat Config-style composition in `vite.config.ts`, use spreadable fragments and collapse them
|
|
116
|
+
back to Vite+'s object-shaped `lint` config:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { defineConfig } from "vite-plus";
|
|
120
|
+
import { defineVizeLintConfig, flatConfigs } from "oxlint-plugin-vize";
|
|
121
|
+
|
|
122
|
+
export default defineConfig({
|
|
123
|
+
lint: defineVizeLintConfig(...flatConfigs.recommended, ...flatConfigs.ecosystem, {
|
|
124
|
+
ignorePatterns: ["dist/**"],
|
|
125
|
+
rules: {
|
|
126
|
+
"no-console": "warn",
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The exported fragments include `flatConfigs.recommended`, `flatConfigs.happyPath`,
|
|
133
|
+
`flatConfigs.essential`, `flatConfigs.ecosystem`, `flatConfigs.nuxt`, `flatConfigs.opinionated`,
|
|
134
|
+
and `flatConfigs.all`, plus the same `*WithTypeAware` variants as `configs`.
|
|
135
|
+
|
|
109
136
|
### With `oxlint` or `oxlint-vize`
|
|
110
137
|
|
|
111
138
|
Enable Oxlint's built-in `vue` plugin as well as this JS plugin:
|
|
@@ -144,7 +171,7 @@ export default {
|
|
|
144
171
|
};
|
|
145
172
|
```
|
|
146
173
|
|
|
147
|
-
`configs.recommended`, `configs.essential`, `configs.ecosystem`, `configs.opinionated`, `configs.nuxt`, and `configs.all` intentionally skip Vize's unstable type-aware rules for now. If you explicitly want those experimental rules too, use `configs.recommendedWithTypeAware`, `configs.ecosystemWithTypeAware`, `configs.opinionatedWithTypeAware`, or `createVizeRuleConfig({ includeTypeAware: true, preset: ... })`. Set `settings.vize.typeAware: true` to run the shared full-file Patina pass with Corsa enabled; explicitly configured `vize/type/*` rules also opt in when they are queried one by one.
|
|
174
|
+
`configs.recommended`, `configs.happyPath`, `configs.essential`, `configs.ecosystem`, `configs.opinionated`, `configs.nuxt`, and `configs.all` intentionally skip Vize's unstable type-aware rules for now. If you explicitly want those experimental rules too, use `configs.recommendedWithTypeAware`, `configs.happyPathWithTypeAware`, `configs.ecosystemWithTypeAware`, `configs.opinionatedWithTypeAware`, or `createVizeRuleConfig({ includeTypeAware: true, preset: ... })`. Set `settings.vize.typeAware: true` to run the shared full-file Patina pass with Corsa enabled; explicitly configured `vize/type/*` rules also opt in when they are queried one by one.
|
|
148
175
|
|
|
149
176
|
You can pass Patina settings through `settings.vize`:
|
|
150
177
|
|
|
@@ -161,11 +188,12 @@ You can pass Patina settings through `settings.vize`:
|
|
|
161
188
|
}
|
|
162
189
|
```
|
|
163
190
|
|
|
164
|
-
- `preset` accepts `"general-recommended"`, `"essential"`, `"ecosystem"`, `"incremental"`, `"opinionated"`, or `"
|
|
191
|
+
- `preset` accepts `"general-recommended"`/`"happy-path"`, `"essential"`, `"ecosystem"`, `"incremental"`, `"opinionated"`, `"nuxt"`, or `"all"`.
|
|
165
192
|
- `preset` defaults to `"general-recommended"`.
|
|
166
193
|
- Bundle presets keep out-of-bundle rules quiet even if they are still listed in `rules`.
|
|
167
194
|
- `"incremental"` skips bundle gating and runs only the Vize rules you explicitly configure in Oxlint.
|
|
168
|
-
- `"
|
|
195
|
+
- `"all"` is accepted as a settings alias for `"incremental"`; use `configs.all` or `createVizeLintConfig({ preset: "all" })` when you also want every rule emitted.
|
|
196
|
+
- `"ecosystem"` enables Vize's Vue Router, Vue I18n, Pinia, Vue Test Utils, and Void Vue rules without taking on the full opinionated preset.
|
|
169
197
|
- `"opinionated"` is the preset that enables Vize's built-in script rules such as `vize/script/no-options-api`.
|
|
170
198
|
- Legacy aliases such as `"GeneralRecommended"`, `"Essential"`, `"Ecosystem"`, `"Incremental"`, `"Opinionated"`, `"Nuxt"`, and `"happy-path"` are still accepted for compatibility.
|
|
171
199
|
- `helpLevel` accepts `"full"`, `"short"`, or `"none"`. Short help keeps one actionable sentence and removes inline examples and trailing rationale.
|
package/dist/index.d.mts
CHANGED
|
@@ -16,10 +16,12 @@ interface PatinaSettings {
|
|
|
16
16
|
type OxlintRuleSeverity = "error" | "warn" | "off";
|
|
17
17
|
type OxlintRuleEntry = OxlintRuleSeverity | [OxlintRuleSeverity, ...unknown[]];
|
|
18
18
|
type OxlintRuleConfig = Record<string, OxlintRuleEntry>;
|
|
19
|
-
type VizeRuleConfigPreset = Exclude<PatinaPreset, "incremental"> | "all";
|
|
19
|
+
type VizeRuleConfigPreset = Exclude<PatinaPreset, "incremental"> | "happy-path" | "all";
|
|
20
|
+
type VizeRuleConfigPresetInput = VizeRuleConfigPreset | readonly VizeRuleConfigPreset[];
|
|
20
21
|
interface VizeRuleConfigOptions {
|
|
21
22
|
includeTypeAware?: boolean;
|
|
22
|
-
preset?:
|
|
23
|
+
preset?: VizeRuleConfigPresetInput;
|
|
24
|
+
presets?: readonly VizeRuleConfigPreset[];
|
|
23
25
|
}
|
|
24
26
|
declare function createVizeRuleConfig(options?: VizeRuleConfigOptions): OxlintRuleConfig;
|
|
25
27
|
declare const configs: {
|
|
@@ -27,6 +29,8 @@ declare const configs: {
|
|
|
27
29
|
readonly ecosystem: OxlintRuleConfig;
|
|
28
30
|
readonly ecosystemWithTypeAware: OxlintRuleConfig;
|
|
29
31
|
readonly essential: OxlintRuleConfig;
|
|
32
|
+
readonly happyPath: OxlintRuleConfig;
|
|
33
|
+
readonly happyPathWithTypeAware: OxlintRuleConfig;
|
|
30
34
|
readonly nuxt: OxlintRuleConfig;
|
|
31
35
|
readonly opinionated: OxlintRuleConfig;
|
|
32
36
|
readonly opinionatedWithTypeAware: OxlintRuleConfig;
|
|
@@ -47,11 +51,12 @@ type VitePlusLintPlugin = "eslint" | "import" | "jest" | "jsdoc" | "jsx-a11y" |
|
|
|
47
51
|
/**
|
|
48
52
|
* Rule bundles `createVizeLintConfig` accepts.
|
|
49
53
|
*
|
|
50
|
-
* `"all"
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* `"all"`, `"incremental"`, and multiple-preset selections disable preset
|
|
55
|
+
* gating in the bridge. That is what makes them usable: the runtime only accepts
|
|
56
|
+
* one preset, while those shapes need exactly the emitted rule map to run.
|
|
53
57
|
*/
|
|
54
|
-
type VizeLintPreset = PatinaPreset | "all";
|
|
58
|
+
type VizeLintPreset = PatinaPreset | "happy-path" | "all";
|
|
59
|
+
type VizeLintPresetInput = VizeLintPreset | readonly VizeLintPreset[];
|
|
55
60
|
interface VizeLintConfigOptions {
|
|
56
61
|
/**
|
|
57
62
|
* Include Vize's unstable `vize/type/*` rules in the preset rule map. Off by
|
|
@@ -68,30 +73,38 @@ interface VizeLintConfigOptions {
|
|
|
68
73
|
*/
|
|
69
74
|
plugins?: readonly VitePlusLintPlugin[];
|
|
70
75
|
/**
|
|
71
|
-
* Rule bundle to enable. Defaults to `"general-recommended"`, the
|
|
72
|
-
* default. `"incremental"` emits no preset rules, so only `rules`
|
|
76
|
+
* Rule bundle or bundles to enable. Defaults to `"general-recommended"`, the
|
|
77
|
+
* bridge's own default. `"incremental"` emits no preset rules, so only `rules`
|
|
78
|
+
* run.
|
|
73
79
|
*/
|
|
74
|
-
preset?:
|
|
80
|
+
preset?: VizeLintPresetInput;
|
|
81
|
+
/**
|
|
82
|
+
* Additional rule bundles to enable alongside `preset`. This is equivalent to
|
|
83
|
+
* passing an array to `preset`, but reads better when the base preset is kept
|
|
84
|
+
* separate from project-specific add-ons.
|
|
85
|
+
*/
|
|
86
|
+
presets?: readonly VizeLintPreset[];
|
|
75
87
|
/**
|
|
76
88
|
* Extra Oxlint rules merged after the preset rules. Core Oxlint rule ids pass
|
|
77
89
|
* through untouched; `vize/*` ids are validated against the rules the native
|
|
78
|
-
* bridge registers and against the resolved preset.
|
|
90
|
+
* bridge registers and, for a single gated preset, against the resolved preset.
|
|
79
91
|
*/
|
|
80
92
|
rules?: OxlintRuleConfig;
|
|
81
93
|
/**
|
|
82
94
|
* Patina runtime settings forwarded through `settings.vize`. `preset` is
|
|
83
|
-
* intentionally absent: it is derived from `options.preset`
|
|
84
|
-
* the bridge's runtime gate can never disagree.
|
|
95
|
+
* intentionally absent: it is derived from `options.preset`/`options.presets`
|
|
96
|
+
* so the rule map and the bridge's runtime gate can never disagree.
|
|
85
97
|
*/
|
|
86
98
|
settings?: Omit<PatinaSettings, "preset">;
|
|
87
99
|
}
|
|
88
|
-
interface
|
|
100
|
+
interface VizeLintConfigSettings extends Record<string, unknown> {
|
|
101
|
+
vize: PatinaSettings;
|
|
102
|
+
}
|
|
103
|
+
interface VizeLintConfig extends Record<string, unknown> {
|
|
89
104
|
jsPlugins: string[];
|
|
90
105
|
plugins: VitePlusLintPlugin[];
|
|
91
106
|
rules: OxlintRuleConfig;
|
|
92
|
-
settings:
|
|
93
|
-
vize: PatinaSettings;
|
|
94
|
-
};
|
|
107
|
+
settings: VizeLintConfigSettings;
|
|
95
108
|
}
|
|
96
109
|
/**
|
|
97
110
|
* Builds the Oxlint configuration block that runs Patina through this bridge.
|
|
@@ -119,14 +132,57 @@ interface VizeLintConfig {
|
|
|
119
132
|
* - An unknown `vize/*` id only produces Oxlint's
|
|
120
133
|
* `Rule '...' not found in plugin 'vize'` error when Oxlint actually reads the
|
|
121
134
|
* config. A typo in a config Oxlint never reads reports nothing at all.
|
|
122
|
-
* - A `vize/*` id that is outside
|
|
123
|
-
* runtime preset gate (`plugin.ts`), so it stays listed in `rules` and
|
|
124
|
-
* nothing. `preset: "incremental"` is the supported way to run an
|
|
125
|
-
* subset.
|
|
135
|
+
* - A `vize/*` id that is outside a single active preset is dropped by the
|
|
136
|
+
* bridge's runtime preset gate (`plugin.ts`), so it stays listed in `rules` and
|
|
137
|
+
* reports nothing. `preset: "incremental"` is the supported way to run an
|
|
138
|
+
* arbitrary subset.
|
|
126
139
|
*
|
|
127
140
|
* @throws {Error} When `options.rules` names a `vize/*` id the native bridge does
|
|
128
|
-
* not register, or one
|
|
141
|
+
* not register, or one a single resolved preset would silently suppress.
|
|
129
142
|
*/
|
|
130
143
|
declare function createVizeLintConfig(options?: VizeLintConfigOptions): VizeLintConfig;
|
|
131
144
|
//#endregion
|
|
132
|
-
|
|
145
|
+
//#region src/vite-plus-flat-config.d.ts
|
|
146
|
+
interface VizeLintConfigFragment extends Record<string, unknown> {
|
|
147
|
+
extends?: readonly (VizeLintConfigFragment | VizeLintFlatConfig)[];
|
|
148
|
+
ignorePatterns?: readonly string[];
|
|
149
|
+
jsPlugins?: readonly string[];
|
|
150
|
+
overrides?: readonly unknown[];
|
|
151
|
+
plugins?: readonly VitePlusLintPlugin[];
|
|
152
|
+
rules?: VizeLintConfig["rules"];
|
|
153
|
+
settings?: Record<string, unknown> & {
|
|
154
|
+
vize?: PatinaSettings | Record<string, unknown>;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
type VizeLintFlatConfig = readonly VizeLintConfigFragment[];
|
|
158
|
+
/**
|
|
159
|
+
* Returns spreadable Oxlint/Vite+ config fragments for Flat Config style use.
|
|
160
|
+
*
|
|
161
|
+
* The fragments intentionally set `settings.vize.preset` to `"incremental"` so
|
|
162
|
+
* multiple fragments can be composed without one preset's runtime gate
|
|
163
|
+
* suppressing rules emitted by another fragment.
|
|
164
|
+
*/
|
|
165
|
+
declare function createVizeLintFlatConfig(options?: VizeLintConfigOptions): VizeLintFlatConfig;
|
|
166
|
+
declare const flatConfigs: {
|
|
167
|
+
all: VizeLintFlatConfig;
|
|
168
|
+
ecosystem: VizeLintFlatConfig;
|
|
169
|
+
ecosystemWithTypeAware: VizeLintFlatConfig;
|
|
170
|
+
essential: VizeLintFlatConfig;
|
|
171
|
+
happyPath: VizeLintFlatConfig;
|
|
172
|
+
happyPathWithTypeAware: VizeLintFlatConfig;
|
|
173
|
+
nuxt: VizeLintFlatConfig;
|
|
174
|
+
opinionated: VizeLintFlatConfig;
|
|
175
|
+
opinionatedWithTypeAware: VizeLintFlatConfig;
|
|
176
|
+
recommended: VizeLintFlatConfig;
|
|
177
|
+
recommendedWithTypeAware: VizeLintFlatConfig;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Collapses Flat Config-style fragments into the object shape Vite+ accepts.
|
|
181
|
+
*
|
|
182
|
+
* `vp lint` currently expects a single Oxlint config object under `lint`, so this
|
|
183
|
+
* helper lets `vite.config.ts` keep a flat, spread-friendly authoring style while
|
|
184
|
+
* still handing Vite+ the object it can read.
|
|
185
|
+
*/
|
|
186
|
+
declare function defineVizeLintConfig(...entries: readonly (VizeLintConfigFragment | VizeLintFlatConfig)[]): VizeLintConfig;
|
|
187
|
+
//#endregion
|
|
188
|
+
export { type OxlintRuleConfig, type OxlintRuleEntry, type OxlintRuleSeverity, VIZE_JS_PLUGIN_SPECIFIER, type VitePlusLintPlugin, type VizeLintConfig, type VizeLintConfigFragment, type VizeLintConfigOptions, type VizeLintConfigSettings, type VizeLintFlatConfig, type VizeLintPreset, type VizeLintPresetInput, type VizeRuleConfigOptions, type VizeRuleConfigPreset, type VizeRuleConfigPresetInput, configs, createVizeLintConfig, createVizeLintFlatConfig, createVizeRuleConfig, _default as default, defineVizeLintConfig, flatConfigs };
|
package/dist/index.mjs
CHANGED
|
@@ -194,10 +194,10 @@ const PRESET_ALIASES = new Map([
|
|
|
194
194
|
["ecosystem", "ecosystem"],
|
|
195
195
|
["eco", "ecosystem"],
|
|
196
196
|
["incremental", "incremental"],
|
|
197
|
+
["all", "incremental"],
|
|
197
198
|
["opinionated", "opinionated"],
|
|
198
199
|
["opnionated", "opinionated"],
|
|
199
200
|
["strict", "opinionated"],
|
|
200
|
-
["all", "opinionated"],
|
|
201
201
|
["nuxt", "nuxt"]
|
|
202
202
|
]);
|
|
203
203
|
function getVizeSettings(context) {
|
|
@@ -271,6 +271,7 @@ if (import.meta.vitest) {
|
|
|
271
271
|
it("normalizes preset aliases", () => {
|
|
272
272
|
expect(parseVizeSettings({ preset: "recommended" })).toEqual({ preset: "general-recommended" });
|
|
273
273
|
expect(parseVizeSettings({ preset: "incremental" })).toEqual({ preset: "incremental" });
|
|
274
|
+
expect(parseVizeSettings({ preset: "all" })).toEqual({ preset: "incremental" });
|
|
274
275
|
expect(parseVizeSettings({ preset: "ecosystem" })).toEqual({ preset: "ecosystem" });
|
|
275
276
|
expect(parseVizeSettings({ preset: "strict" })).toEqual({ preset: "opinionated" });
|
|
276
277
|
expect(parseVizeSettings({ preset: "Opnionated" })).toEqual({ preset: "opinionated" });
|
|
@@ -583,11 +584,11 @@ var plugin_default = definePlugin({
|
|
|
583
584
|
//#region src/configs.ts
|
|
584
585
|
const TYPE_AWARE_RULE_PREFIX = "type/";
|
|
585
586
|
function createVizeRuleConfig(options = {}) {
|
|
586
|
-
const
|
|
587
|
+
const presets = resolveRuleConfigPresets(options);
|
|
587
588
|
const includeTypeAware = options.includeTypeAware ?? false;
|
|
588
589
|
const rules = {};
|
|
589
590
|
for (const ruleMeta of getPatinaRules()) {
|
|
590
|
-
if (!
|
|
591
|
+
if (!matchesPresets(ruleMeta, presets)) continue;
|
|
591
592
|
if (!includeTypeAware && isTypeAwareRule(ruleMeta)) continue;
|
|
592
593
|
rules[`vize/${ruleMeta.name}`] = toOxlintSeverity(ruleMeta.defaultSeverity);
|
|
593
594
|
}
|
|
@@ -601,6 +602,11 @@ const configs = {
|
|
|
601
602
|
preset: "ecosystem"
|
|
602
603
|
}),
|
|
603
604
|
essential: createVizeRuleConfig({ preset: "essential" }),
|
|
605
|
+
happyPath: createVizeRuleConfig({ preset: "happy-path" }),
|
|
606
|
+
happyPathWithTypeAware: createVizeRuleConfig({
|
|
607
|
+
includeTypeAware: true,
|
|
608
|
+
preset: "happy-path"
|
|
609
|
+
}),
|
|
604
610
|
nuxt: createVizeRuleConfig({ preset: "nuxt" }),
|
|
605
611
|
opinionated: createVizeRuleConfig({ preset: "opinionated" }),
|
|
606
612
|
opinionatedWithTypeAware: createVizeRuleConfig({
|
|
@@ -613,8 +619,18 @@ const configs = {
|
|
|
613
619
|
preset: "general-recommended"
|
|
614
620
|
})
|
|
615
621
|
};
|
|
616
|
-
function
|
|
617
|
-
return preset === "
|
|
622
|
+
function normalizeRuleConfigPreset(preset) {
|
|
623
|
+
return preset === "happy-path" ? "general-recommended" : preset;
|
|
624
|
+
}
|
|
625
|
+
function resolveRuleConfigPresets(options) {
|
|
626
|
+
const requestedPresets = [...toPresetArray$1((options.preset === void 0 && options.presets === void 0 ? "general-recommended" : options.preset) ?? []), ...options.presets ?? []].map(normalizeRuleConfigPreset);
|
|
627
|
+
return [...new Set(requestedPresets)];
|
|
628
|
+
}
|
|
629
|
+
function toPresetArray$1(preset) {
|
|
630
|
+
return Array.isArray(preset) ? preset : [preset];
|
|
631
|
+
}
|
|
632
|
+
function matchesPresets(ruleMeta, presets) {
|
|
633
|
+
return presets.includes("all") || presets.some((preset) => preset !== "all" && ruleMeta.presets.includes(preset));
|
|
618
634
|
}
|
|
619
635
|
function isTypeAwareRule(ruleMeta) {
|
|
620
636
|
return ruleMeta.name.startsWith(TYPE_AWARE_RULE_PREFIX);
|
|
@@ -628,10 +644,41 @@ if (import.meta.vitest) {
|
|
|
628
644
|
it("normalizes Vize warning severity to Oxlint's warn", () => {
|
|
629
645
|
expect(configs.recommended["vize/vue/no-multi-spaces"]).toBe("warn");
|
|
630
646
|
});
|
|
647
|
+
it("accepts the CLI happy-path preset alias", () => {
|
|
648
|
+
expect(createVizeRuleConfig({ preset: "happy-path" })).toEqual(configs.recommended);
|
|
649
|
+
expect(configs.happyPath).toEqual(configs.recommended);
|
|
650
|
+
});
|
|
651
|
+
it("unions multiple presets", () => {
|
|
652
|
+
const config = createVizeRuleConfig({ presets: ["happy-path", "ecosystem"] });
|
|
653
|
+
expect(config["vize/script/valid-define-props"]).toBe("error");
|
|
654
|
+
expect(config["vize/ecosystem/router-link-require-to"]).toBe("error");
|
|
655
|
+
expect(config["vize/script/no-options-api"]).toBeUndefined();
|
|
656
|
+
});
|
|
657
|
+
it("accepts multiple presets through the preset option", () => {
|
|
658
|
+
expect(createVizeRuleConfig({ preset: ["happy-path", "ecosystem"] })).toEqual(createVizeRuleConfig({ presets: ["happy-path", "ecosystem"] }));
|
|
659
|
+
});
|
|
631
660
|
it("filters rules by preset", () => {
|
|
632
661
|
expect(configs.essential["vize/vue/require-v-for-key"]).toBe("error");
|
|
633
662
|
expect(configs.essential["vize/vue/require-scoped-style"]).toBeUndefined();
|
|
634
663
|
});
|
|
664
|
+
it("enables script correctness rules in essential and happy-path", () => {
|
|
665
|
+
expect(configs.essential["vize/script/valid-define-props"]).toBe("error");
|
|
666
|
+
expect(configs.essential["vize/script/no-import-compiler-macros"]).toBe("error");
|
|
667
|
+
expect(configs.recommended["vize/script/valid-define-props"]).toBe("error");
|
|
668
|
+
expect(configs.recommended["vize/script/no-import-compiler-macros"]).toBe("error");
|
|
669
|
+
});
|
|
670
|
+
it("keeps low-noise happy-path script warnings out of essential", () => {
|
|
671
|
+
expect(configs.recommended["vize/script/no-duplicate-attr-inheritance"]).toBe("warn");
|
|
672
|
+
expect(configs.recommended["vize/script/no-reactive-destructure"]).toBeUndefined();
|
|
673
|
+
expect(configs.essential["vize/script/no-duplicate-attr-inheritance"]).toBeUndefined();
|
|
674
|
+
expect(configs.recommended["vize/script/no-unused-emit-declarations"]).toBeUndefined();
|
|
675
|
+
expect(configs.essential["vize/script/no-unused-emit-declarations"]).toBeUndefined();
|
|
676
|
+
});
|
|
677
|
+
it("keeps opinionated script style out of happy-path", () => {
|
|
678
|
+
expect(configs.recommended["vize/script/no-options-api"]).toBeUndefined();
|
|
679
|
+
expect(configs.recommended["vize/script/define-props-declaration"]).toBeUndefined();
|
|
680
|
+
expect(configs.opinionated["vize/script/no-options-api"]).toBe("error");
|
|
681
|
+
});
|
|
635
682
|
it("skips unstable type-aware rules by default", () => {
|
|
636
683
|
expect(configs.opinionated["vize/type/require-typed-props"]).toBeUndefined();
|
|
637
684
|
expect(configs.opinionatedWithTypeAware["vize/type/require-typed-props"]).toBe("warn");
|
|
@@ -677,6 +724,7 @@ if (import.meta.vitest) {
|
|
|
677
724
|
*/
|
|
678
725
|
const VIZE_JS_PLUGIN_SPECIFIER = "oxlint-plugin-vize";
|
|
679
726
|
const VIZE_RULE_PREFIX = "vize/";
|
|
727
|
+
const DEFAULT_VIZE_LINT_PRESET = "general-recommended";
|
|
680
728
|
/**
|
|
681
729
|
* Builds the Oxlint configuration block that runs Patina through this bridge.
|
|
682
730
|
*
|
|
@@ -703,55 +751,74 @@ const VIZE_RULE_PREFIX = "vize/";
|
|
|
703
751
|
* - An unknown `vize/*` id only produces Oxlint's
|
|
704
752
|
* `Rule '...' not found in plugin 'vize'` error when Oxlint actually reads the
|
|
705
753
|
* config. A typo in a config Oxlint never reads reports nothing at all.
|
|
706
|
-
* - A `vize/*` id that is outside
|
|
707
|
-
* runtime preset gate (`plugin.ts`), so it stays listed in `rules` and
|
|
708
|
-
* nothing. `preset: "incremental"` is the supported way to run an
|
|
709
|
-
* subset.
|
|
754
|
+
* - A `vize/*` id that is outside a single active preset is dropped by the
|
|
755
|
+
* bridge's runtime preset gate (`plugin.ts`), so it stays listed in `rules` and
|
|
756
|
+
* reports nothing. `preset: "incremental"` is the supported way to run an
|
|
757
|
+
* arbitrary subset.
|
|
710
758
|
*
|
|
711
759
|
* @throws {Error} When `options.rules` names a `vize/*` id the native bridge does
|
|
712
|
-
* not register, or one
|
|
760
|
+
* not register, or one a single resolved preset would silently suppress.
|
|
713
761
|
*/
|
|
714
762
|
function createVizeLintConfig(options = {}) {
|
|
715
|
-
|
|
763
|
+
return buildVizeLintConfig(options, { forceIncrementalRuntime: false });
|
|
764
|
+
}
|
|
765
|
+
function buildVizeLintConfig(options, buildOptions) {
|
|
766
|
+
const presets = resolveLintPresets(options);
|
|
767
|
+
const runtimePreset = buildOptions.forceIncrementalRuntime ? "incremental" : toRuntimePreset(presets);
|
|
716
768
|
const extraRules = options.rules ?? {};
|
|
717
|
-
assertUsableVizeRules(extraRules,
|
|
769
|
+
assertUsableVizeRules(extraRules, presets, runtimePreset);
|
|
718
770
|
return {
|
|
719
771
|
jsPlugins: [VIZE_JS_PLUGIN_SPECIFIER],
|
|
720
772
|
plugins: [...new Set(["vue", ...options.plugins ?? []])],
|
|
721
773
|
rules: {
|
|
722
|
-
...createPresetRules(
|
|
774
|
+
...createPresetRules(presets, options.includeTypeAware),
|
|
723
775
|
...extraRules
|
|
724
776
|
},
|
|
725
777
|
settings: { vize: {
|
|
726
778
|
...options.settings,
|
|
727
|
-
preset:
|
|
779
|
+
preset: runtimePreset
|
|
728
780
|
} }
|
|
729
781
|
};
|
|
730
782
|
}
|
|
731
|
-
function createPresetRules(
|
|
732
|
-
|
|
783
|
+
function createPresetRules(presets, includeTypeAware) {
|
|
784
|
+
const ruleConfigPresets = presets.filter(isRuleConfigPreset);
|
|
785
|
+
if (ruleConfigPresets.length === 0) return {};
|
|
786
|
+
return createVizeRuleConfig({
|
|
733
787
|
includeTypeAware,
|
|
734
|
-
|
|
788
|
+
presets: ruleConfigPresets
|
|
735
789
|
});
|
|
736
790
|
}
|
|
791
|
+
function resolveLintPresets(options) {
|
|
792
|
+
const requestedPresets = [...toPresetArray((options.preset === void 0 && options.presets === void 0 ? DEFAULT_VIZE_LINT_PRESET : options.preset) ?? []), ...options.presets ?? []].map(normalizeLintPreset);
|
|
793
|
+
return [...new Set(requestedPresets)];
|
|
794
|
+
}
|
|
795
|
+
function toPresetArray(preset) {
|
|
796
|
+
return Array.isArray(preset) ? preset : [preset];
|
|
797
|
+
}
|
|
798
|
+
function normalizeLintPreset(preset) {
|
|
799
|
+
return preset === "happy-path" ? DEFAULT_VIZE_LINT_PRESET : preset;
|
|
800
|
+
}
|
|
801
|
+
function isRuleConfigPreset(preset) {
|
|
802
|
+
return preset !== "incremental";
|
|
803
|
+
}
|
|
737
804
|
/**
|
|
738
|
-
* Maps
|
|
805
|
+
* Maps rule bundles to the `settings.vize.preset` the bridge must run with.
|
|
739
806
|
*
|
|
740
|
-
* `"all"`
|
|
741
|
-
* membership; gating
|
|
742
|
-
* the rules that only belong to the other bundles.
|
|
807
|
+
* `"all"` and multiple concrete presets become `"incremental"` because the
|
|
808
|
+
* bridge gates each rule on preset membership; gating a combined rule map by any
|
|
809
|
+
* single preset would suppress the rules that only belong to the other bundles.
|
|
743
810
|
*/
|
|
744
|
-
function toRuntimePreset(
|
|
745
|
-
|
|
811
|
+
function toRuntimePreset(presets) {
|
|
812
|
+
if (presets.length === 1 && presets[0] !== "all") return presets[0];
|
|
813
|
+
return "incremental";
|
|
746
814
|
}
|
|
747
|
-
function assertUsableVizeRules(rules,
|
|
815
|
+
function assertUsableVizeRules(rules, presets, runtimePreset) {
|
|
748
816
|
const ruleMetaById = new Map(getPatinaRules().map((ruleMeta) => [`${VIZE_RULE_PREFIX}${ruleMeta.name}`, ruleMeta]));
|
|
749
817
|
const configuredIds = Object.keys(rules).filter((ruleId) => ruleId.startsWith(VIZE_RULE_PREFIX)).sort();
|
|
750
818
|
const unknownIds = configuredIds.filter((ruleId) => !ruleMetaById.has(ruleId));
|
|
751
819
|
if (unknownIds.length > 0) throw new Error(`Unknown Vize rule ${pluralizeIds(unknownIds)}: ${unknownIds.join(", ")}. Check the id against the rules oxlint-plugin-vize registers.`);
|
|
752
|
-
const runtimePreset = toRuntimePreset(preset);
|
|
753
820
|
const suppressedIds = configuredIds.filter((ruleId) => isSuppressedByPreset(ruleMetaById.get(ruleId), runtimePreset));
|
|
754
|
-
if (suppressedIds.length > 0) throw new Error(`Vize rule ${pluralizeIds(suppressedIds)} outside
|
|
821
|
+
if (suppressedIds.length > 0) throw new Error(`Vize rule ${pluralizeIds(suppressedIds)} outside ${describePresetSelection(presets)}: ${suppressedIds.join(", ")}. Use preset: "incremental" to run an explicit rule subset, or pick the preset that owns these rules.`);
|
|
755
822
|
}
|
|
756
823
|
/**
|
|
757
824
|
* Mirrors the runtime gate in `plugin.ts`: rules with no preset membership are
|
|
@@ -760,8 +827,139 @@ function assertUsableVizeRules(rules, preset) {
|
|
|
760
827
|
function isSuppressedByPreset(ruleMeta, runtimePreset) {
|
|
761
828
|
return ruleMeta.presets.length > 0 && runtimePreset !== "incremental" && !ruleMeta.presets.includes(runtimePreset);
|
|
762
829
|
}
|
|
830
|
+
function describePresetSelection(presets) {
|
|
831
|
+
const names = presets.map((preset) => preset === DEFAULT_VIZE_LINT_PRESET ? "happy-path" : preset);
|
|
832
|
+
return `the "${names.length === 0 ? "empty" : names.join(", ")}" ${names.length === 1 ? "preset" : "presets"}`;
|
|
833
|
+
}
|
|
763
834
|
function pluralizeIds(ids) {
|
|
764
835
|
return ids.length === 1 ? "id" : "ids";
|
|
765
836
|
}
|
|
766
837
|
//#endregion
|
|
767
|
-
|
|
838
|
+
//#region src/vite-plus-flat-config.ts
|
|
839
|
+
/**
|
|
840
|
+
* Returns spreadable Oxlint/Vite+ config fragments for Flat Config style use.
|
|
841
|
+
*
|
|
842
|
+
* The fragments intentionally set `settings.vize.preset` to `"incremental"` so
|
|
843
|
+
* multiple fragments can be composed without one preset's runtime gate
|
|
844
|
+
* suppressing rules emitted by another fragment.
|
|
845
|
+
*/
|
|
846
|
+
function createVizeLintFlatConfig(options = {}) {
|
|
847
|
+
return [buildVizeLintConfig(options, { forceIncrementalRuntime: true })];
|
|
848
|
+
}
|
|
849
|
+
const flatConfigs = {
|
|
850
|
+
all: createVizeLintFlatConfig({ preset: "all" }),
|
|
851
|
+
ecosystem: createVizeLintFlatConfig({ preset: "ecosystem" }),
|
|
852
|
+
ecosystemWithTypeAware: createVizeLintFlatConfig({
|
|
853
|
+
includeTypeAware: true,
|
|
854
|
+
preset: "ecosystem"
|
|
855
|
+
}),
|
|
856
|
+
essential: createVizeLintFlatConfig({ preset: "essential" }),
|
|
857
|
+
happyPath: createVizeLintFlatConfig({ preset: "happy-path" }),
|
|
858
|
+
happyPathWithTypeAware: createVizeLintFlatConfig({
|
|
859
|
+
includeTypeAware: true,
|
|
860
|
+
preset: "happy-path"
|
|
861
|
+
}),
|
|
862
|
+
nuxt: createVizeLintFlatConfig({ preset: "nuxt" }),
|
|
863
|
+
opinionated: createVizeLintFlatConfig({ preset: "opinionated" }),
|
|
864
|
+
opinionatedWithTypeAware: createVizeLintFlatConfig({
|
|
865
|
+
includeTypeAware: true,
|
|
866
|
+
preset: "opinionated"
|
|
867
|
+
}),
|
|
868
|
+
recommended: createVizeLintFlatConfig({ preset: "general-recommended" }),
|
|
869
|
+
recommendedWithTypeAware: createVizeLintFlatConfig({
|
|
870
|
+
includeTypeAware: true,
|
|
871
|
+
preset: "general-recommended"
|
|
872
|
+
})
|
|
873
|
+
};
|
|
874
|
+
/**
|
|
875
|
+
* Collapses Flat Config-style fragments into the object shape Vite+ accepts.
|
|
876
|
+
*
|
|
877
|
+
* `vp lint` currently expects a single Oxlint config object under `lint`, so this
|
|
878
|
+
* helper lets `vite.config.ts` keep a flat, spread-friendly authoring style while
|
|
879
|
+
* still handing Vite+ the object it can read.
|
|
880
|
+
*/
|
|
881
|
+
function defineVizeLintConfig(...entries) {
|
|
882
|
+
const config = {
|
|
883
|
+
jsPlugins: [],
|
|
884
|
+
plugins: [],
|
|
885
|
+
rules: {},
|
|
886
|
+
settings: { vize: {} }
|
|
887
|
+
};
|
|
888
|
+
const runtimePresets = [];
|
|
889
|
+
for (const entry of entries) mergeFlatConfigEntry(config, runtimePresets, entry);
|
|
890
|
+
config.jsPlugins = [...new Set(config.jsPlugins)];
|
|
891
|
+
config.plugins = [...new Set(config.plugins)];
|
|
892
|
+
if (runtimePresets.includes("incremental")) config.settings.vize.preset = "incremental";
|
|
893
|
+
else {
|
|
894
|
+
const uniquePresets = [...new Set(runtimePresets)];
|
|
895
|
+
if (uniquePresets.length === 1) config.settings.vize.preset = uniquePresets[0];
|
|
896
|
+
else if (uniquePresets.length > 1) config.settings.vize.preset = "incremental";
|
|
897
|
+
}
|
|
898
|
+
return config;
|
|
899
|
+
}
|
|
900
|
+
function mergeFlatConfigEntry(target, runtimePresets, entry) {
|
|
901
|
+
if (Array.isArray(entry)) {
|
|
902
|
+
for (const item of entry) mergeFlatConfigEntry(target, runtimePresets, item);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
for (const extended of entry.extends ?? []) mergeFlatConfigEntry(target, runtimePresets, extended);
|
|
906
|
+
mergeFlatConfigItem(target, runtimePresets, entry);
|
|
907
|
+
}
|
|
908
|
+
function mergeFlatConfigItem(target, runtimePresets, item) {
|
|
909
|
+
if (item.jsPlugins !== void 0) target.jsPlugins.push(...item.jsPlugins);
|
|
910
|
+
if (item.plugins !== void 0) target.plugins.push(...item.plugins);
|
|
911
|
+
if (item.rules !== void 0) target.rules = {
|
|
912
|
+
...target.rules,
|
|
913
|
+
...item.rules
|
|
914
|
+
};
|
|
915
|
+
if (item.ignorePatterns !== void 0) target.ignorePatterns = [...new Set([...readStringArray(target.ignorePatterns) ?? [], ...item.ignorePatterns])];
|
|
916
|
+
if (item.overrides !== void 0) target.overrides = [...readUnknownArray(target.overrides) ?? [], ...item.overrides];
|
|
917
|
+
if (item.settings !== void 0) mergeSettings(target.settings, runtimePresets, item.settings);
|
|
918
|
+
for (const [key, value] of Object.entries(item)) {
|
|
919
|
+
if (value === void 0 || key === "extends" || key === "ignorePatterns" || key === "jsPlugins" || key === "overrides" || key === "plugins" || key === "rules" || key === "settings") continue;
|
|
920
|
+
target[key] = value;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
function mergeSettings(target, runtimePresets, settings) {
|
|
924
|
+
for (const [key, value] of Object.entries(settings)) if (key !== "vize") target[key] = value;
|
|
925
|
+
if (isRecord(settings.vize)) {
|
|
926
|
+
const runtimePreset = normalizeRuntimePresetSetting(settings.vize.preset);
|
|
927
|
+
if (runtimePreset !== null) runtimePresets.push(runtimePreset);
|
|
928
|
+
target.vize = {
|
|
929
|
+
...target.vize,
|
|
930
|
+
...settings.vize
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
function normalizeRuntimePresetSetting(value) {
|
|
935
|
+
if (typeof value !== "string") return null;
|
|
936
|
+
switch (value.replaceAll(/[-_\s]/gu, "").toLowerCase()) {
|
|
937
|
+
case "generalrecommended":
|
|
938
|
+
case "happypath":
|
|
939
|
+
case "happy":
|
|
940
|
+
case "default":
|
|
941
|
+
case "recommended": return "general-recommended";
|
|
942
|
+
case "essential": return "essential";
|
|
943
|
+
case "ecosystem":
|
|
944
|
+
case "eco": return "ecosystem";
|
|
945
|
+
case "incremental":
|
|
946
|
+
case "all": return "incremental";
|
|
947
|
+
case "opinionated":
|
|
948
|
+
case "opnionated":
|
|
949
|
+
case "strict": return "opinionated";
|
|
950
|
+
case "nuxt": return "nuxt";
|
|
951
|
+
default: return null;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
function readStringArray(value) {
|
|
955
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) return null;
|
|
956
|
+
return value;
|
|
957
|
+
}
|
|
958
|
+
function readUnknownArray(value) {
|
|
959
|
+
return Array.isArray(value) ? value : null;
|
|
960
|
+
}
|
|
961
|
+
function isRecord(value) {
|
|
962
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
963
|
+
}
|
|
964
|
+
//#endregion
|
|
965
|
+
export { VIZE_JS_PLUGIN_SPECIFIER, configs, createVizeLintConfig, createVizeLintFlatConfig, createVizeRuleConfig, plugin_default as default, defineVizeLintConfig, flatConfigs };
|
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.
|
|
15
|
+
"@vizejs/native": "0.391.0",
|
|
16
16
|
"tsdown": "0.22.14",
|
|
17
17
|
"typescript": "6.0.3",
|
|
18
18
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
|
|
@@ -43,14 +43,14 @@
|
|
|
43
43
|
"main": "./dist/index.mjs",
|
|
44
44
|
"name": "oxlint-plugin-vize",
|
|
45
45
|
"optionalDependencies": {
|
|
46
|
-
"@vizejs/native-darwin-arm64": "0.
|
|
47
|
-
"@vizejs/native-darwin-x64": "0.
|
|
48
|
-
"@vizejs/native-linux-arm64-gnu": "0.
|
|
49
|
-
"@vizejs/native-linux-arm64-musl": "0.
|
|
50
|
-
"@vizejs/native-linux-x64-gnu": "0.
|
|
51
|
-
"@vizejs/native-linux-x64-musl": "0.
|
|
52
|
-
"@vizejs/native-win32-arm64-msvc": "0.
|
|
53
|
-
"@vizejs/native-win32-x64-msvc": "0.
|
|
46
|
+
"@vizejs/native-darwin-arm64": "0.391.0",
|
|
47
|
+
"@vizejs/native-darwin-x64": "0.391.0",
|
|
48
|
+
"@vizejs/native-linux-arm64-gnu": "0.391.0",
|
|
49
|
+
"@vizejs/native-linux-arm64-musl": "0.391.0",
|
|
50
|
+
"@vizejs/native-linux-x64-gnu": "0.391.0",
|
|
51
|
+
"@vizejs/native-linux-x64-musl": "0.391.0",
|
|
52
|
+
"@vizejs/native-win32-arm64-msvc": "0.391.0",
|
|
53
|
+
"@vizejs/native-win32-x64-msvc": "0.391.0"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"type": "module",
|
|
64
64
|
"types": "./dist/index.d.mts",
|
|
65
|
-
"version": "0.
|
|
65
|
+
"version": "0.391.0",
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "vp pack",
|
|
68
68
|
"check": "vp check src vite.config.ts",
|