@ui5/builder 3.0.0-alpha.10 → 3.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,10 +2,26 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
4
 
5
- A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.10...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.11...HEAD).
6
+
7
+ <a name="v3.0.0-alpha.11"></a>
8
+ ## [v3.0.0-alpha.11] - 2022-10-12
9
+ ### Bug Fixes
10
+ - **package.json:** Downgrade es5-ext dependency [`2b71707`](https://github.com/SAP/ui5-builder/commit/2b71707683480b26aab9957c09e5d22bce8a6450)
11
+
12
+ ### Dependency Updates
13
+ - Bump escope from 3.6.0 to 4.0.0 [`e25af0d`](https://github.com/SAP/ui5-builder/commit/e25af0d9c1184916aa037b4b4ca23d15c5f2a408)
14
+
15
+ ### Features
16
+ - **builder:** Improve support for ES6+ syntax ([#774](https://github.com/SAP/ui5-builder/issues/774)) [`90385fe`](https://github.com/SAP/ui5-builder/commit/90385feb7cea1e5b864cff035b01263a492972a9)
17
+ - **jsdoc:** Improve support for ES6+ syntax ([#785](https://github.com/SAP/ui5-builder/issues/785)) [`187a6a3`](https://github.com/SAP/ui5-builder/commit/187a6a35000145d70bf41e0d8b724e5ea8d8dc78)
18
+
19
+ ### Reverts
20
+ - [FIX] package.json: Downgrade es5-ext dependency
21
+
6
22
 
7
23
  <a name="v3.0.0-alpha.10"></a>
8
- ## [v3.0.0-alpha.10] - 2022-08-08
24
+ ## [v3.0.0-alpha.10] - 2022-08-09
9
25
 
10
26
  <a name="v3.0.0-alpha.9"></a>
11
27
  ## [v3.0.0-alpha.9] - 2022-07-27
@@ -753,6 +769,7 @@ to load the custom bundle file instead.
753
769
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
754
770
 
755
771
 
772
+ [v3.0.0-alpha.11]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.10...v3.0.0-alpha.11
756
773
  [v3.0.0-alpha.10]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.9...v3.0.0-alpha.10
757
774
  [v3.0.0-alpha.9]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.8...v3.0.0-alpha.9
758
775
  [v3.0.0-alpha.8]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.7...v3.0.0-alpha.8
@@ -61,7 +61,7 @@
61
61
  const ModuleName = require("../utils/ModuleName");
62
62
  const SapUiDefine = require("../calls/SapUiDefine");
63
63
  const {parseJS, Syntax} = require("../utils/parseUtils");
64
- const {getValue, isMethodCall, isString} = require("../utils/ASTUtils");
64
+ const {getValue, isMethodCall, getStringValue} = require("../utils/ASTUtils");
65
65
  const log = require("@ui5/logger").getLogger("lbt:analyzer:FioriElementAnalyzer");
66
66
 
67
67
  // ---------------------------------------------------------------------------------------------------------
@@ -165,24 +165,35 @@ class FioriElementsAnalyzer {
165
165
  const TA = defineCall.findImportName("sap/fe/core/TemplateAssembler.js");
166
166
  // console.log("local name for TemplateAssembler: %s", TA);
167
167
  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;
174
173
  }
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
+ }
176
184
  }
177
185
  }
178
186
  return templateName;
179
187
  }
180
188
 
189
+ _isTemplateClassDefinition(TA, node) {
190
+ return isMethodCall(node, [TA, "getTemplateComponent"]) &&
191
+ node.arguments.length > 2 &&
192
+ node.arguments[2].type === "ObjectExpression";
193
+ }
194
+
181
195
  _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"]));
186
197
  }
187
198
  }
188
199
 
@@ -5,7 +5,9 @@ const escope = require("escope");
5
5
  const ModuleName = require("../utils/ModuleName");
6
6
  const {Format: ModuleFormat} = require("../resources/ModuleInfo");
7
7
  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");
9
11
  const log = require("@ui5/logger").getLogger("lbt:analyzer:JSModuleAnalyzer");
10
12
 
11
13
  // ------------------------------------------------------------------------------------------------------------------
