oxc-project/oxc · warning · OxcDiagnostic

Expected a literal string or immutable template literal

Error message

Expected a literal string or immutable template literal

What it means

Diagnostic from the oxlint rule import/no-dynamic-require (restriction category). It fires when the module argument of `require(...)` is not statically known — not a string literal and not a no-substitution template literal. Runtime-computed paths defeat static analysis: bundlers cannot trace them, code navigation breaks, and lint rules that need resolved modules (like no-cycle or no-named-as-default) silently lose visibility. With the `esmodule: true` option, `import(...)` expressions are checked the same way.

Source

Thrown at crates/oxc_linter/src/rules/import/no_dynamic_require.rs:15

use oxc_ast::{AstKind, ast::Expression};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    AstNode,
    context::LintContext,
    rule::{DefaultRuleConfig, Rule},
};

fn no_dnyamic_require_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Expected a literal string or immutable template literal")
        .with_help("Replace the argument with a literal string or immutable template literal")
        .with_label(span)
}

#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct NoDynamicRequire {
    /// When `true`, also check `import()` expressions for dynamic module specifiers.
    esmodule: bool,
}

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Forbids imports that use an expression for the module argument. This includes
    /// dynamically resolved paths in `require` or `import` statements.
    ///
    /// ### Why is this bad?

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace the computed path with a literal string, or a template literal without substitutions
  2. Replace open-ended dynamic loading with an explicit static require map: `const modules = { a: () => require("./a"), b: () => require("./b") }` — keys stay analyzable
  3. For genuinely dynamic loading (plugins, routes), scope the rule off for that file/directory and use bundler-native mechanisms (import.meta.glob, webpack context) deliberately
  4. If import() with expressions is accepted in your codebase, do not enable `esmodule: true`

Example fix

// before
require(`../locales/${locale}.json`);

// after
const localeFiles = {
  en: () => require("../locales/en.json"),
  de: () => require("../locales/de.json"),
};
localeFiles[locale]();
Defensive patterns

Strategy: validation

Validate before calling

// fail on computed require paths before review:
// rg -n "require\\s*\\(\\s*[^'\"`]" src --type js
// .oxlintrc.json: { "rules": { "import/no-dynamic-require": ["warn", { "esmodule": true }] } }

Prevention

When it happens

Trigger: `require(name)`, `require("../" + name)`, `require(\`../${name}\`)`, `require(name())`; also `import(name)` / `import(\`../${name}\`)` when configured with `{ "esmodule": true }`. Only the first argument of require is checked, so `require("./foo", "bar" + "okay")` passes. Emitted from no_dnyamic_require_diagnostic at crates/oxc_linter/src/rules/import/no_dynamic_require.rs:15 via is_static_value.

Common situations: Plugin loaders, i18n/locale loading, and config discovery code that builds paths at runtime; migrations that moved to bundlers (webpack/rollup/vite) where dynamic require breaks or warns; teams enabling esmodule checking after adopting dynamic import().

Related errors


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