Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,13 @@ const UrlRewritePostcssPlugin: PostCSS.PluginCreator<{

return {
postcssPlugin: 'vite-url-rewrite',
OnceExit(root) {
OnceExit(root, { result }) {
// a plugin earlier in the pipeline (e.g. postcss-lightningcss) may
// reassign `result.root` to a freshly parsed tree from its own
// OnceExit hook. PostCSS keeps passing the pre-reassignment `root` to
// subsequently run OnceExit hooks, so read the live root off `result`
// instead of trusting the argument.
root = result.root
const promises: Promise<void>[] = []
root.walkDecls((declaration) => {
const importer = declaration.source?.input.file
Expand Down
10 changes: 10 additions & 0 deletions playground/css/__tests__/css.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@ test('postcss plugin that injects url() at OnceExit', async () => {
isBundled ? /base64/ : '/injected-source/injected-bg.png',
)
})

// a plugin that reassigns `result.root` at OnceExit (like postcss-lightningcss
// does) instead of mutating the existing root in place
test('postcss plugin that reassigns result.root at OnceExit', async () => {
await page.goto(viteTestUrl)
const imported = await page.waitForSelector('.replace-root-once-exit')
expect(await getBg(imported)).toMatch(
isBundled ? /base64/ : '/injected-source/injected-bg.png',
)
})
4 changes: 4 additions & 0 deletions playground/css/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ <h1>CSS</h1>
<p class="inject-url-once-exit">
PostCSS plugin injecting at OnceExit: this should have a background image
</p>
<p class="replace-root-once-exit">
PostCSS plugin replacing result.root at OnceExit: this should have a
background image
</p>

<p class="sass">SASS: This should be orange</p>
<p class="sass-at-import">
Expand Down
1 change: 1 addition & 0 deletions playground/css/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import './less-plugin.less'
import './stylus.styl'
import './manual-chunk.css'
import './postcss-inject-url.css'
import './replace-root.css'

import urlCss from './url-imported.css?url'
appendLinkStylesheet(urlCss)
Expand Down
24 changes: 24 additions & 0 deletions playground/css/postcss.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
testSourceInput,
testInjectUrl,
testInjectUrlOnceExit,
testReplaceRootOnceExit,
],
}

Expand Down Expand Up @@ -116,3 +117,26 @@ function testInjectUrlOnceExit() {
}
}
testInjectUrlOnceExit.postcss = true

/**
* A plugin for testing url() rewriting when a plugin reassigns `result.root`
* to a freshly parsed tree at OnceExit instead of mutating the existing root
* in place (this is what postcss-lightningcss does)
*/
function testReplaceRootOnceExit() {
return {
postcssPlugin: 'replace-root-once-exit',
OnceExit(root, { result, postcss }) {
if (!root.source?.input.file?.endsWith('replace-root.css')) return
root.walkAtRules('replace-root-once-exit', (atRule) => {
atRule.replaceWith(
'.replace-root-once-exit { background: url("./injected-source/injected-bg.png") }',
)
})
result.root = postcss.parse(root.toString(), {
from: root.source.input.file,
})
},
}
}
testReplaceRootOnceExit.postcss = true
1 change: 1 addition & 0 deletions playground/css/replace-root.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@replace-root-once-exit;
Loading