Skip to content

Commit 2cae61e

Browse files
nulladdictaleclarson
authored andcommitted
fix(plugin-legacy): legacy fallback for dynamic import (vitejs#3885)
1 parent 214b160 commit 2cae61e

File tree

2 files changed

+49
-24
lines changed

2 files changed

+49
-24
lines changed

packages/plugin-legacy/README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ export default {
122122
}
123123
```
124124

125+
## Dynamic Import
126+
127+
The legacy plugin offers a way to use native `import()` in the modern build while falling back to the legacy build in browsers with native ESM but without dynamic import support (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:
128+
129+
- Modern bundle is downloaded in all ESM browsers
130+
- Modern bundle throws `SyntaxError` in browsers without dynamic import
131+
125132
## Polyfill Specifiers
126133

127134
Polyfill specifier strings for `polyfills` and `modernPolyfills` can be either of the following:
@@ -147,12 +154,13 @@ export default {
147154

148155
## Content Security Policy
149156

150-
The legacy plugin requires inline scripts for [Safari 10.1 `nomodule` fix](https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc) and SystemJS initialization. If you have a strict CSP policy requirement, you will need to [add the corresponding hashes to your `script-src` list](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_inline_script):
157+
The legacy plugin requires inline scripts for [Safari 10.1 `nomodule` fix](https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc), SystemJS initialization, and dynamic import fallback. If you have a strict CSP policy requirement, you will need to [add the corresponding hashes to your `script-src` list](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_inline_script):
151158

152159
- `MS6/3FCg4WjP9gwgaBGwLpRCY6fZBgwmhVCdrPrNf3E=`
153160
- `tQjf8gvb2ROOMapIxFvFAYBeUJ0v1HCbOcSmDNXGtDo=`
161+
- `T9h4ixy0FtNsCwAyTfBtIY6uV5ZhMeNQIlL42GAKEME=`
154162

155-
These values can also be retrived via
163+
These values can also be retrieved via
156164

157165
```js
158166
const { cspHashes } = require('@vitejs/plugin-legacy')

packages/plugin-legacy/index.js

+39-22
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ const loadBabel = () => babel || (babel = require('@babel/standalone'))
1515
// DO NOT ALTER THIS CONTENT
1616
const safari10NoModuleFix = `!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();`
1717

18+
const legacyPolyfillId = 'vite-legacy-polyfill'
1819
const legacyEntryId = 'vite-legacy-entry'
1920
const systemJSInlineCode = `System.import(document.getElementById('${legacyEntryId}').getAttribute('data-src'))`
21+
const dynamicFallbackInlineCode = `!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above should be ignored");var e=document.getElementById("${legacyPolyfillId}"),n=document.createElement("script");n.src=e.src,n.onload=function(){${systemJSInlineCode}},document.body.appendChild(n)}}();`
22+
23+
const blankDynamicImport = `import('data:text/javascript;base64,Cg==');`
2024

2125
const legacyEnvVarMarker = `__VITE_IS_LEGACY__`
2226

@@ -31,6 +35,7 @@ function viteLegacyPlugin(options = {}) {
3135
let config
3236
const targets = options.targets || 'defaults'
3337
const genLegacy = options.renderLegacyChunks !== false
38+
const genDynamicFallback = genLegacy
3439

3540
const debugFlag = process.env.DEBUG
3641
const isDebug = debugFlag === 'vite:*' || debugFlag === 'vite:legacy'
@@ -76,9 +81,6 @@ function viteLegacyPlugin(options = {}) {
7681
if (!config.build) {
7782
config.build = {}
7883
}
79-
if (genLegacy) {
80-
config.build.polyfillDynamicImport = true
81-
}
8284
}
8385
}
8486

@@ -123,7 +125,7 @@ function viteLegacyPlugin(options = {}) {
123125
}
124126

125127
// legacy bundle
126-
if (legacyPolyfills.size) {
128+
if (legacyPolyfills.size || genDynamicFallback) {
127129
if (!legacyPolyfills.has('es.promise')) {
128130
// check if the target needs Promise polyfill because SystemJS relies
129131
// on it
@@ -228,28 +230,31 @@ function viteLegacyPlugin(options = {}) {
228230
detectPolyfills(raw, { esmodules: true }, modernPolyfills)
229231
}
230232

233+
const ms = new MagicString(raw)
234+
235+
if (genDynamicFallback && chunk.isEntry) {
236+
ms.prepend(blankDynamicImport)
237+
}
238+
231239
if (raw.includes(legacyEnvVarMarker)) {
232240
const re = new RegExp(legacyEnvVarMarker, 'g')
233-
if (config.build.sourcemap) {
234-
const s = new MagicString(raw)
235-
let match
236-
while ((match = re.exec(raw))) {
237-
s.overwrite(
238-
match.index,
239-
match.index + legacyEnvVarMarker.length,
240-
`false`
241-
)
242-
}
243-
return {
244-
code: s.toString(),
245-
map: s.generateMap({ hires: true })
246-
}
247-
} else {
248-
return raw.replace(re, `false`)
241+
let match
242+
while ((match = re.exec(raw))) {
243+
ms.overwrite(
244+
match.index,
245+
match.index + legacyEnvVarMarker.length,
246+
`false`
247+
)
249248
}
250249
}
251250

252-
return null
251+
if (config.build.sourcemap) {
252+
return {
253+
code: ms.toString(),
254+
map: ms.generateMap({ hires: true })
255+
}
256+
}
257+
return ms.toString()
253258
}
254259

255260
if (!genLegacy) {
@@ -358,6 +363,7 @@ function viteLegacyPlugin(options = {}) {
358363
tag: 'script',
359364
attrs: {
360365
nomodule: true,
366+
id: legacyPolyfillId,
361367
src: `${config.base}${legacyPolyfillFilename}`
362368
},
363369
injectTo: 'body'
@@ -392,6 +398,16 @@ function viteLegacyPlugin(options = {}) {
392398
)
393399
}
394400

401+
// 5. inject dynamic import fallback entry
402+
if (genDynamicFallback && legacyPolyfillFilename && legacyEntryFilename) {
403+
tags.push({
404+
tag: 'script',
405+
attrs: { type: 'module' },
406+
children: dynamicFallbackInlineCode,
407+
injectTo: 'head'
408+
})
409+
}
410+
395411
return {
396412
html,
397413
tags
@@ -639,5 +655,6 @@ viteLegacyPlugin.default = viteLegacyPlugin
639655

640656
viteLegacyPlugin.cspHashes = [
641657
createHash('sha256').update(safari10NoModuleFix).digest('base64'),
642-
createHash('sha256').update(systemJSInlineCode).digest('base64')
658+
createHash('sha256').update(systemJSInlineCode).digest('base64'),
659+
createHash('sha256').update(dynamicFallbackInlineCode).digest('base64')
643660
]

0 commit comments

Comments
 (0)