Skip to content

Commit 7b2e0f9

Browse files
fix(css): compile preprocessor langs in virtual CSS modules (#865)
* fix(css): compile preprocessor langs in virtual CSS modules (e.g. Vue SFC) fixes #848 * test: add snapshot for #848 * [autofix.ci] apply automated fixes * refactor: simplify to use getPreprocessorLangFromId directly --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 824ceb7 commit 7b2e0f9

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

packages/css/src/plugin.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
} from './options.ts'
1313
import { CssPostPlugin, type CssStyles } from './post.ts'
1414
import { processWithPostCSS as runPostCSS } from './postcss.ts'
15-
import { compilePreprocessor, getPreprocessorLang } from './preprocessors.ts'
15+
import {
16+
compilePreprocessor,
17+
getPreprocessorLangFromId,
18+
} from './preprocessors.ts'
1619
import {
1720
CSS_LANGS_RE,
1821
CSS_MODULE_RE,
@@ -315,7 +318,7 @@ async function processWithLightningCSS(
315318
logger: Logger,
316319
isModule: boolean,
317320
): Promise<ProcessResult> {
318-
const lang = getPreprocessorLang(cleanId)
321+
const lang = getPreprocessorLangFromId(id)
319322
const cssModules = resolveCssModulesConfig(
320323
config.css.modules,
321324
isModule,
@@ -378,7 +381,7 @@ async function processWithPostCSS(
378381
config: CssPluginConfig,
379382
isModule: boolean,
380383
): Promise<ProcessResult> {
381-
const lang = getPreprocessorLang(cleanId)
384+
const lang = getPreprocessorLangFromId(id)
382385

383386
if (lang) {
384387
const preResult = await compilePreprocessor(

packages/css/src/preprocessors.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises'
22
import path from 'node:path'
33
import { fileURLToPath, pathToFileURL } from 'node:url'
44
import { getSassResolver, resolveWithResolver } from './resolve.ts'
5+
import { CSS_LANGS_RE } from './utils.ts'
56
import type { PreprocessorOptions } from './options.ts'
67

78
export type PreprocessorLang = 'sass' | 'scss' | 'less' | 'styl' | 'stylus'
@@ -26,6 +27,14 @@ export function getPreprocessorLang(
2627
return PREPROCESSOR_LANGS[ext]
2728
}
2829

30+
export function getPreprocessorLangFromId(
31+
id: string,
32+
): PreprocessorLang | undefined {
33+
const match = CSS_LANGS_RE.exec(id)
34+
if (!match) return
35+
return PREPROCESSOR_LANGS[match[1]]
36+
}
37+
2938
export function compilePreprocessor(
3039
lang: PreprocessorLang,
3140
code: string,

packages/css/src/utils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export const RE_CSS: RegExp = /\.css$/
22
export const RE_INLINE: RegExp = /[?&]inline\b/
3-
export const CSS_LANGS_RE: RegExp =
4-
/\.(?:css|less|sass|scss|styl|stylus)(?:$|\?)/
3+
export const CSS_LANGS_RE: RegExp = /\.(css|less|sass|scss|styl|stylus)(?:$|\?)/
54
export const RE_CSS_INLINE: RegExp =
65
/\.(?:css|less|sass|scss|styl|stylus)\?(?:.*&)?inline\b/
76

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## index.mjs
2+
3+
```mjs
4+
import { createElementBlock, openBlock } from "vue";
5+
//#region \0/plugin-vue/export-helper
6+
var export_helper_default = (sfc, props) => {
7+
const target = sfc.__vccOpts || sfc;
8+
for (const [key, val] of props) target[key] = val;
9+
return target;
10+
};
11+
//#endregion
12+
//#region App.vue
13+
const _sfc_main = {};
14+
function _sfc_render(_ctx, _cache) {
15+
return openBlock(), createElementBlock("button", null, "Click");
16+
}
17+
var App_default = /* @__PURE__ */ export_helper_default(_sfc_main, [["render", _sfc_render]]);
18+
//#endregion
19+
export { App_default as App };
20+
21+
```
22+
23+
## style.css
24+
25+
```css
26+
button {
27+
padding: 10px 20px;
28+
border: 1px solid #ccc;
29+
border-radius: 5px;
30+
font-size: 1.2em;
31+
}
32+
33+
```

tests/issues.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,38 @@ describe('issues', () => {
197197
expect(fileMap['style.css']).toContain('.btn')
198198
})
199199

200+
test('#848', async (context) => {
201+
const Vue = (await import('unplugin-vue/rolldown')).default
202+
const { outputFiles, fileMap } = await testBuild({
203+
context,
204+
files: {
205+
'index.ts': `export { default as App } from './App.vue'`,
206+
'App.vue': `<template><button>Click</button></template>
207+
<style lang="scss">
208+
@mixin mybtn {
209+
padding: 10px 20px;
210+
border: 1px solid #ccc;
211+
border-radius: 5px;
212+
}
213+
button {
214+
@include mybtn;
215+
font-size: 1.2em;
216+
}
217+
</style>`,
218+
},
219+
options: {
220+
plugins: [Vue({ isProduction: true })],
221+
deps: { skipNodeModulesBundle: true },
222+
},
223+
})
224+
expect(outputFiles).toContain('style.css')
225+
const css = fileMap['style.css']
226+
expect(css).toContain('padding')
227+
expect(css).toContain('border-radius')
228+
expect(css).not.toContain('@mixin')
229+
expect(css).not.toContain('@include')
230+
})
231+
200232
test('#772', async (context) => {
201233
const { fileMap, outputFiles } = await testBuild({
202234
context,

0 commit comments

Comments
 (0)