11import type { CMKConfig , Resolver } from '@css-modules-kit/core' ;
2- import { isComponentFileName , isCSSModuleFile } from '@css-modules-kit/core' ;
2+ import { isCSSModuleFile } from '@css-modules-kit/core' ;
33import type { Language } from '@volar/language-core' ;
44import ts from 'typescript' ;
5- import { isCSSModuleScript } from '../../language-plugin.js' ;
65import { convertDefaultImportsToNamespaceImports , createPreferencesForCompletion } from '../../util.js' ;
76
87// ref: https://github.com/microsoft/TypeScript/blob/220706eb0320ff46fad8bf80a5e99db624ee7dfb/src/compiler/diagnosticMessages.json
98export const CANNOT_FIND_NAME_ERROR_CODE = 2304 ;
10- export const PROPERTY_DOES_NOT_EXIST_ERROR_CODE = 2339 ;
9+ export const PROPERTY_DOES_NOT_EXIST_ERROR_CODES : [ number , number ] = [ 2339 , 2551 ] ;
1110
1211export function getCodeFixesAtPosition (
1312 language : Language < string > ,
@@ -34,17 +33,15 @@ export function getCodeFixesAtPosition(
3433 excludeNamedImports ( prior , fileName , resolver ) ;
3534 }
3635
37- if ( isComponentFileName ( fileName ) ) {
38- // If a user is trying to use a non-existent token (e.g. `styles.nonExistToken`), provide a code fix to add the token.
39- if ( errorCodes . includes ( PROPERTY_DOES_NOT_EXIST_ERROR_CODE ) ) {
40- const tokenConsumer = getTokenConsumerAtPosition ( fileName , start , language , languageService , project ) ;
41- if ( tokenConsumer ) {
42- prior . push ( {
43- fixName : 'fixMissingCSSRule' ,
44- description : `Add missing CSS rule '.${ tokenConsumer . tokenName } '` ,
45- changes : [ createInsertRuleFileChange ( tokenConsumer . from , tokenConsumer . tokenName , language ) ] ,
46- } ) ;
47- }
36+ // If a user is trying to use a non-existent token (e.g. `styles.nonExistToken`), provide a code fix to add the token.
37+ if ( errorCodes . some ( ( errorCode ) => PROPERTY_DOES_NOT_EXIST_ERROR_CODES . includes ( errorCode ) ) ) {
38+ const tokenConsumer = getTokenConsumerAtPosition ( fileName , start , languageService , project , config ) ;
39+ if ( tokenConsumer ) {
40+ prior . push ( {
41+ fixName : 'fixMissingCSSRule' ,
42+ description : `Add missing CSS rule '.${ tokenConsumer . tokenName } '` ,
43+ changes : [ createInsertRuleFileChange ( tokenConsumer . from , tokenConsumer . tokenName , language ) ] ,
44+ } ) ;
4845 }
4946 }
5047
@@ -85,9 +82,9 @@ interface TokenConsumer {
8582function getTokenConsumerAtPosition (
8683 fileName : string ,
8784 position : number ,
88- language : Language < string > ,
8985 languageService : ts . LanguageService ,
9086 project : ts . server . Project ,
87+ config : CMKConfig ,
9188) : TokenConsumer | undefined {
9289 const sourceFile = project . getSourceFile ( project . projectService . toPath ( fileName ) ) ;
9390 if ( ! sourceFile ) return undefined ;
@@ -99,11 +96,19 @@ function getTokenConsumerAtPosition(
9996 // `expression` is the expression of the property access expression (e.g. `styles` in `styles.foo`).
10097 const expression = propertyAccessExpression . expression ;
10198
102- const definitions = languageService . getDefinitionAtPosition ( fileName , expression . getStart ( ) ) ;
103- if ( definitions && definitions [ 0 ] ) {
104- const script = language . scripts . get ( definitions [ 0 ] . fileName ) ;
105- if ( isCSSModuleScript ( script ) ) {
106- return { tokenName : propertyAccessExpression . name . text , from : definitions [ 0 ] . fileName } ;
99+ let [ definition ] = languageService . getDefinitionAtPosition ( fileName , expression . getStart ( ) ) ?? [ ] ;
100+ if ( ! definition ) return undefined ;
101+
102+ // `definition` is may be `styles` definition in CSS Modules file.
103+ if ( isCSSModuleFile ( definition . fileName ) ) {
104+ return { tokenName : propertyAccessExpression . name . text , from : definition . fileName } ;
105+ } else if ( config . namedExports ) {
106+ // If namespaced import is used, it may be a definition in a component file
107+ // (e.g. the `styles` of `import * as styles from './a.module.css'`).
108+ // In that case, we need to call `getDefinitionAtPosition` again to get the definition in CSS module file.
109+ [ definition ] = languageService . getDefinitionAtPosition ( definition . fileName , definition . textSpan . start ) ?? [ ] ;
110+ if ( definition && isCSSModuleFile ( definition . fileName ) ) {
111+ return { tokenName : propertyAccessExpression . name . text , from : definition . fileName } ;
107112 }
108113 }
109114 return undefined ;
0 commit comments