{"record":{"id":"821ac6bdefbe1060","repo":"oxc-project/oxc","slug":"defineoptions-is-referencing-locally-declared-va","errorCode":null,"errorMessage":"`defineOptions` is referencing locally declared variables.","messagePattern":"`defineOptions` is referencing locally declared variables\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"error","filePath":"crates/oxc_linter/src/rules/vue/valid_define_options.rs","lineNumber":16,"sourceCode":"use oxc_ast::{\n    AstKind,\n    ast::{CallExpression, Expression, IdentifierReference, ObjectPropertyKind},\n};\nuse oxc_ast_visit::{VisitJs, walk_js};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::{GetSpan, Span};\n\nuse crate::{\n    AstNode, ast_util::variable_declaration_kind, context::LintContext,\n    frameworks::FrameworkOptions, rule::Rule,\n};\n\nfn referencing_locally_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"`defineOptions` is referencing locally declared variables.\")\n        .with_label(span)\n}\n\nfn multiple_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"`defineOptions` has been called multiple times.\").with_label(span)\n}\n\nfn not_defined_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Options are not defined.\").with_label(span)\n}\n\nfn disallow_prop_diagnostic(span: Span, prop_name: &str, instead_macro: &str) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        \"`defineOptions()` cannot be used to declare `{prop_name}`. Use `{instead_macro}()` instead.\"\n    ))\n    .with_label(span)\n}\n","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/vue/valid_define_options.rs#L1-L34","documentation":"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.","triggerScenarios":"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 = ... } }`).","commonSituations":"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.","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`."],"exampleFix":"// before\n<script setup>\nconst def = { name: 'Foo' }\ndefineOptions(def)\n</script>\n\n// after\n<script setup>\ndefineOptions({ name: 'Foo' })\n</script>","handlingStrategy":"validation","validationCode":"// Pre-commit check: flag defineOptions args that reference local variables\nconst src = await fs.readFile(file, 'utf8');\nconst setup = src.match(/<script setup[^>]*>([\\s\\S]*?)<\\/script>/)?.[1] ?? '';\nconst calls = [...setup.matchAll(/defineOptions\\(([^)]*)\\)/g)];\nfor (const [, arg] of calls) {\n  const ident = arg.match(/\\b([a-zA-Z_$][\\w$]*)\\b/);\n  if (ident && new RegExp(`(?:const|let|var)\\\\s+${ident[1]}`).test(setup)) {\n    throw new Error(`${file}: defineOptions references local variable '${ident[1]}' — inline it, import it, or move it to a plain <script> block`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["vue","script-setup","compiler-macro","defineoptions","lint"],"backgroundTag":"vue-script-setup-macro-misuse","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}