Skip to content

Commit 7308a04

Browse files
matz3flovogt
authored andcommitted
[INTERNAL] Improve support for ES6+ Syntax
JIRA: CPOUI5FOUNDATION-374
1 parent a6146a5 commit 7308a04

23 files changed

+1073
-113
lines changed

lib/lbt/analyzer/FioriElementsAnalyzer.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,33 @@ class FioriElementsAnalyzer {
165165
const TA = defineCall.findImportName("sap/fe/core/TemplateAssembler.js");
166166
// console.log("local name for TemplateAssembler: %s", TA);
167167
if ( TA && defineCall.factory ) {
168-
defineCall.factory.body.body.forEach( (stmt) => {
169-
if ( stmt.type === Syntax.ReturnStatement &&
170-
isMethodCall(stmt.argument, [TA, "getTemplateComponent"]) &&
171-
stmt.argument.arguments.length > 2 &&
172-
stmt.argument.arguments[2].type === "ObjectExpression" ) {
173-
templateName = this._analyzeTemplateClassDefinition(stmt.argument.arguments[2]) || templateName;
168+
if (defineCall.factory.type === Syntax.ArrowFunctionExpression &&
169+
defineCall.factory.expression === true) {
170+
if ( this._isTemplateClassDefinition(TA, defineCall.factory.body) ) {
171+
templateName =
172+
this._analyzeTemplateClassDefinition(defineCall.factory.body.arguments[2]) || templateName;
174173
}
175-
});
174+
} else {
175+
defineCall.factory.body.body.forEach( (stmt) => {
176+
if ( stmt.type === Syntax.ReturnStatement &&
177+
this._isTemplateClassDefinition(TA, stmt.argument)
178+
) {
179+
templateName =
180+
this._analyzeTemplateClassDefinition(stmt.argument.arguments[2]) || templateName;
181+
}
182+
});
183+
}
176184
}
177185
}
178186
return templateName;
179187
}
180188

189+
_isTemplateClassDefinition(TA, node) {
190+
return isMethodCall(node, [TA, "getTemplateComponent"]) &&
191+
node.arguments.length > 2 &&
192+
node.arguments[2].type === "ObjectExpression";
193+
}
194+
181195
_analyzeTemplateClassDefinition(clazz) {
182196
const defaultValue = getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]);
183197
if ( isString(defaultValue) ) {

lib/lbt/analyzer/JSModuleAnalyzer.js

Lines changed: 87 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const escope = require("escope");
55
const ModuleName = require("../utils/ModuleName");
66
const {Format: ModuleFormat} = require("../resources/ModuleInfo");
77
const UI5ClientConstants = require("../UI5ClientConstants");
8-
const {findOwnProperty, getLocation, getPropertyKey, isMethodCall, isString} = require("../utils/ASTUtils");
8+
const {
9+
findOwnProperty, getLocation, getPropertyKey,
10+
isMethodCall, isString, getStringValue} = require("../utils/ASTUtils");
911
const log = require("@ui5/logger").getLogger("lbt:analyzer:JSModuleAnalyzer");
1012

1113
// ------------------------------------------------------------------------------------------------------------------
@@ -64,7 +66,7 @@ const EnrichedVisitorKeys = (function() {
6466
BreakStatement: [],
6567
CallExpression: [], // special handling
6668
CatchClause: ["param", "body"],
67-
ChainExpression: [],
69+
ChainExpression: ["expression"],
6870
ClassBody: [],
6971
ClassDeclaration: [],
7072
ClassExpression: [],
@@ -541,21 +543,27 @@ class JSModuleAnalyzer {
541543

542544
function onDeclare(node) {
543545
const args = node.arguments;
544-
if ( args.length > 0 && isString(args[0]) ) {
545-
const name = ModuleName.fromUI5LegacyName( args[0].value );
546-
if ( nModuleDeclarations === 1 && !mainModuleFound) {
547-
// if this is the first declaration, then this is the main module declaration
548-
// note that this overrides an already given name
549-
setMainModuleInfo(name, getDocumentation(node));
550-
} else if ( nModuleDeclarations > 1 && name === info.name ) {
551-
// ignore duplicate declarations (e.g. in behavior file of design time controls)
552-
log.warn(`duplicate declaration of module name at ${getLocation(args)} in ${name}`);
546+
if (args.length > 0) {
547+
const value = getStringValue(args[0]);
548+
if (value !== undefined) {
549+
const name = ModuleName.fromUI5LegacyName(value);
550+
if ( nModuleDeclarations === 1 && !mainModuleFound) {
551+
// if this is the first declaration, then this is the main module declaration
552+
// note that this overrides an already given name
553+
setMainModuleInfo(name, getDocumentation(node));
554+
} else if ( nModuleDeclarations > 1 && name === info.name ) {
555+
// ignore duplicate declarations (e.g. in behavior file of design time controls)
556+
log.warn(`duplicate declaration of module name at ${getLocation(args)} in ${name}`);
557+
} else {
558+
// otherwise it is just a submodule declaration
559+
info.addSubModule(name);
560+
}
561+
return;
553562
} else {
554-
// otherwise it is just a submodule declaration
555-
info.addSubModule(name);
563+
log.error("jQuery.sap.declare: module name could not be determined from first argument:", args[0]);
556564
}
557565
} else {
558-
log.error("jQuery.sap.declare: module name could not be determined from first argument:", args[0]);
566+
log.error("jQuery.sap.declare: module name could not be determined, no arguments are given");
559567
}
560568
}
561569

@@ -569,20 +577,26 @@ class JSModuleAnalyzer {
569577

570578
// determine the name of the module
571579
let name = null;
572-
if ( i < nArgs && isString(args[i]) ) {
573-
name = ModuleName.fromRequireJSName( args[i++].value );
574-
if ( name === defaultName ) {
575-
// hardcoded name equals the file name, so this definition qualifies as main module definition
576-
setMainModuleInfo(name, desc);
577-
} else {
578-
info.addSubModule(name);
579-
if ( candidateName == null ) {
580-
// remember the name and description in case no other module qualifies as main module
581-
candidateName = name;
582-
candidateDescription = desc;
580+
if ( i < nArgs ) {
581+
const value = getStringValue( args[i] );
582+
if ( value !== undefined ) {
583+
name = ModuleName.fromRequireJSName(value);
584+
if ( name === defaultName ) {
585+
// hardcoded name equals the file name, so this definition qualifies as main module definition
586+
setMainModuleInfo(name, desc);
587+
} else {
588+
info.addSubModule(name);
589+
if ( candidateName == null ) {
590+
// remember the name and description in case no other module qualifies as main module
591+
candidateName = name;
592+
candidateDescription = desc;
593+
}
583594
}
595+
i++;
584596
}
585-
} else {
597+
}
598+
599+
if ( !name ) {
586600
nUnnamedDefines++;
587601
if ( nUnnamedDefines > 1 ) {
588602
throw new Error(
@@ -614,15 +628,25 @@ class JSModuleAnalyzer {
614628
// UI5 signature with one or many required modules
615629
for (let i = 0; i < nArgs; i++) {
616630
const arg = args[i];
617-
if ( isString(arg) ) {
618-
const requiredModuleName = ModuleName.fromUI5LegacyName( arg.value );
631+
const value = getStringValue(arg);
632+
if ( value !== undefined ) {
633+
const requiredModuleName = ModuleName.fromUI5LegacyName( value );
619634
info.addDependency(requiredModuleName, conditional);
620-
} else if ( arg.type == Syntax.ConditionalExpression &&
621-
isString(arg.consequent) && isString(arg.alternate) ) {
622-
const requiredModuleName1 = ModuleName.fromUI5LegacyName( arg.consequent.value );
623-
info.addDependency(requiredModuleName1, true);
624-
const requiredModuleName2 = ModuleName.fromUI5LegacyName( arg.alternate.value );
625-
info.addDependency(requiredModuleName2, true);
635+
} else if ( arg.type == Syntax.ConditionalExpression) {
636+
const consequentValue = getStringValue(arg.consequent);
637+
const alternateValue = getStringValue(arg.alternate);
638+
if ( consequentValue !== undefined ) {
639+
const requiredModuleName1 = ModuleName.fromUI5LegacyName( consequentValue );
640+
info.addDependency(requiredModuleName1, true);
641+
}
642+
if ( alternateValue !== undefined ) {
643+
const requiredModuleName2 = ModuleName.fromUI5LegacyName( alternateValue );
644+
info.addDependency(requiredModuleName2, true);
645+
}
646+
if ( consequentValue === undefined || alternateValue === undefined ) {
647+
log.verbose("jQuery.sap.require: cannot evaluate dynamic arguments: ", arg && arg.type);
648+
info.dynamicDependencies = true;
649+
}
626650
} else {
627651
log.verbose("jQuery.sap.require: cannot evaluate dynamic arguments: ", arg && arg.type);
628652
info.dynamicDependencies = true;
@@ -637,9 +661,10 @@ class JSModuleAnalyzer {
637661
const i = 0;
638662

639663
if ( i < nArgs ) {
640-
if ( isString(args[i]) ) {
664+
const value = getStringValue(args[i]);
665+
if ( value !== undefined ) {
641666
// sap.ui.requireSync does not support relative dependencies
642-
const moduleName = ModuleName.fromRequireJSName( args[i].value );
667+
const moduleName = ModuleName.fromRequireJSName( value );
643668
info.addDependency(moduleName, conditional);
644669
} else {
645670
log.verbose("sap.ui.requireSync: cannot evaluate dynamic arguments: ", args[i] && args[i].type);
@@ -654,18 +679,24 @@ class JSModuleAnalyzer {
654679
let i = 0;
655680

656681
// determine the name of the module
657-
if ( i < nArgs && isString(args[i]) ) {
658-
const moduleName = ModuleName.fromRequireJSName( args[i++].value );
659-
info.addSubModule(moduleName);
660-
661-
// add dependencies
662-
// to correctly identify dependencies e.g. of a library-preload
663-
const elementArg = args[i++];
664-
if (elementArg && elementArg.type === Syntax.ArrayExpression) {
665-
elementArg.elements.forEach((element) => {
666-
const dependencyName = ModuleName.resolveRelativeRequireJSName(moduleName, element.value);
667-
info.addDependency(dependencyName, conditional);
668-
});
682+
if ( i < nArgs ) {
683+
const value = getStringValue(args[i++]);
684+
if ( value !== undefined ) {
685+
const moduleName = ModuleName.fromRequireJSName( value );
686+
info.addSubModule(moduleName);
687+
688+
// add dependencies
689+
// to correctly identify dependencies e.g. of a library-preload
690+
const elementArg = args[i++];
691+
if (elementArg && elementArg.type === Syntax.ArrayExpression) {
692+
elementArg.elements.forEach((element) => {
693+
const dependencyName = ModuleName.resolveRelativeRequireJSName(moduleName,
694+
getStringValue(element));
695+
info.addDependency(dependencyName, conditional);
696+
});
697+
}
698+
} else {
699+
log.warn("sap.ui.predefine call has a non supported type for module name (ignored)");
669700
}
670701
} else {
671702
log.warn("sap.ui.predefine call is missing a module name (ignored)");
@@ -692,6 +723,9 @@ class JSModuleAnalyzer {
692723
if ( modules && modules.type == Syntax.ObjectExpression ) {
693724
modules.properties.forEach( function(property) {
694725
let moduleName = getPropertyKey(property);
726+
if ( !moduleName ) {
727+
return;
728+
}
695729
if ( namesUseLegacyNotation ) {
696730
moduleName = ModuleName.fromUI5LegacyName(moduleName);
697731
}
@@ -705,16 +739,17 @@ class JSModuleAnalyzer {
705739
function analyzeDependencyArray(array, conditional, name) {
706740
// console.log(array);
707741
array.forEach( (item) => {
708-
if ( isString(item) ) {
742+
const value = getStringValue(item);
743+
if ( value !== undefined ) {
709744
// ignore special AMD dependencies (require, exports, module)
710-
if ( SPECIAL_AMD_DEPENDENCIES.indexOf(item.value) >= 0 ) {
745+
if ( SPECIAL_AMD_DEPENDENCIES.indexOf(value) >= 0 ) {
711746
return;
712747
}
713748
let requiredModule;
714749
if (name == null) {
715-
requiredModule = ModuleName.fromRequireJSName( item.value );
750+
requiredModule = ModuleName.fromRequireJSName( value );
716751
} else {
717-
requiredModule = ModuleName.resolveRelativeRequireJSName(name, item.value);
752+
requiredModule = ModuleName.resolveRelativeRequireJSName(name, value);
718753
}
719754
info.addDependency( requiredModule, conditional );
720755
} else {

lib/lbt/analyzer/SmartTemplateAnalyzer.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,33 @@ class TemplateComponentAnalyzer {
134134
const TA = defineCall.findImportName("sap/suite/ui/generic/template/lib/TemplateAssembler.js");
135135
// console.log("local name for TemplateAssembler: %s", TA);
136136
if ( TA && defineCall.factory ) {
137-
defineCall.factory.body.body.forEach( (stmt) => {
138-
if ( stmt.type === Syntax.ReturnStatement &&
139-
isMethodCall(stmt.argument, [TA, "getTemplateComponent"]) &&
140-
stmt.argument.arguments.length > 2 &&
141-
stmt.argument.arguments[2].type === "ObjectExpression" ) {
142-
templateName = this._analyzeTemplateClassDefinition(stmt.argument.arguments[2]) || templateName;
137+
if (defineCall.factory.type === Syntax.ArrowFunctionExpression &&
138+
defineCall.factory.expression === true) {
139+
if ( this._isTemplateClassDefinition(TA, defineCall.factory.body) ) {
140+
templateName =
141+
this._analyzeTemplateClassDefinition(defineCall.factory.body.arguments[2]) || templateName;
143142
}
144-
});
143+
} else {
144+
defineCall.factory.body.body.forEach( (stmt) => {
145+
if ( stmt.type === Syntax.ReturnStatement &&
146+
this._isTemplateClassDefinition(TA, stmt.argument)
147+
) {
148+
templateName =
149+
this._analyzeTemplateClassDefinition(stmt.argument.arguments[2]) || templateName;
150+
}
151+
});
152+
}
145153
}
146154
}
147155
return templateName;
148156
}
149157

158+
_isTemplateClassDefinition(TA, node) {
159+
return isMethodCall(node, [TA, "getTemplateComponent"]) &&
160+
node.arguments.length > 2 &&
161+
node.arguments[2].type === "ObjectExpression";
162+
}
163+
150164
_analyzeTemplateClassDefinition(clazz) {
151165
const defaultValue = getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]);
152166
if ( isString(defaultValue) ) {

lib/lbt/analyzer/XMLCompositeAnalyzer.js

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,30 @@ class XMLCompositeAnalyzer {
1919
const XMLC = defineCall.findImportName("sap/ui/core/XMLComposite.js");
2020
// console.log("local name for XMLComposite: %s", XMLC);
2121
if ( XMLC && defineCall.factory ) {
22-
defineCall.factory.body.body.forEach( (stmt) => {
23-
if ( stmt.type === Syntax.VariableDeclaration ) {
24-
stmt.declarations.forEach( (decl) => {
25-
fragmentName = this._checkForXMLCClassDefinition( XMLC, decl.init ) || fragmentName;
26-
});
27-
} else if ( stmt.type === Syntax.ExpressionStatement &&
28-
stmt.expression.type === Syntax.AssignmentExpression ) {
29-
fragmentName = this._checkForXMLCClassDefinition( XMLC, stmt.expression.right ) || fragmentName;
30-
}
31-
});
32-
if ( fragmentName ) {
33-
const fragmentModule = ModuleName.fromUI5LegacyName( fragmentName, ".control.xml" );
22+
if (defineCall.factory.type === Syntax.ArrowFunctionExpression &&
23+
defineCall.factory.expression === true) {
24+
fragmentName = this._checkForXMLCClassDefinition(XMLC, defineCall.factory.body);
25+
} else {
26+
defineCall.factory.body.body.forEach((stmt) => {
27+
if (stmt.type === Syntax.VariableDeclaration) {
28+
stmt.declarations.forEach((decl) => {
29+
fragmentName = this._checkForXMLCClassDefinition(XMLC, decl.init) || fragmentName;
30+
});
31+
} else if (
32+
stmt.type === Syntax.ReturnStatement &&
33+
( stmt?.argument?.type === Syntax.CallExpression && stmt.argument.arguments?.length > 1 &&
34+
stmt.argument.arguments[1].type === Syntax.ObjectExpression)) {
35+
fragmentName =
36+
this._checkForXMLCClassDefinition(XMLC, stmt.argument) || fragmentName;
37+
} else if (stmt.type === Syntax.ExpressionStatement &&
38+
stmt.expression.type === Syntax.AssignmentExpression) {
39+
fragmentName =
40+
this._checkForXMLCClassDefinition(XMLC, stmt.expression.right) || fragmentName;
41+
}
42+
});
43+
}
44+
if (fragmentName) {
45+
const fragmentModule = ModuleName.fromUI5LegacyName(fragmentName, ".control.xml");
3446
log.verbose("fragment control: add dependency to template fragment %s", fragmentModule);
3547
info.addDependency(fragmentModule);
3648
}

lib/lbt/analyzer/analyzeLibraryJS.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22
const {parseJS, Syntax, VisitorKeys} = require("../utils/parseUtils");
33
const {getPropertyKey, isMethodCall, isIdentifier, getStringArray} = require("../utils/ASTUtils");
4+
const log = require("@ui5/logger").getLogger("lbt:analyzer:LibraryJS");
45

56
const CALL__SAP_UI_GETCORE = ["sap", "ui", "getCore"];
67

@@ -25,8 +26,14 @@ async function analyze(resource) {
2526
node.arguments.length === 1 &&
2627
node.arguments[0].type === Syntax.ObjectExpression ) {
2728
node.arguments[0].properties.forEach( (prop) => {
29+
if (prop.type === Syntax.SpreadElement) {
30+
// TODO: Support interpreting SpreadElements
31+
return;
32+
}
33+
2834
const key = getPropertyKey(prop);
2935
const value = prop.value;
36+
3037
if ( key === "noLibraryCSS" &&
3138
(value.type === Syntax.Literal && typeof value.value === "boolean") ) {
3239
libInfo.noLibraryCSS = value.value;
@@ -38,6 +45,14 @@ async function analyze(resource) {
3845
libInfo.controls = getStringArray(value, true);
3946
} else if ( key === "elements" && value.type == Syntax.ArrayExpression ) {
4047
libInfo.elements = getStringArray(value, true);
48+
} else if ( ["designtime", "dependencies", "extensions", "name", "version"].includes(key) ) {
49+
// do nothing, for all other supported properties
50+
} else {
51+
log.error(
52+
"Unexpected property '" + key +
53+
"' or wrong type for '" + key +
54+
"' in sap.ui.getCore().initLibrary call in '" + resource.getPath() + "'"
55+
);
4156
}
4257
});
4358

lib/lbt/bundle/Builder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ async function rewriteDefine({moduleName, moduleContent, moduleSourceMap}) {
648648

649649
// Inject module name if missing
650650
if ( defineCall.arguments.length == 0 ||
651-
defineCall.arguments[0].type !== Syntax.Literal ) {
651+
![Syntax.Literal, Syntax.TemplateLiteral].includes(defineCall.arguments[0].type)) {
652652
let value = `"${ModuleName.toRequireJSName(moduleName)}"`;
653653
let index;
654654

lib/lbt/calls/SapUiDefine.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ class SapUiDefineCall {
4343
this.dependencyInsertionIdx = this.dependencyArray.elements.length;
4444
}
4545

46-
if ( i < args.length && args[i].type === Syntax.FunctionExpression ) {
46+
if ( i < args.length && (
47+
args[i].type === Syntax.FunctionExpression || args[i].type === Syntax.ArrowFunctionExpression)
48+
) {
4749
this.factory = args[i++];
4850
params = this.factory.params;
4951
this.paramNames = params.map( (param) => {

0 commit comments

Comments
 (0)