mirror of
https://github.com/vitejs/vite.git
synced 2024-11-22 07:09:05 +00:00
fix(css): hoist charset (#7678)
This commit is contained in:
parent
485263cdca
commit
29e622cc7c
@ -247,6 +247,15 @@ test('inline css modules', async () => {
|
||||
expect(css).toMatch(/\.inline-module__apply-color-inline___[\w-]{5}/)
|
||||
})
|
||||
|
||||
if (isBuild) {
|
||||
test('@charset hoist', async () => {
|
||||
serverLogs.forEach((log) => {
|
||||
// no warning from esbuild css minifier
|
||||
expect(log).not.toMatch('"@charset" must be the first rule in the file')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('@import dependency w/ style entry', async () => {
|
||||
expect(await getColor('.css-dep')).toBe('purple')
|
||||
})
|
||||
|
5
packages/playground/css/charset.css
Normal file
5
packages/playground/css/charset.css
Normal file
@ -0,0 +1,5 @@
|
||||
@charset "utf-8";
|
||||
|
||||
.utf8 {
|
||||
color: green;
|
||||
}
|
@ -96,6 +96,9 @@
|
||||
<p>Inline CSS module:</p>
|
||||
<pre class="modules-inline"></pre>
|
||||
|
||||
<p>CSS with @charset:</p>
|
||||
<pre class="charset-css"></pre>
|
||||
|
||||
<p class="css-dep">
|
||||
@import dependency w/ style enrtrypoints: this should be purple
|
||||
</p>
|
||||
|
@ -41,6 +41,9 @@ text(
|
||||
import inlineMod from './inline.module.css?inline'
|
||||
text('.modules-inline', inlineMod)
|
||||
|
||||
import charset from './charset.css'
|
||||
text('.charset-css', charset)
|
||||
|
||||
import './dep.css'
|
||||
import './glob-dep.css'
|
||||
|
||||
|
@ -427,10 +427,10 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
|
||||
return `./${path.posix.basename(filename)}`
|
||||
}
|
||||
})
|
||||
// only external @imports should exist at this point - and they need to
|
||||
// be hoisted to the top of the CSS chunk per spec (#1845)
|
||||
if (css.includes('@import')) {
|
||||
css = await hoistAtImports(css)
|
||||
// only external @imports and @charset should exist at this point
|
||||
// hoist them to the top of the CSS chunk per spec (#1845 and #6333)
|
||||
if (css.includes('@import') || css.includes('@charset')) {
|
||||
css = await hoistAtRules(css)
|
||||
}
|
||||
if (minify && config.build.minify) {
|
||||
css = await minifyCSS(css, config)
|
||||
@ -1109,27 +1109,33 @@ async function minifyCSS(css: string, config: ResolvedConfig) {
|
||||
// #1845
|
||||
// CSS @import can only appear at top of the file. We need to hoist all @import
|
||||
// to top when multiple files are concatenated.
|
||||
async function hoistAtImports(css: string) {
|
||||
// #6333
|
||||
// CSS @charset must be the top-first in the file, hoist to top too
|
||||
async function hoistAtRules(css: string) {
|
||||
const postcss = await import('postcss')
|
||||
return (await postcss.default([AtImportHoistPlugin]).process(css)).css
|
||||
return (await postcss.default([AtRuleHoistPlugin]).process(css)).css
|
||||
}
|
||||
|
||||
const AtImportHoistPlugin: PostCSS.PluginCreator<any> = () => {
|
||||
const AtRuleHoistPlugin: PostCSS.PluginCreator<any> = () => {
|
||||
return {
|
||||
postcssPlugin: 'vite-hoist-at-imports',
|
||||
postcssPlugin: 'vite-hoist-at-rules',
|
||||
Once(root) {
|
||||
const imports: PostCSS.AtRule[] = []
|
||||
let charset: PostCSS.AtRule | undefined
|
||||
root.walkAtRules((rule) => {
|
||||
if (rule.name === 'import') {
|
||||
// record in reverse so that can simply prepend to preserve order
|
||||
imports.unshift(rule)
|
||||
} else if (!charset && rule.name === 'charset') {
|
||||
charset = rule
|
||||
}
|
||||
})
|
||||
imports.forEach((i) => root.prepend(i))
|
||||
if (charset) root.prepend(charset)
|
||||
}
|
||||
}
|
||||
}
|
||||
AtImportHoistPlugin.postcss = true
|
||||
AtRuleHoistPlugin.postcss = true
|
||||
|
||||
// Preprocessor support. This logic is largely replicated from @vue/compiler-sfc
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user