Vize

Script Rule Options

Script rule option examples cover restricted globals, restricted members, compiler macro order, and custom event casing. The main Rule Options page lists the complete option table, unknown-field behavior, and scoped replacement semantics.

script/no-restricted-globals

Use this rule when runtime globals must go through project-owned wrappers. Without options, the rule reports process, localStorage, and sessionStorage. A non-empty globals list replaces that built-in list; an empty list falls back to the built-ins so a config typo cannot silently disable the rule.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "script/no-restricted-globals": "error",
    },
    ruleOptions: {
      "script/no-restricted-globals": {
        globals: [
          {
            name: "process",
            message: "Read env through useRuntimeConfig().",
          },
          {
            name: "localStorage",
          },
        ],
      },
    },
  },
});

Bad:

const flag = process.env.FEATURE_FLAG;
const token = localStorage.getItem("auth.token");

Good:

const flag = useRuntimeConfig().featureFlag;
const token = authStorage.read("auth.token");

script/no-restricted-members

Use this rule for project-local member access bans, such as moving browser APIs behind SSR-safe helpers. The rule has no built-in deny list; it only reports when members is non-empty and the rule is enabled. Each entry matches a bare identifier receiver plus a static property name.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "script/no-restricted-members": "error",
    },
    ruleOptions: {
      "script/no-restricted-members": {
        members: [
          {
            object: "window",
            property: "localStorage",
            message: "Use authStorage.",
          },
          {
            object: "globalThis",
            property: "process",
          },
        ],
      },
    },
  },
});

Bad:

const token = window.localStorage.getItem("auth.token");
const env = globalThis.process.env;

Good:

const token = authStorage.read("auth.token");
const env = readServerEnv();

script/custom-event-name-casing

Use this option when emitted custom events should follow one casing convention across both script and template usage. The default is camelCase; set kebab-case for projects that author emitted event names in their template-facing form. Component v-model events beginning with update: are accepted in either mode because their suffix mirrors the corresponding prop name.

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

export default defineConfig({}).withVue({
  linter: {
    rules: {
      "script/custom-event-name-casing": "error",
    },
    ruleOptions: {
      "script/custom-event-name-casing": {
        casing: "kebab-case",
      },
    },
  },
});

Bad with the config above:

const emit = defineEmits(["saveItem"]);
emit("saveItem");

Good:

const emit = defineEmits(["save-item"]);
emit("save-item");

script/define-macros-order

Use order to choose which compiler macros must appear first in <script setup> and in what sequence. The default is ["defineProps", "defineEmits"]. Macros omitted from the list behave like ordinary statements.

The public lintPatinaSfc API accepts this policy through ruleOptionsJson. An entry there overrides the compatibility defineMacrosOrder option; an empty policy object restores the default order, while an omitted entry keeps the compatibility policy. Options alone do not enable an unselected rule.

Patina currently reports macro-order diagnostics without text edits. The pinned ESLint rule can reorder statements; this configuration support does not add that fixer or defineExposeLast.

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

export default defineConfig({}).withVue({
  linter: {
    rules: { "script/define-macros-order": "warn" },
    ruleOptions: {
      "script/define-macros-order": {
        order: ["defineOptions", "defineProps", "defineEmits", "defineSlots"],
      },
    },
  },
});

Bad with the config above:

<script setup lang="ts">
const props = defineProps<{ label: string }>();
defineOptions({ inheritAttrs: false });
</script>

Good:

<script setup lang="ts">
defineOptions({ inheritAttrs: false });
const props = defineProps<{ label: string }>();
</script>

script/define-props-destructuring

The default, only-when-assigned, requires a destructured binding when the result is assigned. A bare defineProps<Props>() is allowed. always also reports bare calls; never reports destructured bindings instead. Vue 3.5 preserves reactivity when destructuring props.

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

export default defineConfig({}).withVue({
  linter: {
    preset: "incremental",
    rules: { "script/define-props-destructuring": "warn" },
    ruleOptions: { "script/define-props-destructuring": { destructure: "only-when-assigned" } },
  },
});

Bad:

<script setup lang="ts">
const props = defineProps<{ size?: string }>();
</script>

Good:

<script setup lang="ts">
const { size = "md" } = defineProps<{ size?: string }>();
</script>

When migrating from ESLint, use the Vize rule ID and typed options separately:

 rules: { "vue/define-props-destructuring": ["warn", "always"] }
 rules: { "script/define-props-destructuring": "warn" },
 ruleOptions: { "script/define-props-destructuring": { destructure: "always" } }

Place the added entries inside the linter object passed to withVue in the Vite+ config above.