Skip to content

Commit

Permalink
fix(plugin-legacy): legacy fallback for dynamic import (#3885)
Browse files Browse the repository at this point in the history
  • Loading branch information
nulladdict authored Jul 23, 2021
1 parent 5b13ad5 commit fc6d8f1
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 24 deletions.
12 changes: 10 additions & 2 deletions packages/plugin-legacy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ export default {
}
```

## Dynamic Import

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:

- Modern bundle is downloaded in all ESM browsers
- Modern bundle throws `SyntaxError` in browsers without dynamic import

## Polyfill Specifiers

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

## Content Security Policy

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):
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):

- `MS6/3FCg4WjP9gwgaBGwLpRCY6fZBgwmhVCdrPrNf3E=`
- `tQjf8gvb2ROOMapIxFvFAYBeUJ0v1HCbOcSmDNXGtDo=`
- `T9h4ixy0FtNsCwAyTfBtIY6uV5ZhMeNQIlL42GAKEME=`

These values can also be retrived via
These values can also be retrieved via

```js
const { cspHashes } = require('@vitejs/plugin-legacy')
Expand Down
61 changes: 39 additions & 22 deletions packages/plugin-legacy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ const loadBabel = () => babel || (babel = require('@babel/standalone'))
// DO NOT ALTER THIS CONTENT
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()}}();`

const legacyPolyfillId = 'vite-legacy-polyfill'
const legacyEntryId = 'vite-legacy-entry'
const systemJSInlineCode = `System.import(document.getElementById('${legacyEntryId}').getAttribute('data-src'))`
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)}}();`

const blankDynamicImport = `import('data:text/javascript;base64,Cg==');`

const legacyEnvVarMarker = `__VITE_IS_LEGACY__`

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

const debugFlag = process.env.DEBUG
const isDebug = debugFlag === 'vite:*' || debugFlag === 'vite:legacy'
Expand Down Expand Up @@ -76,9 +81,6 @@ function viteLegacyPlugin(options = {}) {
if (!config.build) {
config.build = {}
}
if (genLegacy) {
config.build.polyfillDynamicImport = true
}
}
}

Expand Down Expand Up @@ -123,7 +125,7 @@ function viteLegacyPlugin(options = {}) {
}

// legacy bundle
if (legacyPolyfills.size) {
if (legacyPolyfills.size || genDynamicFallback) {
if (!legacyPolyfills.has('es.promise')) {
// check if the target needs Promise polyfill because SystemJS relies
// on it
Expand Down Expand Up @@ -228,28 +230,31 @@ function viteLegacyPlugin(options = {}) {
detectPolyfills(raw, { esmodules: true }, modernPolyfills)
}

const ms = new MagicString(raw)

if (genDynamicFallback && chunk.isEntry) {
ms.prepend(blankDynamicImport)
}

if (raw.includes(legacyEnvVarMarker)) {
const re = new RegExp(legacyEnvVarMarker, 'g')
if (config.build.sourcemap) {
const s = new MagicString(raw)
let match
while ((match = re.exec(raw))) {
s.overwrite(
match.index,
match.index + legacyEnvVarMarker.length,
`false`
)
}
return {
code: s.toString(),
map: s.generateMap({ hires: true })
}
} else {
return raw.replace(re, `false`)
let match
while ((match = re.exec(raw))) {
ms.overwrite(
match.index,
match.index + legacyEnvVarMarker.length,
`false`
)
}
}

return null
if (config.build.sourcemap) {
return {
code: ms.toString(),
map: ms.generateMap({ hires: true })
}
}
return ms.toString()
}

if (!genLegacy) {
Expand Down Expand Up @@ -358,6 +363,7 @@ function viteLegacyPlugin(options = {}) {
tag: 'script',
attrs: {
nomodule: true,
id: legacyPolyfillId,
src: `${config.base}${legacyPolyfillFilename}`
},
injectTo: 'body'
Expand Down Expand Up @@ -392,6 +398,16 @@ function viteLegacyPlugin(options = {}) {
)
}

// 5. inject dynamic import fallback entry
if (genDynamicFallback && legacyPolyfillFilename && legacyEntryFilename) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
children: dynamicFallbackInlineCode,
injectTo: 'head'
})
}

return {
html,
tags
Expand Down Expand Up @@ -639,5 +655,6 @@ viteLegacyPlugin.default = viteLegacyPlugin

viteLegacyPlugin.cspHashes = [
createHash('sha256').update(safari10NoModuleFix).digest('base64'),
createHash('sha256').update(systemJSInlineCode).digest('base64')
createHash('sha256').update(systemJSInlineCode).digest('base64'),
createHash('sha256').update(dynamicFallbackInlineCode).digest('base64')
]

0 comments on commit fc6d8f1

Please sign in to comment.