@@ -7,9 +7,11 @@ import type {
7
7
GlobalPropertyAccessNodeInfo ,
8
8
ModuleDeclarationInfo ,
9
9
NewModuleDeclarationInfo ,
10
+ Position ,
10
11
} from "../autofix.js" ;
11
12
import { findGreatestAccessExpression , matchPropertyAccessExpression } from "../utils.js" ;
12
13
import parseModuleDeclaration from "../../linter/ui5Types/amdTranspiler/parseModuleDeclaration.js" ;
14
+ import parseRequire from "../../linter/ui5Types/amdTranspiler/parseRequire.js" ;
13
15
import { getLogger } from "@ui5/logger" ;
14
16
15
17
const log = getLogger ( "linter:autofix:NoGlobals" ) ;
@@ -47,7 +49,8 @@ export default function generateSolutionNoGlobals(
47
49
} ) ;
48
50
}
49
51
50
- const sapUiDefineCalls : ts . CallExpression [ ] = [ ] ;
52
+ const moduleDeclarations = new Map < ts . CallExpression , ExistingModuleDeclarationInfo > ( ) ;
53
+
51
54
function visitNode ( node : ts . Node ) {
52
55
for ( const nodeInfo of affectedNodesInfo ) {
53
56
if ( node . getStart ( ) === nodeInfo . position . pos ) {
@@ -62,7 +65,31 @@ export default function generateSolutionNoGlobals(
62
65
if ( ts . isCallExpression ( node ) &&
63
66
ts . isPropertyAccessExpression ( node . expression ) ) {
64
67
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
+ }
66
93
}
67
94
}
68
95
ts . forEachChild ( node , visitNode ) ;
@@ -74,72 +101,50 @@ export default function generateSolutionNoGlobals(
74
101
}
75
102
}
76
103
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
+ }
78
122
79
123
for ( const nodeInfo of affectedNodesInfo ) {
80
124
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 ) {
112
127
if ( ! newModuleDeclarations . length ) {
113
128
// throw new Error(`TODO: Implement handling for global access without module declaration`);
114
129
}
115
130
for ( const decl of newModuleDeclarations ) {
116
131
if ( position . pos > decl . declareCall . getStart ( ) ) {
117
- moduleDeclaration = decl ;
132
+ moduleDeclarationInfo = decl ;
118
133
} else {
119
134
break ;
120
135
}
121
136
}
122
137
}
123
- if ( ! moduleDeclaration ) {
138
+ if ( ! moduleDeclarationInfo ) {
124
139
// throw new Error(`TODO: Implement handling for global access without module declaration`);
125
140
}
126
141
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 , {
139
144
nodeInfos : [ ] ,
140
145
} ) ;
141
146
}
142
- moduleDeclaration ?. importRequests . get ( moduleName ) ! . nodeInfos . push ( nodeInfo ) ;
147
+ moduleDeclarationInfo ?. importRequests . get ( moduleName ) ! . nodeInfos . push ( nodeInfo ) ;
143
148
}
144
149
145
150
return moduleDeclarations ;
0 commit comments