@@ -64,7 +66,7 @@ const EnrichedVisitorKeys = (function() {
64
66
  BreakStatement: [],
65
67
  CallExpression: [], // special handling
66
68
  CatchClause: ["param", "body"],
67
- ChainExpression: [],
69
+ ChainExpression: ["expression"],
68
70
  ClassBody: [],
69
71
  ClassDeclaration: [],
70
72
  ClassExpression: [],
@@ -114,7 +116,7 @@ const EnrichedVisitorKeys = (function() {
114
116
  ImportSpecifier: [], // imported, local
115
117
  Literal: [],
116
118
  LabeledStatement: [],
117
- LogicalExpression: [],
119
+ LogicalExpression: ["right"],
118
120
  MemberExpression: [],
119
121
  MetaProperty: toBeDone(["meta", "property"]),
120
122
  MethodDefinition: [],
@@ -541,21 +543,27 @@ class JSModuleAnalyzer {
541
543
 
542
544
  function onDeclare(node) {
543
545
  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;
553
562
  } 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]);
556
564
  }
557
565
  } 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");
559
567
  }
560
568
  }
561
569
 
@@ -569,20 +577,26 @@ class JSModuleAnalyzer {
569
577
 
570
578
  // determine the name of the module
571
579
  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
+ }
583
594
  }
595
+ i++;
584
596
  }
