Skip to content

Commit 90385fe

Browse files
matz3flovogt
andauthored
[FEATURE] builder: Improve support for ES6+ syntax (#774)
JIRA: CPOUI5FOUNDATION-374 Co-authored-by: Florian Vogt <[email protected]>
1 parent 187a6a3 commit 90385fe

24 files changed

+1707
-206
lines changed

lib/lbt/analyzer/FioriElementsAnalyzer.js

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
const ModuleName = require("../utils/ModuleName");
6262
const SapUiDefine = require("../calls/SapUiDefine");
6363
const {parseJS, Syntax} = require("../utils/parseUtils");
64-
const {getValue, isMethodCall, isString} = require("../utils/ASTUtils");
64+
const {getValue, isMethodCall, getStringValue} = require("../utils/ASTUtils");
6565
const log = require("@ui5/logger").getLogger("lbt:analyzer:FioriElementAnalyzer");
6666

6767
// ---------------------------------------------------------------------------------------------------------
@@ -165,24 +165,35 @@ 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) {
182-
const defaultValue = getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]);
183-
if ( isString(defaultValue) ) {
184-
return defaultValue.value;
185-
}
196+
return getStringValue(getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]));
186197
}
187198
}
188199

lib/lbt/analyzer/JSModuleAnalyzer.js

Lines changed: 88 additions & 53 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: [],
@@ -114,7 +116,7 @@ const EnrichedVisitorKeys = (function() {
114116
ImportSpecifier: [], // imported, local
115117
Literal: [],
116118
LabeledStatement: [],
117-
LogicalExpression: [],
119+
LogicalExpression: ["right"],
118120
MemberExpression: [],
119121
MetaProperty: toBeDone(["meta", "property"]),
120122
MethodDefinition: [],
@@ -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: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
const ModuleName = require("../utils/ModuleName");
2929
const SapUiDefine = require("../calls/SapUiDefine");
3030
const {parseJS, Syntax} = require("../utils/parseUtils");
31-
const {getValue, isMethodCall, isString} = require("../utils/ASTUtils");
31+
const {getValue, isMethodCall, getStringValue} = require("../utils/ASTUtils");
3232
const log = require("@ui5/logger").getLogger("lbt:analyzer:SmartTemplateAnalyzer");
3333

3434
// ---------------------------------------------------------------------------------------------------------
@@ -134,24 +134,35 @@ 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) {
151-
const defaultValue = getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]);
152-
if ( isString(defaultValue) ) {
153-
return defaultValue.value;
154-
}
165+
return getStringValue(getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]));
155166
}
156167
}
157168

0 commit comments

Comments
 (0)