|
1 | 1 | import { hash } from 'ohash' |
2 | | -import { defineNuxtPlugin, useRoute } from '#imports' |
| 2 | +import cssnano from 'cssnano' |
| 3 | +import { PurgeCSS } from 'purgecss' |
| 4 | +import purgehtml from 'purgecss-from-html' |
| 5 | +import { defineNuxtPlugin } from '#imports' |
| 6 | +import extractorOptions from '#build/nuxt-style-extractor-options.mjs' |
| 7 | + |
| 8 | +console.log(extractorOptions) |
| 9 | + |
| 10 | +interface Options { |
| 11 | + html: string |
| 12 | + css: string |
| 13 | + name: string |
| 14 | +} |
| 15 | + |
| 16 | +let purgeCssCtx: PurgeCSS |
| 17 | +async function removeUnusedCss(options: Options) { |
| 18 | + const { html, css } = options |
| 19 | + if (!purgeCssCtx) { |
| 20 | + purgeCssCtx = new PurgeCSS() |
| 21 | + } |
| 22 | + const [result] = await purgeCssCtx.purge({ |
| 23 | + content: [{ raw: html, extension: 'html' }], |
| 24 | + css: [{ raw: css }], |
| 25 | + extractors: [{ |
| 26 | + extensions: ['html'], |
| 27 | + extractor: purgehtml, |
| 28 | + }], |
| 29 | + }) |
| 30 | + return result.css || '' |
| 31 | +} |
| 32 | + |
| 33 | +let cssnanoCtx: ReturnType<typeof cssnano> |
| 34 | +async function minifyCss(options: Options) { |
| 35 | + const { css, name } = options |
| 36 | + if (!cssnanoCtx) { |
| 37 | + cssnanoCtx = cssnano() |
| 38 | + } |
| 39 | + |
| 40 | + const result = await cssnanoCtx.process(css, { |
| 41 | + from: name, |
| 42 | + map: false, |
| 43 | + }) |
| 44 | + |
| 45 | + return result.css || '' |
| 46 | +} |
| 47 | + |
| 48 | +async function optimiseCss(options: Options) { |
| 49 | + if (extractorOptions.removeUnused) { |
| 50 | + options.css = await removeUnusedCss(options) |
| 51 | + } |
| 52 | + |
| 53 | + if (extractorOptions.minify) { |
| 54 | + options.css = await minifyCss(options) |
| 55 | + } |
| 56 | + |
| 57 | + return options.css |
| 58 | +} |
3 | 59 |
|
4 | 60 | export default defineNuxtPlugin({ |
5 | 61 | name: 'inject-style-id', |
6 | 62 | setup(nuxt) { |
7 | | - const route = useRoute() |
8 | | - nuxt.ssrContext?.head.use({ |
9 | | - hooks: { |
10 | | - 'ssr:render'(ctx) { |
11 | | - let style = '' |
12 | | - ctx.tags = ctx.tags.filter((tag) => { |
13 | | - if (tag.tag === 'style' && tag.innerHTML) { |
14 | | - style += tag.innerHTML |
15 | | - return false |
| 63 | + nuxt.hook('app:rendered', async (nuxtCtx) => { |
| 64 | + const html = nuxtCtx.renderResult?.html || '' |
| 65 | + nuxtCtx.ssrContext?.head.use({ |
| 66 | + hooks: { |
| 67 | + async 'ssr:render'(ctx) { |
| 68 | + let style = '' |
| 69 | + ctx.tags = ctx.tags.filter((tag) => { |
| 70 | + if (tag.tag === 'style' && tag.innerHTML) { |
| 71 | + style += tag.innerHTML |
| 72 | + return false |
| 73 | + } |
| 74 | + return true |
| 75 | + }) |
| 76 | + |
| 77 | + if (!style) { |
| 78 | + return |
16 | 79 | } |
17 | | - return true |
18 | | - }) |
19 | | - if (style) { |
20 | | - const key = hash([route.matched, style]) + '.css' |
| 80 | + |
| 81 | + const name = hash([html, style]) + '.css' |
| 82 | + |
| 83 | + const oldCss = await $fetch(`/_css/${name}`) |
| 84 | + |
| 85 | + if (oldCss === '') { |
| 86 | + return |
| 87 | + } |
| 88 | + |
| 89 | + if (oldCss) { |
| 90 | + ctx.tags.push({ |
| 91 | + tag: 'link', |
| 92 | + props: { |
| 93 | + rel: 'stylesheet', |
| 94 | + href: `/_css/${name}`, |
| 95 | + }, |
| 96 | + }) |
| 97 | + return |
| 98 | + } |
| 99 | + |
| 100 | + const css = await optimiseCss({ |
| 101 | + html, |
| 102 | + css: style, |
| 103 | + name, |
| 104 | + }) |
| 105 | + |
| 106 | + await $fetch('/_css', { |
| 107 | + body: { |
| 108 | + name, |
| 109 | + css, |
| 110 | + }, |
| 111 | + method: 'POST', |
| 112 | + }) |
| 113 | + |
| 114 | + if (css === '') { |
| 115 | + return |
| 116 | + } |
| 117 | + |
21 | 118 | ctx.tags.push({ |
22 | | - tag: 'style', |
| 119 | + tag: 'link', |
23 | 120 | props: { |
24 | | - 'data-style-extractor-key': key, |
| 121 | + rel: 'stylesheet', |
| 122 | + href: `/_css/${name}`, |
25 | 123 | }, |
26 | | - innerHTML: style, |
27 | 124 | }) |
28 | | - } |
| 125 | + }, |
29 | 126 | }, |
30 | | - }, |
| 127 | + }) |
31 | 128 | }) |
32 | 129 | }, |
33 | 130 | }) |
0 commit comments