diff --git a/docs/config/index.md b/docs/config/index.md index 040799ce7a0a0d..7fff83874adfd2 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -260,6 +260,7 @@ export default defineConfig(async ({ command, mode }) => { | 'dashes' | 'dashesOnly' | null + isCssModule?: string | RegExp | ((id: string) => boolean) } ``` diff --git a/docs/guide/features.md b/docs/guide/features.md index 2053a087517eac..7868a0cb13be48 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -159,7 +159,7 @@ If the project contains valid PostCSS config (any format supported by [postcss-l ### CSS Modules -Any CSS file ending with `.module.css` is considered a [CSS modules file](https://github.com/css-modules/css-modules). Importing such a file will return the corresponding module object: +Any CSS file ending with `.module.css` is considered a [CSS modules file](https://github.com/css-modules/css-modules). You can change this behavior using the [`css.modules.isCssModule` option](/config/#css-modules). Importing such a file will return the corresponding module object: ```css /* example.module.css */ diff --git a/packages/playground/css/custom-module.css b/packages/playground/css/custom-module.css new file mode 100644 index 00000000000000..2bb50b4ffcf006 --- /dev/null +++ b/packages/playground/css/custom-module.css @@ -0,0 +1,3 @@ +.apply-color { + color: olive; +} diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index 7a79bb1629f989..460de10f0f0351 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -67,6 +67,12 @@

CSS

Imported CSS module:


 
+  

+ use CSS modules not just .module.xxx: this should be olive +

+

Imported custom filename with CSS module :

+

+
   

CSS modules w/ SASS: this should be orangered

Imported SASS module:


diff --git a/packages/playground/css/main.js b/packages/playground/css/main.js
index 24a278c8687940..ea312453fcfda5 100644
--- a/packages/playground/css/main.js
+++ b/packages/playground/css/main.js
@@ -16,6 +16,12 @@ import mod from './mod.module.css'
 document.querySelector('.modules').classList.add(mod['apply-color'])
 text('.modules-code', JSON.stringify(mod, null, 2))
 
+import customMod from './custom-module.css'
+document
+  .querySelector('.custom-modules')
+  .classList.add(customMod['apply-color'])
+text('.custom-modules-code', JSON.stringify(customMod, null, 2))
+
 import sassMod from './mod.module.scss'
 document.querySelector('.modules-sass').classList.add(sassMod['apply-color'])
 text('.modules-sass-code', JSON.stringify(sassMod, null, 2))
@@ -50,6 +56,13 @@ if (import.meta.hot) {
     text('.modules-code', JSON.stringify(newMod.default, null, 2))
   })
 
+  import.meta.hot.accept('./custom-module.css', (newMod) => {
+    const list = document.querySelector('.custom-modules').classList
+    list.remove(mod.applyColor)
+    list.add(newMod.applyColor)
+    text('.custom-modules-code', JSON.stringify(newMod.default, null, 2))
+  })
+
   import.meta.hot.accept('./mod.module.scss', (newMod) => {
     const list = document.querySelector('.modules-sass').classList
     list.remove(mod.applyColor)
diff --git a/packages/playground/css/vite.config.js b/packages/playground/css/vite.config.js
index e4dc8d5a9f265f..eca317dadb273e 100644
--- a/packages/playground/css/vite.config.js
+++ b/packages/playground/css/vite.config.js
@@ -13,7 +13,11 @@ module.exports = {
   },
   css: {
     modules: {
-      generateScopedName: '[name]__[local]___[hash:base64:5]'
+      generateScopedName: '[name]__[local]___[hash:base64:5]',
+
+      // string: '.module'
+      // function: id => id.includes('.module')
+      isCssModule: /custom-module|\.module/
 
       // example of how getJSON can be used to generate
       // typescript typings for css modules class names
diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts
index 8f7dbdae363fe8..7c05cf9423a63c 100644
--- a/packages/vite/src/node/plugins/css.ts
+++ b/packages/vite/src/node/plugins/css.ts
@@ -82,6 +82,7 @@ export interface CSSModulesOptions {
     | 'dashes'
     | 'dashesOnly'
     | null
+  isCssModule?: string | RegExp | ((id: string) => boolean)
 }
 
 const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)`
@@ -590,6 +591,25 @@ function getCssResolversKeys(
   return Object.keys(resolvers) as unknown as Array
 }
 
+function isCssModule(
+  modulesOptions: CSSOptions['modules'],
+  id: string
+): boolean {
+  if (modulesOptions && modulesOptions.isCssModule) {
+    const { isCssModule } = modulesOptions
+    if (typeof isCssModule === 'function') {
+      return isCssModule(id)
+    } else if (
+      typeof isCssModule === 'string' ||
+      isCssModule instanceof RegExp
+    ) {
+      const regexp = new RegExp(isCssModule)
+      return regexp.test(id)
+    }
+  }
+  return modulesOptions !== false && cssModuleRE.test(id)
+}
+
 async function compileCSS(
   id: string,
   code: string,
@@ -605,7 +625,7 @@ async function compileCSS(
   deps?: Set
 }> {
   const { modules: modulesOptions, preprocessorOptions } = config.css || {}
-  const isModule = modulesOptions !== false && cssModuleRE.test(id)
+  const isModule = isCssModule(modulesOptions, id)
   // although at serve time it can work without processing, we do need to
   // crawl them in order to register watch dependencies.
   const needInlineImport = code.includes('@import')