Skip to content

Commit

Permalink
feat(ssr): support preload dynamic css file in html head (#5705)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangyuang authored Jan 5, 2022
1 parent cddd986 commit 07fca95
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 2 deletions.
18 changes: 18 additions & 0 deletions packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { editFile, getColor, isBuild, untilUpdated } from '../../testUtils'
import { port } from './serve'
import fetch from 'node-fetch'
import { resolve } from 'path'

const url = `http://localhost:${port}`

Expand Down Expand Up @@ -162,3 +163,20 @@ test('import.meta.url', async () => {
await page.goto(url)
expect(await page.textContent('.protocol')).toEqual('file:')
})

test('dynamic css file should be preloaded', async () => {
if (isBuild) {
await page.goto(url)
const homeHtml = await (await fetch(url)).text()
const re = /link rel="modulepreload".*?href="\/assets\/(Home\.\w{8}\.js)"/
const filename = re.exec(homeHtml)[1]
const manifest = require(resolve(
process.cwd(),
'./packages/temp/ssr-vue/dist/client/ssr-manifest.json'
))
const depFile = manifest[filename]
for (const file of depFile) {
expect(homeHtml).toMatch(file)
}
}
})
15 changes: 15 additions & 0 deletions packages/playground/ssr-vue/src/assets/button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.btn {
background-color: #65b587;
border-radius: 8px;
border-style: none;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: 500;
height: 40px;
line-height: 20px;
list-style: none;
outline: none;
padding: 10px 16px;
}
16 changes: 16 additions & 0 deletions packages/playground/ssr-vue/src/components/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createVNode, defineComponent } from 'vue'
import '../assets/button.css'

export default defineComponent({
setup() {
return () => {
return createVNode(
'div',
{
class: 'btn'
},
'dynamicBtn'
)
}
}
})
8 changes: 8 additions & 0 deletions packages/playground/ssr-vue/src/entry-server.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createApp } from './main'
import { renderToString } from 'vue/server-renderer'
import path, { basename } from 'path'

export async function render(url, manifest) {
const { app, router } = createApp()
Expand Down Expand Up @@ -31,6 +32,13 @@ function renderPreloadLinks(modules, manifest) {
files.forEach((file) => {
if (!seen.has(file)) {
seen.add(file)
const filename = basename(file)
if (manifest[filename]) {
for (const depFile of manifest[filename]) {
links += renderPreloadLink(depFile)
seen.add(depFile)
}
}
links += renderPreloadLink(file)
}
})
Expand Down
6 changes: 6 additions & 0 deletions packages/playground/ssr-vue/src/pages/About.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<template>
<h1>{{ msg }}</h1>
<p class="import-meta-url">{{ url }}</p>
<Button>CommonButton</Button>
</template>

<script>
import Button from '../components/button'
export default {
async setup() {
const url = import.meta.env.SSR
Expand All @@ -13,6 +16,9 @@ export default {
msg: 'About',
url
}
},
components: {
Button
}
}
</script>
Expand Down
6 changes: 6 additions & 0 deletions packages/playground/ssr-vue/src/pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
<p class="import-meta-url">{{ state.url }}</p>
<p class="protocol">{{ state.protocol }}</p>
<p class="nested-virtual">msg from nested virtual module: {{ virtualMsg }}</p>
<Button>CommonButton</Button>
<div>
encrypted message:
<p class="encrypted-msg">{{ encryptedMsg }}</p>
</div>

<ImportType />
</template>
Expand All @@ -18,6 +23,7 @@
import foo from '@foo'
import { msg as virtualMsg } from '@virtual-file'
import { reactive, defineAsyncComponent } from 'vue'
import Button from '../components/button'
const ImportType = load('ImportType')
const Foo = defineAsyncComponent(() =>
import('../components/Foo').then((mod) => mod.Foo)
Expand Down
56 changes: 54 additions & 2 deletions packages/vite/src/node/ssr/ssrManifestPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { relative } from 'path'
import { normalizePath } from '@rollup/pluginutils'
import { relative, basename, join, dirname } from 'path'
import { parse as parseImports } from 'es-module-lexer'
import type { ImportSpecifier } from 'es-module-lexer'
import type { OutputChunk } from 'rollup'
import type { ResolvedConfig } from '..'
import type { Plugin } from '../plugin'
import { chunkToEmittedCssFileMap } from '../plugins/css'
import { chunkToEmittedAssetsMap } from '../plugins/asset'
import { preloadMethod } from '../plugins/importAnalysisBuild'
import { normalizePath } from '../utils'

export function ssrManifestPlugin(config: ResolvedConfig): Plugin {
// module id => preload assets mapping
Expand Down Expand Up @@ -40,6 +44,54 @@ export function ssrManifestPlugin(config: ResolvedConfig): Plugin {
})
}
}
if (chunk.code.includes(preloadMethod)) {
// generate css deps map
const code = chunk.code
let imports: ImportSpecifier[]
try {
imports = parseImports(code)[0].filter((i) => i.d > -1)
} catch (e: any) {
this.error(e, e.idx)
}
if (imports.length) {
for (let index = 0; index < imports.length; index++) {
const {
s: start,
e: end,
n: name,
d: dynamicIndex
} = imports[index]
if (dynamicIndex) {
// check the chunk being imported
const url = code.slice(start, end)
const deps: string[] = []
const ownerFilename = chunk.fileName
// literal import - trace direct imports and add to deps
const analyzed: Set<string> = new Set<string>()
const addDeps = (filename: string) => {
if (filename === ownerFilename) return
if (analyzed.has(filename)) return
analyzed.add(filename)
const chunk = bundle[filename] as OutputChunk | undefined
if (chunk) {
const cssFiles = chunkToEmittedCssFileMap.get(chunk)
if (cssFiles) {
cssFiles.forEach((file) => {
deps.push(`/${file}`)
})
}
chunk.imports.forEach(addDeps)
}
}
const normalizedFile = normalizePath(
join(dirname(chunk.fileName), url.slice(1, -1))
)
addDeps(normalizedFile)
ssrManifest[basename(name!)] = deps
}
}
}
}
}
}

Expand Down

0 comments on commit 07fca95

Please sign in to comment.