feat: add deno target#21247
Conversation
Add a `deno` build target that emits ESM and treats node.js built-in modules as external, baking in the `node:` specifier that Deno requires (for both `import` and `require`/createRequire). Deno is a node+web platform, so web specifiers (npm:, jsr:, http(s)://) stay external too. - target.js: deno target properties (ESM-first, globalThis, fetch wasm) - defaults: default experiments.outputModule for deno, deno externals preset, deno resolve condition and loader target - DenoTargetPlugin: externalize node core modules as node:-prefixed - tests: configCases/deno/* gated to the Deno runtime + Defaults snapshot
Accept `deno[X[.Y]]` (e.g. deno2, deno1.40) like the node target, and resolve version-dependent features per version: import.meta.dirname and import.meta.filename are emitted natively only for Deno >= 1.40.
DenoTargetPlugin now keeps npm:, jsr: and http(s):// specifiers external (alongside node:), so they stay external on the deno target even when the web externals preset is disabled — Deno resolves them at runtime.
…API test - UniversalCompileAsyncWasmPlugin now reads the wasm binary via Deno.readFile when running under Deno, instead of falling into the browser fetch branch or the Node-only bare 'fs' import (which Deno can't resolve). - Add a deno configCase asserting the Deno standard API global is preserved.
Deno defines `self`, so the universal wasm runtime already takes the `fetch` branch, and Deno supports `fetch()` of `file:` URLs (since 1.16) plus `WebAssembly.instantiateStreaming` - the recommended, streaming path. The Deno.readFile branch was based on a wrong premise and lost streaming.
- Add configCases/deno/protocol-externals: builds a deno target importing via npm:, jsr: and https:// protocols and asserts they stay external (stubbed offline via test.config.js modules, so it runs without network). - Make the deno cases emit ESM in the harness by setting experiments.outputModule explicitly (the harness picks the bundle extension before defaults apply). - Feature-gate the runtime-agnostic deno cases on supportsGlobalThis so they run in the Node integration suite too; keep Deno-only cases on the Deno global.
🦋 Changeset detectedLatest commit: 02f9d9c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| ); | ||
|
|
||
| // Deno's own import protocols are resolved by the runtime, never bundled. | ||
| const DENO_PROTOCOLS = /^(?:npm|jsr|https?):/; |
There was a problem hiding this comment.
Does Deno support using HTTP only? Because you're only adding support for HTTPS
There was a problem hiding this comment.
Good thing to check — it actually already covers both. The pattern is https?:, where the ? makes the s optional, so it matches http: and https: (same as the http(s):// noted in the changeset). Deno does support plain http:// imports too (commonly http://localhost in dev), so both are kept external here.
Generated by Claude Code
| @@ -0,0 +1,47 @@ | |||
| /* | |||
| MIT License http://www.opensource.org/licenses/mit-license.php | |||
| Author Tobias Koppers @sokra | |||
There was a problem hiding this comment.
Haha, wouldn’t that be your copyright?
There was a problem hiding this comment.
Good catch — that was boilerplate copied over from NodeTargetPlugin.js. Updated the header to Author Alexander Akait @alexander-akait.
Generated by Claude Code
There was a problem hiding this comment.
You should add that to the agents.md file
There was a problem hiding this comment.
Good idea — added a short Source file headers note under Coding Standards in AGENTS.md (CLAUDE.md is a symlink to it): new files get the MIT header with the Author line set to the actual author, not copied from another file.
Generated by Claude Code
Align the bun target with the deno target work (#21247): populate `compiler.platform.bun` (Compiler.js default + defaults platform object), return `"bun"` from the loader-target default, and switch wasm loading to the web-standard `fetch` runtime (Bun resolves `file:` URLs) instead of a proprietary `Bun.file` path. Drops the now-unneeded BunCompileAsyncWasmPlugin and `async-bun` wasm type; `bun:*` externals and the ESM defaults stay.
# Conflicts: # lib/config/defaults.js # schemas/WebpackOptions.check.js
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21247 +/- ##
==========================================
+ Coverage 92.80% 92.81% +0.01%
==========================================
Files 589 590 +1
Lines 64616 64646 +30
Branches 17978 17992 +14
==========================================
+ Hits 59964 59999 +35
+ Misses 4652 4647 -5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
This PR is packaged and the instant preview is available (96b3b70). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@96b3b70
yarn add -D webpack@https://pkg.pr.new/webpack@96b3b70
pnpm add -D webpack@https://pkg.pr.new/webpack@96b3b70 |
Add deno to the browserslist configCase externalsPresets snapshots and the CLI flags snapshot (the new --externals-presets-deno option), matching the externalsPresets.deno default introduced for the deno target.
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "many-modules-esm", scenario '{"name":"mode-development","mode":"development"}' |
1.2 MB | 1.9 MB | -40.02% |
| ❌ | Memory | benchmark "wasm-modules-async", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
189.2 KB | 247.7 KB | -23.63% |
| ⚡ | Memory | benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
358.3 KB | 130.6 KB | ×2.7 |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/deno-target (02f9d9c) with main (743256f)
The ESM runner executes bundles in an isolated vm context that didn't include Deno's runtime global, so the deno standard-api case saw `typeof Deno === 'undefined'` under Deno. Pass the Deno global through, like process/URL/TextEncoder.
Summary
Adds a first-class
denobuild target (target: "deno", with versions likedeno2/deno1.40, resolved the same way asnode14.5). It emits ESM by default and is treated as anode+webplatform, so it slots into the existing universal handling.Deno-specific behavior:
node:specifier (Deno does not resolve barefs), for bothimportandrequire/createRequire.npm:,jsr:,node:,http(s)://— are kept external (resolved by the runtime), even if thewebexternals preset is disabled.import.meta.dirname/filenameonly for Deno ≥ 1.40).The default runtime intentionally uses the web-standard APIs Deno implements natively (
import(),fetch,import.meta.url) rather than proprietaryDeno.*calls, since those are the idiomatic and optimal paths under Deno.What kind of change does this PR introduce?
feat
Did you add tests for your changes?
Yes.
test/Defaults.unittest.jsgainstarget: "deno"andtarget: "deno1.40"snapshots, andtest/configCases/deno/*covers node-builtinnode:externals, code splitting, protocol externals (npm:/jsr:/https:, stubbed offline),import.meta.dirname, web+node globals, and theDenoglobal. Runtime-agnostic cases run in the Node integration suite; cases needing the Deno runtime are gated to it.Does this PR introduce a breaking change?
No. It only adds a new opt-in
targetvalue and a newexternalsPresets.deno.If relevant, what needs to be documented once your changes are merged or what have you already documented?
The
targetconfiguration docs should list the newdeno/deno[X[.Y]]value, and the externals docs should mention theexternalsPresets.denopreset.Use of AI
AI (Claude Code) was used to help map where target properties thread through
lib/, draft the implementation and tests, and verify behavior with local builds and the test suite. All changes were reviewed and validated locally (yarn tsc, targetedjestruns, lint/format) before submitting.Generated by Claude Code