Skip to content

Commit e88dbe9

Browse files
committed
fix: avoid to generate empty css chunk files
backports #1464
1 parent fa79114 commit e88dbe9

7 files changed

+193
-46
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"source-map": "^0.6.1",
6666
"style-loader": "^2.0.0",
6767
"stylus": "^0.54.7",
68-
"stylus-loader": "^3.0.2",
68+
"stylus-loader": "^4.1.1",
6969
"sugarss": "^3.0.1",
7070
"ts-jest": "^26.2.0",
7171
"ts-loader": "^8.0.6",

src/index.ts

+27-24
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,35 @@ export default function loader(
173173
// styles
174174
let stylesCode = ``
175175
let hasCSSModules = false
176+
const nonWhitespaceRE = /\S+/
176177
if (descriptor.styles.length) {
177-
descriptor.styles.forEach((style: SFCStyleBlock, i: number) => {
178-
const src = style.src || resourcePath
179-
const attrsQuery = attrsToQuery(style.attrs, 'css')
180-
// make sure to only pass id when necessary so that we don't inject
181-
// duplicate tags when multiple components import the same css file
182-
const idQuery = style.scoped ? `&id=${id}` : ``
183-
const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${resourceQuery}`
184-
const styleRequest = stringifyRequest(src + query)
185-
if (style.module) {
186-
if (!hasCSSModules) {
187-
stylesCode += `\nconst cssModules = script.__cssModules = {}`
188-
hasCSSModules = true
178+
descriptor.styles
179+
.filter((style) => style.src || nonWhitespaceRE.test(style.content))
180+
.forEach((style: SFCStyleBlock, i: number) => {
181+
const src = style.src || resourcePath
182+
const attrsQuery = attrsToQuery(style.attrs, 'css')
183+
// make sure to only pass id when necessary so that we don't inject
184+
// duplicate tags when multiple components import the same css file
185+
const idQuery = style.scoped ? `&id=${id}` : ``
186+
const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${resourceQuery}`
187+
const styleRequest = stringifyRequest(src + query)
188+
if (style.module) {
189+
if (!hasCSSModules) {
190+
stylesCode += `\nconst cssModules = script.__cssModules = {}`
191+
hasCSSModules = true
192+
}
193+
stylesCode += genCSSModulesCode(
194+
id,
195+
i,
196+
styleRequest,
197+
style.module,
198+
needsHotReload
199+
)
200+
} else {
201+
stylesCode += `\nimport ${styleRequest}`
189202
}
190-
stylesCode += genCSSModulesCode(
191-
id,
192-
i,
193-
styleRequest,
194-
style.module,
195-
needsHotReload
196-
)
197-
} else {
198-
stylesCode += `\nimport ${styleRequest}`
199-
}
200-
// TODO SSR critical CSS collection
201-
})
203+
// TODO SSR critical CSS collection
204+
})
202205
}
203206

