Skip to content

Commit

Permalink
fix(manifest): include assets referenced via CSS in manifest entries
Browse files Browse the repository at this point in the history
fix #1827
  • Loading branch information
yyx990803 committed Feb 1, 2021
1 parent 2144ce9 commit 34894a2
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 46 deletions.
24 changes: 23 additions & 1 deletion packages/playground/assets/__tests__/assets.spec.ts
@@ -1,5 +1,12 @@
import { createHash } from 'crypto'
import { findAssetFile, getBg, getColor, isBuild } from '../../testUtils'
import {
findAssetFile,
getBg,
getColor,
isBuild,
listAssets,
readManifest
} from '../../testUtils'

const assetMatch = isBuild
? /\/foo\/assets\/asset\.\w{8}\.png/
Expand Down Expand Up @@ -133,3 +140,18 @@ test('?url import', async () => {
: `/foo/foo.js`
)
})

if (isBuild) {
test('manifest', async () => {
const manifest = readManifest('foo')
const entry = manifest['index.html']

for (const file of listAssets('foo')) {
if (file.endsWith('.css')) {
expect(entry.css).toContain(`assets/${file}`)
} else if (!file.endsWith('.js')) {
expect(entry.assets).toContain(`assets/${file}`)
}
}
})
}
3 changes: 2 additions & 1 deletion packages/playground/assets/vite.config.js
Expand Up @@ -5,6 +5,7 @@ module.exports = {
base: '/foo/',
publicDir: 'static',
build: {
outDir: 'dist/foo'
outDir: 'dist/foo',
manifest: true
}
}
@@ -1,6 +1,4 @@
import fs from 'fs'
import path from 'path'
import { findAssetFile, getColor, isBuild, testDir } from '../../testUtils'
import { findAssetFile, getColor, isBuild, readManifest } from '../../testUtils'

