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

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,23 @@
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.2...HEAD).
6
+
7
+ <a name="v3.0.0-beta.2"></a>
8
+ ## [v3.0.0-beta.2] - 2022-11-17
9
+ ### Breaking Changes
10
+ - Removal of manifestBundler and generateManifestBundle ([#838](https://github.com/SAP/ui5-builder/issues/838)) [`07a5be2`](https://github.com/SAP/ui5-builder/commit/07a5be2b6d9aa23cf78ddd17951c832d6dec7bef)
11
+ - **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)
12
+
13
+ ### Features
14
+ - Support ES2022 language features ([#848](https://github.com/SAP/ui5-builder/issues/848)) [`f9b8457`](https://github.com/SAP/ui5-builder/commit/f9b845726731a0e02ec4a499e2a1a82a639174a8)
15
+
16
+ ### BREAKING CHANGE
17
+
18
+ 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`.
19
+
20
+ The manifestBundler processor and generateManifestBundle task has been removed because it is no longer required for the HTML5 repository in Cloud Foundry.
21
+
6
22
 
7
23
  <a name="v3.0.0-beta.1"></a>
8
24
  ## [v3.0.0-beta.1] - 2022-11-07
@@ -787,6 +803,7 @@ to load the custom bundle file instead.
787
803
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
788
804
 
789
805
 
806
+ [v3.0.0-beta.2]: https://github.com/SAP/ui5-builder/compare/v3.0.0-beta.1...v3.0.0-beta.2
790
807
  [v3.0.0-beta.1]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.12...v3.0.0-beta.1
791
808
  [v3.0.0-alpha.12]: https://github.com/SAP/ui5-builder/compare/v3.0.0-alpha.11...v3.0.0-alpha.12
792
809
  [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
  *
@@ -507,9 +507,10 @@ function stripChainWrappers(rootNode, path) {
507
507
 
508
508
  function isTemplateLiteralWithoutExpression(node) {
509
509
  return (
510
- node?.type === Syntax.TemplateLiteral &&
511
- node?.expressions?.length === 0 &&
512
- node?.quasis?.length === 1
510
+ node &&
511
+ node.type === Syntax.TemplateLiteral &&
512
+ node.expressions && node.expressions.length === 0 &&
513
+ node.quasis && node.quasis.length === 1
513
514
  );
514
515
  }
515
516
 
@@ -801,9 +802,11 @@ function convertValueWithRaw(node, type, propertyName) {
801
802
  }
802
803
 
803
804
  } else if ( isTemplateLiteralWithoutExpression(node) ) {
805
+ let value = node.quasis[0].value || {};
806
+
804
807
  return {
805
- value: node?.quasis?.[0]?.value?.cooked,
806
- raw: node?.quasis?.[0]?.value?.raw,
808
+ value: value.cooked,
809
+ raw: value.raw,
807
810
  };
808
811
  }
809
812
 
@@ -2495,6 +2498,7 @@ exports.handlers = {
2495
2498
  e.doclet.meta.__shortpath = getRelativePath(filepath);
2496
2499
  _ui5data.resource = currentModule.resource;
2497
2500
  _ui5data.module = currentModule.name || currentModule.module;
2501
+ _ui5data.initialLongname = e.doclet.longname;
2498
2502
 
2499
2503
  const localDecl = findLocalDeclaration(e.doclet.meta.lineno, e.doclet.meta.columnno);
2500
2504
  if ( localDecl ) {
@@ -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
  });
@@ -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.2",
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
- }