204207
let code = [

test/advanced.spec.ts

+63-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { SourceMapConsumer } from 'source-map'
22
import { fs as mfs } from 'memfs'
3-
import { bundle, mockBundleAndRun } from './utils'
3+
import { bundle, mockBundleAndRun, normalizeNewline, genId } from './utils'
4+
5+
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
46

57
test('support chaining with other loaders', async () => {
68
const { componentModule } = await mockBundleAndRun({
@@ -83,9 +85,67 @@ test.skip('source map', async () => {
8385
expect(pos.line).toBe(9)
8486
})
8587

86-
test.todo('extract CSS')
88+
test('extract CSS', async () => {
89+
await bundle({
90+
entry: 'extract-css.vue',
91+
modify: (config: any) => {
92+
config.module.rules = [
93+
{
94+
test: /\.vue$/,
95+
use: 'vue-loader',
96+
},
97+
{
98+
test: /\.css$/,
99+
use: [MiniCssExtractPlugin.loader, 'css-loader'],
100+
},
101+
{
102+
test: /\.stylus$/,
103+
use: [MiniCssExtractPlugin.loader, 'css-loader', 'stylus-loader'],
104+
},
105+
]
106+
},
107+
plugins: [
108+
new MiniCssExtractPlugin({
109+
filename: 'test.output.css',
110+
}),
111+
],
112+
})
113+
114+
const css = normalizeNewline(mfs.readFileSync('/test.output.css').toString())
115+
const id = `data-v-${genId('extract-css.vue')}`
116+
expect(css).toContain(`h1 {\n color: #f00;\n}`)
117+
// extract + scoped
118+
expect(css).toContain(`h2[${id}] {\n color: green;\n}`)
119+
})
87120

88-
test.todo('extract CSS with code spliting')
121+
// #1464
122+
test('extract CSS with code spliting', async () => {
123+
await bundle({
124+
entry: 'extract-css-chunks.vue',
125+
modify: (config: any) => {
126+
config.module.rules = [
127+
{
128+
test: /\.vue$/,
129+
use: 'vue-loader',
130+
},
131+
{
132+
test: /\.css$/,
133+
use: [MiniCssExtractPlugin.loader, 'css-loader'],
134+
},
135+
]
136+
},
137+
plugins: [
138+
new MiniCssExtractPlugin({
139+
filename: 'test.output.css',
140+
}),
141+
],
142+
})
143+
144+
const css = normalizeNewline(mfs.readFileSync('/test.output.css').toString())
145+
expect(css).toContain(`h1 {\n color: red;\n}`)
146+
expect(mfs.existsSync('/empty.test.output.css')).toBe(false)
147+
expect(mfs.existsSync('/basic.test.output.css')).toBe(true)
148+
})
89149

90150
test.todo('support rules with oneOf')
91151

test/fixtures/empty-style.vue

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<template>
2+
<h1>empty style</h1>
3+
</template>
4+
5+
<style>
6+
</style>

test/fixtures/extract-css-chunks.vue

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<template>
2+
<div>
3+
<basic></basic>
4+
<empty-style></empty-style>
5+
</div>
6+
</template>
7+
8+
<script>
9+
export default {
10+
components: {
11+
Basic: () => import(/* webpackChunkName: "basic" */ './basic.vue'),
12+
EmptyStyle: () => import(/* webpackChunkName: "empty" */ './empty-style.vue')
13+
}
14+
}
15+
</script>
16+
17+
<style>
18+
h1 {
19+
color: red;
20+
}
21+
</style>

test/fixtures/extract-css.vue

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<style lang="stylus">
2+
h1
3+
color red
4+
</style>
5+
6+
<style scoped>
7+
h2 {
8+
color: green;
9+
}
10+
</style>

yarn.lock

+65-18
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,27 @@
10521052
"@types/yargs" "^15.0.0"
10531053
chalk "^4.0.0"
10541054

1055+
"@nodelib/[email protected]":
1056+
version "2.1.3"
1057+
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
1058+
integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
1059+
dependencies:
1060+
"@nodelib/fs.stat" "2.0.3"
1061+
run-parallel "^1.1.9"
1062+
1063+
"@nodelib/[email protected]", "@nodelib/fs.stat@^2.0.2":
1064+
version "2.0.3"
1065+
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
1066+
integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
1067+
1068+
"@nodelib/fs.walk@^1.2.3":
1069+
version "1.2.4"
1070+
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
1071+
integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
1072+
dependencies:
1073+
"@nodelib/fs.scandir" "2.1.3"
1074+
fastq "^1.6.0"
1075+
10551076
"@sinonjs/commons@^1.7.0":
10561077
version "1.8.1"
10571078
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
@@ -3489,6 +3510,18 @@ fast-deep-equal@^3.1.1:
34893510
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
34903511
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
34913512

3513+
fast-glob@^3.2.4:
3514+
version "3.2.4"
3515+
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
3516+
integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
3517+
dependencies:
3518+
"@nodelib/fs.stat" "^2.0.2"
3519+
"@nodelib/fs.walk" "^1.2.3"
3520+
glob-parent "^5.1.0"
3521+
merge2 "^1.3.0"
3522+
micromatch "^4.0.2"
3523+
picomatch "^2.2.1"
3524+
34923525
[email protected], fast-json-stable-stringify@^2.0.0:
34933526
version "2.1.0"
34943527
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -3499,6 +3532,13 @@ fast-levenshtein@~2.0.6:
34993532
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
35003533
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
35013534

3535+
fastq@^1.6.0:
3536+
version "1.9.0"
3537+
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947"
3538+
integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==
3539+
dependencies:
3540+
reusify "^1.0.4"
3541+
35023542
faye-websocket@^0.10.0:
35033543
version "0.10.0"
35043544
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -3779,7 +3819,7 @@ glob-parent@^3.1.0:
37793819
is-glob "^3.1.0"
37803820
path-dirname "^1.0.0"
37813821

3782-
glob-parent@~5.1.0:
3822+
glob-parent@^5.1.0, glob-parent@~5.1.0:
37833823
version "5.1.1"
37843824
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
37853825
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
@@ -5229,11 +5269,6 @@ lodash.camelcase@^4.3.0:
52295269
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
52305270
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
52315271

5232-
lodash.clonedeep@^4.5.0:
5233-
version "4.5.0"
5234-
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
5235-
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
5236-
52375272
52385273
version "4.1.2"
52395274
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -5411,6 +5446,11 @@ merge-stream@^2.0.0:
54115446
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
54125447
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
54135448

5449+
merge2@^1.3.0:
5450+
version "1.4.1"
5451+
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
5452+
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
5453+
54145454
methods@~1.1.2:
54155455
version "1.1.2"
54165456
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -6820,6 +6860,11 @@ retry@^0.12.0:
68206860
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
68216861
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
68226862

6863+
reusify@^1.0.4:
6864+
version "1.0.4"
6865+
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
6866+
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
6867+
68236868
right-align@^0.1.1:
68246869
version "0.1.3"
68256870
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@@ -6854,6 +6899,11 @@ rsvp@^4.8.4:
68546899
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
68556900
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
68566901

6902+
run-parallel@^1.1.9:
6903+
version "1.1.10"
6904+
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef"
6905+
integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==
6906+
68576907
run-queue@^1.0.0, run-queue@^1.0.3:
68586908
version "1.0.3"
68596909
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
@@ -7513,14 +7563,16 @@ style-loader@^2.0.0:
75137563
loader-utils "^2.0.0"
75147564
schema-utils "^3.0.0"
75157565

7516-
stylus-loader@^3.0.2:
7517-
version "3.0.2"
7518-
resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6"
7519-
integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==
7566+
stylus-loader@^4.1.1:
7567+
version "4.1.1"
7568+
resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.1.1.tgz#0e94f5d6274932a2dad054d1a736b32146ac7a99"
7569+
integrity sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA==
75207570
dependencies:
7521-
loader-utils "^1.0.2"
7522-
lodash.clonedeep "^4.5.0"
7523-
when "~3.6.x"
7571+
fast-glob "^3.2.4"
7572+
klona "^2.0.4"
7573+
loader-utils "^2.0.0"
7574+
normalize-path "^3.0.0"
7575+
schema-utils "^3.0.0"
75247576

75257577
stylus@^0.54.7:
75267578
version "0.54.8"
@@ -8275,11 +8327,6 @@ whatwg-url@^8.0.0:
82758327
tr46 "^2.0.2"
82768328
webidl-conversions "^6.1.0"
82778329

8278-
when@~3.6.x:
8279-
version "3.6.4"
8280-
resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
8281-
integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=
8282-
82838330
which-module@^2.0.0:
82848331
version "2.0.0"
82858332
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"

0 commit comments

Comments
 (0)