@ui5/builder 3.0.0-beta.2 → 3.0.0-beta.3

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,16 @@
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-beta.2...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.3...HEAD).
6
+
7
+ <a name="v3.0.0-beta.3"></a>
8
+ ## [v3.0.0-beta.3] - 2022-11-29
9
+ ### Features
10
+ - **jsdoc:** Support destructuring of enums for defaultValue ([#775](https://github.com/SAP/ui5-builder/issues/775)) [`523f365`](https://github.com/SAP/ui5-builder/commit/523f365cb917997c5031d245309c21e9e4b3e311)
11
+
6
12
 
7
13
  <a name="v3.0.0-beta.2"></a>
8
- ## [v3.0.0-beta.2] - 2022-11-17
14
+ ## [v3.0.0-beta.2] - 2022-11-29
9
15
  ### Breaking Changes
10
16
  - Removal of manifestBundler and generateManifestBundle ([#838](https://github.com/SAP/ui5-builder/issues/838)) [`07a5be2`](https://github.com/SAP/ui5-builder/commit/07a5be2b6d9aa23cf78ddd17951c832d6dec7bef)
11
17
  - **JSDoc:** Fail build when jsdoc command failed ([#845](https://github.com/SAP/ui5-builder/issues/845)) [`c2916b4`](https://github.com/SAP/ui5-builder/commit/c2916b4f1d49b5500e4b51143d4e6065ac200eef)
@@ -803,6 +809,7 @@ to load the custom bundle file instead.
803
809
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
804
810
 
805
811
 
812
+ [v3.0.0-beta.3]: https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.2...v3.0.0-beta.3
806
813
  [v3.0.0-beta.2]: https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.1...v3.0.0-beta.2
807
814
  [v3.0.0-beta.1]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.12...v3.0.0-beta.1
808
815
  [v3.0.0-alpha.12]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.11...v3.0.0-alpha.12
@@ -66,6 +66,7 @@ const fs = require('jsdoc/fs');
66
66
  const path = require('jsdoc/path');
67
67
  const logger = require('jsdoc/util/logger');
68
68
  const pluginConfig = (env.conf && env.conf.templates && env.conf.templates.ui5) || env.opts.sapui5 || {};
69
+ const escope = require("escope");
69
70
 
70
71
  /* ---- logging ---- */
71
72
 
@@ -115,6 +116,9 @@ let docletUid = 0;
115
116
 
116
117
  let currentProgram;
117
118
 
119
+ // Scope Manager
120
+ let scopeManager;
121
+
118
122
  /**
119
123
  * Information about the current module.
120
124
  *
@@ -255,25 +259,33 @@ function analyzeModuleDefinition(node) {
255
259
  currentModule.dependencies = convertValue(args[arg], "string[]");
256
260
  arg++;
257
261
  }
258
- if ( arg < args.length &&
262
+ if ( arg < args.length &&
259
263
  [Syntax.FunctionExpression, Syntax.ArrowFunctionExpression].includes(args[arg].type)) {
260
264
 
261
265
  currentModule.factory = args[arg];
262
266
  arg++;
263
267
  }
268
+
264
269
  if ( currentModule.dependencies && currentModule.factory ) {
265
270
  for ( let i = 0; i < currentModule.dependencies.length && i < currentModule.factory.params.length; i++ ) {
266
- const name =
267
- (currentModule.factory.params[i].type === Syntax.ObjectPattern)
268
- ? currentModule.factory.params[i].properties[0].value.name // ObjectPattern means destructuring of the parameter
269
- : currentModule.factory.params[i].name; // simple Identifier
271
+ let names = [];
270
272
 
271
- const module = resolveModuleName(currentModule.module, currentModule.dependencies[i]);
272
- debug(` import ${name} from '${module}'`);
273
- currentModule.localNames[name] = {
274
- module: module
275
- // no (or empty) path
276
- };
273
+ if ( [Syntax.ObjectPattern, Syntax.ArrayPattern].includes(currentModule.factory.params[i].type) ) { // ObjectPattern/ArrayPattern means destructuring of the parameter of a function
274
+ names = resolveObjectPatternChain(currentModule.factory.params[i], null, []);
275
+
276
+ } else if (currentModule.factory.params[i].type === Syntax.Identifier) { // simple Identifier
277
+ names = [{original: currentModule.factory.params[i].name}];
278
+ }
279
+
280
+ names.forEach(name => {
281
+ const module = resolveModuleName(currentModule.module, currentModule.dependencies[i]);
282
+ debug(` import ${name.renamed || name.original} from '${module}'`);
283
+
284
+ currentModule.localNames[name.renamed || name.original] = {
285
+ module: module,
286
+ ...(name.path ? {path: name.path} : {})
287
+ };
288
+ });
277
289
  }
278
290
  }
279
291
  if ( currentModule.factory ) {
@@ -460,7 +472,7 @@ function createPropertyMap(node, defaultKey) {
460
472
 
461
473
  /**
462
474
  * Resolves potential wrapper expressions like: ChainExpression, AwaitExpression, etc.
463
- * @param {Node} node
475
+ * @param {Node} node
464
476
  * @returns {Node} the resolved node
465
477
  */
466
478
  function resolvePotentialWrapperExpression(node) {
@@ -484,9 +496,9 @@ function resolvePotentialWrapperExpression(node) {
484
496
 
485
497
  /**
486
498
  * Strips the ChainExpression wrapper if such
487
- *
488
- * @param {Node} rootNode
489
- * @param {String} path
499
+ *
500
+ * @param {Node} rootNode
501
+ * @param {String} path
490
502
  * @returns {Node}
491
503
  */
492
504
  function stripChainWrappers(rootNode, path) {
@@ -516,8 +528,8 @@ function isTemplateLiteralWithoutExpression(node) {
516
528
 
517
529
  /**
518
530
  * Checks whether a node is Literal or TemplateLiteral without an expression
519
- *
520
- * @param {Node} node
531
+ *
532
+ * @param {Node} node
521
533
  * @returns {String}
522
534
  */
523
535
  function isStringLiteral(node) {
@@ -564,8 +576,8 @@ function isArrowFuncExpression(node) {
564
576
 
565
577
  /**
566
578
  * Checks whether the node is of a "returning" type
567
- *
568
- * @param {Node} node
579
+ *
580
+ * @param {Node} node
569
581
  * @returns {Boolean}
570
582
  */
571
583
  function isReturningNode(node) {
@@ -643,6 +655,254 @@ function isPotentialEnum(node) {
643
655
  return node.properties.every((prop) => isCompileTimeConstant(prop.value));
644
656
  }
645
657
 
658
+ // ---- ES6+ Destructuring ---------------------------------------------------------
659
+
660
+ /**
661
+ * Resolves (nested) Object/ArrayPattern nodes and builds a "path"
662
+ *
663
+ * @param {Node} valueNode
664
+ * @param {Node} keyNode
665
+ * @param {Array<String>} keyChain
666
+ * @returns Array<Object<Node, Node, Array<String>>>
667
+ */
668
+ function resolveObjectPatternChain (valueNode, keyNode, keyChain) {
669
+ let chainSequence = [];
670
+
671
+ if (valueNode && valueNode.type === Syntax.ObjectPattern) {
672
+ for (let i = 0; i < valueNode.properties.length; i++) {
673
+ chainSequence = chainSequence.concat(
674
+ resolveObjectPatternChain(
675
+ valueNode.properties[i].value,
676
+ valueNode.properties[i].key,
677
+ [...keyChain, valueNode.properties[i].key.name] )
678
+ );
679
+ }
680
+ } else if (valueNode && valueNode.type === Syntax.ArrayPattern) {
681
+ for (let i = 0; i < valueNode.elements.length; i++) {
682
+ chainSequence = chainSequence.concat(
683
+ resolveObjectPatternChain(
684
+ valueNode.elements[i],
685
+ valueNode.elements[i],
686
+ [...keyChain, String(i)]
687
+ )
688
+ );
689
+ }
690
+ } else {
691
+
692
+ let result = { original: keyNode.name, path: keyChain.join(".") };
693
+
694
+ if (keyNode.name !== valueNode.name) {
695
+ // Renaming
696
+ result.renamed = valueNode.name;
697
+ }
698
+
699
+ chainSequence.push(result);
700
+ }
701
+
702
+ return chainSequence;
703
+ }
704
+
705
+ /**
706
+ * Tries to resolve an ENUM, regardless where it is defined and being destructured.
707
+ *
708
+ * @param {Node} node
709
+ * @param {String} type
710
+ * @returns {Object}
711
+ */
712
+ function resolvePotentialEnum(node, type) {
713
+ let value = resolveFullyQuantifiedName(node);
714
+
715
+ if ( value.startsWith(type + ".") ) {
716
+ // starts with fully qualified enum name -> cut off name
717
+ value = value.slice(type.length + 1);
718
+ return {
719
+ value: value,
720
+ raw: value
721
+ };
722
+ }
723
+ }
724
+
725
+ /**
726
+ * Returns the {Node} of the destructured argument of a (arrow) function.
727
+ *
728
+ * @param {Definition|ParameterDefinition} varDefinition
729
+ * @returns {Node}
730
+ */
731
+ function getFuncArgumentDestructNode(varDefinition) {
732
+ if (
733
+ [
734
+ Syntax.ArrowFunctionExpression,
735
+ Syntax.FunctionDeclaration,
736
+ Syntax.FunctionExpression,
737
+ ].includes(varDefinition.node.type)
738
+ ) {
739
+ return varDefinition.node.params[varDefinition.index];
740
+ }
741
+
742
+ // return undefined;
743
+ }
744
+
745
+ /**
746
+ * Checks whether a variable has been destructured.
747
+ *
748
+ * @param {Variable} variable
749
+ * @returns
750
+ */
751
+ function isVarDestructuring(variable) {
752
+ const defNode =
753
+ variable &&
754
+ variable.defs &&
755
+ ( getFuncArgumentDestructNode(variable.defs[0]) // (arrow) function argument
756
+ || variable.defs[0].node.id ); // variable definition
757
+
758
+ return defNode && [Syntax.ObjectPattern, Syntax.ArrayPattern].includes( defNode.type );
759
+ }
760
+
761
+ /**
762
+ * Checks whether a var has been renamed while destructuring i.e. {A: b} = SomeObject
763
+ *
764
+ * @param {Variable} variable
765
+ * @returns {Object}
766
+ */
767
+ function checkVarRenaming(variable) {
768
+ // variable.defs[0].node.id.type === Syntax.ObjectPattern -> Renaming
769
+ // variable.defs[0].node.id.properties[0].key.name === variable.name; // Original
770
+ // variable.defs[0].node.id.properties[0].value.name === variable.name; // Renamed
771
+
772
+ // If variable.defs (Variable definitions within the source code) are more 1, then we'd not be able to
773
+ // determine which defintion to use. For example:
774
+ // function doSomething({a}, b) {
775
+ // console.log(a);
776
+ // var { c : a } = { b };
777
+ // console.log(a);
778
+ // }
779
+ // doSomething({a:42}, 5);
780
+ //
781
+ // So, we'd not able to analyze which "a" to which console.log to map
782
+ if (
783
+ !variable
784
+ || !variable.defs
785
+ || variable.defs.length !== 1
786
+ ) {
787
+ return null;
788
+ }
789
+
790
+ const varDefinition = variable.defs[0];
791
+ const defNode = getFuncArgumentDestructNode(varDefinition) // (arrow) function argument
792
+ || varDefinition.node.id; // variable definition
793
+
794
+ return resolveObjectPatternChain(defNode, null, []).find(
795
+ ({ original, renamed }) =>
796
+ renamed === variable.name || original === variable.name
797
+ );
798
+ }
799
+
800
+ /**
801
+ * Builds the fully quantified name when there's a destructuring of a variable
802
+ *
803
+ * @param {Node} node
804
+ * @returns {string}
805
+ */
806
+ function resolveFullyQuantifiedName(node) {
807
+ // The missing part is on the left side. The right side is clear.
808
+ // We would eiter ways resolve to the same leftmost token.
809
+ let leftMostName = getLeftmostName(node);
810
+ let originalName = getObjectName(node) || "";
811
+ const currentScope = getEnclosingVariableScope(node);
812
+
813
+ if (!currentScope) {
814
+ return "";
815
+ }
816
+
817
+ while (leftMostName) {
818
+ const curVar = currentScope.set.get(leftMostName);
819
+
820
+ if (!curVar) {
821
+ break;
822
+ }
823
+
824
+ if ( !isVarDestructuring(curVar) ) {
825
+ // Not a destructuring
826
+ return getResolvedName(originalName, leftMostName);
827
+ }
828
+
829
+ const potentialRenaming = checkVarRenaming(curVar);
830
+ if (potentialRenaming) {
831
+ let renamedChunks = originalName.split(".");
832
+ renamedChunks = getResolvedName( originalName, renamedChunks[0] ).split(".");
833
+
834
+ // when getResolvedName() was not able to resolve the renaming of a variable
835
+ // with currentModule.localNames[].path i.e. in "chained" destructuring, where
836
+ // the variable is not within localNames registry
837
+ if ( potentialRenaming.renamed === renamedChunks[0] ) { // Checks for direct renaming
838
+ renamedChunks[0] = potentialRenaming.original;
839
+ }
840
+ // Considers the 'path' if it differs from the original name i.e. there's some namespace before it
841
+ if ( potentialRenaming.original === renamedChunks[0] && potentialRenaming.path !== potentialRenaming.original ) {
842
+ renamedChunks[0] = potentialRenaming.path;
843
+ }
844
+
845
+ originalName = renamedChunks.join(".");
846
+ }
847
+
848
+ const writeExpr = curVar.references.filter((ref) => ref.writeExpr);
849
+
850
+ // The same case as variable.defs- we're not able to determine the correct chain in case of multiple writeExpr
851
+ if (writeExpr.length !== 1) {
852
+ // writeExpr.length === 0 means an function argument and then we need
853
+ // just to return the already build originalName
854
+ return writeExpr.length === 0 ? originalName : "";
855
+ }
856
+
857
+ const writeExprNode = writeExpr[0].writeExpr;
858
+
859
+ if ( writeExprNode.type === Syntax.MemberExpression && !writeExprNode.computed && writeExprNode.object.type === Syntax.Identifier ) {
860
+ leftMostName = writeExprNode.object.name;
861
+
862
+ } else if (writeExprNode.type === Syntax.MemberExpression && writeExprNode.object.type === Syntax.MemberExpression) { // Standalone variable without leading dot notation namespace
863
+ leftMostName = getResolvedObjectName(writeExprNode);
864
+
865
+ } else if (writeExprNode.type === Syntax.Identifier) {
866
+ leftMostName = writeExprNode.name;
867
+
868
+ } else {
869
+ leftMostName = "";
870
+ }
871
+
872
+ if (leftMostName) {
873
+ originalName = leftMostName + "." + originalName;
874
+ }
875
+ }
876
+
877
+ return originalName;
878
+ }
879
+
880
+ /**
881
+ * Gets enclosing scope
882
+ *
883
+ * @param {Node} node
884
+ * @returns {Scope}
885
+ */
886
+ function getEnclosingVariableScope (node) {
887
+ // Get to the nearest upper scope
888
+ let nearestScopeableNode = node;
889
+ let nearestScope = scopeManager.acquire(nearestScopeableNode);
890
+ while (
891
+ !nearestScope &&
892
+ nearestScopeableNode &&
893
+ nearestScopeableNode.parent
894
+ ) {
895
+ nearestScopeableNode = nearestScopeableNode.parent;
896
+ nearestScope = scopeManager.acquire(nearestScopeableNode);
897
+ }
898
+
899
+ // FunctionExpression with a name hold the value of the name in its scope.
900
+ // So, we need to unwrap to get to the real scope with var definitions
901
+ return nearestScope.functionExpressionScope
902
+ ? nearestScope.childScopes[0]
903
+ : nearestScope;
904
+ }
905
+
646
906
  function isCompileTimeConstant(node) {
647
907
  return node && node.type === Syntax.Literal;
648
908
  }
@@ -675,6 +935,11 @@ function getLeftmostName(node) {
675
935
  function getResolvedObjectName(node) {
676
936
  const name = getObjectName(node);
677
937
  const _import = getLeftmostName(node);
938
+
939
+ return getResolvedName(name, _import);
940
+ }
941
+
942
+ function getResolvedName(name, _import) {
678
943
  const local = _import && currentModule.localNames[_import];
679
944
  if ( name && local && (local.class || local.module) ) {
680
945
  let resolvedName;
@@ -734,18 +999,12 @@ function convertValueWithRaw(node, type, propertyName) {
734
999
  } else if ( isMemberExpression(node) && type ) {
735
1000
 
736
1001
  // enum value (a.b.c)
737
- value = getResolvedObjectName(node);
738
- if ( value.indexOf(type + ".") === 0 ) {
739
- // starts with fully qualified enum name -> cut off name
740
- value = value.slice(type.length + 1);
741
- return {
742
- value: value,
743
- raw: value
744
- };
745
- // } else if ( value.indexOf(type.split(".").slice(-1)[0] + ".") === 0 ) {
746
- // // unqualified name might be a local name (just a guess - would need static code analysis for proper solution)
747
- // return value.slice(type.split(".").slice(-1)[0].length + 1);
1002
+ const potentialEnum = resolvePotentialEnum(node, type);
1003
+
1004
+ if ( potentialEnum ) {
1005
+ return potentialEnum;
748
1006
  } else {
1007
+ value = getResolvedObjectName(node);
749
1008
  warning(`did not understand default value '${value}'${propertyName ? " of property '" + propertyName + "'" : ""}, falling back to source`);
750
1009
  let raw = value;
751
1010
  if ( currentSource && node.range ) {
@@ -773,6 +1032,15 @@ function convertValueWithRaw(node, type, propertyName) {
773
1032
  raw: local.raw
774
1033
  };
775
1034
  }
1035
+
1036
+ // This could be an ENUM which has been destructured up to the enum value part. In that case the node.type === Syntax.Identifier
1037
+ // For example, the `Solid` const in the following snippet:
1038
+ // sap.ui.define(["sap/m/library"], ( { BackgroundDesign } ) => {
1039
+ // const { Solid } = BackgroundDesign;
1040
+ const potentialEnum = resolvePotentialEnum(node, type);
1041
+ if ( potentialEnum ) {
1042
+ return potentialEnum;
1043
+ }
776
1044
  } else if ( node.type === Syntax.ArrayExpression ) {
777
1045
 
778
1046
  if ( node.elements.length === 0 ) {
@@ -1232,16 +1500,16 @@ function determineValueRange(expression, varname, inverse) {
1232
1500
  && expression.left.type === Syntax.BinaryExpression
1233
1501
  && expression.right.type === Syntax.BinaryExpression ) {
1234
1502
 
1235
- if ( expression.operator === "&&"
1236
- && determineValueRangeBorder(range, expression.left, varname, inverse)
1503
+ if ( expression.operator === "&&"
1504
+ && determineValueRangeBorder(range, expression.left, varname, inverse)
1237
1505
  && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1238
1506
  return range;
1239
1507
  } else if ( ["||", "??"].includes(expression.operator)
1240
- && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1508
+ && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1241
1509
  || determineValueRangeBorder(range, expression.right, varname, inverse) )) {
1242
1510
  return range;
1243
1511
  }
1244
-
1512
+
1245
1513
  } else if ( expression.type === Syntax.BinaryExpression
1246
1514
  && determineValueRangeBorder(range, expression, varname, inverse) ) {
1247
1515
  return range;
@@ -2698,6 +2966,7 @@ exports.astNodeVisitor = {
2698
2966
 
2699
2967
  if ( node.type === Syntax.Program ) {
2700
2968
  currentProgram = node;
2969
+ scopeManager = escope.analyze(currentProgram);
2701
2970
  }
2702
2971
 
2703
2972
  function processExtendCall(extendCall, comment, commentAlreadyProcessed) {
@@ -2859,7 +3128,7 @@ exports.astNodeVisitor = {
2859
3128
  * to retain the full record type structure.
2860
3129
  *
2861
3130
  * JSDoc is copyright (c) 2011-present Michael Mathews micmath@gmail.com and the contributors to JSDoc.
2862
- */
3131
+ */
2863
3132
  function getTypeStrings(parsedType, isOutermostType) {
2864
3133
  let applications;
2865
3134
  let typeString;
@@ -2964,11 +3233,11 @@ exports.astNodeVisitor = {
2964
3233
  // #### BEGIN: MODIFIED BY SAP
2965
3234
  if ( tagInfo && (/function/.test(tagInfo.typeExpression) || /\{.*\}/.test(tagInfo.typeExpression)) && tagInfo.parsedType ) {
2966
3235
  // #### END: MODIFIED BY SAP
2967
- // console.info("old typeExpression", tagInfo.typeExpression);
2968
- // console.info("old parse tree", tagInfo.parsedType);
2969
- // console.info("old parse result", tagInfo.type);
3236
+ // console.info("old typeExpression", tagInfo.typeExpression);
3237
+ // console.info("old parse tree", tagInfo.parsedType);
3238
+ // console.info("old parse result", tagInfo.type);
2970
3239
  tagInfo.type = getTypeStrings(tagInfo.parsedType);
2971
- // console.info("new parse result", tagInfo.type);
3240
+ // console.info("new parse result", tagInfo.type);
2972
3241
  }
2973
3242
  return tagInfo;
2974
3243
  }
@@ -87,8 +87,11 @@ export default async function({
87
87
  // Omit -dbg files for optimize bundles and vice versa
88
88
  const filterTag = optimize ?
89
89
  taskUtil.STANDARD_TAGS.IsDebugVariant : taskUtil.STANDARD_TAGS.HasDebugVariant;
90
- combo = await combo.filter(function(resource) {
91
- return !taskUtil.getTag(resource, filterTag);
90
+ combo = taskUtil.resourceFactory.createFilterReader({
91
+ reader: combo,
92
+ callback: function(resource) {
93
+ return !taskUtil.getTag(resource, filterTag);
94
+ }
92
95
  });
93
96
  }
94
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/builder",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0-beta.3",
4
4
  "description": "UI5 Tooling - Builder",
5
5
  "author": {
6
6
  "name": "SAP SE",