@vitejs/plugin-legacy 1.8.0 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js DELETED
@@ -1,720 +0,0 @@
1
- // @ts-check
2
- const path = require('path')
3
- const { createHash } = require('crypto')
4
- const { build } = require('vite')
5
- const MagicString = require('magic-string').default
6
-
7
- // lazy load babel since it's not used during dev
8
- let babel
9
- /**
10
- * @return {import('@babel/standalone')}
11
- */
12
- const loadBabel = () => babel || (babel = require('@babel/standalone'))
13
-
14
- // https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc
15
- // DO NOT ALTER THIS CONTENT
16
- 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()}}();`
17
-
18
- const legacyPolyfillId = 'vite-legacy-polyfill'
19
- const legacyEntryId = 'vite-legacy-entry'
20
- const systemJSInlineCode = `System.import(document.getElementById('${legacyEntryId}').getAttribute('data-src'))`
21
-
22
- const detectDynamicImportVarName = '__vite_is_dynamic_import_support'
23
- const detectDynamicImportVarInitCode = `var ${detectDynamicImportVarName}=false;`
24
- const detectDynamicImportCode = `try{import("_").catch(()=>1);}catch(e){}window.${detectDynamicImportVarName}=true;`
25
- const dynamicFallbackInlineCode = `!function(){if(window.${detectDynamicImportVarName})return;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)}();`
26
-
27
- const forceDynamicImportUsage = `export function __vite_legacy_guard(){import('data:text/javascript,')};`
28
-
29
- const legacyEnvVarMarker = `__VITE_IS_LEGACY__`
30
-
31
- /**
32
- * @param {import('.').Options} options
33
- * @returns {import('vite').Plugin[]}
34
- */
35
- function viteLegacyPlugin(options = {}) {
36
- /**
37
- * @type {import('vite').ResolvedConfig}
38
- */
39
- let config
40
- const targets = options.targets || 'defaults'
41
- const genLegacy = options.renderLegacyChunks !== false
42
- const genDynamicFallback = genLegacy
43
-
44
- const debugFlags = (process.env.DEBUG || '').split(',')
45
- const isDebug =
46
- debugFlags.includes('vite:*') || debugFlags.includes('vite:legacy')
47
-
48
- const facadeToLegacyChunkMap = new Map()
49
- const facadeToLegacyPolyfillMap = new Map()
50
- const facadeToModernPolyfillMap = new Map()
51
- const modernPolyfills = new Set()
52
- // System JS relies on the Promise interface. It needs to be polyfilled for IE 11. (array.iterator is mandatory for supporting Promise.all)
53
- const DEFAULT_LEGACY_POLYFILL = [
54
- 'core-js/modules/es.promise',
55
- 'core-js/modules/es.array.iterator'
56
- ]
57
- const legacyPolyfills = new Set(DEFAULT_LEGACY_POLYFILL)
58
-
59
- if (Array.isArray(options.modernPolyfills)) {
60
- options.modernPolyfills.forEach((i) => {
61
- modernPolyfills.add(
62
- i.includes('/') ? `core-js/${i}` : `core-js/modules/${i}.js`
63
- )
64
- })
65
- }
66
- if (Array.isArray(options.polyfills)) {
67
- options.polyfills.forEach((i) => {
68
- if (i.startsWith(`regenerator`)) {
69
- legacyPolyfills.add(`regenerator-runtime/runtime.js`)
70
- } else {
71
- legacyPolyfills.add(
72
- i.includes('/') ? `core-js/${i}` : `core-js/modules/${i}.js`
73
- )
74
- }
75
- })
76
- }
77
- if (Array.isArray(options.additionalLegacyPolyfills)) {
78
- options.additionalLegacyPolyfills.forEach((i) => {
79
- legacyPolyfills.add(i)
80
- })
81
- }
82
-
83
- /**
84
- * @type {import('vite').Plugin}
85
- */
86
- const legacyConfigPlugin = {
87
- name: 'vite:legacy-config',
88
-
89
- apply: 'build',
90
- config(config) {
91
- if (!config.build) {
92
- config.build = {}
93
- }
94
-
95
- if (!config.build.cssTarget) {
96
- // Hint for esbuild that we are targeting legacy browsers when minifying CSS.
97
- // Full CSS compat table available at https://github.com/evanw/esbuild/blob/78e04680228cf989bdd7d471e02bbc2c8d345dc9/internal/compat/css_table.go
98
- // But note that only the `HexRGBA` feature affects the minify outcome.
99
- // HSL & rebeccapurple values will be minified away regardless the target.
100
- // So targeting `chrome61` suffices to fix the compatiblity issue.
101
- config.build.cssTarget = 'chrome61'
102
- }
103
- }
104
- }
105
-
106
- /**
107
- * @type {import('vite').Plugin}
108
- */
109
- const legacyGenerateBundlePlugin = {
110
- name: 'vite:legacy-generate-polyfill-chunk',
111
- apply: 'build',
112
-
113
- async generateBundle(opts, bundle) {
114
- if (config.build.ssr) {
115
- return
116
- }
117
-
118
- if (!isLegacyBundle(bundle, opts)) {
119
- if (!modernPolyfills.size) {
120
- return
121
- }
122
- isDebug &&
123
- console.log(
124
- `[@vitejs/plugin-legacy] modern polyfills:`,
125
- modernPolyfills
126
- )
127
- await buildPolyfillChunk(
128
- 'polyfills-modern',
129
- modernPolyfills,
130
- bundle,
131
- facadeToModernPolyfillMap,
132
- config.build,
133
- options.externalSystemJS
134
- )
135
- return
136
- }
137
-
138
- if (!genLegacy) {
139
- return
140
- }
141
-
142
- // legacy bundle
143
- if (legacyPolyfills.size || genDynamicFallback) {
144
- if (!legacyPolyfills.has('es.promise')) {
145
- // check if the target needs Promise polyfill because SystemJS relies
146
- // on it
147
- detectPolyfills(`Promise.resolve()`, targets, legacyPolyfills)
148
- }
149
-
150
- isDebug &&
151
- console.log(
152
- `[@vitejs/plugin-legacy] legacy polyfills:`,
153
- legacyPolyfills
154
- )
155
-
156
- await buildPolyfillChunk(
157
- 'polyfills-legacy',
158
- legacyPolyfills,
159
- bundle,
160
- facadeToLegacyPolyfillMap,
161
- // force using terser for legacy polyfill minification, since esbuild
162
- // isn't legacy-safe
163
- config.build,
164
- options.externalSystemJS
165
- )
166
- }
167
- }
168
- }
169
-
170
- /**
171
- * @type {import('vite').Plugin}
172
- */
173
- const legacyPostPlugin = {
174
- name: 'vite:legacy-post-process',
175
- enforce: 'post',
176
- apply: 'build',
177
-
178
- configResolved(_config) {
179
- if (_config.build.lib) {
180
- throw new Error('@vitejs/plugin-legacy does not support library mode.')
181
- }
182
- config = _config
183
-
184
- if (!genLegacy || config.build.ssr) {
185
- return
186
- }
187
-
188
- /**
189
- * @param {string | ((chunkInfo: import('rollup').PreRenderedChunk) => string)} fileNames
190
- * @param {string?} defaultFileName
191
- * @returns {string | ((chunkInfo: import('rollup').PreRenderedChunk) => string)}
192
- */
193
- const getLegacyOutputFileName = (
194
- fileNames,
195
- defaultFileName = '[name]-legacy.[hash].js'
196
- ) => {
197
- if (!fileNames) {
198
- return path.posix.join(config.build.assetsDir, defaultFileName)
199
- }
200
-
201
- return (chunkInfo) => {
202
- let fileName =
203
- typeof fileNames === 'function' ? fileNames(chunkInfo) : fileNames
204
-
205
- if (fileName.includes('[name]')) {
206
- // [name]-[hash].[format] -> [name]-legacy-[hash].[format]
207
- fileName = fileName.replace('[name]', '[name]-legacy')
208
- } else {
209
- // entry.js -> entry-legacy.js
210
- fileName = fileName.replace(/(.+)\.(.+)/, '$1-legacy.$2')
211
- }
212
-
213
- return fileName
214
- }
215
- }
216
-
217
- /**
218
- * @param {import('rollup').OutputOptions} options
219
- * @returns {import('rollup').OutputOptions}
220
- */
221
- const createLegacyOutput = (options = {}) => {
222
- return {
223
- ...options,
224
- format: 'system',
225
- entryFileNames: getLegacyOutputFileName(options.entryFileNames),
226
- chunkFileNames: getLegacyOutputFileName(options.chunkFileNames)
227
- }
228
- }
229
-
230
- const { rollupOptions } = config.build
231
- const { output } = rollupOptions
232
- if (Array.isArray(output)) {
233
- rollupOptions.output = [...output.map(createLegacyOutput), ...output]
234
- } else {
235
- rollupOptions.output = [createLegacyOutput(output), output || {}]
236
- }
237
- },
238
-
239
- renderChunk(raw, chunk, opts) {
240
- if (config.build.ssr) {
241
- return
242
- }
243
-
244
- if (!isLegacyChunk(chunk, opts)) {
245
- if (
246
- options.modernPolyfills &&
247
- !Array.isArray(options.modernPolyfills)
248
- ) {
249
- // analyze and record modern polyfills
250
- detectPolyfills(raw, { esmodules: true }, modernPolyfills)
251
- }
252
-
253
- const ms = new MagicString(raw)
254
-
255
- if (genDynamicFallback && chunk.isEntry) {
256
- ms.prepend(forceDynamicImportUsage)
257
- }
258
-
259
- if (raw.includes(legacyEnvVarMarker)) {
260
- const re = new RegExp(legacyEnvVarMarker, 'g')
261
- let match
262
- while ((match = re.exec(raw))) {
263
- ms.overwrite(
264
- match.index,
265
- match.index + legacyEnvVarMarker.length,
266
- `false`
267
- )
268
- }
269
- }
270
-
271
- if (config.build.sourcemap) {
272
- return {
273
- code: ms.toString(),
274
- map: ms.generateMap({ hires: true })
275
- }
276
- }
277
- return ms.toString()
278
- }
279
-
280
- if (!genLegacy) {
281
- return
282
- }
283
-
284
- // @ts-ignore avoid esbuild transform on legacy chunks since it produces
285
- // legacy-unsafe code - e.g. rewriting object properties into shorthands
286
- opts.__vite_skip_esbuild__ = true
287
-
288
- // @ts-ignore force terser for legacy chunks. This only takes effect if
289
- // minification isn't disabled, because that leaves out the terser plugin
290
- // entirely.
291
- opts.__vite_force_terser__ = true
292
-
293
- // @ts-ignore
294
- // In the `generateBundle` hook,
295
- // we'll delete the assets from the legacy bundle to avoid emitting duplicate assets.
296
- // But that's still a waste of computing resource.
297
- // So we add this flag to avoid emitting the asset in the first place whenever possible.
298
- opts.__vite_skip_asset_emit__ = true
299
-
300
- // @ts-ignore avoid emitting assets for legacy bundle
301
- const needPolyfills =
302
- options.polyfills !== false && !Array.isArray(options.polyfills)
303
-
304
- // transform the legacy chunk with @babel/preset-env
305
- const sourceMaps = !!config.build.sourcemap
306
- const { code, map } = loadBabel().transform(raw, {
307
- babelrc: false,
308
- configFile: false,
309
- compact: true,
310
- sourceMaps,
311
- inputSourceMap: sourceMaps && chunk.map,
312
- presets: [
313
- // forcing our plugin to run before preset-env by wrapping it in a
314
- // preset so we can catch the injected import statements...
315
- [
316
- () => ({
317
- plugins: [
318
- recordAndRemovePolyfillBabelPlugin(legacyPolyfills),
319
- replaceLegacyEnvBabelPlugin(),
320
- wrapIIFEBabelPlugin()
321
- ]
322
- })
323
- ],
324
- [
325
- 'env',
326
- {
327
- targets,
328
- modules: false,
329
- bugfixes: true,
330
- loose: false,
331
- useBuiltIns: needPolyfills ? 'usage' : false,
332
- corejs: needPolyfills
333
- ? {
334
- version: require('core-js/package.json').version,
335
- proposals: false
336
- }
337
- : undefined,
338
- shippedProposals: true,
339
- ignoreBrowserslistConfig: options.ignoreBrowserslistConfig
340
- }
341
- ]
342
- ]
343
- })
344
-
345
- return { code, map }
346
- },
347
-
348
- transformIndexHtml(html, { chunk }) {
349
- if (config.build.ssr) return
350
- if (!chunk) return
351
- if (chunk.fileName.includes('-legacy')) {
352
- // The legacy bundle is built first, and its index.html isn't actually
353
- // emitted. Here we simply record its corresponding legacy chunk.
354
- facadeToLegacyChunkMap.set(chunk.facadeModuleId, chunk.fileName)
355
- return
356
- }
357
-
358
- /**
359
- * @type {import('vite').HtmlTagDescriptor[]}
360
- */
361
- const tags = []
362
- const htmlFilename = chunk.facadeModuleId.replace(/\?.*$/, '')
363
-
364
- // 1. inject modern polyfills
365
- const modernPolyfillFilename = facadeToModernPolyfillMap.get(
366
- chunk.facadeModuleId
367
- )
368
- if (modernPolyfillFilename) {
369
- tags.push({
370
- tag: 'script',
371
- attrs: {
372
- type: 'module',
373
- src: `${config.base}${modernPolyfillFilename}`
374
- }
375
- })
376
- } else if (modernPolyfills.size) {
377
- throw new Error(
378
- `No corresponding modern polyfill chunk found for ${htmlFilename}`
379
- )
380
- }
381
-
382
- if (!genLegacy) {
383
- return { html, tags }
384
- }
385
-
386
- // 2. inject Safari 10 nomodule fix
387
- tags.push({
388
- tag: 'script',
389
- attrs: { nomodule: true },
390
- children: safari10NoModuleFix,
391
- injectTo: 'body'
392
- })
393
-
394
- // 3. inject legacy polyfills
395
- const legacyPolyfillFilename = facadeToLegacyPolyfillMap.get(
396
- chunk.facadeModuleId
397
- )
398
- if (legacyPolyfillFilename) {
399
- tags.push({
400
- tag: 'script',
401
- attrs: {
402
- nomodule: true,
403
- id: legacyPolyfillId,
404
- src: `${config.base}${legacyPolyfillFilename}`
405
- },
406
- injectTo: 'body'
407
- })
408
- } else if (legacyPolyfills.size) {
409
- throw new Error(
410
- `No corresponding legacy polyfill chunk found for ${htmlFilename}`
411
- )
412
- }
413
-
414
- // 4. inject legacy entry
415
- const legacyEntryFilename = facadeToLegacyChunkMap.get(
416
- chunk.facadeModuleId
417
- )
418
- if (legacyEntryFilename) {
419
- tags.push({
420
- tag: 'script',
421
- attrs: {
422
- nomodule: true,
423
- // we set the entry path on the element as an attribute so that the
424
- // script content will stay consistent - which allows using a constant
425
- // hash value for CSP.
426
- id: legacyEntryId,
427
- 'data-src': config.base + legacyEntryFilename
428
- },
429
- children: systemJSInlineCode,
430
- injectTo: 'body'
431
- })
432
- } else {
433
- throw new Error(
434
- `No corresponding legacy entry chunk found for ${htmlFilename}`
435
- )
436
- }
437
-
438
- // 5. inject dynamic import fallback entry
439
- if (genDynamicFallback && legacyPolyfillFilename && legacyEntryFilename) {
440
- tags.push({
441
- tag: 'script',
442
- attrs: { type: 'module' },
443
- children: detectDynamicImportVarInitCode,
444
- injectTo: 'head'
445
- })
446
- tags.push({
447
- tag: 'script',
448
- attrs: { type: 'module' },
449
- children: detectDynamicImportCode,
450
- injectTo: 'head'
451
- })
452
- tags.push({
453
- tag: 'script',
454
- attrs: { type: 'module' },
455
- children: dynamicFallbackInlineCode,
456
- injectTo: 'head'
457
- })
458
- }
459
-
460
- return {
461
- html,
462
- tags
463
- }
464
- },
465
-
466
- generateBundle(opts, bundle) {
467
- if (config.build.ssr) {
468
- return
469
- }
470
-
471
- if (isLegacyBundle(bundle, opts)) {
472
- // avoid emitting duplicate assets
473
- for (const name in bundle) {
474
- if (bundle[name].type === 'asset') {
475
- delete bundle[name]
476
- }
477
- }
478
- }
479
- }
480
- }
481
-
482
- let envInjectionFailed = false
483
- /**
484
- * @type {import('vite').Plugin}
485
- */
486
- const legacyEnvPlugin = {
487
- name: 'vite:legacy-env',
488
-
489
- config(config, env) {
490
- if (env) {
491
- return {
492
- define: {
493
- 'import.meta.env.LEGACY':
494
- env.command === 'serve' || config.build.ssr
495
- ? false
496
- : legacyEnvVarMarker
497
- }
498
- }
499
- } else {
500
- envInjectionFailed = true
501
- }
502
- },
503
-
504
- configResolved(config) {
505
- if (envInjectionFailed) {
506
- config.logger.warn(
507
- `[@vitejs/plugin-legacy] import.meta.env.LEGACY was not injected due ` +
508
- `to incompatible vite version (requires vite@^2.0.0-beta.69).`
509
- )
510
- }
511
- }
512
- }
513
-
514
- return [
515
- legacyConfigPlugin,
516
- legacyGenerateBundlePlugin,
517
- legacyPostPlugin,
518
- legacyEnvPlugin
519
- ]
520
- }
521
-
522
- /**
523
- * @param {string} code
524
- * @param {any} targets
525
- * @param {Set<string>} list
526
- */
527
- function detectPolyfills(code, targets, list) {
528
- const { ast } = loadBabel().transform(code, {
529
- ast: true,
530
- babelrc: false,
531
- configFile: false,
532
- presets: [
533
- [
534
- 'env',
535
- {
536
- targets,
537
- modules: false,
538
- useBuiltIns: 'usage',
539
- corejs: { version: 3, proposals: false },
540
- shippedProposals: true,
541
- ignoreBrowserslistConfig: true
542
- }
543
- ]
544
- ]
545
- })
546
- for (const node of ast.program.body) {
547
- if (node.type === 'ImportDeclaration') {
548
- const source = node.source.value
549
- if (
550
- source.startsWith('core-js/') ||
551
- source.startsWith('regenerator-runtime/')
552
- ) {
553
- list.add(source)
554
- }
555
- }
556
- }
557
- }
558
-
559
- /**
560
- * @param {string} name
561
- * @param {Set<string>} imports
562
- * @param {import('rollup').OutputBundle} bundle
563
- * @param {Map<string, string>} facadeToChunkMap
564
- * @param {import('vite').BuildOptions} buildOptions
565
- */
566
- async function buildPolyfillChunk(
567
- name,
568
- imports,
569
- bundle,
570
- facadeToChunkMap,
571
- buildOptions,
572
- externalSystemJS
573
- ) {
574
- let { minify, assetsDir } = buildOptions
575
- minify = minify ? 'terser' : false
576
- const res = await build({
577
- // so that everything is resolved from here
578
- root: __dirname,
579
- configFile: false,
580
- logLevel: 'error',
581
- plugins: [polyfillsPlugin(imports, externalSystemJS)],
582
- build: {
583
- write: false,
584
- target: false,
585
- minify,
586
- assetsDir,
587
- rollupOptions: {
588
- input: {
589
- [name]: polyfillId
590
- },
591
- output: {
592
- format: name.includes('legacy') ? 'iife' : 'es',
593
- manualChunks: undefined
594
- }
595
- }
596
- }
597
- })
598
- const _polyfillChunk = Array.isArray(res) ? res[0] : res
599
- if (!('output' in _polyfillChunk)) return
600
- const polyfillChunk = _polyfillChunk.output[0]
601
-
602
- // associate the polyfill chunk to every entry chunk so that we can retrieve
603
- // the polyfill filename in index html transform
604
- for (const key in bundle) {
605
- const chunk = bundle[key]
606
- if (chunk.type === 'chunk' && chunk.facadeModuleId) {
607
- facadeToChunkMap.set(chunk.facadeModuleId, polyfillChunk.fileName)
608
- }
609
- }
610
-
611
- // add the chunk to the bundle
612
- bundle[polyfillChunk.name] = polyfillChunk
613
- }
614
-
615
- const polyfillId = '\0vite/legacy-polyfills'
616
-
617
- /**
618
- * @param {Set<string>} imports
619
- * @return {import('rollup').Plugin}
620
- */
621
- function polyfillsPlugin(imports, externalSystemJS) {
622
- return {
623
- name: 'vite:legacy-polyfills',
624
- resolveId(id) {
625
- if (id === polyfillId) {
626
- return id
627
- }
628
- },
629
- load(id) {
630
- if (id === polyfillId) {
631
- return (
632
- [...imports].map((i) => `import "${i}";`).join('') +
633
- (externalSystemJS ? '' : `import "systemjs/dist/s.min.js";`)
634
- )
635
- }
636
- }
637
- }
638
- }
639
-
640
- /**
641
- * @param {import('rollup').RenderedChunk} chunk
642
- * @param {import('rollup').NormalizedOutputOptions} options
643
- */
644
- function isLegacyChunk(chunk, options) {
645
- return options.format === 'system' && chunk.fileName.includes('-legacy')
646
- }
647
-
648
- /**
649
- * @param {import('rollup').OutputBundle} bundle
650
- * @param {import('rollup').NormalizedOutputOptions} options
651
- */
652
- function isLegacyBundle(bundle, options) {
653
- if (options.format === 'system') {
654
- const entryChunk = Object.values(bundle).find(
655
- (output) => output.type === 'chunk' && output.isEntry
656
- )
657
-
658
- return !!entryChunk && entryChunk.fileName.includes('-legacy')
659
- }
660
-
661
- return false
662
- }
663
-
664
- /**
665
- * @param {Set<string>} polyfills
666
- */
667
- function recordAndRemovePolyfillBabelPlugin(polyfills) {
668
- return ({ types: t }) => ({
669
- name: 'vite-remove-polyfill-import',
670
- post({ path }) {
671
- path.get('body').forEach((p) => {
672
- if (t.isImportDeclaration(p)) {
673
- polyfills.add(p.node.source.value)
674
- p.remove()
675
- }
676
- })
677
- }
678
- })
679
- }
680
-
681
- function replaceLegacyEnvBabelPlugin() {
682
- return ({ types: t }) => ({
683
- name: 'vite-replace-env-legacy',
684
- visitor: {
685
- Identifier(path) {
686
- if (path.node.name === legacyEnvVarMarker) {
687
- path.replaceWith(t.booleanLiteral(true))
688
- }
689
- }
690
- }
691
- })
692
- }
693
-
694
- function wrapIIFEBabelPlugin() {
695
- return ({ types: t, template }) => {
696
- const buildIIFE = template(';(function(){%%body%%})();')
697
-
698
- return {
699
- name: 'vite-wrap-iife',
700
- post({ path }) {
701
- if (!this.isWrapped) {
702
- this.isWrapped = true
703
- path.replaceWith(t.program(buildIIFE({ body: path.node.body })))
704
- }
705
- }
706
- }
707
- }
708
- }
709
-
710
- module.exports = viteLegacyPlugin
711
-
712
- viteLegacyPlugin.default = viteLegacyPlugin
713
-
714
- viteLegacyPlugin.cspHashes = [
715
- createHash('sha256').update(safari10NoModuleFix).digest('base64'),
716
- createHash('sha256').update(systemJSInlineCode).digest('base64'),
717
- createHash('sha256').update(detectDynamicImportVarInitCode).digest('base64'),
718
- createHash('sha256').update(detectDynamicImportCode).digest('base64'),
719
- createHash('sha256').update(dynamicFallbackInlineCode).digest('base64')
720
- ]