Skip to content

refactor: ESM-only packages (WIP) - #15000

Open
posva wants to merge 18 commits into
minorfrom
refactor/esm-only
Open

refactor: ESM-only packages (WIP)#15000
posva wants to merge 18 commits into
minorfrom
refactor/esm-only

Conversation

@posva

@posva posva commented Jun 22, 2026

Copy link
Copy Markdown
Member

Move all packages to ESM-only and drop legacy baggage.

  • Drop CJS builds and dual-format packaging. Conditional maps kept for vue (node/default) and compiler-sfc (browser/default). Root "types" kept for node10 tsconfigs. All packages 🟢 on @arethetypeswrong/cli.
  • Rename dist/<pkg>.esm-bundler.jsdist/<pkg>.js.
  • Trim esm-browser builds to the ones in use (see table).
  • Only the vue IIFE/global builds are kept.
  • Node-only features previously exclusive to CJS builds (renderToNodeStream(), on-the-fly SSR template compilation) now work in the ESM Node build (__CJS__ flag removed).
  • Remove the Vue 2 compat layer: @vue/compat, __COMPAT__, compat/ dirs (~7.3k lines).

dist size per package

Built JS bundles only (min + unmin), excluding .d.ts and source maps. Base column measured on minor before the last main merge, which explains the small drift on otherwise untouched packages (runtime-vapor, vue).

package base (minor) this PR Δ
compiler-core 608.5 kB 217.9 kB −390.6 kB (−64%)
compiler-dom 602.3 kB 25.9 kB −576.4 kB (−96%)
compiler-sfc 2317 kB 2305.3 kB −11.7 kB (−1%)
compiler-ssr 40 kB 39.2 kB −0.8 kB (−2%)
compiler-vapor 1080.4 kB 182.6 kB −897.8 kB (−83%)
reactivity 346 kB 142.4 kB −203.6 kB (−59%)
runtime-core 699.8 kB 257.9 kB −441.9 kB (−63%)
runtime-dom 1141.4 kB 58.9 kB −1082.5 kB (−95%)
runtime-vapor 261 kB 267.5 kB +6.5 kB (+3%)
server-renderer 441.1 kB 279.0 kB −162.1 kB (−37%)
shared 97.4 kB 31.5 kB −65.9 kB (−68%)
vue-compat 5601.3 kB — (removed) −5601.3 kB (−100%)
vue 3209.6 kB 3225.6 kB +16.0 kB (0%)
total 16.45 MB 7.03 MB −9.41 MB (−57%)

Build outputs kept / removed

✅ kept · ❌ removed · 🆕 added · — never had. esm-bundler is renamed to .js (shown under ESM).

package CJS (+prod) ESM (.js) esm-browser esm-browser.prod global/IIFE (+prod)
compiler-core
compiler-dom
compiler-sfc 🆕
compiler-ssr 🆕
compiler-vapor 🆕
reactivity
runtime-core
runtime-dom
runtime-vapor
server-renderer
shared
vue
vue-compat

vue additionally keeps its runtime-only and vapor browser builds (vue.runtime.*, vue.runtime-with-vapor.esm-browser[.prod]); only CJS was dropped. vue-compat is removed as a package.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd742532-e008-473b-a9e4-d5d76c48d229

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/esm-only

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@netlify

netlify Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploy Preview for vue-sfc-playground failed. Why did it fail? →

Name Link
🔨 Latest commit 6497a9b
🔍 Latest deploy log https://app.netlify.com/projects/vue-sfc-playground/deploys/6a3935407c79e10008255d6f

@pkg-pr-new

pkg-pr-new Bot commented Jun 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@15000
npm i https://pkg.pr.new/@vue/compiler-core@15000
yarn add https://pkg.pr.new/@vue/compiler-core@15000.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@15000
npm i https://pkg.pr.new/@vue/compiler-dom@15000
yarn add https://pkg.pr.new/@vue/compiler-dom@15000.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@15000
npm i https://pkg.pr.new/@vue/compiler-sfc@15000
yarn add https://pkg.pr.new/@vue/compiler-sfc@15000.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@15000
npm i https://pkg.pr.new/@vue/compiler-ssr@15000
yarn add https://pkg.pr.new/@vue/compiler-ssr@15000.tgz

@vue/compiler-vapor

pnpm add https://pkg.pr.new/@vue/compiler-vapor@15000
npm i https://pkg.pr.new/@vue/compiler-vapor@15000
yarn add https://pkg.pr.new/@vue/compiler-vapor@15000.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@15000
npm i https://pkg.pr.new/@vue/reactivity@15000
yarn add https://pkg.pr.new/@vue/reactivity@15000.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@15000
npm i https://pkg.pr.new/@vue/runtime-core@15000
yarn add https://pkg.pr.new/@vue/runtime-core@15000.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@15000
npm i https://pkg.pr.new/@vue/runtime-dom@15000
yarn add https://pkg.pr.new/@vue/runtime-dom@15000.tgz

@vue/runtime-vapor

pnpm add https://pkg.pr.new/@vue/runtime-vapor@15000
npm i https://pkg.pr.new/@vue/runtime-vapor@15000
yarn add https://pkg.pr.new/@vue/runtime-vapor@15000.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@15000
npm i https://pkg.pr.new/@vue/server-renderer@15000
yarn add https://pkg.pr.new/@vue/server-renderer@15000.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@15000
npm i https://pkg.pr.new/@vue/shared@15000
yarn add https://pkg.pr.new/@vue/shared@15000.tgz

vue

pnpm add https://pkg.pr.new/vue@15000
npm i https://pkg.pr.new/vue@15000
yarn add https://pkg.pr.new/vue@15000.tgz

commit: f9bc7d0

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
vue.global.prod.js 174 kB (-198 B) 62.8 kB (-177 B) 56 kB (-170 B)
compiler-dom.global.prod.js
runtime-dom.global.prod.js

Usages

Name Size Gzip Brotli
createApp (CAPI only) 52.1 kB (-6 B) 20.4 kB (+22 B) 18.6 kB (+43 B)
createApp 61 kB (-80 B) 23.7 kB (+47 B) 21.5 kB (-4 B)
createApp + vaporInteropPlugin 113 kB (-84 B) 41.1 kB (+5 B) 37 kB (-55 B)
createVaporApp 28.6 kB (-6 B) 11.1 kB (-3 B) 10.2 kB (+1 B)
createSSRApp 65.9 kB (-77 B) 25.6 kB (+2 B) 23.2 kB (-10 B)
createVaporSSRApp 33.8 kB (-6 B) 13 kB (+4 B) 11.9 kB (-7 B)
defineCustomElement 67.6 kB (-80 B) 25.7 kB (+36 B) 23.4 kB (+23 B)
defineVaporCustomElement 44.4 kB (-6 B) 16.2 kB (-1 B) 14.8 kB (-5 B)
overall 76.1 kB (-75 B) 29 kB (-42 B) 26.3 kB (-20 B)

@posva posva changed the title refactor: ESM-only packages refactor: ESM-only packages (WIP) Jun 22, 2026
@posva
posva force-pushed the refactor/esm-only branch from 6497a9b to ecebd25 Compare June 22, 2026 13:21
@sxzz

sxzz commented Jul 16, 2026

Copy link
Copy Markdown
Member

We can upgrade magic-string to v1.0.0 as well. It migrated to pure ESM.

posva added 14 commits July 16, 2026 12:37
The single `esm` build of @vue/compiler-core and @vue/compiler-dom is now
consumed directly by Node tooling (e.g. @vitejs/plugin-vue's compileScript /
compileTemplate) since CJS builds were dropped. Those builds were compiled
with __BROWSER__=true, which forces prefixIdentifiers off and throws compiler
error 48 (X_PREFIX_ID_NOT_SUPPORTED), breaking every SFC/SSR build.

Scope the bundler `esm` format of compiler-core and compiler-dom to
__BROWSER__=false while leaving their browser-targeted formats (global /
esm-browser) untouched. Runtime packages keep __BROWSER__=true for their esm
build (e.g. runtime-dom's useCssVars guard).
- server-renderer: prod:false — drop the unused server-renderer.esm-browser.prod.js
- reactivity: drop esm-browser from published formats (dev bundle was dead, prod only needed for benchmarks)
- build.js: honor explicitly requested formats for a single named target so `prebench` can still build reactivity's esm-browser bundle on demand
The esm-browser build of @vue/server-renderer imports node:stream (in
renderToStream.ts), tree-shaken out of browser bundles. The external
list only had the bare 'stream' specifier, so rolldown emitted an
UNRESOLVED_IMPORT warning that turns fatal on stricter CI/Netlify
builds. Add 'node:stream' to treeShakenDeps to suppress it.
@posva
posva force-pushed the refactor/esm-only branch from b51559c to 15bfbf4 Compare July 16, 2026 10:48
@skirtles-code

Copy link
Copy Markdown
Contributor

Aren't the esm-browser builds still needed?

For example, if someone wants to use @vue/reactivity via an import map, would that not be considered valid usage?

From a quick search through GitHub I found examples of people doing this, e.g.:

https://github.com/faisalhakim47/jurukasa-web/blob/46303e9c01be403d7a2919515f03f67858bab884/index.html#L22

I don't know what that application does, but it looks like valid usage to me.

More generally, wouldn't we want to encourage people to use esm-browser builds instead of global/IIFE builds? For any package where we can justify publishing an IIFE build, shouldn't we also be publishing an esm-browser build too?

@posva

posva commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

That's a good point. I need to add them back. I wonder if we are ready to drop the iife builds? At least for everything except vue

Edit

posva added 2 commits July 17, 2026 16:55
The dev + prod esm-browser builds are useful for direct browser/CDN
usage of @vue/reactivity, so publish them again instead of building
the prod bundle on demand for benchmarks only.
- compiler-dom, runtime-dom: esm-only published output; remove the
  unpkg/jsdelivr fields since there is no browser-loadable bundle left
- reactivity: point unpkg/jsdelivr at the esm-browser build instead
- size-global: build each target separately so runtime-dom and
  compiler-dom global prod sizes stay tracked via the on-demand
  single-target format override in build.js (bundles are no longer
  published but remain the size-regression signal per package)
- buildOptions.name kept: scripts/dev.js still uses it for local
  global-format dev builds
@TheAlexLichter

TheAlexLichter commented Jul 17, 2026

Copy link
Copy Markdown

I suppose they are mainly loaded via CDN (and in turn do not show up in grep.app)? But having esm-browser only seems fine to me.

@posva

posva commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

Updated the table and simplified the list of changes to be more comprehensible

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants