Skip to content

Commit

Permalink
fix(vite): correctly css hash in build, close #1081
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Jun 10, 2022
1 parent be3e42c commit 7f81af3
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 24 deletions.
2 changes: 2 additions & 0 deletions packages/core/src/types.ts
Expand Up @@ -456,7 +456,9 @@ export interface UserOnlyOptions<Theme extends {} = {}> {
export interface UnocssPluginContext<Config extends UserConfig = UserConfig> {
ready: Promise<LoadConfigResult<Config>>
uno: UnoGenerator
/** All tokens scanned */
tokens: Set<string>
/** Map for all module's raw content */
modules: BetterMap<string, string>
filter: (code: string, id: string) => boolean
extract: (code: string, id?: string) => Promise<void>
Expand Down
56 changes: 32 additions & 24 deletions packages/vite/src/modes/global/build.ts
@@ -1,5 +1,6 @@
import { resolve } from 'path'
import type { Plugin } from 'vite'
import type { UnocssPluginContext } from '@unocss/core'
import type { GenerateResult, UnocssPluginContext } from '@unocss/core'
import {
HASH_PLACEHOLDER_RE, LAYER_MARK_ALL, LAYER_PLACEHOLDER_RE,
RESOLVED_ID_RE,
Expand All @@ -12,7 +13,7 @@ import {
} from '../../integration'
import type { VitePluginConfig } from '../../types'

export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, filter, getConfig }: UnocssPluginContext<VitePluginConfig>): Plugin[] {
export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, filter, getConfig }: UnocssPluginContext<VitePluginConfig>): Plugin[] {
const vfsLayerMap = new Map<string, string>()
const layerImporterMap = new Map<string, string>()
let tasks: Promise<any>[] = []
Expand All @@ -21,7 +22,7 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, fi
const cssPostPlugins = new Map<string | undefined, Plugin | undefined>()
const cssPlugins = new Map<string | undefined, Plugin | undefined>()

async function transformCSS(css: string, id: string, dir: string | undefined) {
async function applyCssTransform(css: string, id: string, dir: string | undefined) {
const {
postcss = true,
} = await getConfig()
Expand All @@ -39,13 +40,26 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, fi
return css
}

let lastTokenSize = 0
let lastResult: GenerateResult | undefined
async function generateAll() {
await Promise.all(tasks)
if (lastResult && lastTokenSize === tokens.size)
return lastResult
lastResult = await uno.generate(tokens, { minify: true })
lastTokenSize = tokens.size
return lastResult
}

return [
{
name: 'unocss:global:build:scan',
apply: 'build',
enforce: 'pre',
buildStart() {
tasks = []
lastTokenSize = 0
lastResult = undefined
},
transform(code, id) {
if (filter(code, id))
Expand Down Expand Up @@ -83,36 +97,31 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, fi
}
},
async configResolved(config) {
cssPostPlugins.set(config.build.outDir, config.plugins.find(i => i.name === 'vite:css-post'))
cssPlugins.set(config.build.outDir, config.plugins.find(i => i.name === 'vite:css'))
const distDir = resolve(config.root, config.build.outDir)
cssPostPlugins.set(distDir, config.plugins.find(i => i.name === 'vite:css-post'))
cssPlugins.set(distDir, config.plugins.find(i => i.name === 'vite:css'))
await ready
},
// we inject a hash to chunk before the dist hash calculation to make sure
// the hash is different when unocss changes
async renderChunk(_, chunk, options) {
if (!cssPostPlugins.get(options.dir))
// skip hash generation on non-entry chunk
if (!Object.keys(chunk.modules).some(i => i.match(RESOLVED_ID_RE)))
return null

const chunks = Object.keys(chunk.modules).filter(i => modules.has(i))
if (!chunks.length)
const cssPost = cssPostPlugins.get(options.dir)
if (!cssPost) {
this.warn('[unocss] failed to find vite:css-post plugin. It might be an internal bug of UnoCSS')
return null
}

let { css } = await generateAll()
const fakeCssId = `${chunk.fileName}-unocss-hash.css`
const tokens = new Set<string>()
await Promise.all(chunks.map(c => uno.applyExtractors(modules.get(c) || '', c, tokens)))
let { css } = await uno.generate(tokens, { minify: true })
if (!css)
return null

// skip hash generation on non-entry chunk
if (!Object.keys(chunk.modules).some(i => i.match(RESOLVED_ID_RE)))
return null

css = await transformCSS(css, fakeCssId, options.dir)
css = await applyCssTransform(css, fakeCssId, options.dir)

const hash = getHash(css)
// @ts-expect-error no this context
await cssPostPlugins.get(options.dir).transform(getHashPlaceholder(hash), fakeCssId)
await cssPost.transform!.call({} as any, getHashPlaceholder(hash), fakeCssId)

// fool the css plugin to generate the css in corresponding chunk
chunk.modules[fakeCssId] = {
code: null,
Expand Down Expand Up @@ -146,8 +155,7 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, fi
return
}

await Promise.all(tasks)
const result = await uno.generate(tokens, { minify: true })
const result = await generateAll()
let replaced = false

for (const file of cssFiles) {
Expand All @@ -158,7 +166,7 @@ export function GlobalModeBuildPlugin({ uno, ready, extract, tokens, modules, fi

chunk.source = await replaceAsync(css, LAYER_PLACEHOLDER_RE, async (_, __, layer) => {
replaced = true
return await transformCSS(layer === LAYER_MARK_ALL
return await applyCssTransform(layer === LAYER_MARK_ALL
? result.getLayers(undefined, Array.from(vfsLayerMap.values()))
: result.getLayer(layer) || '', `${chunk.fileName}.css`, options.dir)
})
Expand Down

0 comments on commit 7f81af3

Please sign in to comment.