Skip to content

The format option has two meanings #34

Description

@romankurakin

Volt.Builder reads config :volt, :format as the bundle format, so its value is an atom such as :esm. Volt.JS.Format and Volt.JS.Helpers read the same key as formatter options and expect a keyword list.

The result is that a project which asks the bundler for ESM cannot use Volt.Formatter or mix volt.js.check at all. Both fail immediately, before they look at a single file.

Environment

  • :volt 0.17.11
  • :oxc 0.17.8
  • Elixir 1.20.4, Erlang/OTP 29
  • Debian 13, linux aarch64

Reproduction

# config/config.exs
config :volt,
  entry: "assets/js/app.js",
  root: "assets",
  outdir: "priv/static/assets",
  format: :esm
mix volt.js.check

What happens

There are two independent failures, in two different functions.

Volt.JS.Format.load_config/0 matches only nil or a list, so the atom falls through the case:

** (CaseClauseError) no case clause matching:

    :esm

    (volt 0.17.11) lib/volt/js/format.ex:30: Volt.JS.Format.load_config/0

Volt.JS.Helpers.discovery_config/1 returns the atom unchanged, and discover_files/1 then calls Keyword.get/3 on it. This is the failure behind mix volt.js.check --type-aware:

** (FunctionClauseError) no function clause matching in Keyword.get/3

    # 1
    :esm

Volt.Formatter runs into the first failure as soon as mix format reaches a JavaScript file:

mix format failed for file: assets/js/hook-dom.js
** (CaseClauseError) no case clause matching:

    :esm

    (volt 0.17.11) lib/volt/js/format.ex:30: Volt.JS.Format.load_config/0
    (volt 0.17.11) lib/volt/formatter.ex:34: Volt.Formatter.format/2

Why the obvious workaround does not apply

Removing format: :esm is not an option here. shared_entries?/3 at builder.ex:83 shares modules between entry points only when the bundle is ESM, and the page loads that bundle with <script type="module">.

Suggested fix

Both functions should treat the key as formatter options only when it actually holds a keyword list, and fall back to the JSON configuration otherwise. That leaves the public API untouched, introduces no new configuration key, and keeps every configuration that works today working.

I am happy to open it as a pull request.

Workaround in the meantime

Volt.JS.Format.load_json_config/0 is public and does not read the application environment:

OXC.Format.run!(source, "hook.js", Volt.JS.Format.load_json_config())

That covers formatting. There is no equivalent for mix volt.js.check, which fails before it starts.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions