1- import { isComponentFileName } from '@css-modules-kit/core' ;
1+ import type { CMKConfig , Resolver } from '@css-modules-kit/core' ;
2+ import { isComponentFileName , isCSSModuleFile , STYLES_EXPORT_NAME } from '@css-modules-kit/core' ;
23import type { Language } from '@volar/language-core' ;
34import ts from 'typescript' ;
45import { isCSSModuleScript } from '../../language-plugin.js' ;
@@ -10,13 +11,19 @@ export function getCodeFixesAtPosition(
1011 language : Language < string > ,
1112 languageService : ts . LanguageService ,
1213 project : ts . server . Project ,
14+ resolver : Resolver ,
15+ config : CMKConfig ,
1316) : ts . LanguageService [ 'getCodeFixesAtPosition' ] {
1417 // eslint-disable-next-line max-params
1518 return ( fileName , start , end , errorCodes , formatOptions , preferences ) => {
1619 const prior = Array . from (
1720 languageService . getCodeFixesAtPosition ( fileName , start , end , errorCodes , formatOptions , preferences ) ?? [ ] ,
1821 ) ;
1922
23+ if ( config . namedExports ) {
24+ convertDefaultImportsToNamespaceImports ( prior , fileName , resolver ) ;
25+ }
26+
2027 if ( isComponentFileName ( fileName ) ) {
2128 // If a user is trying to use a non-existent token (e.g. `styles.nonExistToken`), provide a code fix to add the token.
2229 if ( errorCodes . includes ( PROPERTY_DOES_NOT_EXIST_ERROR_CODE ) ) {
@@ -35,6 +42,35 @@ export function getCodeFixesAtPosition(
3542 } ;
3643}
3744
45+ function convertDefaultImportsToNamespaceImports (
46+ codeFixes : ts . CodeFixAction [ ] ,
47+ fileName : string ,
48+ resolver : Resolver ,
49+ ) : void {
50+ // Convert default imports to namespace imports for CSS modules.
51+ // For example, convert `import styles from './styles.module.css'` to `import * as styles from './styles.module.css'`.
52+ for ( const codeFix of codeFixes ) {
53+ if ( codeFix . fixName === 'import' ) {
54+ // Check if the code fix is to add an import for a CSS module.
55+ const match = codeFix . description . match ( / ^ A d d i m p o r t f r o m " ( .* ) " $ / u) ;
56+ if ( ! match ) continue ;
57+ const specifier = match [ 1 ] ! ;
58+ const resolved = resolver ( specifier , { request : fileName } ) ;
59+ if ( ! resolved || ! isCSSModuleFile ( resolved ) ) continue ;
60+
61+ // If the specifier is a CSS module, convert the import to a namespace import.
62+ for ( const change of codeFix . changes ) {
63+ for ( const textChange of change . textChanges ) {
64+ textChange . newText = textChange . newText . replace (
65+ `import ${ STYLES_EXPORT_NAME } from` ,
66+ `import * as ${ STYLES_EXPORT_NAME } from` ,
67+ ) ;
68+ }
69+ }
70+ }
71+ }
72+ }
73+
3874interface TokenConsumer {
3975 /** The token name (e.g. `foo` in `styles.foo`) */
4076 tokenName : string ;
0 commit comments