Skip to content

Commit 30d2938

Browse files
committedMay 30, 2023
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
1 parent 6ad8056 commit 30d2938

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed
 

‎src/cssModules.ts

+5-3
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

+11-4
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,15 @@ export const pitch = function () {
6868
? [styleInlineLoaderPath]
6969
: loaders.slice(0, cssLoaderIndex + 1)
7070
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
71+
72+
const { namedExport = false } = // @ts-ignore
73+
loaders[cssLoaderIndex]?.options?.modules || {}
74+
7175
return genProxyModule(
7276
[...afterLoaders, stylePostLoaderPath, ...beforeLoaders],
7377
context,
74-
!!query.module || query.inline != null
78+
!!query.module || query.inline != null,
79+
namedExport
7580
)
7681
}
7782
}
@@ -90,15 +95,17 @@ export const pitch = function () {
9095
function genProxyModule(
9196
loaders: (Loader | string)[],
9297
context: LoaderContext<VueLoaderOptions>,
93-
exportDefault = true
98+
exportDefault = true,
99+
cssNamedExport = false
94100
) {
95101
const request = genRequest(loaders, context)
96102
// return a proxy module which simply re-exports everything from the
97103
// actual request. Note for template blocks the compiled module has no
98104
// default export.
99105
return (
100-
(exportDefault ? `export { default } from ${request}; ` : ``) +
101-
`export * from ${request}`
106+
(exportDefault && !cssNamedExport
107+
? `export { default } from ${request}; `
108+
: ``) + `export * from ${request}`
102109
)
103110
}
104111

‎src/pluginWebpack5.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class VueLoaderPlugin {
154154
// get vue-loader options
155155
const vueLoaderUseIndex = vueUse.findIndex((u) => {
156156
// FIXME: this code logic is incorrect when project paths starts with `vue-loader-something`
157-
return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
157+
return /^vue-loader|^@\S+\/vue-loader/.test(u.loader)
158158
})
159159

160160
if (vueLoaderUseIndex < 0) {

‎test/style.spec.ts

+83
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,89 @@ test('CSS Modules', async () => {
161161
)
162162
})
163163

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

0 commit comments

Comments
 (0)
Failed to load comments.