Skip to content

Commit

Permalink
feat(plugin-legacy): add option to output only legacy builds (#10139)
Browse files Browse the repository at this point in the history
Co-authored-by: “amirkian007” <amircc1200@gmail.com>
Co-authored-by: sapphi-red <green@sapphi.red>
  • Loading branch information
3 people committed Jul 4, 2023
1 parent 02c6bc3 commit 931b24f
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 37 deletions.
7 changes: 7 additions & 0 deletions packages/plugin-legacy/README.md
Expand Up @@ -117,6 +117,13 @@ npm add -D terser

Defaults to `false`. Enabling this option will exclude `systemjs/dist/s.min.js` inside polyfills-legacy chunk.

### `renderModernChunks`

- **Type:** `boolean`
- **Default:** `true`

Set to `false` to only output the legacy bundles that support all target browsers.

## Browsers that supports ESM but does not support widely-available features

The legacy plugin offers a way to use widely-available features natively in the modern build, while falling back to the legacy build in browsers with native ESM but without those features supported (e.g. Legacy Edge). This feature works by injecting a runtime check and loading the legacy bundle with SystemJs runtime if needed. There are the following drawbacks:
Expand Down
96 changes: 59 additions & 37 deletions packages/plugin-legacy/src/index.ts
Expand Up @@ -125,6 +125,12 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
let targets: Options['targets']

const genLegacy = options.renderLegacyChunks !== false
const genModern = options.renderModernChunks !== false
if (!genLegacy && !genModern) {
throw new Error(
'`renderLegacyChunks` and `renderModernChunks` cannot be both false',
)
}

const debugFlags = (process.env.DEBUG || '').split(',')
const isDebug =
Expand All @@ -136,7 +142,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
const modernPolyfills = new Set<string>()
const legacyPolyfills = new Set<string>()

if (Array.isArray(options.modernPolyfills)) {
if (Array.isArray(options.modernPolyfills) && genModern) {
options.modernPolyfills.forEach((i) => {
modernPolyfills.add(
i.includes('/') ? `core-js/${i}` : `core-js/modules/${i}.js`,
Expand Down Expand Up @@ -343,9 +349,15 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
const { rollupOptions } = config.build
const { output } = rollupOptions
if (Array.isArray(output)) {
rollupOptions.output = [...output.map(createLegacyOutput), ...output]
rollupOptions.output = [
...output.map(createLegacyOutput),
...(genModern ? output : []),
]
} else {
rollupOptions.output = [createLegacyOutput(output), output || {}]
rollupOptions.output = [
createLegacyOutput(output),
...(genModern ? [output || {}] : []),
]
}
},

Expand All @@ -357,7 +369,8 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
if (!isLegacyChunk(chunk, opts)) {
if (
options.modernPolyfills &&
!Array.isArray(options.modernPolyfills)
!Array.isArray(options.modernPolyfills) &&
genModern
) {
// analyze and record modern polyfills
await detectPolyfills(raw, { esmodules: true }, modernPolyfills)
Expand Down Expand Up @@ -455,50 +468,59 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
if (config.build.ssr) return
if (!chunk) return
if (chunk.fileName.includes('-legacy')) {
// The legacy bundle is built first, and its index.html isn't actually
// emitted. Here we simply record its corresponding legacy chunk.
// The legacy bundle is built first, and its index.html isn't actually emitted if
// modern bundle will be generated. Here we simply record its corresponding legacy chunk.
facadeToLegacyChunkMap.set(chunk.facadeModuleId, chunk.fileName)
return
if (genModern) {
return
}
}
if (!genModern) {
html = html.replace(/<script type="module".*?<\/script>/g, '')
}

const tags: HtmlTagDescriptor[] = []
const htmlFilename = chunk.facadeModuleId?.replace(/\?.*$/, '')

// 1. inject modern polyfills
const modernPolyfillFilename = facadeToModernPolyfillMap.get(
chunk.facadeModuleId,
)

if (modernPolyfillFilename) {
tags.push({
tag: 'script',
attrs: {
type: 'module',
crossorigin: true,
src: toAssetPathFromHtml(
modernPolyfillFilename,
chunk.facadeModuleId!,
config,
),
},
})
} else if (modernPolyfills.size) {
throw new Error(
`No corresponding modern polyfill chunk found for ${htmlFilename}`,
if (genModern) {
const modernPolyfillFilename = facadeToModernPolyfillMap.get(
chunk.facadeModuleId,
)

if (modernPolyfillFilename) {
tags.push({
tag: 'script',
attrs: {
type: 'module',
crossorigin: true,
src: toAssetPathFromHtml(
modernPolyfillFilename,
chunk.facadeModuleId!,
config,
),
},
})
} else if (modernPolyfills.size) {
throw new Error(
`No corresponding modern polyfill chunk found for ${htmlFilename}`,
)
}
}

if (!genLegacy) {
return { html, tags }
}

// 2. inject Safari 10 nomodule fix
tags.push({
tag: 'script',
attrs: { nomodule: true },
children: safari10NoModuleFix,
injectTo: 'body',
})
if (genModern) {
tags.push({
tag: 'script',
attrs: { nomodule: genModern },
children: safari10NoModuleFix,
injectTo: 'body',
})
}

// 3. inject legacy polyfills
const legacyPolyfillFilename = facadeToLegacyPolyfillMap.get(
Expand All @@ -508,7 +530,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
tags.push({
tag: 'script',
attrs: {
nomodule: true,
nomodule: genModern,
crossorigin: true,
id: legacyPolyfillId,
src: toAssetPathFromHtml(
Expand All @@ -534,7 +556,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
tags.push({
tag: 'script',
attrs: {
nomodule: true,
nomodule: genModern,
crossorigin: true,
// we set the entry path on the element as an attribute so that the
// script content will stay consistent - which allows using a constant
Expand All @@ -556,7 +578,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
}

// 5. inject dynamic import fallback entry
if (genLegacy && legacyPolyfillFilename && legacyEntryFilename) {
if (legacyPolyfillFilename && legacyEntryFilename && genModern) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
Expand All @@ -582,7 +604,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
return
}

if (isLegacyBundle(bundle, opts)) {
if (isLegacyBundle(bundle, opts) && genModern) {
// avoid emitting duplicate assets
for (const name in bundle) {
if (bundle[name].type === 'asset' && !/.+\.map$/.test(name)) {
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-legacy/src/types.ts
Expand Up @@ -24,4 +24,8 @@ export interface Options {
* default: false
*/
externalSystemJS?: boolean
/**
* default: true
*/
renderModernChunks?: boolean
}

0 comments on commit 931b24f

Please sign in to comment.