oxlint-plugin-vize

Oxlint JS plugin bridge for Vize Patina
101,235 909
Install
npm install oxlint-plugin-vize
Latest Version:0.519.0
License:MIT
Last Updated:Oct 21, 2026
Links: GitHub  ·  npm
Maintainer: ubugeeei

oxlint-plugin-vize

Oxlint JS plugin bridge for Vize Patina.

This package lets Oxlint execute Patina through Vize's native binding while still using Oxlint's JS plugin model and rule configuration.

[!IMPORTANT] oxlint-plugin-vize is a terminal-first Vue SFC linting package. 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.

Main Features

  • Runs Vize Patina rules inside Oxlint as vize/* diagnostics, so Vue-specific findings can live beside Oxlint core rules in one command.
  • 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.
  • 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.
  • 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.
  • Supports runtime settings through settings.vize, including locale, preset, and helpLevel.
  • 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.
  • Resolves Vize native bindings through platform-specific optional dependencies, so published installs do not need a separate @vizejs/native package.
  • 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.

Performance

The bridge is optimized around Oxlint's per-rule execution model:

  • The first enabled Patina rule on a file runs native linting for that rule only.
  • If a second Patina rule is encountered on the same file, the bridge upgrades to one shared full-file Patina pass and reuses that result for the remaining Patina rules.
  • Exact source revisions and rule results are cached for up to 128 recently used file/settings pairs; edits replace revision-local diagnostics and reporting state.

Installation

oxlint-plugin-vize targets Node 22 and Node 24+ (^22 || >= 24). In this repository, Vite+ reads package.json#devEngines.runtime for you, so the usual setup is:

vp install
vp run --filter './npm/native' build
vp run --filter './npm/oxlint' build

Install vp once from the Vite+ install guide, then add it with:

vp install -D oxlint oxlint-plugin-vize

oxlint-plugin-vize pulls the appropriate Vize native binding for the current platform through optional dependencies, so no separate @vizejs/native install is required for published builds.

Usage

With ESLint

Use the independent oxlint-plugin-vize/eslint entry with the project's Vue parser:

import vize from "oxlint-plugin-vize/eslint";
import vueParser from "vue-eslint-parser";

export default [
  {
    files: ["**/*.vue"],
    languageOptions: { parser: vueParser },
    plugins: { vize },
    settings: { vize: { preset: "incremental" } },
    rules: { "vize/vue/require-v-for-key": "error" },
  },
];

This entry lints the parser's complete source, including an editor's unsaved text. It reports original template, script, and style locations and batches the enabled Vize rules once per file. Native text edits support ESLint's --fix; suggestions are not invented from help text. Install ESLint and vue-eslint-parser in the project, and keep the plugin and native binding versions together. The default package entry remains the Oxlint bridge. Unpaired UTF-16 surrogates are refused before native conversion. Invalid native ranges and inconsistent locations are errors. Files containing lone CR or Unicode line separators currently require a matching native location convention; the entry refuses mismatched positions rather than moving reports.

Which file you configure depends on which command you run.

Command Reads
vp lint, vp check the lint block in vite.config.ts
oxlint, oxlint-vize .oxlintrc.json (or -c <path>)

