vite 2.6.0-beta.0 → 2.6.0-beta.4

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.

Potentially problematic release.


This version of vite might be problematic. Click here for more details.

Files changed (110) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE.md +161 -3011
  3. package/bin/vite.js +2 -1
  4. package/dist/node/chunks/{dep-91aa74e8.js → dep-1e0a75a8.js} +58 -32
  5. package/dist/node/chunks/dep-1e0a75a8.js.map +1 -0
  6. package/dist/node/chunks/{dep-e36486f6.js → dep-ac1b4bf9.js} +1 -1
  7. package/dist/node/chunks/dep-ac1b4bf9.js.map +1 -0
  8. package/dist/node/chunks/{dep-c7e510f9.js → dep-ba6b30a0.js} +27538 -4953
  9. package/dist/node/chunks/dep-ba6b30a0.js.map +1 -0
  10. package/dist/node/chunks/{dep-11213a75.js → dep-c4cf6e92.js} +19 -6
  11. package/dist/node/chunks/dep-c4cf6e92.js.map +1 -0
  12. package/dist/node/chunks/{dep-eb6ef720.js → dep-d574094c.js} +18 -5
  13. package/dist/node/chunks/dep-d574094c.js.map +1 -0
  14. package/dist/node/chunks/{dep-0d2f9464.js → dep-e39b05d6.js} +18 -5
  15. package/dist/node/chunks/dep-e39b05d6.js.map +1 -0
  16. package/dist/node/cli.js +60 -23992
  17. package/dist/node/cli.js.map +1 -1
  18. package/dist/node/index.d.ts +26 -8
  19. package/dist/node/index.js +18 -15
  20. package/dist/node/index.js.map +1 -1
  21. package/dist/node/terser.js +102 -55
  22. package/package.json +28 -13
  23. package/types/package.json +3 -0
  24. package/api-extractor.json +0 -54
  25. package/dist/node/chunks/dep-0d2f9464.js.map +0 -1
  26. package/dist/node/chunks/dep-11213a75.js.map +0 -1
  27. package/dist/node/chunks/dep-91aa74e8.js.map +0 -1
  28. package/dist/node/chunks/dep-c7e510f9.js.map +0 -1
  29. package/dist/node/chunks/dep-e36486f6.js.map +0 -1
  30. package/dist/node/chunks/dep-eb6ef720.js.map +0 -1
  31. package/dist/node/terser.js.map +0 -1
  32. package/rollup.config.js +0 -389
  33. package/scripts/patchTypes.js +0 -70
  34. package/src/node/__tests__/asset.spec.ts +0 -156
  35. package/src/node/__tests__/build.spec.ts +0 -67
  36. package/src/node/__tests__/config.spec.ts +0 -166
  37. package/src/node/__tests__/packages/name/package.json +0 -3
  38. package/src/node/__tests__/packages/noname/package.json +0 -1
  39. package/src/node/__tests__/plugins/css.spec.ts +0 -116
  40. package/src/node/__tests__/scan.spec.ts +0 -118
  41. package/src/node/__tests__/utils.spec.ts +0 -40
  42. package/src/node/build.ts +0 -783
  43. package/src/node/cli.ts +0 -239
  44. package/src/node/config.ts +0 -1033
  45. package/src/node/constants.ts +0 -87
  46. package/src/node/importGlob.ts +0 -173
  47. package/src/node/index.ts +0 -88
  48. package/src/node/logger.ts +0 -167
  49. package/src/node/optimizer/esbuildDepPlugin.ts +0 -216
  50. package/src/node/optimizer/index.ts +0 -410
  51. package/src/node/optimizer/registerMissing.ts +0 -102
  52. package/src/node/optimizer/scan.ts +0 -457
  53. package/src/node/plugin.ts +0 -138
  54. package/src/node/plugins/asset.ts +0 -365
  55. package/src/node/plugins/assetImportMetaUrl.ts +0 -99
  56. package/src/node/plugins/clientInjections.ts +0 -72
  57. package/src/node/plugins/css.ts +0 -1279
  58. package/src/node/plugins/dataUri.ts +0 -64
  59. package/src/node/plugins/define.ts +0 -107
  60. package/src/node/plugins/esbuild.ts +0 -280
  61. package/src/node/plugins/html.ts +0 -673
  62. package/src/node/plugins/importAnalysis.ts +0 -614
  63. package/src/node/plugins/importAnalysisBuild.ts +0 -334
  64. package/src/node/plugins/index.ts +0 -69
  65. package/src/node/plugins/json.ts +0 -75
  66. package/src/node/plugins/loadFallback.ts +0 -19
  67. package/src/node/plugins/manifest.ts +0 -123
  68. package/src/node/plugins/modulePreloadPolyfill.ts +0 -100
  69. package/src/node/plugins/preAlias.ts +0 -22
  70. package/src/node/plugins/reporter.ts +0 -244
  71. package/src/node/plugins/resolve.ts +0 -925
  72. package/src/node/plugins/terser.ts +0 -40
  73. package/src/node/plugins/wasm.ts +0 -72
  74. package/src/node/plugins/worker.ts +0 -117
  75. package/src/node/preview.ts +0 -82
  76. package/src/node/server/__tests__/fixtures/none/nested/package.json +0 -3
  77. package/src/node/server/__tests__/fixtures/pnpm/nested/package.json +0 -3
  78. package/src/node/server/__tests__/fixtures/pnpm/package.json +0 -3
  79. package/src/node/server/__tests__/fixtures/pnpm/pnpm-workspace.yaml +0 -0
  80. package/src/node/server/__tests__/fixtures/yarn/nested/package.json +0 -3
  81. package/src/node/server/__tests__/fixtures/yarn/package.json +0 -6
  82. package/src/node/server/__tests__/search-root.spec.ts +0 -31
  83. package/src/node/server/hmr.ts +0 -489
  84. package/src/node/server/http.ts +0 -198
  85. package/src/node/server/index.ts +0 -705
  86. package/src/node/server/middlewares/base.ts +0 -52
  87. package/src/node/server/middlewares/error.ts +0 -98
  88. package/src/node/server/middlewares/indexHtml.ts +0 -170
  89. package/src/node/server/middlewares/proxy.ts +0 -124
  90. package/src/node/server/middlewares/spaFallback.ts +0 -32
  91. package/src/node/server/middlewares/static.ts +0 -153
  92. package/src/node/server/middlewares/time.ts +0 -18
  93. package/src/node/server/middlewares/transform.ts +0 -196
  94. package/src/node/server/moduleGraph.ts +0 -200
  95. package/src/node/server/openBrowser.ts +0 -101
  96. package/src/node/server/pluginContainer.ts +0 -546
  97. package/src/node/server/searchRoot.ts +0 -70
  98. package/src/node/server/send.ts +0 -54
  99. package/src/node/server/sourcemap.ts +0 -54
  100. package/src/node/server/transformRequest.ts +0 -168
  101. package/src/node/server/ws.ts +0 -131
  102. package/src/node/ssr/__tests__/ssrTransform.spec.ts +0 -309
  103. package/src/node/ssr/ssrExternal.ts +0 -161
  104. package/src/node/ssr/ssrManifestPlugin.ts +0 -53
  105. package/src/node/ssr/ssrModuleLoader.ts +0 -214
  106. package/src/node/ssr/ssrStacktrace.ts +0 -75
  107. package/src/node/ssr/ssrTransform.ts +0 -452
  108. package/src/node/tsconfig.json +0 -14
  109. package/src/node/utils.ts +0 -565
  110. package/tsconfig.base.json +0 -11
