Skip to content

Commit d3ca9f8

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
1 parent b96af78 commit d3ca9f8

File tree

3 files changed

+99
-7
lines changed

3 files changed

+99
-7
lines changed

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
@@ -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

test/style.spec.ts

Lines changed: 83 additions & 0 deletions
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)
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