Vize

Oxlint Plugin

oxlint-plugin-vize lets Oxlint execute Vize Patina diagnostics through Oxlint's JS plugin system. Use it when you want Oxlint's Rust-native JS and TS rules together with Vize's Vue-aware diagnostics in one run.

For the native lint and type-checking pipeline outside Oxlint, see Static Analysis.

Important

The package is available on npm, but the integration is still early. For human-readable terminal output, prefer oxlint-vize -f stylish while original SFC range fidelity continues to improve.

Installation

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

vp install -D oxlint oxlint-plugin-vize

oxlint-plugin-vize resolves the matching Vize native binding through optional dependencies, so most users do not need to install @vizejs/native separately.

Which File To Configure

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

Warning

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.

vize init picks the right file for you: it detects whether your lint command is vp lint or oxlint and writes the configuration that command reads, or writes both when both are in use. It also refuses to write anything rather than fall back to a file your lint command ignores.

Basic Usage With vp lint

Warning

vp lint uses the direct Oxlint JS plugin lifecycle. With Oxlint 1.78 and 1.86, a .vue file without <script> or <script setup> never invokes Vize's per-file rules, even when they are enabled in the lint block. vp check has the same limitation when it runs that lint path. For template-only SFCs, use the native Vize task described below or oxlint-vize.

createVizeLintConfig() returns a complete Vite+ lint block, so the jsPlugins entry that loads the bridge 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. Keeping them in lockstep matters because the bridge silently drops any vize/* rule outside the active preset, so a rule listed under a mismatched preset reports nothing at all. createVizeLintConfig throws for that case, and for unknown vize/* ids, rather than leaving you with a config that looks enabled and stays silent.

  • preset: "incremental" runs only the rules you list.
  • preset: ["happy-path", "ecosystem"] or presets: ["happy-path", "ecosystem"] unions multiple bundles. The helper emits settings.vize.preset: "incremental" for those configs so the runtime gate cannot suppress one bundle's rules while another bundle is active.
  • preset: "all" runs every bundle at once.
  • plugins keeps the rest of your built-in Oxlint plugins. They are merged with vue, never replaced, because narrowing the list would silently drop everything those plugins report. A create-vue project passes ["eslint", "typescript", "unicorn", "oxc"].
  • Spread the result ({ ...createVizeLintConfig(), ignorePatterns: ["dist/**"] }) to merge it into an existing lint block.

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

// vite.config.ts
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.

For native Vue linting, including template-only SFCs, install vize and @vizejs/vite-plugin and use its Vite+ configuration:

import { defineConfig } from "@vizejs/vite-plugin/vite-plus";

export default defineConfig({
  lint: { vize: { preset: "happy-path" } },
});

Run the generated task with vp run lint. If a package script already uses lint, the generated task is vp run vize:lint; explicit task names take precedence. See Rules for native rule options. Calling vp lint still selects the direct Oxlint path.

Basic Usage With oxlint And oxlint-vize

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

If you use a JS or TS Oxlint config, 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,
};

Available preset exports include:

  • configs.recommended
  • configs.happyPath
  • configs.essential
  • configs.opinionated
  • configs.nuxt
  • configs.all
  • configs.recommendedWithTypeAware
  • configs.happyPathWithTypeAware
  • configs.ecosystemWithTypeAware
  • configs.opinionatedWithTypeAware
vp exec oxlint-vize -c .oxlintrc.json -f stylish src

oxlint-vize is a thin wrapper around oxlint that smooths over scriptless .vue edge cases while upstream JS plugin coverage continues improving.

Settings

Settings are passed through settings.vize:

{
  "settings": {
    "vize": {
      "locale": "ja",
      "preset": "general-recommended",
      "helpLevel": "short",
      "typeAware": true
    }
  }
}
  • locale controls the diagnostic language.
  • preset accepts "general-recommended"/"happy-path", "essential", "ecosystem", "incremental", "opinionated", "nuxt", or "all".
  • Without a runtime preset, the bridge runs explicitly configured rules as "incremental". This also applies when Oxlint does not propagate settings through extends. The configuration helpers still default to the "general-recommended" bundle.
  • incremental runs only the rules you explicitly configure.
  • rules accepts rule names (with or without the vize/ prefix) or an Oxlint rule map. A map with vize/ keys selects only those entries; a native map supports vue/, script/, css/, style/, type/, nuxt/, and ecosystem/ names. It batches matching rules into one native lint call per file. Oxlint's top-level rules still controls which diagnostics are reported and their severity.
  • all is accepted as a settings alias for incremental; use configs.all or createVizeLintConfig({ preset: "all" }) when you also want every rule emitted.
  • helpLevel accepts "full", "short", or "none".
  • typeAware: true enables Corsa-backed vize/type/* rules during shared Patina passes.
  • corsaPath selects the Corsa or tsgo executable for type-aware linting.
  • showHelp and settings.patina are still accepted for backward compatibility.

For an explicit subset, share the rule map with the native batch so rule options are preserved:

const vueRules = {
  "vize/vue/require-v-for-key": "error",
  "vize/vue/no-v-html": "warn",
  "vize/vue/attribute-hyphenation": ["error", "always"],
};

export default {
  plugins: ["vue"],
  jsPlugins: ["oxlint-plugin-vize"],
  settings: {
    vize: { preset: "incremental", helpLevel: "none", rules: vueRules },
  },
  rules: vueRules,
};

createVizeLintConfig and defineVizeLintConfig generate this selection automatically from the final Vize rule map, including rule options and disabled entries. A manual name list batches rules using their default options; a configured rule with different options runs separately. File overrides that add rules or change options also run separately, so a shared batch cannot hide their diagnostics. Overrides that only disable rules or change severity continue to use Oxlint's reporting policy.

Current Limitations

  • Direct oxlint, vp lint, and the corresponding vp check lint path omit Vize callbacks for scriptless .vue files in Oxlint 1.78 and 1.86. Use the native vp run lint task or oxlint-vize if your project includes template-only SFCs.
  • Oxlint JS plugins still anchor ranges to the extracted script program, so template and style diagnostics do not yet preserve original SFC ranges in every formatter.
  • stylish is currently the best human-readable formatter for mixed Oxlint + Vize output. JSON and other machine-readable formats should be treated as best-effort for original template/style positions.
  • Type-aware rule exports are experimental. Use a *WithTypeAware config and set settings.vize.typeAware: true when you want the shared full-file pass to run those rules eagerly.

The source-built n8n fixture replay checks all 1,369 licensed SFCs, 51 configured Vize rules/options, and six per-file rule overrides through the native bridge and oxlint-vize. This includes 19 scriptless inputs; it does not establish direct SDK callback coverage for them. The two n8n-local plugins and its complete workspace configuration remain outside that qualification.

Local Development

nix develop
vp install --frozen-lockfile
vp run --filter './npm/native' build
vp run --filter './npm/oxlint' build