test('should load both stylesheets', async () => {
expect(await getColor('h1')).toBe('red')
Expand All @@ -15,9 +13,7 @@ if (isBuild) {
})

test('should generate correct manifest', async () => {
const manifest = JSON.parse(
fs.readFileSync(path.join(testDir, 'dist', 'manifest.json'), 'utf-8')
)
const manifest = readManifest()
expect(manifest['index.html'].css.length).toBe(2)
expect(manifest['other.js'].css.length).toBe(1)
})
Expand Down
11 changes: 11 additions & 0 deletions packages/playground/testUtils.ts
Expand Up @@ -75,6 +75,11 @@ export function removeFile(filename: string) {
fs.unlinkSync(path.resolve(testDir, filename))
}

export function listAssets(base = '') {
const assetsDir = path.join(testDir, 'dist', base, 'assets')
return fs.readdirSync(assetsDir)
}

export function findAssetFile(match: string | RegExp, base = '') {
const assetsDir = path.join(testDir, 'dist', base, 'assets')
const files = fs.readdirSync(assetsDir)
Expand All @@ -84,6 +89,12 @@ export function findAssetFile(match: string | RegExp, base = '') {
return file ? fs.readFileSync(path.resolve(assetsDir, file), 'utf-8') : ''
}

export function readManifest(base = '') {
return JSON.parse(
fs.readFileSync(path.join(testDir, 'dist', base, 'manifest.json'), 'utf-8')
)
}

/**
* Poll a getter until the value it returns includes the expected value.
*/
Expand Down
17 changes: 10 additions & 7 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -59,18 +59,13 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
},

renderChunk(code, chunk) {
let emitted = chunkToEmittedAssetsMap.get(chunk)
let match
let s
while ((match = assetUrlQuotedRE.exec(code))) {
s = s || (s = new MagicString(code))
const [full, fileHandle, postfix = ''] = match
const file = this.getFileName(fileHandle)
if (!emitted) {
emitted = new Set()
chunkToEmittedAssetsMap.set(chunk, emitted)
}
emitted.add(file)
registerAssetToChunk(chunk, file)
const outputFilepath = config.base + file + postfix
s.overwrite(
match.index,
Expand Down Expand Up @@ -104,6 +99,15 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
}
}

export function registerAssetToChunk(chunk: RenderedChunk, file: string) {
let emitted = chunkToEmittedAssetsMap.get(chunk)
if (!emitted) {
emitted = new Set()
chunkToEmittedAssetsMap.set(chunk, emitted)
}
emitted.add(cleanUrl(file))
}

export function checkPublicFile(
url: string,
{ publicDir }: ResolvedConfig
Expand Down Expand Up @@ -178,7 +182,6 @@ async function fileToBuiltUrl(
const file = cleanUrl(id)
const { search, hash } = parseUrl(id)
const postfix = (search || '') + (hash || '')
// TODO preserve fragment hash or queries
const content = await fsp.readFile(file)

let url
Expand Down
73 changes: 42 additions & 31 deletions packages/vite/src/node/plugins/css.ts
Expand Up @@ -16,7 +16,6 @@ import postcssrc from 'postcss-load-config'
import {
NormalizedOutputOptions,
OutputChunk,
PluginContext,
RenderedChunk,
RollupError,
SourceMap
Expand All @@ -31,7 +30,12 @@ import {
PluginCreator
} from 'postcss'
import { ResolveFn, ViteDevServer } from '../'
import { assetUrlRE, fileToDevUrl, urlToBuiltUrl } from './asset'
import {
assetUrlRE,
fileToDevUrl,
registerAssetToChunk,
urlToBuiltUrl
} from './asset'
import MagicString from 'magic-string'
import type {
ImporterReturnType,
Expand Down Expand Up @@ -255,13 +259,45 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
return null
}

// resolve asset URL placeholders to their built file URLs and perform
// minification if necessary
const process = async (
css: string,
{
inlined,
minify
}: {
inlined: boolean
minify: boolean
}
) => {
// replace asset url references with resolved url.
const isRelativeBase = config.base === '' || config.base.startsWith('.')
css = css.replace(assetUrlRE, (_, fileId, postfix = '') => {
const filename = this.getFileName(fileId) + postfix
registerAssetToChunk(chunk, filename)
if (!isRelativeBase || inlined) {
// absoulte base or relative base but inlined (injected as style tag into
// index.html) use the base as-is
return config.base + filename
} else {
// relative base + extracted CSS - asset file will be in the same dir
return `./${path.posix.basename(filename)}`
}
})
if (minify && config.build.minify) {
css = await minifyCSS(css, config)
}
return css
}

if (config.build.cssCodeSplit) {
if (isPureCssChunk) {
// this is a shared CSS-only chunk that is empty.
pureCssChunks.add(chunk.fileName)
}
if (opts.format === 'es') {
chunkCSS = await processChunkCSS(chunkCSS, config, this, false)
chunkCSS = await process(chunkCSS, { inlined: false, minify: true })
// emit corresponding css file
const fileHandle = this.emitFile({
name: chunk.name + '.css',
Expand All @@ -274,7 +310,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
)
} else if (!config.build.ssr) {
// legacy build, inline css
chunkCSS = await processChunkCSS(chunkCSS, config, this, true)
chunkCSS = await process(chunkCSS, { inlined: true, minify: true })
const style = `__vite_style__`
const injectCode =
`var ${style} = document.createElement('style');` +
Expand All @@ -292,7 +328,8 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
}
}
} else {
chunkCSS = await processChunkCSS(chunkCSS, config, this, false, false)
// non-split extracted CSS will be minified togethter
chunkCSS = await process(chunkCSS, { inlined: false, minify: false })
outputToExtractedCSSMap.set(
opts,
(outputToExtractedCSSMap.get(opts) || '') + chunkCSS
Expand Down Expand Up @@ -663,32 +700,6 @@ function rewriteCssUrls(
})
}

async function processChunkCSS(
css: string,
config: ResolvedConfig,
pluginCtx: PluginContext,
isInlined: boolean,
minify = true
): Promise<string> {
// replace asset url references with resolved url.
const isRelativeBase = config.base === '' || config.base.startsWith('.')
css = css.replace(assetUrlRE, (_, fileId, postfix = '') => {
const filename = pluginCtx.getFileName(fileId) + postfix
if (!isRelativeBase || isInlined) {
// absoulte base or relative base but inlined (injected as style tag into
// index.html) use the base as-is
return config.base + filename
} else {
// relative base + extracted CSS - asset file will be in the same dir
return `./${path.posix.basename(filename)}`
}
})
if (minify && config.build.minify) {
css = await minifyCSS(css, config)
}
return css
}

let CleanCSS: any

async function minifyCSS(css: string, config: ResolvedConfig) {
Expand Down

0 comments on commit 34894a2

Please sign in to comment.