@@ -1,673 +0,0 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import { Plugin } from '../plugin'
4
- import { ViteDevServer } from '../server'
5
- import { OutputAsset, OutputBundle, OutputChunk } from 'rollup'
6
- import {
7
- slash,
8
- cleanUrl,
9
- isExternalUrl,
10
- isDataUrl,
11
- generateCodeFrame,
12
- processSrcSet
13
- } from '../utils'
14
- import { ResolvedConfig } from '../config'
15
- import MagicString from 'magic-string'
16
- import {
17
- checkPublicFile,
18
- assetUrlRE,
19
- urlToBuiltUrl,
20
- getAssetFilename
21
- } from './asset'
22
- import { isCSSRequest, chunkToEmittedCssFileMap } from './css'
23
- import { modulePreloadPolyfillId } from './modulePreloadPolyfill'
24
- import {
25
- AttributeNode,
26
- NodeTransform,
27
- NodeTypes,
28
- ElementNode
29
- } from '@vue/compiler-dom'
30
-
31
- const htmlProxyRE = /\?html-proxy&index=(\d+)\.js$/
32
- export const isHTMLProxy = (id: string): boolean => htmlProxyRE.test(id)
33
-
34
- const htmlCommentRE = /<!--[\s\S]*?-->/g
35
- const scriptModuleRE =
36
- /(<script\b[^>]*type\s*=\s*(?:"module"|'module')[^>]*>)(.*?)<\/script>/gims
37
-
38
- export function htmlInlineScriptProxyPlugin(): Plugin {
39
- return {
40
- name: 'vite:html',
41
-
42
- resolveId(id) {
43
- if (htmlProxyRE.test(id)) {
44
- return id
45
- }
46
- },
47
-
48
- load(id) {
49
- const proxyMatch = id.match(htmlProxyRE)
50
- if (proxyMatch) {
51
- const index = Number(proxyMatch[1])
52
- const file = cleanUrl(id)
53
- const html = fs.readFileSync(file, 'utf-8').replace(htmlCommentRE, '')
54
- let match: RegExpExecArray | null | undefined
55
- scriptModuleRE.lastIndex = 0
56
- for (let i = 0; i <= index; i++) {
57
- match = scriptModuleRE.exec(html)
58
- }
59
- if (match) {
60
- return match[2]
61
- } else {
62
- throw new Error(`No matching html proxy module found from ${id}`)
63
- }
64
- }
65
- }
66
- }
67
- }
68
-
69
- // this extends the config in @vue/compiler-sfc with <link href>
70
- export const assetAttrsConfig: Record<string, string[]> = {
71
- link: ['href'],
72
- video: ['src', 'poster'],
73
- source: ['src', 'srcset'],
74
- img: ['src', 'srcset'],
75
- image: ['xlink:href', 'href'],
76
- use: ['xlink:href', 'href']
77
- }
78
-
79
- export const isAsyncScriptMap = new WeakMap<
80
- ResolvedConfig,
81
- Map<string, boolean>
82
- >()
83
-
84
- export async function traverseHtml(
85
- html: string,
86
- filePath: string,
87
- visitor: NodeTransform
88
- ): Promise<void> {
89
- // lazy load compiler
90
- const { parse, transform } = await import('@vue/compiler-dom')
91
- // @vue/compiler-core doesn't like lowercase doctypes
92
- html = html.replace(/<!doctype\s/i, '<!DOCTYPE ')
93
- try {
94
- const ast = parse(html, { comments: true })
95
- transform(ast, {
96
- nodeTransforms: [visitor]
97
- })
98
- } catch (e) {
99
- const parseError = {
100
- loc: filePath,
101
- frame: '',
102
- ...formatParseError(e, filePath, html)
103
- }
104
- throw new Error(
105
- `Unable to parse ${JSON.stringify(parseError.loc)}\n${parseError.frame}`
106
- )
107
- }
108
- }
109
-
110
- export function getScriptInfo(node: ElementNode): {
111
- src: AttributeNode | undefined
112
- isModule: boolean
113
- isAsync: boolean
114
- } {
115
- let src: AttributeNode | undefined
116
- let isModule = false
117
- let isAsync = false
118
- for (let i = 0; i < node.props.length; i++) {
119
- const p = node.props[i]
120
- if (p.type === NodeTypes.ATTRIBUTE) {
121
- if (p.name === 'src') {
122
- src = p
123
- } else if (p.name === 'type' && p.value && p.value.content === 'module') {
124
- isModule = true
125
- } else if (p.name === 'async') {
126
- isAsync = true
127
- }
128
- }
129
- }
130
- return { src, isModule, isAsync }
131
- }
132
-
133
- function formatParseError(e: any, id: string, html: string): Error {
134
- // normalize the error to rollup format
135
- if (e.loc) {
136
- e.frame = generateCodeFrame(html, e.loc.start.offset)
137
- e.loc = {
138
- file: id,
139
- line: e.loc.start.line,
140
- column: e.loc.start.column
141
- }
142
- }
143
- return e
144
- }
145
-
146
- /**
147
- * Compiles index.html into an entry js module
148
- */
149
- export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
150
- const [preHooks, postHooks] = resolveHtmlTransforms(config.plugins)
151
- const processedHtml = new Map<string, string>()
152
- const isExcludedUrl = (url: string) =>
153
- url.startsWith('#') ||
154
- isExternalUrl(url) ||
155
- isDataUrl(url) ||
156
- checkPublicFile(url, config)
157
-
158
- return {
159
- name: 'vite:build-html',
160
-
161
- buildStart() {
162
- isAsyncScriptMap.set(config, new Map())
163
- },
164
-
165
- async transform(html, id) {
166
- if (id.endsWith('.html')) {
167
- const publicPath = `/${slash(path.relative(config.root, id))}`
168
- // pre-transform
169
- html = await applyHtmlTransforms(html, preHooks, {
170
- path: publicPath,
171
- filename: id
172
- })
173
-
174
- let js = ''
175
- const s = new MagicString(html)
176
- const assetUrls: AttributeNode[] = []
177
- let inlineModuleIndex = -1
178
-
179
- let everyScriptIsAsync = true
180
- let someScriptsAreAsync = false
181
- let someScriptsAreDefer = false
182
-
183
- await traverseHtml(html, id, (node) => {
184
- if (node.type !== NodeTypes.ELEMENT) {
185
- return
186
- }
187
-
188
- let shouldRemove = false
189
-
190
- // script tags
191
- if (node.tag === 'script') {
192
- const { src, isModule, isAsync } = getScriptInfo(node)
193
-
194
- const url = src && src.value && src.value.content
195
- if (url && checkPublicFile(url, config)) {
196
- // referencing public dir url, prefix with base
197
- s.overwrite(
198
- src!.value!.loc.start.offset,
199
- src!.value!.loc.end.offset,
200
- `"${config.base + url.slice(1)}"`
201
- )
202
- }
203
-
204
- if (isModule) {
205
- inlineModuleIndex++
206
- if (url && !isExcludedUrl(url)) {
207
- // <script type="module" src="..."/>
208
- // add it as an import
209
- js += `\nimport ${JSON.stringify(url)}`
210
- shouldRemove = true
211
- } else if (node.children.length) {
212
- // <script type="module">...</script>
213
- js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"`
214
- shouldRemove = true
215
- }
216
-
217
- everyScriptIsAsync &&= isAsync
218
- someScriptsAreAsync ||= isAsync
219
- someScriptsAreDefer ||= !isAsync
220
- }
221
- }
222
-
223
- // For asset references in index.html, also generate an import
224
- // statement for each - this will be handled by the asset plugin
225
- const assetAttrs = assetAttrsConfig[node.tag]
226
- if (assetAttrs) {
227
- for (const p of node.props) {
228
- if (
229
- p.type === NodeTypes.ATTRIBUTE &&
230
- p.value &&
231
- assetAttrs.includes(p.name)
232
- ) {
233
- const url = p.value.content
234
- if (!isExcludedUrl(url)) {
235
- if (node.tag === 'link' && isCSSRequest(url)) {
236
- // CSS references, convert to import
237
- js += `\nimport ${JSON.stringify(url)}`
238
- shouldRemove = true
239
- } else {
240
- assetUrls.push(p)
241
- }
242
- } else if (checkPublicFile(url, config)) {
243
- s.overwrite(
244
- p.value.loc.start.offset,
245
- p.value.loc.end.offset,
246
- `"${config.base + url.slice(1)}"`
247
- )
248
- }
249
- }
250
- }
251
- }
252
-
253
- if (shouldRemove) {
254
- // remove the script tag from the html. we are going to inject new
255
- // ones in the end.
256
- s.remove(node.loc.start.offset, node.loc.end.offset)
257
- }
258
- })
259
-
260
- isAsyncScriptMap.get(config)!.set(id, everyScriptIsAsync)
261
-
262
- if (someScriptsAreAsync && someScriptsAreDefer) {
263
- config.logger.warn(
264
- `\nMixed async and defer script modules in ${id}, output script will fallback to defer. Every script, including inline ones, need to be marked as async for your output script to be async.`
265
- )
266
- }
267
-
268
- // for each encountered asset url, rewrite original html so that it
269
- // references the post-build location.
270
- for (const attr of assetUrls) {
271
- const value = attr.value!
272
- try {
273
- const url =
274
- attr.name === 'srcset'
275
- ? await processSrcSet(value.content, ({ url }) =>
276
- urlToBuiltUrl(url, id, config, this)
277
- )
278
- : await urlToBuiltUrl(value.content, id, config, this)
279
-
280
- s.overwrite(
281
- value.loc.start.offset,
282
- value.loc.end.offset,
283
- `"${url}"`
284
- )
285
- } catch (e) {
286
- // #1885 preload may be pointing to urls that do not exist
287
- // locally on disk
288
- if (e.code !== 'ENOENT') {
289
- throw e
290
- }
291
- }
292
- }
293
-
294
- processedHtml.set(id, s.toString())
295
-
296
- // inject module preload polyfill
297
- if (config.build.polyfillModulePreload) {
298
- js = `import "${modulePreloadPolyfillId}";\n${js}`
299
- }
300
-
301
- return js
302
- }
303
- },
304
-
305
- async generateBundle(options, bundle) {
306
- const analyzedChunk: Map<OutputChunk, number> = new Map()
307
- const getImportedChunks = (
308
- chunk: OutputChunk,
309
- seen: Set<string> = new Set()
310
- ): OutputChunk[] => {
311
- const chunks: OutputChunk[] = []
312
- chunk.imports.forEach((file) => {
313
- const importee = bundle[file]
314
- if (importee?.type === 'chunk' && !seen.has(file)) {
315
- seen.add(file)
316
-
317
- // post-order traversal
318
- chunks.push(...getImportedChunks(importee, seen))
319
- chunks.push(importee)
320
- }
321
- })
322
- return chunks
323
- }
324
-
325
- const toScriptTag = (
326
- chunk: OutputChunk,
327
- isAsync: boolean
328
- ): HtmlTagDescriptor => ({
329
- tag: 'script',
330
- attrs: {
331
- ...(isAsync ? { async: true } : {}),
332
- type: 'module',
333
- crossorigin: true,
334
- src: toPublicPath(chunk.fileName, config)
335
- }
336
- })
337
-
338
- const toPreloadTag = (chunk: OutputChunk): HtmlTagDescriptor => ({
339
- tag: 'link',
340
- attrs: {
341
- rel: 'modulepreload',
342
- href: toPublicPath(chunk.fileName, config)
343
- }
344
- })
345
-
346
- const getCssTagsForChunk = (
347
- chunk: OutputChunk,
348
- seen: Set<string> = new Set()
349
- ): HtmlTagDescriptor[] => {
350
- const tags: HtmlTagDescriptor[] = []
351
- if (!analyzedChunk.has(chunk)) {
352
- analyzedChunk.set(chunk, 1)
353
- chunk.imports.forEach((file) => {
354
- const importee = bundle[file]
355
- if (importee?.type === 'chunk') {
356
- tags.push(...getCssTagsForChunk(importee, seen))
357
- }
358
- })
359
- }
360
-
361
- const cssFiles = chunkToEmittedCssFileMap.get(chunk)
362
- if (cssFiles) {
363
- cssFiles.forEach((file) => {
364
- if (!seen.has(file)) {
365
- seen.add(file)
366
- tags.push({
367
- tag: 'link',
368
- attrs: {
369
- rel: 'stylesheet',
370
- href: toPublicPath(file, config)
371
- }
372
- })
373
- }
374
- })
375
- }
376
- return tags
377
- }
378
-
379
- for (const [id, html] of processedHtml) {
380
- const isAsync = isAsyncScriptMap.get(config)!.get(id)!
381
-
382
- // resolve asset url references
383
- let result = html.replace(assetUrlRE, (_, fileHash, postfix = '') => {
384
- return config.base + getAssetFilename(fileHash, config) + postfix
385
- })
386
-
387
- // find corresponding entry chunk
388
- const chunk = Object.values(bundle).find(
389
- (chunk) =>
390
- chunk.type === 'chunk' &&
391
- chunk.isEntry &&
392
- chunk.facadeModuleId === id
393
- ) as OutputChunk | undefined
394
- let canInlineEntry = false
395
-
396
- // inject chunk asset links
397
- if (chunk) {
398
- // an entry chunk can be inlined if
399
- // - it's an ES module (e.g. not generated by the legacy plugin)
400
- // - it contains no meaningful code other than import statments
401
- if (options.format === 'es' && isEntirelyImport(chunk.code)) {
402
- canInlineEntry = true
403
- }
404
-
405
- // when not inlined, inject <script> for entry and modulepreload its dependencies
406
- // when inlined, discard entry chunk and inject <script> for everything in post-order
407
- const imports = getImportedChunks(chunk)
408
- const assetTags = canInlineEntry
409
- ? imports.map((chunk) => toScriptTag(chunk, isAsync))
410
- : [toScriptTag(chunk, isAsync), ...imports.map(toPreloadTag)]
411
-
412
- assetTags.push(...getCssTagsForChunk(chunk))
413
-
414
- result = injectToHead(result, assetTags)
415
- }
416
-
417
- // inject css link when cssCodeSplit is false
418
- if (!config.build.cssCodeSplit) {
419
- const cssChunk = Object.values(bundle).find(
420
- (chunk) => chunk.type === 'asset' && chunk.name === 'style.css'
421
- ) as OutputAsset | undefined
422
- if (cssChunk) {
423
- result = injectToHead(result, [
424
- {
425
- tag: 'link',
426
- attrs: {
427
- rel: 'stylesheet',
428
- href: toPublicPath(cssChunk.fileName, config)
429
- }
430
- }
431
- ])
432
- }
433
- }
434
-
435
- const shortEmitName = path.posix.relative(config.root, id)
436
- result = await applyHtmlTransforms(result, postHooks, {
437
- path: '/' + shortEmitName,
438
- filename: id,
439
- bundle,
440
- chunk
441
- })
442
-
443
- if (chunk && canInlineEntry) {
444
- // all imports from entry have been inlined to html, prevent rollup from outputting it
445
- delete bundle[chunk.fileName]
446
- }
447
-
448
- this.emitFile({
449
- type: 'asset',
450
- fileName: shortEmitName,
451
- source: result
452
- })
453
- }
454
- }
455
- }
456
- }
457
-
458
- export interface HtmlTagDescriptor {
459
- tag: string
460
- attrs?: Record<string, string | boolean | undefined>
461
- children?: string | HtmlTagDescriptor[]
462
- /**
463
- * default: 'head-prepend'
464
- */
465
- injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend'
466
- }
467
-
468
- export type IndexHtmlTransformResult =
469
- | string
470
- | HtmlTagDescriptor[]
471
- | {
472
- html: string
473
- tags: HtmlTagDescriptor[]
474
- }
475
-
476
- export interface IndexHtmlTransformContext {
477
- /**
478
- * public path when served
479
- */
480
- path: string
481
- /**
482
- * filename on disk
483
- */
484
- filename: string
485
- server?: ViteDevServer
486
- bundle?: OutputBundle
487
- chunk?: OutputChunk
488
- originalUrl?: string
489
- }
490
-
491
- export type IndexHtmlTransformHook = (
492
- html: string,
493
- ctx: IndexHtmlTransformContext
494
- ) => IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void>
495
-
496
- export type IndexHtmlTransform =
497
- | IndexHtmlTransformHook
498
- | {
499
- enforce?: 'pre' | 'post'
500
- transform: IndexHtmlTransformHook
501
- }
502
-
503
- export function resolveHtmlTransforms(
504
- plugins: readonly Plugin[]
505
- ): [IndexHtmlTransformHook[], IndexHtmlTransformHook[]] {
506
- const preHooks: IndexHtmlTransformHook[] = []
507
- const postHooks: IndexHtmlTransformHook[] = []
508
-
509
- for (const plugin of plugins) {
510
- const hook = plugin.transformIndexHtml
511
- if (hook) {
512
- if (typeof hook === 'function') {
513
- postHooks.push(hook)
514
- } else if (hook.enforce === 'pre') {
515
- preHooks.push(hook.transform)
516
- } else {
517
- postHooks.push(hook.transform)
518
- }
519
- }
520
- }
521
-
522
- return [preHooks, postHooks]
523
- }
524
-
525
- export async function applyHtmlTransforms(
526
- html: string,
527
- hooks: IndexHtmlTransformHook[],
528
- ctx: IndexHtmlTransformContext
529
- ): Promise<string> {
530
- const headTags: HtmlTagDescriptor[] = []
531
- const headPrependTags: HtmlTagDescriptor[] = []
532
- const bodyTags: HtmlTagDescriptor[] = []
533
- const bodyPrependTags: HtmlTagDescriptor[] = []
534
-
535
- for (const hook of hooks) {
536
- const res = await hook(html, ctx)
537
- if (!res) {
538
- continue
539
- }
540
- if (typeof res === 'string') {
541
- html = res
542
- } else {
543
- let tags: HtmlTagDescriptor[]
544
- if (Array.isArray(res)) {
545
- tags = res
546
- } else {
547
- html = res.html || html
548
- tags = res.tags
549
- }
550
- for (const tag of tags) {
551
- if (tag.injectTo === 'body') {
552
- bodyTags.push(tag)
553
- } else if (tag.injectTo === 'body-prepend') {
554
- bodyPrependTags.push(tag)
555
- } else if (tag.injectTo === 'head') {
556
- headTags.push(tag)
557
- } else {
558
- headPrependTags.push(tag)
559
- }
560
- }
561
- }
562
- }
563
-
564
- // inject tags
565
- if (headPrependTags.length) {
566
- html = injectToHead(html, headPrependTags, true)
567
- }
568
- if (headTags.length) {
569
- html = injectToHead(html, headTags)
570
- }
571
- if (bodyPrependTags.length) {
572
- html = injectToBody(html, bodyPrependTags, true)
573
- }
574
- if (bodyTags.length) {
575
- html = injectToBody(html, bodyTags)
576
- }
577
-
578
- return html
579
- }
580
-
581
- const importRE = /\bimport\s*("[^"]*[^\\]"|'[^']*[^\\]');*/g
582
- const commentRE = /\/\*[\s\S]*?\*\/|\/\/.*$/gm
583
- function isEntirelyImport(code: string) {
584
- // only consider "side-effect" imports, which match <script type=module> semantics exactly
585
- // the regexes will remove too little in some exotic cases, but false-negatives are alright
586
- return !code.replace(importRE, '').replace(commentRE, '').trim().length
587
- }
588
-
589
- function toPublicPath(filename: string, config: ResolvedConfig) {
590
- return isExternalUrl(filename) ? filename : config.base + filename
591
- }
592
-
593
- const headInjectRE = /<\/head>/
594
- const headPrependInjectRE = [/<head>/, /<!doctype html>/i]
595
- function injectToHead(
596
- html: string,
597
- tags: HtmlTagDescriptor[],
598
- prepend = false
599
- ) {
600
- const tagsHtml = serializeTags(tags)
601
- if (prepend) {
602
- // inject after head or doctype
603
- for (const re of headPrependInjectRE) {
604
- if (re.test(html)) {
605
- return html.replace(re, `$&\n${tagsHtml}`)
606
- }
607
- }
608
- } else {
609
- // inject before head close
610
- if (headInjectRE.test(html)) {
611
- return html.replace(headInjectRE, `${tagsHtml}\n $&`)
612
- }
613
- }
614
- // if no <head> tag is present, just prepend
615
- return tagsHtml + `\n` + html
616
- }
617
-
618
- const bodyInjectRE = /<\/body>/
619
- const bodyPrependInjectRE = /<body[^>]*>/
620
- function injectToBody(
621
- html: string,
622
- tags: HtmlTagDescriptor[],
623
- prepend = false
624
- ) {
625
- if (prepend) {
626
- // inject after body open
627
- const tagsHtml = `\n` + serializeTags(tags)
628
- if (bodyPrependInjectRE.test(html)) {
629
- return html.replace(bodyPrependInjectRE, `$&\n${tagsHtml}`)
630
- }
631
- // if no body, prepend
632
- return tagsHtml + `\n` + html
633
- } else {
634
- // inject before body close
635
- const tagsHtml = `\n` + serializeTags(tags)
636
- if (bodyInjectRE.test(html)) {
637
- return html.replace(bodyInjectRE, `${tagsHtml}\n$&`)
638
- }
639
- // if no body, append
640
- return html + `\n` + tagsHtml
641
- }
642
- }
643
-
644
- const unaryTags = new Set(['link', 'meta', 'base'])
645
-
646
- function serializeTag({ tag, attrs, children }: HtmlTagDescriptor): string {
647
- if (unaryTags.has(tag)) {
648
- return `<${tag}${serializeAttrs(attrs)}>`
649
- } else {
650
- return `<${tag}${serializeAttrs(attrs)}>${serializeTags(children)}</${tag}>`
651
- }
652
- }
653
-
654
- function serializeTags(tags: HtmlTagDescriptor['children']): string {
655
- if (typeof tags === 'string') {
656
- return tags
657
- } else if (tags) {
658
- return ` ${tags.map(serializeTag).join('\n ')}`
659
- }
660
- return ''
661
- }
662
-
663
- function serializeAttrs(attrs: HtmlTagDescriptor['attrs']): string {
664
- let res = ''
665
- for (const key in attrs) {
666
- if (typeof attrs[key] === 'boolean') {
667
- res += attrs[key] ? ` ${key}` : ``
668
- } else {
669
- res += ` ${key}=${JSON.stringify(attrs[key])}`
670
- }
671
- }
672
- return res
673
- }