Skip to content

Commit

Permalink
fix(css): preserve dynamic import css code (fix #5348) (#7746)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed May 9, 2022
1 parent aeb5b74 commit 12d0cc0
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 10 deletions.
18 changes: 12 additions & 6 deletions packages/vite/src/node/plugins/importAnalysisBuild.ts
Expand Up @@ -145,11 +145,15 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
d: dynamicIndex
} = imports[index]

if (dynamicIndex > -1 && insertPreload) {
const isDynamic = dynamicIndex > -1

if (isDynamic && insertPreload) {
needPreloadHelper = true
const original = source.slice(expStart, expEnd)
const replacement = `${preloadMethod}(() => ${original},${isModernFlag}?"${preloadMarker}":void 0)`
str().overwrite(expStart, expEnd, replacement, { contentOnly: true })
str().prependLeft(expStart, `${preloadMethod}(() => `)
str().appendRight(
expEnd,
`,${isModernFlag}?"${preloadMarker}":void 0)`
)
}

// Differentiate CSS imports that use the default export from those that
Expand All @@ -159,14 +163,16 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
if (
specifier &&
isCSSRequest(specifier) &&
source.slice(expStart, start).includes('from') &&
// always inject ?used query when it is a dynamic import
// because there is no way to check whether the default export is used
(source.slice(expStart, start).includes('from') || isDynamic) &&
// already has ?used query (by import.meta.glob)
!specifier.match(/\?used(&|$)/) &&
// edge case for package names ending with .css (e.g normalize.css)
!(bareImportRE.test(specifier) && !specifier.includes('/'))
) {
const url = specifier.replace(/\?|$/, (m) => `?used${m ? '&' : ''}`)
str().overwrite(start, end, dynamicIndex > -1 ? `'${url}'` : url, {
str().overwrite(start, end, isDynamic ? `'${url}'` : url, {
contentOnly: true
})
}
Expand Down
18 changes: 17 additions & 1 deletion playground/css-codesplit/__tests__/css-codesplit.spec.ts
@@ -1,15 +1,31 @@
import { findAssetFile, getColor, isBuild, readManifest } from '../../testUtils'

test('should load both stylesheets', async () => {
test('should load all stylesheets', async () => {
expect(await getColor('h1')).toBe('red')
expect(await getColor('h2')).toBe('blue')
expect(await getColor('.dynamic')).toBe('green')
})

test('should load dynamic import with inline', async () => {
const css = await page.textContent('.dynamic-inline')
expect(css).toMatch('.inline')

expect(await getColor('.inline')).not.toBe('yellow')
})

test('should load dynamic import with module', async () => {
const css = await page.textContent('.dynamic-module')
expect(css).toMatch('_mod_')

expect(await getColor('.mod')).toBe('yellow')
})

if (isBuild) {
test('should remove empty chunk', async () => {
expect(findAssetFile(/style.*\.js$/)).toBe('')
expect(findAssetFile('main.*.js$')).toMatch(`/* empty css`)
expect(findAssetFile('other.*.js$')).toMatch(`/* empty css`)
expect(findAssetFile(/async.*\.js$/)).toBe('')
})

test('should generate correct manifest', async () => {
Expand Down
3 changes: 3 additions & 0 deletions playground/css-codesplit/async.css
@@ -0,0 +1,3 @@
.dynamic {
color: green;
}
9 changes: 9 additions & 0 deletions playground/css-codesplit/index.html
@@ -1,2 +1,11 @@
<h1>This should be red</h1>
<h2>This should be blue</h2>

<p class="dynamic">This should be green</p>
<p class="inline">This should not be yellow</p>
<p class="dynamic-inline"></p>
<p class="mod">This should be yellow</p>
<p class="dynamic-module"></p>

<script type="module" src="/main.js"></script>
<div id="app"></div>
3 changes: 3 additions & 0 deletions playground/css-codesplit/inline.css
@@ -0,0 +1,3 @@
.inline {
color: yellow;
}
15 changes: 12 additions & 3 deletions playground/css-codesplit/main.js
@@ -1,6 +1,15 @@
import './style.css'
import './main.css'

document.getElementById(
'app'
).innerHTML = `<h1>This should be red</h1><h2>This should be blue</h2>`
import('./async.css')

import('./inline.css?inline').then((css) => {
document.querySelector('.dynamic-inline').textContent = css.default
})

import('./mod.module.css').then((css) => {
document.querySelector('.dynamic-module').textContent = JSON.stringify(
css.default
)
document.querySelector('.mod').classList.add(css.default.mod)
})
3 changes: 3 additions & 0 deletions playground/css-codesplit/mod.module.css
@@ -0,0 +1,3 @@
.mod {
color: yellow;
}

0 comments on commit 12d0cc0

Please sign in to comment.