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
- Inline the literal directly into the call: `defineOptions({ name: 'Foo' })`.
- If the value must stay a variable, move its declaration to a plain `<script>` block or another module and reference/import it from there.
- If referencing a binding, make it a `const` initialized with a literal (e.g. `const name = 'Foo'; defineOptions({ name })`).
- 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
- Keep defineOptions arguments as inline object literals only.
- If a variable is needed, declare it in a sibling plain <script> block or import it from another module.
- Run oxlint (vue/valid-define-options is a correctness rule) in CI and editor on every .vue file.
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
- `defineOptions` has been called multiple times.
- Options are not defined.
- `defineOptions()` cannot be used to declare `{prop_name}`. U
- `defineOptions()` cannot accept type arguments.
- [HIRBuilder] expected block {:?} to exist
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/821ac6bdefbe1060.
Report an issue: GitHub.