oxc-project/oxc · error · OxcDiagnostic

`defineOptions` is referencing locally declared variables.

Error message

`defineOptions` is referencing locally declared variables.

What it means

Diagnostic from the oxlint rule `vue/valid-define-options` (crates/oxc_linter/src/rules/vue/valid_define_options.rs). The rule runs on .vue files compiled in Vue `<script setup>` mode and validates the `defineOptions` compiler macro. `defineOptions` must receive options the Vue compiler can evaluate at compile time, so referencing variables declared locally in the same `<script setup>` block is reported. A reference is considered safe only if it resolves to an import specifier, to a `const` initialized with a literal (string/number/boolean/null/bigint/regex), or to a binding declared inside the options object itself; unresolved references (e.g. from a sibling plain `<script>` block) are treated as non-local and allowed.

Source

Thrown at crates/oxc_linter/src/rules/vue/valid_define_options.rs:16

use oxc_ast::{
    AstKind,
    ast::{CallExpression, Expression, IdentifierReference, ObjectPropertyKind},
};
use oxc_ast_visit::{VisitJs, walk_js};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

use crate::{
    AstNode, ast_util::variable_declaration_kind, context::LintContext,
    frameworks::FrameworkOptions, rule::Rule,
};

fn referencing_locally_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("`defineOptions` is referencing locally declared variables.")
        .with_label(span)
}

fn multiple_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("`defineOptions` has been called multiple times.").with_label(span)
}

fn not_defined_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Options are not defined.").with_label(span)
}

fn disallow_prop_diagnostic(span: Span, prop_name: &str, instead_macro: &str) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!(
        "`defineOptions()` cannot be used to declare `{prop_name}`. Use `{instead_macro}()` instead."
    ))
    .with_label(span)
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Inline the literal directly into the call: `defineOptions({ name: 'Foo' })`.
  2. If the value must stay a variable, move its declaration to a plain `<script>` block or another module and reference/import it from there.
  3. If referencing a binding, make it a `const` initialized with a literal (e.g. `const name = 'Foo'; defineOptions({ name })`).
  4. If the usage is intentional and unavoidable, disable the rule for the line with `// oxlint-disable vue/valid-define-options`.

Example fix

// before
<script setup>
const def = { name: 'Foo' }
defineOptions(def)
</script>

// after
<script setup>
defineOptions({ name: 'Foo' })
</script>
Defensive patterns

Strategy: validation

Validate before calling

// Pre-commit check: flag defineOptions args that reference local variables
const src = await fs.readFile(file, 'utf8');
const setup = src.match(/<script setup[^>]*>([\s\S]*?)<\/script>/)?.[1] ?? '';
const calls = [...setup.matchAll(/defineOptions\(([^)]*)\)/g)];
for (const [, arg] of calls) {
  const ident = arg.match(/\b([a-zA-Z_$][\w$]*)\b/);
  if (ident && new RegExp(`(?:const|let|var)\\s+${ident[1]}`).test(setup)) {
    throw new Error(`${file}: defineOptions references local variable '${ident[1]}' — inline it, import it, or move it to a plain <script> block`);
  }
}

Prevention

When it happens

Trigger: In a .vue file with FrameworkOptions::VueSetup, calling `defineOptions(arg)` (or nesting a reference inside the object literal) where an IdentifierReference resolves to a root binding that is neither an import specifier nor a `const x = <literal>` declarator nor declared within the options object span. Concrete triggers: `const def = { name: 'Foo' }; defineOptions(def)`; `let x = 1; defineOptions({ inheritAttrs: x })`. Passing tests show what does NOT trigger: `const def = 'foo'; defineOptions({ name: def })` (const literal), `import { def } from './defs'; defineOptions(def)`, and objects with local variables inside method bodies (`methods: { foo() { const msg = ... } }`).

Common situations: Refactoring Options API components to `<script setup>` while hoisting a shared options object into the same block; extracting `defineOptions` arguments into 'temporary' variables during cleanup; copy-pasting config objects that were computed at runtime. Note the sibling `<script>` pattern (`const def` in plain `<script>`, `defineOptions(def)` in `<script setup>`) is intentionally allowed, so teams moving that const into `<script setup>` suddenly see the error.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/821ac6bdefbe1060. Report an issue: GitHub.