@ui5/builder 3.0.0-beta.1 → 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,7 +2,29 @@
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.1...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
+
12
+
13
+ <a name="v3.0.0-beta.2"></a>
14
+ ## [v3.0.0-beta.2] - 2022-11-29
15
+ ### Breaking Changes
16
+ - Removal of manifestBundler and generateManifestBundle ([#838](https://github.com/SAP/ui5-builder/issues/838)) [`07a5be2`](https://github.com/SAP/ui5-builder/commit/07a5be2b6d9aa23cf78ddd17951c832d6dec7bef)
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)
18
+
19
+ ### Features
20
+ - Support ES2022 language features ([#848](https://github.com/SAP/ui5-builder/issues/848)) [`f9b8457`](https://github.com/SAP/ui5-builder/commit/f9b845726731a0e02ec4a499e2a1a82a639174a8)
21
+
22
+ ### BREAKING CHANGE
23
+
24
+ The `jsdocGenerator` processor and the corresponding `generateJsdoc` task will now throw an error when JSDoc reports an error (exit code != 0). This will also fail the build when running `ui5 build jsdoc`.
25
+
26
+ The manifestBundler processor and generateManifestBundle task has been removed because it is no longer required for the HTML5 repository in Cloud Foundry.
27
+
6
28
 
7
29
  <a name="v3.0.0-beta.1"></a>
8
30
  ## [v3.0.0-beta.1] - 2022-11-07
@@ -787,6 +809,8 @@ to load the custom bundle file instead.
787
809
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
788
810
 
789
811
 
812
+ [v3.0.0-beta.3]: https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.2...v3.0.0-beta.3
813
+ [v3.0.0-beta.2]: https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.1...v3.0.0-beta.2
790
814
  [v3.0.0-beta.1]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.12...v3.0.0-beta.1
791
815
  [v3.0.0-alpha.12]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.11...v3.0.0-alpha.12
792
816
  [v3.0.0-alpha.11]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.10...v3.0.0-alpha.11
@@ -135,10 +135,10 @@ const EnrichedVisitorKeys = (function() {
135
135
  * All properties in an object pattern are executed.
136
136
  */
137
137
  ObjectPattern: [], // properties
138
- // PrivateIdentifier: [], // will come with ES2022
138
+ PrivateIdentifier: [],
139
139
  Program: [],
140
140
  Property: [],
141
- // PropertyDefinition: [], // will come with ES2022
141
+ PropertyDefinition: [],
142
142
  /*
143
143
  * argument of the rest element is always executed under the same condition as the rest element itself
144
144
  */
@@ -146,7 +146,7 @@ const EnrichedVisitorKeys = (function() {
146
146
  ReturnStatement: [],
147
147
  SequenceExpression: [],
148
148
  SpreadElement: [], // the argument of the spread operator always needs to be evaluated - argument
149
- // StaticBlock: [], // will come with ES2022
149
+ StaticBlock: [],
150
150
  Super: [],
151
151
  SwitchStatement: [],
152
152
  SwitchCase: ["test", "consequent"], // test and consequent are executed only conditionally
@@ -196,15 +196,6 @@ const EnrichedVisitorKeys = (function() {
196
196
  return;
197
197
  }
198
198
 
199
- // Ignore new ES2022 syntax as we currently use ES2021 (see parseUtils.js)
200
- if (
201
- type === "PrivateIdentifier" ||
202
- type === "PropertyDefinition" ||
203
- type === "StaticBlock"
204
- ) {
205
- return;
206
- }
207
-
208
199
  const visitorKeys = VisitorKeys[type];
209
200
  const condKeys = TempKeys[type];
210
201
  if ( condKeys === undefined ) {
@@ -535,6 +526,15 @@ class JSModuleAnalyzer {
535
526
  }
536
527
  break;
537
528
 
529
+ case Syntax.PropertyDefinition:
530
+
531
+ // Instance properties (static=false) are only initialized when an instance is created (conditional)
532
+ // but a computed key is always evaluated on class initialization (eager)
533
+ visit(node.key, conditional);
534
+ visit(node.value, !node.static || conditional);
535
+
536
+ break;
537
+
538
538
  default:
539
539
  if ( condKeys == null ) {
540
540
  log.error("Unhandled AST node type " + node.type, node);
@@ -49,7 +49,7 @@ function createRawInfo(rawModule) {
49
49
  }
50
50
 
51
51
  export function getDependencyInfos( name, content ) {
52
- const infos = {};
52
+ const infos = Object.create(null);
53
53
  parser.parseString(content, (err, result) => {
54
54
  if ( result &&
55
55
  result.library &&
@@ -39,7 +39,7 @@ class ModuleInfo {
39
39
  constructor(name) {
40
40
  this._name = name;
41
41
  this.subModules = [];
42
- this._dependencies = {};
42
+ this._dependencies = Object.create(null);
43
43
  this.dynamicDependencies = false;
44
44
 
45
45
  /**
@@ -66,7 +66,7 @@ class ResourceCollector {
66
66
  *
67
67
  * If no component is given, orphans will only be reported but not added to any component (default).
68
68
  *
69
- * @param {object<string, string[]>} list component to list of components
69
+ * @param {Object<string, string[]>} list component to list of components
70
70
  */
71
71
  setExternalResources(list) {
72
72
  this._externalResources = list;
@@ -56,6 +56,8 @@ export function isMethodCall(node, methodPath) {
56
56
  }
57
57
 
58
58
  export function isNamedObject(node, objectPath, length) {
59
+ // TODO: Support PrivateIdentifier (foo.#bar)
60
+
59
61
  // console.log("checking for named object ", node, objectPath, length);
60
62
  while ( length > 1 &&
61
63
  node.type === Syntax.MemberExpression &&
@@ -15,7 +15,7 @@ export default async function(resource) {
15
15
  let propertiesFileSourceEncoding = project && project.getPropertiesFileSourceEncoding();
16
16
 
17
17
  if (!propertiesFileSourceEncoding) {
18
- if (project && ["0.1", "1.0", "1.1"].includes(project.getSpecVersion())) {
18
+ if (project && project.getSpecVersion().lte("1.1")) {
19
19
  // default encoding to "ISO-8859-1" for old specVersions
20
20
  propertiesFileSourceEncoding = "ISO-8859-1";
21
21
  } else {
@@ -7,7 +7,7 @@ export function parseJS(code, userOptions = {}) {
7
7
  // allowed options and their defaults
8
8
  const options = {
9
9
  comment: false,
10
- ecmaVersion: 2021, // NOTE: Adopt JSModuleAnalyzer.js to allow new Syntax when upgrading to newer ECMA versions
10
+ ecmaVersion: 2022, // NOTE: Adopt JSModuleAnalyzer.js to allow new Syntax when upgrading to newer ECMA versions
11
11
  range: false,
12
12
  sourceType: "script",
13
13
  };
@@ -170,18 +170,16 @@ async function buildJsdoc({sourcePath, configPath}) {
170
170
  "--verbose",
171
171
  sourcePath
172
172
  ];
173
- return new Promise((resolve, reject) => {
173
+ const exitCode = await new Promise((resolve /* , reject */) => {
174
174
  const child = spawn("node", args, {
175
175
  stdio: ["ignore", "ignore", "inherit"]
176
176
  });
177
- child.on("close", function(code) {
178
- if (code === 0 || code === 1) {
179
- resolve();
180
- } else {
181
- reject(new Error(`JSDoc child process closed with code ${code}`));
182
- }
183
- });
177
+ child.on("close", resolve);
184
178
  });
179
+
180
+ if (exitCode !== 0) {
181
+ throw new Error(`JSDoc reported an error, check the log for issues (exit code: ${exitCode})`);
182
+ }
185
183
  }
186
184
 
187
185
  jsdocGenerator._generateJsdocConfig = generateJsdocConfig;
@@ -989,11 +989,12 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
989
989
  const {sources} = options;
990
990
  let {librarySrcDir, libraryTestDir} = options;
991
991
 
992
- // Using path.join to normalize POSIX paths to Windows paths on Windows
993
- librarySrcDir = path.join(librarySrcDir);
994
- libraryTestDir = path.join(libraryTestDir);
995
-
996
992
  if (Array.isArray(sources) && typeof librarySrcDir === "string" && typeof libraryTestDir === "string") {
993
+
994
+ // Using path.join to normalize POSIX paths to Windows paths on Windows
995
+ librarySrcDir = path.join(librarySrcDir);
996
+ libraryTestDir = path.join(libraryTestDir);
997
+
997
998
  /**
998
999
  * Calculate prefix to check
999
1000
  *
@@ -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) {
@@ -507,16 +519,17 @@ function stripChainWrappers(rootNode, path) {
507
519
 
508
520
  function isTemplateLiteralWithoutExpression(node) {
509
521
  return (
510
- node?.type === Syntax.TemplateLiteral &&
511
- node?.expressions?.length === 0 &&
512
- node?.quasis?.length === 1
522
+ node &&
523
+ node.type === Syntax.TemplateLiteral &&
524
+ node.expressions && node.expressions.length === 0 &&
525
+ node.quasis && node.quasis.length === 1
513
526
  );
514
527
  }
515
528
 
516
529
  /**
517
530
  * Checks whether a node is Literal or TemplateLiteral without an expression
518
- *
519
- * @param {Node} node
531
+ *
532
+ * @param {Node} node
520
533
  * @returns {String}
521
534
  */
522
535
  function isStringLiteral(node) {
@@ -563,8 +576,8 @@ function isArrowFuncExpression(node) {
563
576
 
564
577
  /**
565
578
  * Checks whether the node is of a "returning" type
566
- *
567
- * @param {Node} node
579
+ *
580
+ * @param {Node} node
568
581
  * @returns {Boolean}
569
582
  */
570
583
  function isReturningNode(node) {
@@ -642,6 +655,254 @@ function isPotentialEnum(node) {
642
655
  return node.properties.every((prop) => isCompileTimeConstant(prop.value));
643
656
  }
644
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
+
645
906
  function isCompileTimeConstant(node) {
646
907
  return node && node.type === Syntax.Literal;
647
908
  }
@@ -674,6 +935,11 @@ function getLeftmostName(node) {
674
935
  function getResolvedObjectName(node) {
675
936
  const name = getObjectName(node);
676
937
  const _import = getLeftmostName(node);
938
+
939
+ return getResolvedName(name, _import);
940
+ }
941
+
942
+ function getResolvedName(name, _import) {
677
943
  const local = _import && currentModule.localNames[_import];
678
944
  if ( name && local && (local.class || local.module) ) {
679
945
  let resolvedName;
@@ -733,18 +999,12 @@ function convertValueWithRaw(node, type, propertyName) {
733
999
  } else if ( isMemberExpression(node) && type ) {
734
1000
 
735
1001
  // enum value (a.b.c)
736
- value = getResolvedObjectName(node);
737
- if ( value.indexOf(type + ".") === 0 ) {
738
- // starts with fully qualified enum name -> cut off name
739
- value = value.slice(type.length + 1);
740
- return {
741
- value: value,
742
- raw: value
743
- };
744
- // } else if ( value.indexOf(type.split(".").slice(-1)[0] + ".") === 0 ) {
745
- // // unqualified name might be a local name (just a guess - would need static code analysis for proper solution)
746
- // return value.slice(type.split(".").slice(-1)[0].length + 1);
1002
+ const potentialEnum = resolvePotentialEnum(node, type);
1003
+
1004
+ if ( potentialEnum ) {
1005
+ return potentialEnum;
747
1006
  } else {
1007
+ value = getResolvedObjectName(node);
748
1008
  warning(`did not understand default value '${value}'${propertyName ? " of property '" + propertyName + "'" : ""}, falling back to source`);
749
1009
  let raw = value;
750
1010
  if ( currentSource && node.range ) {
@@ -772,6 +1032,15 @@ function convertValueWithRaw(node, type, propertyName) {
772
1032
  raw: local.raw
773
1033
  };
774
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
+ }
775
1044
  } else if ( node.type === Syntax.ArrayExpression ) {
776
1045
 
777
1046
  if ( node.elements.length === 0 ) {
@@ -801,9 +1070,11 @@ function convertValueWithRaw(node, type, propertyName) {
801
1070
  }
802
1071
 
803
1072
  } else if ( isTemplateLiteralWithoutExpression(node) ) {
1073
+ let value = node.quasis[0].value || {};
1074
+
804
1075
  return {
805
- value: node?.quasis?.[0]?.value?.cooked,
806
- raw: node?.quasis?.[0]?.value?.raw,
1076
+ value: value.cooked,
1077
+ raw: value.raw,
807
1078
  };
808
1079
  }
809
1080
 
@@ -1229,16 +1500,16 @@ function determineValueRange(expression, varname, inverse) {
1229
1500
  && expression.left.type === Syntax.BinaryExpression
1230
1501
  && expression.right.type === Syntax.BinaryExpression ) {
1231
1502
 
1232
- if ( expression.operator === "&&"
1233
- && determineValueRangeBorder(range, expression.left, varname, inverse)
1503
+ if ( expression.operator === "&&"
1504
+ && determineValueRangeBorder(range, expression.left, varname, inverse)
1234
1505
  && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1235
1506
  return range;
1236
1507
  } else if ( ["||", "??"].includes(expression.operator)
1237
- && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1508
+ && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1238
1509
  || determineValueRangeBorder(range, expression.right, varname, inverse) )) {
1239
1510
  return range;
1240
1511
  }
1241
-
1512
+
1242
1513
  } else if ( expression.type === Syntax.BinaryExpression
1243
1514
  && determineValueRangeBorder(range, expression, varname, inverse) ) {
1244
1515
  return range;
@@ -2495,6 +2766,7 @@ exports.handlers = {
2495
2766
  e.doclet.meta.__shortpath = getRelativePath(filepath);
2496
2767
  _ui5data.resource = currentModule.resource;
2497
2768
  _ui5data.module = currentModule.name || currentModule.module;
2769
+ _ui5data.initialLongname = e.doclet.longname;
2498
2770
 
2499
2771
  const localDecl = findLocalDeclaration(e.doclet.meta.lineno, e.doclet.meta.columnno);
2500
2772
  if ( localDecl ) {
@@ -2694,6 +2966,7 @@ exports.astNodeVisitor = {
2694
2966
 
2695
2967
  if ( node.type === Syntax.Program ) {
2696
2968
  currentProgram = node;
2969
+ scopeManager = escope.analyze(currentProgram);
2697
2970
  }
2698
2971
 
2699
2972
  function processExtendCall(extendCall, comment, commentAlreadyProcessed) {
@@ -2855,7 +3128,7 @@ exports.astNodeVisitor = {
2855
3128
  * to retain the full record type structure.
2856
3129
  *
2857
3130
  * JSDoc is copyright (c) 2011-present Michael Mathews micmath@gmail.com and the contributors to JSDoc.
2858
- */
3131
+ */
2859
3132
  function getTypeStrings(parsedType, isOutermostType) {
2860
3133
  let applications;
2861
3134
  let typeString;
@@ -2960,11 +3233,11 @@ exports.astNodeVisitor = {
2960
3233
  // #### BEGIN: MODIFIED BY SAP
2961
3234
  if ( tagInfo && (/function/.test(tagInfo.typeExpression) || /\{.*\}/.test(tagInfo.typeExpression)) && tagInfo.parsedType ) {
2962
3235
  // #### END: MODIFIED BY SAP
2963
- // console.info("old typeExpression", tagInfo.typeExpression);
2964
- // console.info("old parse tree", tagInfo.parsedType);
2965
- // 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);
2966
3239
  tagInfo.type = getTypeStrings(tagInfo.parsedType);
2967
- // console.info("new parse result", tagInfo.type);
3240
+ // console.info("new parse result", tagInfo.type);
2968
3241
  }
2969
3242
  return tagInfo;
2970
3243
  }
@@ -2877,7 +2877,11 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
2877
2877
  }
2878
2878
  tag("method");
2879
2879
  attrib("name", name || member.name);
2880
- if ( member.__ui5.module && member.__ui5.module !== symbol.__ui5.module ) {
2880
+ // write out module and export only when the module is different from the module of the parent entity
2881
+ // and when the member was not cloned (e.g. because it is borrowed)
2882
+ if ( member.__ui5.module
2883
+ && member.__ui5.module !== symbol.__ui5.module
2884
+ && member.__ui5.initialLongname === member.longname ) {
2881
2885
  attrib("module", member.__ui5.module);
2882
2886
  attrib("export", member.__ui5.globalOnly ? GLOBAL_ONLY : member.__ui5.export, '', true);
2883
2887
  }
@@ -178,7 +178,7 @@ class LibraryLessGenerator {
178
178
  * @param {@ui5/fs/Resource[]} parameters.resources List of <code>library.source.less</code>
179
179
  * resources
180
180
  * @param {fs|module:@ui5/fs/fsInterface} parameters.fs Node fs or custom
181
- * [fs interface]{@link module:resources/module:@ui5/fs/fsInterface}
181
+ * [fs interface]{@link module:@ui5/fs/fsInterface}
182
182
  * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with library.less resources
183
183
  */
184
184
  async function createLibraryLess({resources, fs}) {
@@ -349,7 +349,7 @@ async function createManifest(
349
349
  }
350
350
 
351
351
  function collectThemes() {
352
- const themes = {};
352
+ const themes = Object.create(null);
353
353
 
354
354
  // find theme resources and determine theme names from their paths
355
355
  libBundle.getResources(/(?:[^/]+\/)*themes\//).forEach((res) => {
@@ -14,7 +14,7 @@ import escapeUnicode from "escape-unicode";
14
14
  const CHAR_CODE_OF_LAST_ASCII_CHARACTER = 127;
15
15
 
16
16
  // use memoization for escapeUnicode function for performance
17
- const memoizeEscapeUnicodeMap = {};
17
+ const memoizeEscapeUnicodeMap = Object.create(null);
18
18
  const memoizeEscapeUnicode = function(sChar) {
19
19
  if (memoizeEscapeUnicodeMap[sChar]) {
20
20
  return memoizeEscapeUnicodeMap[sChar];
@@ -25,7 +25,7 @@ function getTimestamp() {
25
25
  /**
26
26
  * Manifest libraries as defined in the manifest.json file
27
27
  *
28
- * @typedef {object<string, {lazy: boolean}>} ManifestLibraries
28
+ * @typedef {Object<string, {lazy: boolean}>} ManifestLibraries
29
29
  *
30
30
  * sample:
31
31
  * <pre>
@@ -60,15 +60,15 @@ function getTimestamp() {
60
60
  const processManifest = async (manifestResource) => {
61
61
  const manifestContent = await manifestResource.getString();
62
62
  const manifestObject = JSON.parse(manifestContent);
63
- const manifestInfo = {};
63
+ const manifestInfo = Object.create(null);
64
64
 
65
65
  // sap.ui5/dependencies is used for the "manifestHints/libs"
66
66
  if (manifestObject["sap.ui5"]) {
67
67
  const manifestDependencies = manifestObject["sap.ui5"]["dependencies"];
68
68
  if (manifestDependencies && manifestDependencies.libs) {
69
- const libs = {};
69
+ const libs = Object.create(null);
70
70
  for (const [libKey, libValue] of Object.entries(manifestDependencies.libs)) {
71
- libs[libKey] = {};
71
+ libs[libKey] = Object.create(null);
72
72
  if (libValue.lazy) {
73
73
  libs[libKey].lazy = true;
74
74
  }
@@ -230,7 +230,7 @@ class DependencyInfo {
230
230
  * @returns {object} the object with sorted keys
231
231
  */
232
232
  const sortObjectKeys = (obj) => {
233
- const sortedObject = {};
233
+ const sortedObject = Object.create(null);
234
234
  const keys = Object.keys(obj);
235
235
  keys.sort();
236
236
  keys.forEach((key) => {
@@ -433,7 +433,7 @@ export default async function({options}) {
433
433
  let components;
434
434
  artifactInfos.forEach((artifactInfo) => {
435
435
  artifactInfo.embeds.forEach((embeddedArtifactInfo) => {
436
- const componentObject = {};
436
+ const componentObject = Object.create(null);
437
437
  const bundledComponents = artifactInfo.bundledComponents;
438
438
  const componentName = embeddedArtifactInfo.componentName;
439
439
  if (!bundledComponents.has(componentName)) {
@@ -446,7 +446,7 @@ export default async function({options}) {
446
446
  componentObject.manifestHints = manifestHints;
447
447
  }
448
448
 
449
- components = components || {};
449
+ components = components || Object.create(null);
450
450
  components[componentName] = componentObject;
451
451
  });
452
452
  });
@@ -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
 
@@ -37,9 +37,12 @@ export default async function({
37
37
  }) {
38
38
  let nonDbgWorkspace = workspace;
39
39
  if (taskUtil) {
40
- nonDbgWorkspace = await workspace.filter(function(resource) {
41
- // Remove any debug variants
42
- return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
40
+ nonDbgWorkspace = taskUtil.resourceFactory.createFilterReader({
41
+ reader: workspace,
42
+ callback: function(resource) {
43
+ // Remove any debug variants
44
+ return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
45
+ }
43
46
  });
44
47
  }
45
48
 
@@ -246,9 +246,12 @@ function getSapUiCoreBunDef(name, filters, preload) {
246
246
  export default async function({workspace, taskUtil, options: {skipBundles = [], excludes = [], projectName}}) {
247
247
  let nonDbgWorkspace = workspace;
248
248
  if (taskUtil) {
249
- nonDbgWorkspace = await workspace.filter(function(resource) {
250
- // Remove any debug variants
251
- return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
249
+ nonDbgWorkspace = taskUtil.resourceFactory.createFilterReader({
250
+ reader: workspace,
251
+ callback: function(resource) {
252
+ // Remove any debug variants
253
+ return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
254
+ }
252
255
  });
253
256
  }
254
257
 
@@ -273,7 +276,7 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
273
276
  // This is mainly to have an easier check without version parsing or using semver.
274
277
  // If no project/specVersion is available, the bundles should also be created to not break potential
275
278
  // existing use cases without a properly formed/formatted project tree.
276
- if (!taskUtil || ["0.1", "1.1", "2.0"].includes(taskUtil.getProject().getSpecVersion())) {
279
+ if (!taskUtil || taskUtil.getProject().getSpecVersion().lte("2.0")) {
277
280
  const isEvo = resources.find((resource) => {
278
281
  return resource.getPath() === "/resources/ui5loader.js";
279
282
  });
@@ -281,9 +284,12 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
281
284
  let unoptimizedModuleNameMapping;
282
285
  let unoptimizedResources = resources;
283
286
  if (taskUtil) {
284
- const unoptimizedWorkspace = await workspace.filter(function(resource) {
285
- // Remove any non-debug variants
286
- return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
287
+ const unoptimizedWorkspace = taskUtil.resourceFactory.createFilterReader({
288
+ reader: workspace,
289
+ callback: function(resource) {
290
+ // Remove any non-debug variants
291
+ return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
292
+ }
287
293
  });
288
294
  unoptimizedResources =
289
295
  await unoptimizedWorkspace.byGlob("/**/*.{js,json,xml,html,properties,library,js.map}");
@@ -15,7 +15,7 @@ import {getNonDebugName} from "../../../lbt/utils/ModuleName.js";
15
15
  * @returns {object} Module name mapping
16
16
  */
17
17
  export default function({resources, taskUtil}) {
18
- const moduleNameMapping = {};
18
+ const moduleNameMapping = Object.create(null);
19
19
  for (let i = resources.length - 1; i >= 0; i--) {
20
20
  const resource = resources[i];
21
21
  if (taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant)) {
@@ -57,7 +57,7 @@ export default function({workspace, options}) {
57
57
  const basePath = `/resources/${namespace}/`;
58
58
  return workspace.byGlob(`/resources/${namespace}/**/*`)
59
59
  .then(async (resources) => {
60
- const cachebusterInfo = {};
60
+ const cachebusterInfo = Object.create(null);
61
61
  const signer = getSigner(signatureType);
62
62
 
63
63
  await Promise.all(resources.map(async (resource) => {
@@ -11,7 +11,6 @@ const taskInfos = {
11
11
  transformBootstrapHtml: {path: "./transformBootstrapHtml.js"},
12
12
  generateLibraryManifest: {path: "./generateLibraryManifest.js"},
13
13
  generateVersionInfo: {path: "./generateVersionInfo.js"},
14
- generateManifestBundle: {path: "./bundlers/generateManifestBundle.js"},
15
14
  generateFlexChangesBundle: {path: "./bundlers/generateFlexChangesBundle.js"},
16
15
  generateComponentPreload: {path: "./bundlers/generateComponentPreload.js"},
17
16
  generateResourcesJson: {path: "./generateResourcesJson.js"},
@@ -26,7 +25,7 @@ export async function getTask(taskName) {
26
25
  const taskInfo = taskInfos[taskName];
27
26
 
28
27
  if (!taskInfo) {
29
- if (["createDebugFiles", "uglify"].includes(taskName)) {
28
+ if (["createDebugFiles", "uglify", "generateManifestBundle"].includes(taskName)) {
30
29
  throw new Error(
31
30
  `Standard task ${taskName} has been removed in UI5 Tooling 3.0. ` +
32
31
  `Please see the migration guide at https://sap.github.io/ui5-tooling/updates/migrate-v3/`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/builder",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0-beta.3",
4
4
  "description": "UI5 Tooling - Builder",
5
5
  "author": {
6
6
  "name": "SAP SE",
@@ -28,7 +28,7 @@
28
28
  "./internal/jsdoc/template/publish": "./lib/processors/jsdoc/lib/ui5/template/publish.cjs"
29
29
  },
30
30
  "engines": {
31
- "node": ">= 16.13.2",
31
+ "node": "^16.18.0 || >=18.0.0",
32
32
  "npm": ">= 8"
33
33
  },
34
34
  "scripts": {
@@ -119,44 +119,42 @@
119
119
  },
120
120
  "dependencies": {
121
121
  "@jridgewell/sourcemap-codec": "^1.4.14",
122
- "@ui5/fs": "^3.0.0-beta.2",
123
- "@ui5/logger": "^3.0.1-beta.0",
124
- "cheerio": "1.0.0-rc.9",
122
+ "@ui5/fs": "^3.0.0-beta.3",
123
+ "@ui5/logger": "^3.0.1-beta.1",
124
+ "cheerio": "1.0.0-rc.12",
125
125
  "escape-unicode": "^0.2.0",
126
126
  "escope": "^4.0.0",
127
- "espree": "^9.3.1",
128
- "graceful-fs": "^4.2.9",
129
- "jsdoc": "^3.6.7",
127
+ "espree": "^9.4.1",
128
+ "graceful-fs": "^4.2.10",
129
+ "jsdoc": "^3.6.11",
130
130
  "less-openui5": "^0.11.2",
131
131
  "make-dir": "^3.1.0",
132
132
  "pretty-data": "^0.40.0",
133
133
  "replacestream": "^4.0.3",
134
134
  "rimraf": "^3.0.2",
135
- "semver": "^7.3.5",
136
- "terser": "^5.14.2",
137
- "xml2js": "^0.4.23",
138
- "yazl": "^2.5.1"
135
+ "semver": "^7.3.8",
136
+ "terser": "^5.16.0",
137
+ "xml2js": "^0.4.23"
139
138
  },
140
139
  "devDependencies": {
141
140
  "@istanbuljs/esm-loader-hook": "^0.2.0",
142
- "@ui5/project": "^3.0.0-alpha.10",
143
- "ava": "^4.3.3",
144
- "chai": "^4.3.4",
141
+ "@ui5/project": "^3.0.0-beta.3",
142
+ "ava": "^5.1.0",
143
+ "chai": "^4.3.7",
145
144
  "chai-fs": "^2.0.0",
146
145
  "chokidar-cli": "^3.0.0",
147
146
  "cross-env": "^7.0.3",
148
147
  "depcheck": "^1.4.3",
149
- "docdash": "^1.2.0",
150
- "eslint": "^8.26.0",
148
+ "docdash": "^2.0.0",
149
+ "eslint": "^8.28.0",
151
150
  "eslint-config-google": "^0.14.0",
152
151
  "eslint-plugin-ava": "^13.2.0",
153
- "eslint-plugin-jsdoc": "^37.6.3",
154
- "esmock": "^2.0.1",
155
- "extract-zip": "^2.0.1",
152
+ "eslint-plugin-jsdoc": "^39.6.4",
153
+ "esmock": "^2.0.9",
156
154
  "nyc": "^15.1.0",
157
155
  "open-cli": "^7.1.0",
158
156
  "recursive-readdir": "^2.2.3",
159
- "sinon": "^14.0.0",
157
+ "sinon": "^14.0.2",
160
158
  "tap-nyan": "^1.1.0",
161
159
  "tap-xunit": "^2.4.1"
162
160
  }
@@ -1,181 +0,0 @@
1
- import posixPath from "node:path/posix";
2
- import yazl from "yazl";
3
- import {createResource} from "@ui5/fs/resourceFactory";
4
- import logger from "@ui5/logger";
5
- const log = logger.getLogger("builder:processors:bundlers:manifestBundler");
6
-
7
- /**
8
- * Repository to handle i18n resource files
9
- *
10
- * @private
11
- */
12
- class I18nResourceList {
13
- /**
14
- * Constructor
15
- */
16
- constructor() {
17
- this.propertyFiles = new Map();
18
- }
19
-
20
- /**
21
- * Adds a i18n resource to the repository
22
- *
23
- * @param {string} directory Path to the i18n resource
24
- * @param {@ui5/fs/Resource} resource i18n resource
25
- */
26
- add(directory, resource) {
27
- const normalizedDirectory = posixPath.normalize(directory);
28
- if (!this.propertyFiles.has(normalizedDirectory)) {
29
- this.propertyFiles.set(normalizedDirectory, [resource]);
30
- } else {
31
- this.propertyFiles.get(normalizedDirectory).push(resource);
32
- }
33
- }
34
-
35
- /**
36
- * Gets all registered i18n files within the provided path
37
- *
38
- * @param {string} directory Path to search for
39
- * @returns {Array} Array of resources files
40
- */
41
- get(directory) {
42
- return this.propertyFiles.get(posixPath.normalize(directory)) || [];
43
- }
44
- }
45
-
46
-
47
- /**
48
- * @public
49
- * @module @ui5/builder/processors/bundlers/manifestBundler
50
- */
51
-
52
- /**
53
- * Creates a manifest bundle from the provided resources.
54
- *
55
- * @public
56
- * @function default
57
- * @static
58
- *
59
- * @param {object} parameters Parameters
60
- * @param {@ui5/fs/Resource[]} parameters.resources List of resources to be processed
61
- * @param {object} parameters.options Options
62
- * @param {string} parameters.options.namespace Namespace of the project
63
- * @param {string} parameters.options.bundleName Name of the bundled zip file
64
- * @param {string} parameters.options.propertiesExtension Extension name of the properties files, e.g. ".properties"
65
- * @param {string} parameters.options.descriptor Descriptor name
66
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with manifest bundle resources
67
- */
68
- export default ({resources, options: {namespace, bundleName, propertiesExtension, descriptor}}) => {
69
- function bundleNameToUrl(bundleName, appId) {
70
- if (!bundleName.startsWith(appId)) {
71
- return null;
72
- }
73
- const relativeBundleName = bundleName.substring(appId.length + 1);
74
- return relativeBundleName.replace(/\./g, "/") + propertiesExtension;
75
- }
76
-
77
- function addDescriptorI18nInfos(descriptorI18nInfos, manifest) {
78
- function addI18nInfo(i18nPath) {
79
- if (i18nPath.startsWith("ui5:")) {
80
- log.warn(`Using the ui5:// protocol for i18n bundles is currently not supported ('${i18nPath}' in ${manifest.path})`);
81
- return;
82
- }
83
- descriptorI18nInfos.set(
84
- posixPath.join(posixPath.dirname(manifest.path), posixPath.dirname(i18nPath)),
85
- posixPath.basename(i18nPath, propertiesExtension)
86
- );
87
- }
88
-
89
- const content = JSON.parse(manifest.content);
90
- const appI18n = content["sap.app"]["i18n"];
91
- let bundleUrl;
92
- // i18n section in sap.app can be either a string or an object with bundleUrl
93
- if (typeof appI18n === "object") {
94
- if (appI18n.bundleUrl) {
95
- bundleUrl = appI18n.bundleUrl;
96
- } else if (appI18n.bundleName) {
97
- bundleUrl = bundleNameToUrl(appI18n.bundleName, content["sap.app"]["id"]);
98
- }
99
- } else if (typeof appI18n === "string") {
100
- bundleUrl = appI18n;
101
- } else {
102
- bundleUrl = "i18n/i18n.properties";
103
- }
104
- if (bundleUrl) {
105
- addI18nInfo(bundleUrl);
106
- }
107
-
108
- if (typeof appI18n === "object" && Array.isArray(appI18n.enhanceWith)) {
109
- appI18n.enhanceWith.forEach((enhanceWithEntry) => {
110
- let bundleUrl;
111
- if (enhanceWithEntry.bundleUrl) {
112
- bundleUrl = enhanceWithEntry.bundleUrl;
113
- } else if (enhanceWithEntry.bundleName) {
114
- bundleUrl = bundleNameToUrl(enhanceWithEntry.bundleName, content["sap.app"]["id"]);
115
- }
116
- if (bundleUrl) {
117
- addI18nInfo(bundleUrl);
118
- }
119
- });
120
- }
121
- }
122
-
123
- return Promise.all(resources.map((resource) =>
124
- resource.getBuffer().then((content) => {
125
- const basename = posixPath.basename(resource.getPath());
126
- return {
127
- name: basename,
128
- isManifest: basename === descriptor,
129
- path: resource.getPath(),
130
- content: content
131
- };
132
- })
133
- )).then((resources) => {
134
- const archiveContent = new Map();
135
- const descriptorI18nInfos = new Map();
136
- const i18nResourceList = new I18nResourceList();
137
-
138
- resources.forEach((resource) => {
139
- if (resource.isManifest) {
140
- addDescriptorI18nInfos(descriptorI18nInfos, resource);
141
- archiveContent.set(resource.path, resource.content);
142
- } else {
143
- const directory = posixPath.dirname(resource.path);
144
- i18nResourceList.add(directory, resource);
145
- }
146
- });
147
-
148
- descriptorI18nInfos.forEach((rootName, directory) => {
149
- const i18nResources = i18nResourceList.get(directory)
150
- .filter((resource) => resource.name.startsWith(rootName));
151
-
152
- if (i18nResources.length) {
153
- i18nResources.forEach((resource) => archiveContent.set(resource.path, resource.content));
154
- } else {
155
- log.warn(`Could not find any resources for i18n bundle '${directory}'`);
156
- }
157
- });
158
-
159
- return archiveContent;
160
- }).then((archiveContent) => new Promise((resolve) => {
161
- const zip = new yazl.ZipFile();
162
- const basePath = `/resources/${namespace}/`;
163
- archiveContent.forEach((content, path) => {
164
- if (!path.startsWith(basePath)) {
165
- log.verbose(`Not bundling resource with path ${path} since it is not based on path ${basePath}`);
166
- return;
167
- }
168
- // Remove base path. Absolute paths are not allowed in ZIP files
169
- const normalizedPath = path.replace(basePath, "");
170
- zip.addBuffer(content, normalizedPath);
171
- });
172
- zip.end();
173
-
174
- const pathPrefix = "/resources/" + namespace + "/";
175
- const res = createResource({
176
- path: pathPrefix + bundleName,
177
- stream: zip.outputStream
178
- });
179
- resolve([res]);
180
- }));
181
- };
@@ -1,58 +0,0 @@
1
- import logger from "@ui5/logger";
2
- const log = logger.getLogger("builder:tasks:bundlers:generateManifestBundle");
3
- import manifestBundler from "../../processors/bundlers/manifestBundler.js";
4
-
5
- const DESCRIPTOR = "manifest.json";
6
- const PROPERTIES_EXT = ".properties";
7
- const BUNDLE_NAME = "manifest-bundle.zip";
8
-
9
- /**
10
- * @public
11
- * @module @ui5/builder/tasks/bundlers/generateManifestBundle
12
- */
13
-
14
- /* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
15
- /**
16
- * Task for manifestBundler.
17
- *
18
- * @public
19
- * @function default
20
- * @static
21
- *
22
- * @param {object} parameters Parameters
23
- * @param {@ui5/fs/DuplexCollection} parameters.workspace DuplexCollection to read and write files
24
- * @param {object} parameters.options Options
25
- * @param {string} parameters.options.projectName Project name
26
- * @param {string} parameters.options.projectNamespace Project namespace
27
- * @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
28
- */
29
- export default async function({workspace, options = {}}) {
30
- const {projectName} = options;
31
- // Backward compatibility: "namespace" option got renamed to "projectNamespace"
32
- const namespace = options.projectNamespace || options.namespace;
33
-
34
- if (!projectName || !namespace) {
35
- throw new Error("[generateManifestBundle]: One or more mandatory options not provided");
36
- }
37
-
38
- const allResources = await workspace.byGlob(`/resources/${namespace}/**/{${DESCRIPTOR},*${PROPERTIES_EXT}}`);
39
- if (allResources.length === 0) {
40
- log.verbose(`Could not find a "${DESCRIPTOR}" file for project ${projectName}, ` +
41
- `creation of "${BUNDLE_NAME}" is skipped!`);
42
- return;
43
- }
44
-
45
- const processedResources = await manifestBundler({
46
- resources: allResources,
47
- options: {
48
- descriptor: DESCRIPTOR,
49
- propertiesExtension: PROPERTIES_EXT,
50
- bundleName: BUNDLE_NAME,
51
- namespace
52
- }
53
- });
54
-
55
- await Promise.all(processedResources.map((resource) => {
56
- return workspace.write(resource);
57
- }));
58
- }