@@ -13,6 +13,7 @@ import fs from 'node:fs';
1313import path from 'node:path' ;
1414import type { CSSModules , Rspack } from '@rsbuild/core' ;
1515import LineDiff from 'line-diff' ;
16+ import type { PluginOptions } from './index.js' ;
1617
1718export type CssLoaderModules =
1819 | boolean
@@ -108,13 +109,17 @@ const cssModuleToNamedExports = (cssModuleKeys: string[]) => {
108109 . join ( '\n' ) ;
109110} ;
110111
111- const cssModuleToInterface = ( cssModulesKeys : string [ ] ) => {
112+ const cssModuleToInterface = (
113+ cssModulesKeys : string [ ] ,
114+ looseTyping : boolean ,
115+ ) => {
116+ const spaces = ' ' ;
112117 const interfaceFields = cssModulesKeys
113118 . sort ( )
114- . map ( ( key ) => ` ${ wrapQuotes ( key ) } : string;` )
119+ . map ( ( key ) => `${ spaces } ${ wrapQuotes ( key ) } : string;` )
115120 . join ( '\n' ) ;
116121
117- return `interface CssExports {\n${ interfaceFields } \n}` ;
122+ return `interface CssExports {\n${ interfaceFields } \n${ looseTyping ? ` ${ spaces } [key: string]: string;\n` : '' } }` ;
118123} ;
119124
120125const filenameToTypingsFilename = ( filename : string ) => {
@@ -194,7 +199,7 @@ const getCSSModulesKeys = (content: string, namedExport: boolean): string[] => {
194199 return Array . from ( keys ) ;
195200} ;
196201
197- function codegen ( keys : string [ ] , namedExport : boolean ) {
202+ function codegen ( keys : string [ ] , namedExport : boolean , looseTyping : boolean ) {
198203 const bannerMessage =
199204 '// This file is automatically generated.\n// Please do not change this file!' ;
200205 if ( namedExport ) {
@@ -203,21 +208,27 @@ function codegen(keys: string[], namedExport: boolean) {
203208
204209 const cssModuleExport =
205210 'declare const cssExports: CssExports;\nexport default cssExports;\n' ;
206- return `${ bannerMessage } \n${ cssModuleToInterface ( keys ) } \n${ cssModuleExport } ` ;
211+ return `${ bannerMessage } \n${ cssModuleToInterface ( keys , looseTyping ) } \n${ cssModuleExport } ` ;
207212}
208213
209214export default function (
210- this : Rspack . LoaderContext < {
211- mode : string ;
212- modules : CssLoaderModules ;
213- } > ,
215+ this : Rspack . LoaderContext <
216+ {
217+ mode : string ;
218+ modules : CssLoaderModules ;
219+ } & PluginOptions
220+ > ,
214221 content : string ,
215222 ...rest : any [ ]
216223) : void {
217224 const { failed, success } = makeDoneHandlers ( this . async ( ) , content , rest ) ;
218225
219226 const { resourcePath, resourceQuery, resourceFragment } = this ;
220- const { mode = 'emit' , modules = true } = this . getOptions ( ) || { } ;
227+ const {
228+ mode = 'emit' ,
229+ modules = true ,
230+ looseTyping = false ,
231+ } = this . getOptions ( ) || { } ;
221232
222233 if ( ! validModes . includes ( mode ) ) {
223234 failed ( new Error ( `Invalid mode option: ${ mode } ` ) ) ;
@@ -237,7 +248,7 @@ export default function (
237248
238249 const namedExport = isNamedExport ( modules ) ;
239250 const cssModulesKeys = getCSSModulesKeys ( content , namedExport ) ;
240- const cssModulesCode = codegen ( cssModulesKeys , namedExport ) ;
251+ const cssModulesCode = codegen ( cssModulesKeys , namedExport , looseTyping ) ;
241252
242253 if ( mode === 'verify' ) {
243254 read ( ( err , fileContents ) => {
0 commit comments