@@ -9,7 +9,7 @@ const UI5ClientConstants = require("../UI5ClientConstants");
99const { findOwnProperty, getLocation, getPropertyKey, isMethodCall, isString} = require ( "../utils/ASTUtils" ) ;
1010const log = require ( "@ui5/logger" ) . getLogger ( "lbt:analyzer:JSModuleAnalyzer" ) ;
1111
12- // --------------------------------------------------------------------------------------------------------------------
12+ // ------------------------------------------------------------------------------------------------------------------
1313
1414const EnrichedVisitorKeys = ( function ( ) {
1515 const VisitorKeys = require ( "estraverse" ) . VisitorKeys ;
@@ -24,17 +24,44 @@ const EnrichedVisitorKeys = (function() {
2424 * E.g. in an IfExpression, the 'test' is always executed, whereas 'consequent'
2525 * and 'alternate' are only executed under certain conditions.
2626 *
27- * The object is checked against the 'official' list of node types
28- * and node keys as defined by 'estraverse' This helps to ensure that no new
29- * syntax addition is missed and that the configured keys are valid.
27+ * While visiting the AST of a JavaSCript file, the JSModuleAnalyzer uses this information
28+ * to decide whether a code block is executed conditionally or unconditionally.
29+ * Besides this information which is inherent to the language, the analyzer uses
30+ * additional knowledge about special APIS / constructs (e.g. the factory function of
31+ * an AMD module is known to be executed when the module is executed, an IIFE is known to
32+ * be executed etc.)
33+ *
34+ * To be more robust against the evolution of the language, the object below is checked
35+ * against the 'official' list of node types and node keys as defined by 'estraverse'.
36+ * This helps to ensure that no new syntax addition is missed and that the configured
37+ * keys are valid.
3038 */
3139 const TempKeys = {
3240 AssignmentExpression : [ ] ,
33- AssignmentPattern : toBeDone ( [ "left" , "right" ] ) ,
41+ /*
42+ * function( >>>a=3<<<, b) {...}
43+ * var [>>>a=3<<<, b] = [...];
44+ *
45+ * The default value expression (right) is only evaluated when there's no other value in
46+ * the context of the pattern (e.g. destructuring or function call don't provide a value),
47+ * so it's a conditional branch.
48+ */
49+ AssignmentPattern : [ "right" ] ,
3450 ArrayExpression : [ ] ,
35- ArrayPattern : toBeDone ( [ "elements" ] ) ,
51+ /*
52+ * var >>>[a=3, b]<<< = [...];
53+ * All elements in an array pattern are unconditional.
54+ */
55+ ArrayPattern : [ ] , // elements
56+ /*
57+ * The body of an arrow function is only executed when the arrow function is executed
58+ */
3659 ArrowFunctionExpression : [ "body" ] ,
37- AwaitExpression : toBeDone ( [ "argument" ] ) , // CAUTION: It's deferred to ES7.
60+ /*
61+ * The argument of await is always executed
62+ * TODO how to handle code after the await expression?
63+ */
64+ AwaitExpression : [ ] , // argument
3865 BlockStatement : [ ] ,
3966 BinaryExpression : [ ] ,
4067 BreakStatement : [ ] ,
@@ -49,12 +76,16 @@ const EnrichedVisitorKeys = (function() {
4976 ContinueStatement : [ ] ,
5077 DebuggerStatement : [ ] ,
5178 DirectiveStatement : [ ] ,
52- DoWhileStatement : [ ] , // condition is executed on the same conditions as the surrounding block, potentially repeated, block is always entered and might be repeated
79+ /*
80+ * 'condition' is executed on the same conditions as the surrounding block, potentially repeated,
81+ * 'block' is always entered and might be repeated
82+ */
83+ DoWhileStatement : [ ] ,
5384 EmptyStatement : [ ] ,
54- ExportAllDeclaration : toBeDone ( [ "source" ] ) ,
55- ExportDefaultDeclaration : toBeDone ( [ "declaration" ] ) ,
56- ExportNamedDeclaration : toBeDone ( [ " declaration" , " specifiers" , " source" ] ) ,
57- ExportSpecifier : toBeDone ( [ " exported" , " local" ] ) ,
85+ ExportAllDeclaration : [ ] , // no parts of an export are conditional - source
86+ ExportDefaultDeclaration : [ ] , // no parts of an export are conditional - declaration
87+ ExportNamedDeclaration : [ ] , // no parts of an export are conditional - declaration, specifiers, source
88+ ExportSpecifier : [ ] , // no parts of an export are conditional exported, local
5889 ExpressionStatement : [ ] ,
5990 ForStatement : [ "update" , "body" ] ,
6091 ForInStatement : [ "body" ] ,
@@ -64,10 +95,22 @@ const EnrichedVisitorKeys = (function() {
6495 GeneratorExpression : toBeDone ( [ "blocks" , "filter" , "body" ] ) , // CAUTION: It's deferred to ES7.
6596 Identifier : [ ] ,
6697 IfStatement : [ "consequent" , "alternate" ] ,
67- ImportDeclaration : toBeDone ( [ "specifiers" , "source" ] ) ,
68- ImportDefaultSpecifier : toBeDone ( [ "local" ] ) ,
69- ImportNamespaceSpecifier : toBeDone ( [ "local" ] ) ,
70- ImportSpecifier : toBeDone ( [ "imported" , "local" ] ) ,
98+ /*
99+ * all parts of an import declaration are executed unconditionally
100+ */
101+ ImportDeclaration : [ ] , // specifiers, source
102+ /*
103+ * import >>>a<<< from 'module';
104+ */
105+ ImportDefaultSpecifier : [ ] , // local
106+ /*
107+ * import >>>* as b<<< from 'module';
108+ */
109+ ImportNamespaceSpecifier : [ ] , // local
110+ /*
111+ * import {>>>a as c<<<,b} from 'module';
112+ */
113+ ImportSpecifier : [ ] , // imported, local
71114 Literal : [ ] ,
72115 LabeledStatement : [ ] ,
73116 LogicalExpression : [ ] ,
@@ -77,27 +120,45 @@ const EnrichedVisitorKeys = (function() {
77120 ModuleSpecifier : [ ] ,
78121 NewExpression : [ ] ,
79122 ObjectExpression : [ ] ,
80- ObjectPattern : toBeDone ( [ "properties" ] ) ,
123+ /*
124+ * >>>{a,b,c}<<< = {...}
125+ *
126+ * All properties in an object pattern are executed.
127+ */
128+ ObjectPattern : [ ] , // properties
81129 Program : [ ] ,
82130 Property : [ ] ,
83- RestElement : toBeDone ( [ "argument" ] ) ,
131+ /*
132+ * argument of the rest element is always executed under the same condition as the rest element itself
133+ */
134+ RestElement : [ ] , // argument
84135 ReturnStatement : [ ] ,
85136 SequenceExpression : [ ] ,
86- SpreadElement : toBeDone ( [ "argument" ] ) ,
137+ SpreadElement : [ ] , // the argument of the spread operator always needs to be evaluated - argument
87138 Super : [ ] ,
88139 SwitchStatement : [ ] ,
89140 SwitchCase : [ "test" , "consequent" ] , // test and consequent are executed only conditionally
90- TaggedTemplateExpression : toBeDone ( [ "tag" , "quasi" ] ) ,
141+ /*
142+ * all parts of a tagged template literal are executed under the same condition as the context
143+ */
144+ TaggedTemplateExpression : [ ] , // tag, quasi
91145 TemplateElement : [ ] ,
92- TemplateLiteral : toBeDone ( [ "quasis" , "expressions" ] ) ,
146+ /*
147+ * all parts of a template literal are executed under the same condition as the context
148+ */
149+ TemplateLiteral : [ ] , // quasis, expressions
93150 ThisExpression : [ ] ,
94151 ThrowStatement : [ ] ,
95152 TryStatement : [ "handler" ] , // handler is called conditionally
96153 UnaryExpression : [ ] ,
97154 UpdateExpression : [ ] ,
98155 VariableDeclaration : [ ] ,
99156 VariableDeclarator : [ ] ,
100- WhileStatement : [ "body" ] , // condition is executed on the same conditions as the surrounding block and potentially repeated, block maybe entered only conditionally but can be repeated
157+ /*
158+ * 'condition' is executed on the same conditions as the surrounding block and potentially repeated,
159+ * 'block' maybe entered only conditionally but can be repeated
160+ */
161+ WhileStatement : [ "body" ] ,
101162 WithStatement : [ ] ,
102163 YieldExpression : [ ]
103164 } ;
@@ -151,6 +212,9 @@ const CALL_JQUERY_SAP_IS_DECLARED = [["jQuery", "$"], "sap", "isDeclared"];
151212const CALL_JQUERY_SAP_REQUIRE = [ [ "jQuery" , "$" ] , "sap" , "require" ] ;
152213const CALL_JQUERY_SAP_REGISTER_PRELOADED_MODULES = [ [ "jQuery" , "$" ] , "sap" , "registerPreloadedModules" ] ;
153214
215+ function isCallableExpression ( node ) {
216+ return node . type == Syntax . FunctionExpression || node . type == Syntax . ArrowFunctionExpression ;
217+ }
154218
155219/**
156220 * Analyzes an already parsed JSDocument to collect information about the contained module(s).
@@ -233,7 +297,7 @@ class JSModuleAnalyzer {
233297 if ( iArg < args . length && args [ iArg ] . type == Syntax . ArrayExpression ) {
234298 iArg ++ ;
235299 }
236- if ( iArg < args . length && args [ iArg ] . type == Syntax . FunctionExpression ) {
300+ if ( iArg < args . length && isCallableExpression ( args [ iArg ] ) ) {
237301 // unconditionally execute the factory function
238302 visit ( args [ iArg ] . body , conditional ) ;
239303 }
@@ -256,7 +320,7 @@ class JSModuleAnalyzer {
256320 analyzeDependencyArray ( args [ iArg ] . elements , conditional , null ) ;
257321 iArg ++ ;
258322 }
259- if ( iArg < args . length && args [ iArg ] . type == Syntax . FunctionExpression ) {
323+ if ( iArg < args . length && isCallableExpression ( args [ iArg ] ) ) {
260324 // analyze the callback function
261325 visit ( args [ iArg ] . body , conditional ) ;
262326 }
@@ -275,7 +339,7 @@ class JSModuleAnalyzer {
275339 let legacyCall = isMethodCall ( node , CALL_JQUERY_SAP_REGISTER_PRELOADED_MODULES ) ;
276340 info . setFormat ( legacyCall ? ModuleFormat . UI5_LEGACY : ModuleFormat . UI5_DEFINE ) ;
277341 onRegisterPreloadedModules ( node , legacyCall ) ;
278- } else if ( node . callee . type === Syntax . FunctionExpression ) {
342+ } else if ( isCallableExpression ( node . callee ) ) {
279343 // recognizes a scope function declaration + argument
280344 visit ( node . arguments , conditional ) ;
281345 // NODE-TODO defaults of callee?
0 commit comments