From b0716a3b2cec8f4f56f57babe825c4feb4969a55 Mon Sep 17 00:00:00 2001 From: andylizi Date: Mon, 9 Aug 2021 17:34:23 +0800 Subject: [PATCH 1/3] feat(html): inline entry chunk into html when possible --- packages/vite/src/node/plugins/html.ts | 82 +++++++++++++++++--------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 21f8bff0b406dc..8da6800cd03ba5 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -273,30 +273,43 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { } }, - async generateBundle(_, bundle) { + async generateBundle(options, bundle) { const analyzedChunk: Map = new Map() - const getPreloadLinksForChunk = ( + const getImportedChunks = ( chunk: OutputChunk, seen: Set = new Set() - ): HtmlTagDescriptor[] => { - const tags: HtmlTagDescriptor[] = [] + ): OutputChunk[] => { + const chunks: OutputChunk[] = [] chunk.imports.forEach((file) => { const importee = bundle[file] if (importee?.type === 'chunk' && !seen.has(file)) { seen.add(file) - tags.push({ - tag: 'link', - attrs: { - rel: 'modulepreload', - href: toPublicPath(file, config) - } - }) - tags.push(...getPreloadLinksForChunk(importee, seen)) + + // post-order traversal + chunks.push(...getImportedChunks(importee, seen)) + chunks.push(importee) } }) - return tags + return chunks } + const toScriptTag = (chunk: OutputChunk): HtmlTagDescriptor => ({ + tag: 'script', + attrs: { + type: 'module', + crossorigin: true, + src: toPublicPath(chunk.fileName, config) + } + }) + + const toPreloadTag = (chunk: OutputChunk): HtmlTagDescriptor => ({ + tag: 'link', + attrs: { + rel: 'modulepreload', + href: toPublicPath(chunk.fileName, config) + } + }) + const getCssTagsForChunk = ( chunk: OutputChunk, seen: Set = new Set() @@ -343,23 +356,25 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { chunk.isEntry && chunk.facadeModuleId === id ) as OutputChunk | undefined + let canInlineEntry = false // inject chunk asset links if (chunk) { - const assetTags = [ - // js entry chunk for this page - { - tag: 'script', - attrs: { - type: 'module', - crossorigin: true, - src: toPublicPath(chunk.fileName, config) - } - }, - // preload for imports - ...getPreloadLinksForChunk(chunk), - ...getCssTagsForChunk(chunk) - ] + // an entry chunk can be inlined if + // - it's an ES module (e.g. not generated by the legacy plugin) + // - it contains no meaningful code other than import statments + if (options.format === 'es' && isEntirelyImport(chunk.code)) { + canInlineEntry = true + } + + // when not inlined, inject diff --git a/packages/playground/html/inline/shared-2.html b/packages/playground/html/inline/shared-2.html new file mode 100644 index 00000000000000..69e8b612a3f75d --- /dev/null +++ b/packages/playground/html/inline/shared-2.html @@ -0,0 +1,2 @@ +

+
diff --git a/packages/playground/html/inline/shared.js b/packages/playground/html/inline/shared.js
new file mode 100644
index 00000000000000..531a2786543146
--- /dev/null
+++ b/packages/playground/html/inline/shared.js
@@ -0,0 +1,4 @@
+import './dep3'
+import { log } from './common'
+
+log('shared')
diff --git a/packages/playground/html/inline/unique.html b/packages/playground/html/inline/unique.html
new file mode 100644
index 00000000000000..96a7b67adf90d5
--- /dev/null
+++ b/packages/playground/html/inline/unique.html
@@ -0,0 +1,2 @@
+

+
diff --git a/packages/playground/html/inline/unique.js b/packages/playground/html/inline/unique.js
new file mode 100644
index 00000000000000..df15756c072268
--- /dev/null
+++ b/packages/playground/html/inline/unique.js
@@ -0,0 +1,4 @@
+import { log } from './common'
+import './dep2'
+
+log('unique')
diff --git a/packages/playground/html/vite.config.js b/packages/playground/html/vite.config.js
index ba56b2f2555bd9..61ee5107d819c4 100644
--- a/packages/playground/html/vite.config.js
+++ b/packages/playground/html/vite.config.js
@@ -8,7 +8,10 @@ module.exports = {
     rollupOptions: {
       input: {
         main: resolve(__dirname, 'index.html'),
-        nested: resolve(__dirname, 'nested/index.html')
+        nested: resolve(__dirname, 'nested/index.html'),
+        inline1: resolve(__dirname, 'inline/shared-1.html'),
+        inline2: resolve(__dirname, 'inline/shared-2.html'),
+        inline3: resolve(__dirname, 'inline/unique.html'),
       }
     }
   },

From 5cc7826ebc65bdb5d21d1be79f21a36eab645f98 Mon Sep 17 00:00:00 2001
From: andylizi 
Date: Mon, 9 Aug 2021 18:49:12 +0800
Subject: [PATCH 3/3] test(html): skip inline test in serve mode

---
 .../playground/html/__tests__/html.spec.ts    | 60 ++++++++++---------
 1 file changed, 31 insertions(+), 29 deletions(-)

diff --git a/packages/playground/html/__tests__/html.spec.ts b/packages/playground/html/__tests__/html.spec.ts
index 2c591d64a25daa..4d06ee7463cffd 100644
--- a/packages/playground/html/__tests__/html.spec.ts
+++ b/packages/playground/html/__tests__/html.spec.ts
@@ -95,34 +95,36 @@ describe('nested w/ query', () => {
   testPage(true)
 })
 
-describe('inline entry', () => {
-  const _countTags = (selector) => page.$$eval(selector, (t) => t.length)
-  const countScriptTags = _countTags.bind(this, 'script[type=module]')
-  const countPreloadTags = _countTags.bind(this, 'link[rel=modulepreload]')
-
-  test('is inlined', async () => {
-    await page.goto(viteTestUrl + '/inline/shared-1.html?v=1')
-    expect(await countScriptTags()).toBeGreaterThan(1)
-    expect(await countPreloadTags()).toBe(0)
-  })
-
-  test('is not inlined', async () => {
-    await page.goto(viteTestUrl + '/inline/unique.html?v=1')
-    expect(await countScriptTags()).toBe(1)
-    expect(await countPreloadTags()).toBeGreaterThan(0)
-  })
-
-  test('execution order when inlined', async () => {
-    await page.goto(viteTestUrl + '/inline/shared-2.html?v=1')
-    expect((await page.textContent('#output')).trim()).toBe(
-      'dep1 common dep2 dep3 shared'
-    )
-  })
+if (isBuild) {
+  describe('inline entry', () => {
+    const _countTags = (selector) => page.$$eval(selector, (t) => t.length)
+    const countScriptTags = _countTags.bind(this, 'script[type=module]')
+    const countPreloadTags = _countTags.bind(this, 'link[rel=modulepreload]')
+
+    test('is inlined', async () => {
+      await page.goto(viteTestUrl + '/inline/shared-1.html?v=1')
+      expect(await countScriptTags()).toBeGreaterThan(1)
+      expect(await countPreloadTags()).toBe(0)
+    })
+
+    test('is not inlined', async () => {
+      await page.goto(viteTestUrl + '/inline/unique.html?v=1')
+      expect(await countScriptTags()).toBe(1)
+      expect(await countPreloadTags()).toBeGreaterThan(0)
+    })
+
+    test('execution order when inlined', async () => {
+      await page.goto(viteTestUrl + '/inline/shared-2.html?v=1')
+      expect((await page.textContent('#output')).trim()).toBe(
+        'dep1 common dep2 dep3 shared'
+      )
+    })
 
-  test('execution order when not inlined', async () => {
-    await page.goto(viteTestUrl + '/inline/unique.html?v=1')
-    expect((await page.textContent('#output')).trim()).toBe(
-      'dep1 common dep2 unique'
-    )
+    test('execution order when not inlined', async () => {
+      await page.goto(viteTestUrl + '/inline/unique.html?v=1')
+      expect((await page.textContent('#output')).trim()).toBe(
+        'dep1 common dep2 unique'
+      )
+    })
   })
-})
+}