[!IMPORTANT] Vite+ never reads .oxlintrc.json. A .oxlintrc.json carrying jsPlugins and vize/* rules looks configured, but vp lint ignores the file, so Oxlint never sees a vize/* rule id and reports zero Vize diagnostics while exiting 0. vp lint --init does not migrate an existing .oxlintrc.json either: it writes a fresh lint block and leaves the old file in place. If you use vp lint, configure the lint block.

With vp lint (Vite+)

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.

// vite.config.ts
import { defineConfig } from "vite-plus";
import { createVizeLintConfig } from "oxlint-plugin-vize";

export default defineConfig({
  lint: createVizeLintConfig({
    preset: "happy-path",
    rules: {
      "no-console": "warn",
    },
    settings: {
      helpLevel: "short",
    },
  }),
});

preset drives both the emitted rule map and settings.vize.preset when a single bundle is selected, so the two can never disagree. That matters because the bridge silently drops any vize/* rule outside the active preset: listing vize/ecosystem/router-link-require-to while the active preset is general-recommended reports nothing at all. createVizeLintConfig throws for that case, and for unknown vize/* ids, instead of leaving you with a config that looks enabled and reports nothing.

Use preset: "incremental" when you want only the rules you list, preset: "all" for every bundle at once, and preset: ["happy-path", "ecosystem"] or presets: ["happy-path", "ecosystem"] when you want several bundles without taking on all of them. Multiple-bundle configs emit settings.vize.preset: "incremental" so the runtime gate cannot suppress one selected bundle's rules while another bundle is active.

The emitted block always enables Oxlint's built-in vue plugin. Pass plugins to keep the rest of your project's plugin list; they are merged with vue, never replaced, because narrowing the list would silently drop everything those plugins report. A create-vue project keeps its generated set like this:

export default defineConfig({
  lint: {
    ...createVizeLintConfig({
      plugins: ["eslint", "typescript", "unicorn", "oxc"],
    }),
    ignorePatterns: ["dist/**"],
  },
});

For Flat Config-style composition in vite.config.ts, use spreadable fragments and collapse them back to Vite+'s object-shaped lint config:

import { defineConfig } from "vite-plus";
import { defineVizeLintConfig, flatConfigs } from "oxlint-plugin-vize";

export default defineConfig({
  lint: defineVizeLintConfig(...flatConfigs.recommended, ...flatConfigs.ecosystem, {
    ignorePatterns: ["dist/**"],
    rules: {
      "no-console": "warn",
    },
  }),
});

The exported fragments include flatConfigs.recommended, flatConfigs.happyPath, flatConfigs.essential, flatConfigs.ecosystem, flatConfigs.nuxt, flatConfigs.opinionated, and flatConfigs.all, plus the same *WithTypeAware variants as configs.

With oxlint or oxlint-vize

Enable Oxlint's built-in vue plugin as well as this JS plugin:

{
  "plugins": ["vue"],
  "jsPlugins": ["oxlint-plugin-vize"],
  "rules": {
    "eqeqeq": "error",
    "no-console": "warn",
    "vize/vue/require-v-for-key": "error",
    "vize/vue/no-v-html": "warn"
  }
}

This bridge only adds the vize/* rules. Oxlint's existing core rules and built-in plugin rules still run as configured, so checks like eqeqeq, no-console, or your existing vue/* setup continue to report normally.

If you want a lower-config JS/TS Oxlint setup, the package also exports preset rule maps:

import { configs } from "oxlint-plugin-vize";

export default {
  plugins: ["vue"],
  jsPlugins: ["oxlint-plugin-vize"],
  settings: {
    vize: {
      helpLevel: "short",
      preset: "opinionated",
      typeAware: true,
    },
  },
  rules: configs.opinionatedWithTypeAware,
};

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.

You can pass Patina settings through settings.vize:

{
  "settings": {
    "vize": {
      "locale": "ja",
      "preset": "essential",
      "helpLevel": "short",
      "typeAware": true
    }
  }
}
  • preset accepts "general-recommended"/"happy-path", "essential", "ecosystem", "incremental", "opinionated", "nuxt", or "all".
  • preset defaults to "general-recommended".
  • Bundle presets keep out-of-bundle rules quiet even if they are still listed in rules.
  • "incremental" skips bundle gating and runs only the Vize rules you explicitly configure in Oxlint.
  • "all" is accepted as a settings alias for "incremental"; use configs.all or createVizeLintConfig({ preset: "all" }) when you also want every rule emitted.
  • "ecosystem" enables Vize's Vue Router, Vue I18n, Pinia, Vue Test Utils, and Void Vue rules without taking on the full opinionated preset.
  • "opinionated" is the preset that enables Vize's built-in script rules such as vize/script/no-options-api.
  • Legacy aliases such as "GeneralRecommended", "Essential", "Ecosystem", "Incremental", "Opinionated", "Nuxt", and "happy-path" are still accepted for compatibility.
  • helpLevel accepts "full", "short", or "none". Short help keeps one actionable sentence and removes inline examples and trailing rationale.
  • helpLevel: "full" only expands the Patina remediation text. It does not restore original-SFC formatter anchors or machine-readable range fidelity.
  • typeAware: true enables Corsa-backed vize/type/* rules during shared Patina passes.
  • corsaPath selects a Corsa or TypeScript 7 native executable for type-aware linting. Omit it to use Vize's normal resolver.
  • showHelp is still accepted for backward compatibility, but helpLevel is the preferred setting.

For example, this keeps Oxlint focused on correctness-only Vize diagnostics while still allowing your existing Oxlint rules to run unchanged:

{
  "settings": {
    "vize": {
      "preset": "essential",
      "helpLevel": "short"
    }
  },
  "rules": {
    "vize/vue/require-v-for-key": "error",
    "vize/vue/require-scoped-style": "error"
  }
}

In that config, vize/vue/require-v-for-key can report, while vize/vue/require-scoped-style stays silent because it belongs to the broader "general-recommended" preset.

If you want to adopt Vize one rule at a time, use "preset": "incremental". In that mode, preset membership no longer suppresses configured rules, so only the Vize rules you list under rules will run.

For day-to-day terminal runs, the recommended command today is:

vp exec oxlint-vize -c .oxlintrc.json -f stylish src

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.

Oxlint type-aware rules in Vue files

Oxlint's own type-aware rules (typescript/no-floating-promises, typescript/no-misused-promises and the rest of the tsgolint set) do not look inside .vue files. When you run oxlint-vize --type-aware, or your .oxlintrc.json sets options.typeAware: true, the wrapper runs those rules on each SFC's <script> and <script setup> blocks too, with the severities from your resolved configuration, and reports them at the original .vue lines and columns. Imports between .vue and .ts files resolve as they do for TypeScript. Template expressions are not part of this pass; Vize's vize/type/* rules cover them.

Limitations

  • The type-aware pass for Vue script blocks uses your root configuration's resolved rules; per-file overrides and nested configs do not change which type-aware rules run on Vue files. Your tsconfig.json must include the .vue files' directories with a **/*.ts pattern, because each SFC is checked through a temporary Component.vue.ts sibling that is removed after the run.
  • 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.
  • The wrapper's temporary files are not copied back when Oxlint fix flags are used; it warns when --fix or --fix-suggestions is passed.
  • With Oxlint 1.78, complete explicit/discovered JSON configs containing ignorePatterns or overrides, and root TS/MTS configs with object extends, the wrapper retains original file selection and ordered Vue overrides. This mode supports POSIX paths; inherited JSON configs, import/type-aware project resolution, suppression state, ambiguous filenames, unsupported outside-root patterns and unknown option forms produce actionable errors.
  • Discovered nested configs require an explicit config. JSONC configurations keep the existing wrapper path; filtering for those configurations and other Oxlint versions requires separate qualification.
  • 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.
  • Direct oxlint original-SFC ranges depend on the JS plugin reporting work tracked in oxc-project/oxc#20465.
  • 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.

Current expectations

  • This release is meant for terminal-first workflows.
  • The oxlint-vize wrapper reports original Patina positions in stylish and json; direct oxlint remains limited by its extracted-script JS plugin input.
  • Once Oxlint preserves original Vue positions for JS plugins, the temporary location bridge can be removed.

Related Packages

@vizejs/nuxt-lint-config

Shareable Nuxt-aware lint preset core for the Vize toolchain

47,114 909
@geoql/oxlint-plugin-vue-doctor

oxlint JS plugin for Vue 3 anti-patterns and AI-slop detection. Pairs with oxlin...

21,549 7
@geoql/oxlint-plugin-nuxt-doctor

oxlint JS plugin for Nuxt 4 anti-patterns and AI-slop detection. Pairs with oxli...

21,363 7
@aquila-learning/oxlint-config-vue2

Aquila oxlint for Vue 2

13,851 0

Version History

Version Released License
0.519.0 MIT
0.518.0 MIT
0.517.0 MIT
0.516.0 MIT
0.515.0 MIT