Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nitro + Rollup: externalized JSON imports are emitted with the removed assert key

Reproductions for two independent defects that make import attributes unusable in a Nitro server build. Both are silent at build time: the build is green, the server starts, and only the route that touches the module returns a 500.

# Directory What it shows
1 1-rollup/ Rollup alone rewrites with { type: "json" } to assert { type: "json" } on external modules (output.importAttributesKey defaults to "assert")
2 2-nitro-v2/ The same, end to end, in a nitropack@2.13.4 build → HTTP 500 ERR_IMPORT_ATTRIBUTE_MISSING
3 3-nitro-v2-ts/ Nitro 2 additionally drops import attributes entirely from .ts/.js sources (esbuild target: "es2019")
4 4-nitro-v3/ Case 2 still reproduces on nitro@3.0.260610-beta with builder: "rollup". Case 3 does not (oxc), and the default rolldown builder is fine

Verified on Node.js v24.19.0, Windows 11, npm.

Why assert is the wrong key for everything Nitro targets

Nitro declares "engines": { "node": "^20.19.0 || >=22.12.0" } (both v2 and v3).

  • with is supported since Node 21.0 / 20.10, backported to 18.20 — i.e. by every version in that range.
  • assert was removed in Node 22.0.0 (nodejs/node#52104, "esm: drop support for import assertions").

So with is understood by every runtime in that range and assert by none of the ones from 22 upwards. The only versions that would prefer assert — Node 16.14–18.19 and 20.0–20.9 — are below Nitro's own floor. Outside Node: Deno 2 turned assert into a hard error, and Bun accepts both.

The static form fails as SyntaxError: Unexpected identifier 'assert'; the dynamic form that Rollup emits for import() is syntactically valid but loses the attribute's meaning, so Node rejects the module with TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: … needs an import attribute of "type: json".

Rollup's own default is known and changes only in Rollup 5 (rollup/rollup#6248, merged to the rollup-5 branch on 2026‑01‑30; the user-facing report is #5685, open since 2024‑09). No 5.x is published, and output.importAttributesKey is the documented workaround until then. This repository is about the two things downstream of that: Nitro never sets the option, and Nitro 2 removes the attributes before Rollup ever sees them.

1 — Rollup only

cd 1-rollup && npm install && npm start
input           export const load = () => import("json-dep/data.json", { with: { type: "json" } })
default         const load = () => import('json-dep/data.json', { assert: { type: 'json' } });
with the option const load = () => import('json-dep/data.json', { with: { type: 'json' } });

2 — Nitro 2, external JSON module

nitro.config.ts marks a JSON module as external with a five-line Rollup plugin. This mirrors @nuxt/icon's serverBundle.externalizeIconsJson, which is where this was found in the wild: the module generates a correct with { type: "json" } import and has no way to know that it will be re-printed as assert.

A plugin is needed because Nitro's own externals.external option cannot express this: the externals plugin gates on isValidNodeImport, which returns false for a .json file, so Nitro always inlines it. Every real occurrence of this bug therefore comes from a module or a user plugin.

cd 2-nitro-v2 && npm install && npm run build
grep -o "import('json-dep/data.json'[^)]*)" .output/server/chunks/routes/index.mjs
npm start   # then: curl localhost:3000

Emitted, and the response:

import('json-dep/data.json', { assert: { type: 'json' } })

HTTP 500
TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "…/packages/json-dep/data.json"
  needs an import attribute of "type: json"

Uncommenting output: { importAttributesKey: 'with' } in nitro.config.ts emits with { type: 'json' } and the route returns {"hello":"world"}.

3 — Nitro 2 drops the attributes in .ts / .js sources

Same project, with the route written in TypeScript instead of .mjs — the form every real Nitro and Nuxt app uses.

cd 3-nitro-v2-ts && npm install && npm run build
grep -o "import('json-dep/data.json'[^)]*)" .output/server/chunks/routes/index.mjs
import('json-dep/data.json')

The attribute is gone, not renamed. Nitro 2 transpiles .ts/.js/.tsx/.jsx with esbuild({ target: "es2019" }), and esbuild silently strips import attributes for every esXXXX target — es2025 included. The gate is whether the named target shipped attributes, not how recent the year is:

esbuild.transformSync('const p = import("x.json", { with: { type: "json" } })', { loader: 'ts', target: 'es2019' })
// -> const p = import("x.json");
esbuild.transformSync(same, { loader: 'ts', target: 'es2019', supported: { 'import-attributes': true } })
// -> const p = import("x.json", { with: { type: "json" } });
es2019 dropped · es2022 dropped · es2024 dropped · es2025 dropped
esnext kept · node18 kept · node20 kept · chrome120 kept

.mjs files are not matched by that plugin's loader map, which is why case 2 above had to be written as .mjs to demonstrate the other bug in isolation. Runtime result is the same 500.

4 — Nitro 3 beta

cd 4-nitro-v3 && npm install && npm run build
grep -o "import('json-dep/data.json'[^)]*)" .output/server/_chunks/index.mjs
npm start   # then: curl localhost:3000
import('json-dep/data.json', { assert: { type: 'json' } })

HTTP 500  TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]

The attribute-key defect is unchanged on the rollup builder. Two things are different on v3: the TypeScript defect is gone (oxc preserves the attributes), and rollup is no longer the default builder — rolldown is, and it emits with correctly, so 4-nitro-v3/nitro.config.ts opts in with builder: 'rollup'.

Suggested fixes

  • Nitro — set output.importAttributesKey: "with" in the Rollup output config (src/rollup/config.ts on v2; src/build/rollup/config.ts and the rollup branch of src/build/vite/bundler.ts on main). Every runtime Nitro supports accepts with; none of the modern ones accept assert.
  • Nitro 2 — pass supported: { "import-attributes": true } to the esbuild plugin so the es2019 target stops stripping them.
  • @nuxt/icon — set the same key from the plugin it already installs, via outputOptions, so users are covered on Nitro versions that predate the fix.

About

Reproductions: Nitro/Rollup emit import attributes with the removed `assert` key, and Nitro 2 drops them in .ts sources

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages