Skip to content

legacy: support lowering to ES5 #21951

@martinarvaiaccedo

Description

@martinarvaiaccedo

Describe the regression

He vite team! First of all thanks for your hard work on the rolldown version, its amazing to see the performance benefits! We rely on vite legacy to build for old - and slow - smart tvs and set top boxes, therefore sometimes we need to go back even to ES5. With rolldown vite-legacy, I realized the polyfills-legacy chunk is not being transpiled and will have es6 code in. I managed to patch it to be working again.

Thanks for looking into this!

Root Cause

The buildPolyfillChunk() function generates a polyfills bundle using Vite/Rolldown, but the output is not transpiled to ES5. The chunk contains:

  1. Core-js polyfills (ES6+ code)
  2. SystemJS library code (ES6+ code)

Since the polyfills are meant to run before any other code on legacy browsers, they must be ES5-compatible. Modern bundlers don't transpile to ES5 by default.

Solution Applied

Added Babel transpilation to ES5 in buildPolyfillChunk() for legacy/iife format chunks:

if (format === "iife" && polyfillChunk && legacyTargets) {
  const babelTransformOptions = {
    babelrc: false,
    configFile: false,
    compact: !!minify,
    sourceMaps: !!sourcemap,
    sourceType: 'script',  // IIFE, not ES modules
    inputSourceMap: void 0,
    targets: legacyTargets,
    assumptions: babelAssumptions || {},
    browserslistConfigFile: false,
    presets: [[(await import("@babel/preset-env")).default, {
      bugfixes: true,
      modules: false,
      useBuiltIns: false,
      shippedProposals: true,
      exclude: ['transform-typeof-symbol']  // Critical: prevents breaking Symbol polyfills
    }]]
  };
  
  const result = babel$1.transform(polyfillChunk.code, babelTransformOptions);
  if (result) {
    polyfillChunk = {
      ...polyfillChunk,
      code: result.code,
      map: result.map
    };
  }
}

Also removed prependModenChunkLegacyGuardPlugin() from the polyfills build plugins - polyfills shouldn't have the modern browser guard export.

Impact

  • Before: 335KB polyfills-legacy chunk with ES6+ syntax → crashes on Chrome 52
  • After: 204KB polyfills-legacy chunk with ES5 syntax → works on legacy browsers

Notes

  • exclude: ['transform-typeof-symbol'] is critical to prevent Babel from breaking Symbol detection in core-js polyfills
  • sourceType: 'script' is required since the polyfills chunk is IIFE, not ES modules
  • Main legacy entry chunks already get proper Babel transpilation via renderChunk(), but polyfills bypass that path

Here's a proposed patch - i didnt raise a PR as I was not sure if you plan to support this at all :)

@vitejs__plugin-legacy.patch

Reproduction

https://stackblitz.com/edit/vitejs-vite-xap7pp8d

Expected Behavior

polyfills-legacy chunk is not transpiled

Actual Behavior

should be transpiled to es5

Steps to Reproduce

Reproduce in this container https://stackblitz.com/edit/vitejs-vite-xap7pp8d?file=dist%2Fassets%2Fpolyfills-legacy-ULSa55Dz.js

  • check polyfills-legacy, it contains arrow functions => even though target is chrome 38
  • change back to normal vite in package.json and observe that it will build to legacy correctly

System Info

Envinfo can not be ran, interestingly.

Stacklbitz's:



  System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 20.19.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.8.2 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    @vitejs/plugin-legacy: 7.2.1 => 7.2.1 
    rolldown-vite:  7.1.17

Used Package Manager

npm

Logs

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions