oxc-project/oxc · error · anyhow::Error

TS5042

TS5042

Error message

Option 'project' cannot be mixed with source files on a command line.

What it means

Diagnostic from oxlint's vue/valid-define-emits rule (crates/oxc_linter/src/rules/vue/valid_define_emits.rs). It fires when the `defineEmits` argument references a variable declared locally in the same `<script setup>` scope (`const def = {...}; defineEmits(def)`). Compiler macros are hoisted and transformed at compile time, so referencing setup-local values can break compilation or produce wrong output. Variables imported from other modules, or declared in the sibling plain `<script>` block, are fine.

Source

Thrown at crates/oxc_type_checker/src/execute/tsc.rs:75

    for file in program.files() {
        println!("  {}", file.file_name().display());
    }
    println!("({} files)", program.len());
    Ok(())
}

/// Resolve the command line into the `tsconfig.json` (or config file) to load, mirroring
/// the project-resolution block of tsgo's `tscCompilation`.
///
/// Returns:
/// - `Ok(Some(path))` — a resolved config file to load,
/// - `Ok(None)` — source files were given with no config file (compile them directly),
/// - `Err(_)` — one of `tsc`'s command-line errors, ready to print.
fn resolve_config_file(command: &TypeCheckCommand, cwd: &Path) -> Result<Option<PathBuf>> {
    if let Some(project) = &command.project {
        // TS5042
        if !command.files.is_empty() {
            bail!("Option 'project' cannot be mixed with source files on a command line.");
        }

        let file_or_directory = to_path(cwd, project);
        if file_or_directory.is_dir() {
            // A directory: look for `tsconfig.json` inside it.
            let config_file = file_or_directory.join("tsconfig.json");
            if config_file.is_file() {
                Ok(Some(config_file))
            } else {
                // TS5081
                bail!(
                    "Cannot find a tsconfig.json file at the current directory: {}.",
                    config_file.display()
                );
            }
        } else if file_or_directory.exists() {
            // An explicit config file (need not be named `tsconfig.json`).
            Ok(Some(file_or_directory))

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Inline the object/array literal into the call: `defineEmits({ notify: null })`.
  2. Or move the definition to a module and import it: `import { emitsDef } from './defs'; defineEmits(emitsDef);`.
  3. In TS prefer a type-only declaration: `defineEmits<(e: 'notify') => void>()`.

Example fix

// before
const def = { notify: null };
defineEmits(def);

// after
import { def } from './emits-defs';
defineEmits(def);
// or simply: defineEmits({ notify: null })
Defensive patterns

Strategy: validation

Validate before calling

// defineEmits must not reference setup-local variables
if (/const\s+(\w+)[\s\S]*?defineEmits\s*\(\s*\1\s*\)/.test(setupSource)) {
  throw new Error('defineEmits references a locally declared variable; inline it or import it');
}

Prevention

When it happens

Trigger: `<script setup> const def = { notify: null }; defineEmits(def) </script>` — the argument identifier resolves to a binding declared inside the setup scope. The identical code with `def` imported from './defs' or declared in a normal `<script>` block passes the rule.

Common situations: Sharing props/emits definition objects between components via copy-paste; extracting the object 'for readability' inside the setup block.

Related errors


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