Skip to content

Commit a6b387b

Browse files
committed
feat(cssModules ): namedExport support
For v17 Support css-loader : `options.modules.namedexport` https://github.com/webpack-contrib/css-loader/tree/v4.3.0#namedexport https://github.com/webpack-contrib/css-loader#namedexport feat(cssModules ): namedExport support
1 parent 6986365 commit a6b387b

File tree

5 files changed

+102
-11
lines changed

5 files changed

+102
-11
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-loader",
3-
"version": "17.4.2",
3+
"version": "17.4.3",
44
"license": "MIT",
55
"author": "Evan You",
66
"repository": "vuejs/vue-loader",

src/cssModules.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ export function genCSSModulesCode(
66
needsHotReload: boolean
77
): string {
88
const styleVar = `style${index}`
9-
let code = `\nimport ${styleVar} from ${request}`
9+
let code = `\nimport * as ${styleVar} from ${request}`
1010

1111
// inject variable
1212
const name = typeof moduleName === 'string' ? moduleName : '$style'
13-
code += `\ncssModules["${name}"] = ${styleVar}`
13+
14+
// omit no default export error
15+
code += `\ncssModules["${name}"] = {...${styleVar}}.default || ${styleVar}`
1416

1517
if (needsHotReload) {
1618
code += `
1719
if (module.hot) {
1820
module.hot.accept(${request}, () => {
19-
cssModules["${name}"] = ${styleVar}
21+
cssModules["${name}"] = {...${styleVar}}.default || ${styleVar}
2022
__VUE_HMR_RUNTIME__.rerender("${id}")
2123
})
2224
}`

src/pitcher.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,16 @@ export const pitch = function () {
105105
? [styleInlineLoaderPath]
106106
: loaders.slice(0, cssLoaderIndex + 1)
107107
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
108+
109+
const { namedExport = false } = // @ts-ignore
110+
loaders[cssLoaderIndex]?.options?.modules || {}
111+
108112
return genProxyModule(
109113
[...afterLoaders, stylePostLoaderPath, ...beforeLoaders],
110114
context,
111115
!!query.module || query.inline != null,
112-
(query.lang as string) || 'css'
116+
(query.lang as string) || 'css',
117+
namedExport
113118
)
114119
}
115120
}
@@ -134,15 +139,17 @@ function genProxyModule(
134139
loaders: (Loader | string)[],
135140
context: LoaderContext<VueLoaderOptions>,
136141
exportDefault = true,
137-
lang = 'js'
142+
lang = 'js',
143+
cssNamedExport = false
138144
) {
139145
const request = genRequest(loaders, lang, context)
140146
// return a proxy module which simply re-exports everything from the
141147
// actual request. Note for template blocks the compiled module has no
142148
// default export.
143149
return (
144-
(exportDefault ? `export { default } from ${request}; ` : ``) +
145-
`export * from ${request}`
150+
(exportDefault && !cssNamedExport
151+
? `export { default } from ${request}; `
152+
: ``) + `export * from ${request}`
146153
)
147154
}
148155

src/pluginWebpack5.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ const NormalModule = require('webpack/lib/NormalModule')
1313
const BasicEffectRulePlugin = require('webpack/lib/rules/BasicEffectRulePlugin')
1414
const BasicMatcherRulePlugin = require('webpack/lib/rules/BasicMatcherRulePlugin')
1515
const UseEffectRulePlugin = require('webpack/lib/rules/UseEffectRulePlugin')
16-
const RuleSetCompiler =
17-
require('webpack/lib/rules/RuleSetCompiler') as RuleSetCompiler
16+
const RuleSetCompiler = require('webpack/lib/rules/RuleSetCompiler') as RuleSetCompiler
1817

1918
let objectMatcherRulePlugins = []
2019
try {
@@ -155,7 +154,7 @@ class VueLoaderPlugin {
155154
// get vue-loader options
156155
const vueLoaderUseIndex = vueUse.findIndex((u) => {
157156
// FIXME: this code logic is incorrect when project paths starts with `vue-loader-something`
158-
return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
157+
return /^vue-loader|^@\S+[\/\\]vue-loader/.test(u.loader)
159158
})
160159

161160
if (vueLoaderUseIndex < 0) {

test/style.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,89 @@ test('CSS Modules', async () => {
171171
)
172172
})
173173

174+
test('CSS Modules namedExport', async () => {
175+
const testWithIdent = async (
176+
localIdentName: string | undefined,
177+
regexToMatch: RegExp
178+
) => {
179+
const baseLoaders = [
180+
{
181+
loader: 'style-loader',
182+
options: {
183+
modules: {
184+
namedExport: true,
185+
},
186+
},
187+
},
188+
{
189+
loader: 'css-loader',
190+
options: {
191+
modules: {
192+
localIdentName,
193+
namedExport: true,
194+
},
195+
},
196+
},
197+
]
198+
199+
const { window, instance } = await mockBundleAndRun({
200+
entry: 'css-modules.vue',
201+
modify: (config: any) => {
202+
config!.module!.rules = [
203+
{
204+
test: /\.vue$/,
205+
loader: 'vue-loader',
206+
},
207+
{
208+
test: /\.css$/,
209+
use: baseLoaders,
210+
},
211+
{
212+
test: /\.stylus$/,
213+
use: [...baseLoaders, 'stylus-loader'],
214+
},
215+
]
216+
},
217+
})
218+
219+
// get local class name
220+
const className = instance.$style.red
221+
expect(className).toMatch(regexToMatch)
222+
223+
// class name in style
224+
let style = [].slice
225+
.call(window.document.querySelectorAll('style'))
226+
.map((style: any) => {
227+
return style!.textContent
228+
})
229+
.join('\n')
230+
style = normalizeNewline(style)
231+
expect(style).toContain('.' + className + ' {\n color: red;\n}')
232+
233+
// animation name
234+
const match = style.match(/@keyframes\s+(\S+)\s+{/)
235+
expect(match).toHaveLength(2)
236+
const animationName = match[1]
237+
expect(animationName).not.toBe('fade')
238+
expect(style).toContain('animation: ' + animationName + ' 1s;')
239+
240+
// default module + pre-processor + scoped
241+
const anotherClassName = instance.$style.red
242+
expect(anotherClassName).toMatch(regexToMatch)
243+
const id = 'data-v-' + genId('css-modules.vue')
244+
expect(style).toContain('.' + anotherClassName + '[' + id + ']')
245+
}
246+
247+
// default ident
248+
await testWithIdent(undefined, /^\w{21,}/)
249+
250+
// custom ident
251+
await testWithIdent(
252+
'[path][name]---[local]---[hash:base64:5]',
253+
/css-modules---red---\w{5}/
254+
)
255+
})
256+
174257
test('CSS Modules Extend', async () => {
175258
const baseLoaders = [
176259
'style-loader',

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy