diff --git a/package.json b/package.json index b36e30b00..8ba5d0de0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-loader", - "version": "17.4.2", + "version": "17.4.3", "license": "MIT", "author": "Evan You", "repository": "vuejs/vue-loader", diff --git a/src/cssModules.ts b/src/cssModules.ts index e95a27e91..9df21b609 100644 --- a/src/cssModules.ts +++ b/src/cssModules.ts @@ -6,17 +6,19 @@ export function genCSSModulesCode( needsHotReload: boolean ): string { const styleVar = `style${index}` - let code = `\nimport ${styleVar} from ${request}` + let code = `\nimport * as ${styleVar} from ${request}` // inject variable const name = typeof moduleName === 'string' ? moduleName : '$style' - code += `\ncssModules["${name}"] = ${styleVar}` + + // omit no default export error + code += `\ncssModules["${name}"] = {...${styleVar}}.default || ${styleVar}` if (needsHotReload) { code += ` if (module.hot) { module.hot.accept(${request}, () => { - cssModules["${name}"] = ${styleVar} + cssModules["${name}"] = {...${styleVar}}.default || ${styleVar} __VUE_HMR_RUNTIME__.rerender("${id}") }) }` diff --git a/src/pitcher.ts b/src/pitcher.ts index 426dd1c7d..af077345b 100644 --- a/src/pitcher.ts +++ b/src/pitcher.ts @@ -105,11 +105,16 @@ export const pitch = function () { ? [styleInlineLoaderPath] : loaders.slice(0, cssLoaderIndex + 1) const beforeLoaders = loaders.slice(cssLoaderIndex + 1) + + const { namedExport = false } = // @ts-ignore + loaders[cssLoaderIndex]?.options?.modules || {} + return genProxyModule( [...afterLoaders, stylePostLoaderPath, ...beforeLoaders], context, !!query.module || query.inline != null, - (query.lang as string) || 'css' + (query.lang as string) || 'css', + namedExport ) } } @@ -134,15 +139,17 @@ function genProxyModule( loaders: (Loader | string)[], context: LoaderContext, exportDefault = true, - lang = 'js' + lang = 'js', + cssNamedExport = false ) { const request = genRequest(loaders, lang, context) // return a proxy module which simply re-exports everything from the // actual request. Note for template blocks the compiled module has no // default export. return ( - (exportDefault ? `export { default } from ${request}; ` : ``) + - `export * from ${request}` + (exportDefault && !cssNamedExport + ? `export { default } from ${request}; ` + : ``) + `export * from ${request}` ) } diff --git a/src/pluginWebpack5.ts b/src/pluginWebpack5.ts index b2286975e..c78639db9 100644 --- a/src/pluginWebpack5.ts +++ b/src/pluginWebpack5.ts @@ -13,8 +13,7 @@ const NormalModule = require('webpack/lib/NormalModule') const BasicEffectRulePlugin = require('webpack/lib/rules/BasicEffectRulePlugin') const BasicMatcherRulePlugin = require('webpack/lib/rules/BasicMatcherRulePlugin') const UseEffectRulePlugin = require('webpack/lib/rules/UseEffectRulePlugin') -const RuleSetCompiler = - require('webpack/lib/rules/RuleSetCompiler') as RuleSetCompiler +const RuleSetCompiler = require('webpack/lib/rules/RuleSetCompiler') as RuleSetCompiler let objectMatcherRulePlugins = [] try { @@ -155,7 +154,7 @@ class VueLoaderPlugin { // get vue-loader options const vueLoaderUseIndex = vueUse.findIndex((u) => { // FIXME: this code logic is incorrect when project paths starts with `vue-loader-something` - return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader) + return /^vue-loader|^@\S+[\/\\]vue-loader/.test(u.loader) }) if (vueLoaderUseIndex < 0) { diff --git a/test/style.spec.ts b/test/style.spec.ts index f208bb16c..e88496cbc 100644 --- a/test/style.spec.ts +++ b/test/style.spec.ts @@ -171,6 +171,89 @@ test('CSS Modules', async () => { ) }) +test('CSS Modules namedExport', async () => { + const testWithIdent = async ( + localIdentName: string | undefined, + regexToMatch: RegExp + ) => { + const baseLoaders = [ + { + loader: 'style-loader', + options: { + modules: { + namedExport: true, + }, + }, + }, + { + loader: 'css-loader', + options: { + modules: { + localIdentName, + namedExport: true, + }, + }, + }, + ] + + const { window, instance } = await mockBundleAndRun({ + entry: 'css-modules.vue', + modify: (config: any) => { + config!.module!.rules = [ + { + test: /\.vue$/, + loader: 'vue-loader', + }, + { + test: /\.css$/, + use: baseLoaders, + }, + { + test: /\.stylus$/, + use: [...baseLoaders, 'stylus-loader'], + }, + ] + }, + }) + + // get local class name + const className = instance.$style.red + expect(className).toMatch(regexToMatch) + + // class name in style + let style = [].slice + .call(window.document.querySelectorAll('style')) + .map((style: any) => { + return style!.textContent + }) + .join('\n') + style = normalizeNewline(style) + expect(style).toContain('.' + className + ' {\n color: red;\n}') + + // animation name + const match = style.match(/@keyframes\s+(\S+)\s+{/) + expect(match).toHaveLength(2) + const animationName = match[1] + expect(animationName).not.toBe('fade') + expect(style).toContain('animation: ' + animationName + ' 1s;') + + // default module + pre-processor + scoped + const anotherClassName = instance.$style.red + expect(anotherClassName).toMatch(regexToMatch) + const id = 'data-v-' + genId('css-modules.vue') + expect(style).toContain('.' + anotherClassName + '[' + id + ']') + } + + // default ident + await testWithIdent(undefined, /^\w{21,}/) + + // custom ident + await testWithIdent( + '[path][name]---[local]---[hash:base64:5]', + /css-modules---red---\w{5}/ + ) +}) + test('CSS Modules Extend', async () => { const baseLoaders = [ 'style-loader', 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