@@ -7,9 +7,11 @@ import type {
77 GlobalPropertyAccessNodeInfo ,
88 ModuleDeclarationInfo ,
99 NewModuleDeclarationInfo ,
10+ Position ,
1011} from "../autofix.js" ;
1112import { findGreatestAccessExpression , matchPropertyAccessExpression } from "../utils.js" ;
1213import parseModuleDeclaration from "../../linter/ui5Types/amdTranspiler/parseModuleDeclaration.js" ;
14+ import parseRequire from "../../linter/ui5Types/amdTranspiler/parseRequire.js" ;
1315import { getLogger } from "@ui5/logger" ;
1416
1517const log = getLogger ( "linter:autofix:NoGlobals" ) ;
@@ -47,7 +49,8 @@ export default function generateSolutionNoGlobals(
4749 } ) ;
4850 }
4951
50- const sapUiDefineCalls : ts . CallExpression [ ] = [ ] ;
52+ const moduleDeclarations = new Map < ts . CallExpression , ExistingModuleDeclarationInfo > ( ) ;
53+
5154 function visitNode ( node : ts . Node ) {
5255 for ( const nodeInfo of affectedNodesInfo ) {
5356 if ( node . getStart ( ) === nodeInfo . position . pos ) {
@@ -62,7 +65,31 @@ export default function generateSolutionNoGlobals(
6265 if ( ts . isCallExpression ( node ) &&
6366 ts . isPropertyAccessExpression ( node . expression ) ) {
6467 if ( matchPropertyAccessExpression ( node . expression , "sap.ui.define" ) ) {
65- sapUiDefineCalls . push ( node ) ;
68+ try {
69+ moduleDeclarations . set ( node , {
70+ moduleDeclaration : parseModuleDeclaration ( node . arguments , checker ) ,
71+ importRequests : new Map ( ) ,
72+ } ) ;
73+ } catch ( err ) {
74+ const errorMessage = err instanceof Error ? err . message : String ( err ) ;
75+ log . verbose ( `Failed to parse sap.ui.define ` +
76+ `call in ${ sourceFile . fileName } : ${ errorMessage } ` ) ;
77+ }
78+ } else if ( matchPropertyAccessExpression ( node . expression , "sap.ui.require" ) ) {
79+ try {
80+ const requireExpression = parseRequire ( node . arguments , checker ) ;
81+ // Only handle async require calls, not sap.ui.require probing
82+ if ( requireExpression . async ) {
83+ moduleDeclarations . set ( node , {
84+ moduleDeclaration : parseModuleDeclaration ( node . arguments , checker ) ,
85+ importRequests : new Map ( ) ,
86+ } ) ;
87+ }
88+ } catch ( err ) {
89+ const errorMessage = err instanceof Error ? err . message : String ( err ) ;
90+ log . verbose ( `Failed to parse sap.ui.require ` +
91+ `call in ${ sourceFile . fileName } : ${ errorMessage } ` ) ;
92+ }
6693 }
6794 }
6895 ts . forEachChild ( node , visitNode ) ;
@@ -74,72 +101,50 @@ export default function generateSolutionNoGlobals(
74101 }
75102 }
76103
77- const moduleDeclarations = new Map < ts . CallExpression , ExistingModuleDeclarationInfo > ( ) ;
104+ function getModuleDeclarationForPosition ( position : Position ) : ModuleDeclarationInfo | undefined {
105+ const potentialDeclarations : { declaration : ModuleDeclarationInfo ; start : number } [ ] = [ ] ;
106+ for ( const [ _ , moduleDeclarationInfo ] of moduleDeclarations ) {
107+ const { moduleDeclaration} = moduleDeclarationInfo ;
108+ const factory = "factory" in moduleDeclaration ? moduleDeclaration . factory : moduleDeclaration . callback ;
109+ if ( ! factory || factory . getStart ( ) > position . pos || factory . getEnd ( ) < position . pos ) {
110+ continue ;
111+ }
112+ potentialDeclarations . push ( {
113+ declaration : moduleDeclarationInfo ,
114+ start : factory . getStart ( ) ,
115+ } ) ;
116+ }
117+ // Sort by start position so that the declaration closest to the position is returned
118+ // This is relevant in case of nested sap.ui.require calls
119+ potentialDeclarations . sort ( ( a , b ) => a . start - b . start ) ;
120+ return potentialDeclarations . pop ( ) ?. declaration ;
121+ }
78122
79123 for ( const nodeInfo of affectedNodesInfo ) {
80124 const { moduleName, position} = nodeInfo ;
81- // Find relevant sap.ui.define call
82- let defineCall : ts . CallExpression | undefined | null ;
83- if ( sapUiDefineCalls . length === 1 ) {
84- defineCall = sapUiDefineCalls [ 0 ] ;
85- } else if ( sapUiDefineCalls . length > 1 ) {
86- for ( const sapUiDefineCall of sapUiDefineCalls ) {
87- if ( sapUiDefineCall . getStart ( ) < position . pos ) {
88- defineCall = sapUiDefineCall ;
89- }
90- }
91- }
92- if ( defineCall === undefined ) {
93- defineCall = null ;
94- }
95- let moduleDeclaration : ModuleDeclarationInfo | undefined ;
96- if ( defineCall ) {
97- moduleDeclaration = moduleDeclarations . get ( defineCall ) ;
98- if ( ! moduleDeclaration ) {
99- try {
100- moduleDeclaration = {
101- moduleDeclaration : parseModuleDeclaration ( defineCall . arguments , checker ) ,
102- importRequests : new Map ( ) ,
103- } ;
104- moduleDeclarations . set ( defineCall , moduleDeclaration ) ;
105- } catch ( err ) {
106- const errorMessage = err instanceof Error ? err . message : String ( err ) ;
107- log . verbose ( `Failed to autofix ${ moduleName } in sap.ui.define ` +
108- `call in ${ sourceFile . fileName } : ${ errorMessage } ` ) ;
109- }
110- }
111- } else {
125+ let moduleDeclarationInfo : ModuleDeclarationInfo | undefined = getModuleDeclarationForPosition ( position ) ;
126+ if ( ! moduleDeclarationInfo ) {
112127 if ( ! newModuleDeclarations . length ) {
113128 // throw new Error(`TODO: Implement handling for global access without module declaration`);
114129 }
115130 for ( const decl of newModuleDeclarations ) {
116131 if ( position . pos > decl . declareCall . getStart ( ) ) {
117- moduleDeclaration = decl ;
132+ moduleDeclarationInfo = decl ;
118133 } else {
119134 break ;
120135 }
121136 }
122137 }
123- if ( ! moduleDeclaration ) {
138+ if ( ! moduleDeclarationInfo ) {
124139 // throw new Error(`TODO: Implement handling for global access without module declaration`);
125140 }
126141
127- // Skip nodes outside of the module declaration factory
128- if ( moduleDeclaration && "moduleDeclaration" in moduleDeclaration ) {
129- const factory = moduleDeclaration . moduleDeclaration . factory ;
130- if ( factory . getStart ( ) > position . pos || factory . getEnd ( ) < position . pos ) {
131- log . silly ( `Skipping global access ${ nodeInfo . globalVariableName } ` +
132- `outside of module declaration factory` ) ;
133- continue ;
134- }
135- }
136-
137- if ( moduleDeclaration && ! moduleDeclaration . importRequests . has ( moduleName ) ) {
138- moduleDeclaration . importRequests . set ( moduleName , {
142+ if ( moduleDeclarationInfo && ! moduleDeclarationInfo . importRequests . has ( moduleName ) ) {
143+ moduleDeclarationInfo . importRequests . set ( moduleName , {
139144 nodeInfos : [ ] ,
140145 } ) ;
141146 }
142- moduleDeclaration ?. importRequests . get ( moduleName ) ! . nodeInfos . push ( nodeInfo ) ;
147+ moduleDeclarationInfo ?. importRequests . get ( moduleName ) ! . nodeInfos . push ( nodeInfo ) ;
143148 }
144149
145150 return moduleDeclarations ;
0 commit comments