fix(nuxt): do not inline global styles in html response (#8666)

This commit is contained in:
Daniel Roe 2022-11-03 15:17:43 -04:00 committed by GitHub
parent cda89b36e1
commit cda498b070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 8 additions and 37 deletions

View File

@ -289,19 +289,15 @@ function renderHTMLDocument (html: NuxtRenderHTMLContext) {
} }
async function renderInlineStyles (usedModules: Set<string> | string[]) { async function renderInlineStyles (usedModules: Set<string> | string[]) {
const { entryCSS } = await getClientManifest()
const styleMap = await getSSRStyles() const styleMap = await getSSRStyles()
const inlinedStyles = new Set<string>() const inlinedStyles = new Set<string>()
for (const mod of ['entry', ...usedModules]) { for (const mod of usedModules) {
if (mod in styleMap) { if (mod in styleMap) {
for (const style of await styleMap[mod]()) { for (const style of await styleMap[mod]()) {
inlinedStyles.add(`<style>${style}</style>`) inlinedStyles.add(`<style>${style}</style>`)
} }
} }
} }
for (const css of entryCSS?.css || []) {
inlinedStyles.add(`<link rel="stylesheet" href=${JSON.stringify(buildAssetsURL(css))} media="print" onload="this.media='all'; this.onload=null;">`)
}
return Array.from(inlinedStyles).join('') return Array.from(inlinedStyles).join('')
} }

View File

@ -5,7 +5,6 @@ import { dirname, relative } from 'pathe'
import { genObjectFromRawEntries } from 'knitwork' import { genObjectFromRawEntries } from 'knitwork'
import { filename } from 'pathe/utils' import { filename } from 'pathe/utils'
import { parseQuery, parseURL } from 'ufo' import { parseQuery, parseURL } from 'ufo'
import { isCSS } from '../utils'
interface SSRStylePluginOptions { interface SSRStylePluginOptions {
srcDir: string srcDir: string
@ -16,7 +15,6 @@ interface SSRStylePluginOptions {
export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin { export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
const cssMap: Record<string, { files: string[], inBundle: boolean }> = {} const cssMap: Record<string, { files: string[], inBundle: boolean }> = {}
const idRefMap: Record<string, string> = {} const idRefMap: Record<string, string> = {}
const globalStyles = new Set<string>()
const relativeToSrcDir = (path: string) => relative(options.srcDir, path) const relativeToSrcDir = (path: string) => relative(options.srcDir, path)
@ -49,8 +47,6 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
}) })
} }
const globalStylesArray = Array.from(globalStyles).map(css => idRefMap[css] && this.getFileName(idRefMap[css])).filter(Boolean)
for (const key in emitted) { for (const key in emitted) {
// Track the chunks we are inlining CSS for so we can omit including links to the .css files // Track the chunks we are inlining CSS for so we can omit including links to the .css files
options.chunksWithInlinedCSS.add(key) options.chunksWithInlinedCSS.add(key)
@ -61,12 +57,10 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
fileName: 'styles.mjs', fileName: 'styles.mjs',
source: source:
[ [
...globalStylesArray.map((css, i) => `import style_${i} from './${css}';`),
'const interopDefault = r => r.default || r || []', 'const interopDefault = r => r.default || r || []',
`export default ${genObjectFromRawEntries([ `export default ${genObjectFromRawEntries(
['entry', `() => [${globalStylesArray.map((_, i) => `style_${i}`).join(', ')}]`], Object.entries(emitted).map(([key, value]) => [key, `() => import('./${this.getFileName(value)}').then(interopDefault)`]) as [string, string][]
...Object.entries(emitted).map(([key, value]) => [key, `() => import('./${this.getFileName(value)}').then(interopDefault)`]) as [string, string][] )}`
])}`
].join('\n') ].join('\n')
}) })
}, },
@ -80,14 +74,6 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
} }
} }
if (chunk.isEntry) {
// Entry
for (const mod in chunk.modules) {
if (isCSS(mod) && !mod.includes('&used')) {
globalStyles.add(relativeToSrcDir(mod))
}
}
}
return null return null
}, },
async transform (code, id) { async transform (code, id) {

View File

@ -126,16 +126,6 @@ export async function buildServer (ctx: ViteBuildContext) {
if (shouldRemoveCSS) { if (shouldRemoveCSS) {
entry.css = [] entry.css = []
} }
// Add entry CSS as prefetch (non-blocking)
if (entry.isEntry) {
manifest.entryCSS = {
file: '',
css: entry.css
}
entry.css = []
entry.dynamicImports = entry.dynamicImports || []
entry.dynamicImports.push('entryCSS')
}
} }
}) })
} }

View File

@ -598,19 +598,18 @@ describe.skipIf(process.env.NUXT_TEST_DEV || process.env.TEST_WITH_WEBPACK)('inl
for (const style of [ for (const style of [
'{--assets:"assets"}', // <script> '{--assets:"assets"}', // <script>
'{--scoped:"scoped"}', // <style lang=css> '{--scoped:"scoped"}', // <style lang=css>
'{--postcss:"postcss"}', // <style lang=postcss> '{--postcss:"postcss"}' // <style lang=postcss>
'{--global:"global"', // entryfile dependency
'{--plugin:"plugin"}' // plugin dependency
]) { ]) {
expect(html).toContain(style) expect(html).toContain(style)
} }
}) })
it('only renders prefetch for entry styles', async () => { it('does not load stylesheet for page styles', async () => {
const html: string = await $fetch('/styles') const html: string = await $fetch('/styles')
expect(html.match(/<link [^>]*href="[^"]*\.css">/g)?.filter(m => m.includes('entry'))?.map(m => m.replace(/\.[^.]*\.css/, '.css'))).toMatchInlineSnapshot(` expect(html.match(/<link [^>]*href="[^"]*\.css">/g)?.filter(m => m.includes('entry'))?.map(m => m.replace(/\.[^.]*\.css/, '.css'))).toMatchInlineSnapshot(`
[ [
"<link rel=\\"prefetch\\" as=\\"style\\" href=\\"/_nuxt/entry.css\\">", "<link rel=\\"preload\\" as=\\"style\\" href=\\"/_nuxt/entry.css\\">",
"<link rel=\\"stylesheet\\" href=\\"/_nuxt/entry.css\\">",
] ]
`) `)
}) })