585
- } else {
597
+ }
598
+
599
+ if ( !name ) {
586
600
  nUnnamedDefines++;
587
601
  if ( nUnnamedDefines > 1 ) {
588
602
  throw new Error(
@@ -614,15 +628,25 @@ class JSModuleAnalyzer {
614
628
  // UI5 signature with one or many required modules
615
629
  for (let i = 0; i < nArgs; i++) {
616
630
  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 );
619
634
  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
+ }
626
650
  } else {
627
651
  log.verbose("jQuery.sap.require: cannot evaluate dynamic arguments: ", arg && arg.type);
628
652
  info.dynamicDependencies = true;
@@ -637,9 +661,10 @@ class JSModuleAnalyzer {
637
661
  const i = 0;
638
662
 
639
663
  if ( i < nArgs ) {
640
- if ( isString(args[i]) ) {
664
+ const value = getStringValue(args[i]);
665
+ if ( value !== undefined ) {
641
666
  // sap.ui.requireSync does not support relative dependencies
642
- const moduleName = ModuleName.fromRequireJSName( args[i].value );
667
+ const moduleName = ModuleName.fromRequireJSName( value );
643
668
  info.addDependency(moduleName, conditional);
644
669
  } else {
645
670
  log.verbose("sap.ui.requireSync: cannot evaluate dynamic arguments: ", args[i] && args[i].type);
@@ -654,18 +679,24 @@ class JSModuleAnalyzer {
654
679
  let i = 0;
655
680
 
656
681
  // 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)");
669
700
  }
670
701
  } else {
671
702
  log.warn("sap.ui.predefine call is missing a module name (ignored)");
@@ -692,6 +723,9 @@ class JSModuleAnalyzer {
692
723
  if ( modules && modules.type == Syntax.ObjectExpression ) {
693
724
  modules.properties.forEach( function(property) {
694
725
  let moduleName = getPropertyKey(property);
726
+ if ( !moduleName ) {
727
+ return;
728
+ }
695
729
  if ( namesUseLegacyNotation ) {
696
730
  moduleName = ModuleName.fromUI5LegacyName(moduleName);
697
731
  }
@@ -705,16 +739,17 @@ class JSModuleAnalyzer {
705
739
  function analyzeDependencyArray(array, conditional, name) {
706
740
  // console.log(array);
707
741
  array.forEach( (item) => {
708
- if ( isString(item) ) {
742
+ const value = getStringValue(item);
743
+ if ( value !== undefined ) {
709
744
  // ignore special AMD dependencies (require, exports, module)
710
- if ( SPECIAL_AMD_DEPENDENCIES.indexOf(item.value) >= 0 ) {
745
+ if ( SPECIAL_AMD_DEPENDENCIES.indexOf(value) >= 0 ) {
711
746
  return;
712
747
  }
713
748
  let requiredModule;
714
749
  if (name == null) {
715
- requiredModule = ModuleName.fromRequireJSName( item.value );
750
+ requiredModule = ModuleName.fromRequireJSName( value );
716
751
  } else {
717
- requiredModule = ModuleName.resolveRelativeRequireJSName(name, item.value);
752
+ requiredModule = ModuleName.resolveRelativeRequireJSName(name, value);
718
753
  }
719
754
  info.addDependency( requiredModule, conditional );
720
755
  } else {
@@ -28,7 +28,7 @@
28
28
  const ModuleName = require("../utils/ModuleName");
29
29
  const SapUiDefine = require("../calls/SapUiDefine");
30
30
  const {parseJS, Syntax} = require("../utils/parseUtils");
31
- const {getValue, isMethodCall, isString} = require("../utils/ASTUtils");
31
+ const {getValue, isMethodCall, getStringValue} = require("../utils/ASTUtils");
32
32
  const log = require("@ui5/logger").getLogger("lbt:analyzer:SmartTemplateAnalyzer");
33
33
 
34
34
  // ---------------------------------------------------------------------------------------------------------
@@ -134,24 +134,35 @@ class TemplateComponentAnalyzer {
134
134
  const TA = defineCall.findImportName("sap/suite/ui/generic/template/lib/TemplateAssembler.js");
135
135
  // console.log("local name for TemplateAssembler: %s", TA);
136
136
  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;
143
142
  }
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
+ }
145
153
  }
146
154
  }
147
155
  return templateName;
148
156
  }
149
157
 
158
+ _isTemplateClassDefinition(TA, node) {
159
+ return isMethodCall(node, [TA, "getTemplateComponent"]) &&
160
+ node.arguments.length > 2 &&
161
+ node.arguments[2].type === "ObjectExpression";
162
+ }
163
+
150
164
  _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"]));
155
166
  }
156
167
  }
157
168
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  const {Syntax} = require("../utils/parseUtils");
4
4
  const SapUiDefine = require("../calls/SapUiDefine");
5
- const {getValue, isMethodCall, isString} = require("../utils/ASTUtils");
5
+ const {getValue, isMethodCall, getStringValue} = require("../utils/ASTUtils");
6
6
  const ModuleName = require("../utils/ModuleName");
7
7
  const log = require("@ui5/logger").getLogger("lbt:analyzer:XMLCompositeAnalyzer");
8
8
 
@@ -19,18 +19,30 @@ class XMLCompositeAnalyzer {
19
19
  const XMLC = defineCall.findImportName("sap/ui/core/XMLComposite.js");
20
20
  // console.log("local name for XMLComposite: %s", XMLC);
21
21
  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");
34
46
  log.verbose("fragment control: add dependency to template fragment %s", fragmentModule);
35
47
  info.addDependency(fragmentModule);
36
48
  }
@@ -42,8 +54,9 @@ class XMLCompositeAnalyzer {
42
54
  let fragmentName;
43
55
  if ( isMethodCall(stmt, [XMLC, "extend"]) ) {
44
56
  // log.verbose(stmt);
45
- if ( stmt.arguments.length > 0 && isString(stmt.arguments[0]) ) {
46
- fragmentName = stmt.arguments[0].value;
57
+ const value = getStringValue(stmt.arguments[0]);
58
+ if ( stmt.arguments.length > 0 && value ) {
59
+ fragmentName = value;
47
60
  }
48
61
  if ( stmt.arguments.length > 1 && stmt.arguments[1].type === Syntax.ObjectExpression ) {
49
62
  fragmentName = this._analyzeXMLCClassDefinition(stmt.arguments[1]) || fragmentName;
@@ -54,10 +67,7 @@ class XMLCompositeAnalyzer {
54
67
 
55
68
  _analyzeXMLCClassDefinition(clazz) {
56
69
  // log.verbose(clazz);
57
- const fragmentName = getValue(clazz, ["fragment"]);
58
- if ( isString(fragmentName) ) {
59
- return fragmentName.value;
60
- }
70
+ return getStringValue(getValue(clazz, ["fragment"]));
61
71
  }
62
72
  }
63
73
 
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  const {parseJS, Syntax, VisitorKeys} = require("../utils/parseUtils");
3
3
  const {getPropertyKey, isMethodCall, isIdentifier, getStringArray} = require("../utils/ASTUtils");
4
+ const log = require("@ui5/logger").getLogger("lbt:analyzer:LibraryJS");
4
5
 
5
6
  const CALL__SAP_UI_GETCORE = ["sap", "ui", "getCore"];
6
7
 
@@ -25,8 +26,14 @@ async function analyze(resource) {
25
26
  node.arguments.length === 1 &&
26
27
  node.arguments[0].type === Syntax.ObjectExpression ) {
27
28
  node.arguments[0].properties.forEach( (prop) => {
29
+ if (prop.type === Syntax.SpreadElement) {
30
+ // SpreadElements are currently not supported
31
+ return;
32
+ }
33
+
28
34
  const key = getPropertyKey(prop);
29
35
  const value = prop.value;
36
+
30
37
  if ( key === "noLibraryCSS" &&
31
38
  (value.type === Syntax.Literal && typeof value.value === "boolean") ) {
32
39
  libInfo.noLibraryCSS = value.value;
@@ -38,6 +45,14 @@ async function analyze(resource) {
38
45
  libInfo.controls = getStringArray(value, true);
39
46
  } else if ( key === "elements" && value.type == Syntax.ArrayExpression ) {
40
47
  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
+ );
41
56
  }
42
57
  });
43
58
 
@@ -648,7 +648,7 @@ async function rewriteDefine({moduleName, moduleContent, moduleSourceMap}) {
648
648
 
649
649
  // Inject module name if missing
650
650
  if ( defineCall.arguments.length == 0 ||
651
- defineCall.arguments[0].type !== Syntax.Literal ) {
651
+ ![Syntax.Literal, Syntax.TemplateLiteral].includes(defineCall.arguments[0].type)) {
652
652
  let value = `"${ModuleName.toRequireJSName(moduleName)}"`;
653
653
  let index;
654
654
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  const {Syntax} = require("../utils/parseUtils");
4
4
  const ModuleName = require("../utils/ModuleName");
5
- const {isString, isBoolean} = require("../utils/ASTUtils");
5
+ const {isBoolean, getStringValue} = require("../utils/ASTUtils");
6
6
 
7
7
  class SapUiDefineCall {
8
8
  constructor(node, moduleName) {
@@ -11,6 +11,7 @@ class SapUiDefineCall {
11
11
  this.dependencyArray = null;
12
12
  this.factory = null;
13
13
  this.exportAsGlobal = false;
14
+ this.paramNames = null;
14
15
 
15
16
  const args = node.arguments;
16
17
  if ( args == null ) {
@@ -27,28 +28,33 @@ class SapUiDefineCall {
27
28
  let i = 0;
28
29
  let params;
29
30
 
30
- if ( i < args.length && isString(args[i]) ) {
31
+ const name = getStringValue(args[i]);
32
+ if ( i < args.length && name ) {
31
33
  // assert(String)
32
- this.name = args[i++].value;
34
+ this.name = name;
35
+ i++;
33
36
  }
34
37
 
35
38
  if ( i < args.length && args[i].type === Syntax.ArrayExpression ) {
36
39
  this.dependencyArray = args[i++];
37
40
  this.dependencies = this.dependencyArray.elements.map( (elem) => {
38
- if ( !isString(elem) ) {
41
+ const value = getStringValue(elem);
42
+ if ( !value ) {
39
43
  throw new TypeError();
40
44
  }
41
- return ModuleName.resolveRelativeRequireJSName(this.name, elem.value);
45
+ return ModuleName.resolveRelativeRequireJSName(this.name, value);
42
46
  });
43
47
  this.dependencyInsertionIdx = this.dependencyArray.elements.length;
44
48
  }
45
49
 
46
- if ( i < args.length && args[i].type === Syntax.FunctionExpression ) {
50
+ if ( i < args.length && (
51
+ args[i].type === Syntax.FunctionExpression || args[i].type === Syntax.ArrowFunctionExpression)
52
+ ) {
47
53
  this.factory = args[i++];
48
54
  params = this.factory.params;
49
55
  this.paramNames = params.map( (param) => {
50
56
  if ( param.type !== Syntax.Identifier ) {
51
- throw new TypeError();
57
+ return null;
52
58
  }
53
59
  return param.name;
54
60
  });
@@ -13,10 +13,31 @@ const {Syntax} = require("../utils/parseUtils");
13
13
  * @returns {boolean} Whether the node is a literal and whether its value matches the given string
14
14
  */
15
15
  function isString(node, literal) {
16
- if ( node == null || node.type !== Syntax.Literal || typeof node.value !== "string" ) {
16
+ const value = getStringValue(node);
17
+ if (value === undefined) {
17
18
  return false;
18
19
  }
19
- return literal == null ? true : node.value === literal;
20
+ return literal == null ? true: value === literal;
21
+ }
22
+
23
+ function getStringValue(node) {
24
+ if (isLiteral(node)) {
25
+ return node.value;
26
+ } else if (isTemplateLiteralWithoutExpression(node)) {
27
+ return node?.quasis?.[0]?.value?.cooked;
28
+ } else {
29
+ return undefined;
30
+ }
31
+ }
32
+
33
+ function isLiteral(node) {
34
+ return node && node.type === Syntax.Literal && typeof node.value === "string";
35
+ }
36
+
37
+ function isTemplateLiteralWithoutExpression(node) {
38
+ return node?.type === Syntax.TemplateLiteral &&
39
+ node?.expressions?.length === 0 &&
40
+ node?.quasis?.length === 1;
20
41
  }
21
42
 
22
43
  function isBoolean(node, literal) {
@@ -47,27 +68,27 @@ function isNamedObject(node, objectPath, length) {
47
68
  }
48
69
 
49
70
  function isIdentifier(node, name) {
50
- if ( node.type != Syntax.Identifier ) {
51
- return false;
52
- }
53
- if ( typeof name == "string" ) {
71
+ if ( node.type === Syntax.Identifier && typeof name == "string" ) {
54
72
  return name === node.name;
73
+ } else if ( node.type === Syntax.Identifier && Array.isArray(name) ) {
74
+ return name.find((name) => name === node.name || name === "*") !== undefined;
75
+ } else if ( node.type === Syntax.ObjectPattern ) {
76
+ return node.properties.filter((childnode) => isIdentifier(childnode.key, name)).length > 0;
77
+ } else if ( node.type === Syntax.ArrayPattern ) {
78
+ return node.elements.filter((childnode) => isIdentifier(childnode, name)).length > 0;
79
+ } else {
80
+ return false;
55
81
  }
56
- for (let i = 0; i < name.length; i++) {
57
- if ( name[i] === node.name || name[i] === "*" ) {
58
- return true;
59
- }
60
- }
61
- return false;
62
82
  }
63
83
 
64
84
  function getPropertyKey(property) {
65
- if ( property.key.type === Syntax.Identifier ) {
85
+ if ( property.type === Syntax.SpreadElement ) {
86
+ // TODO: Support interpreting SpreadElements
87
+ return;
88
+ } else if ( property.key.type === Syntax.Identifier && property.computed !== true ) {
66
89
  return property.key.name;
67
90
  } else if ( property.key.type === Syntax.Literal ) {
68
91
  return String(property.key.value);
69
- } else {
70
- throw new Error();
71
92
  }
72
93
  }
73
94
 
@@ -102,10 +123,15 @@ function getValue(obj, names) {
102
123
  */
103
124
  function getStringArray(array, skipNonStringLiterals) {
104
125
  return array.elements.reduce( (result, item) => {
105
- if ( isString(item) ) {
106
- result.push(item.value);
126
+ const value = getStringValue(item);
127
+ if ( value !== undefined ) {
128
+ result.push(value);
107
129
  } else if ( !skipNonStringLiterals ) {
108
- throw new TypeError("array element is not a string literal:" + item.type);
130
+ if (item.type === Syntax.TemplateLiteral) {
131
+ throw new TypeError("array element is a template literal with expressions");
132
+ } else {
133
+ throw new TypeError("array element is not a string literal: " + item.type);
134
+ }
109
135
  }
110
136
  return result;
111
137
  }, []);
@@ -113,6 +139,7 @@ function getStringArray(array, skipNonStringLiterals) {
113
139
 
114
140
  module.exports = {
115
141
  isString,
142
+ getStringValue,
116
143
  isBoolean,
117
144
  isMethodCall,
118
145
  isNamedObject,