@ui5/builder 4.0.13 → 4.1.1

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,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/v4.0.13...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v4.1.1...HEAD).
6
+
7
+ <a name="v4.1.1"></a>
8
+ ## [v4.1.1] - 2025-10-30
9
+
10
+ <a name="v4.1.0"></a>
11
+ ## [v4.1.0] - 2025-10-23
12
+ ### Features
13
+ - Enhance generateFlexChangeBundle task to add new 'flexBundle' property to the manifest.json ([#1164](https://github.com/SAP/ui5-builder/issues/1164)) [`a48e474`](https://github.com/SAP/ui5-builder/commit/a48e474b1bfb9f2f11da44e908341af370711309)
14
+
6
15
 
7
16
  <a name="v4.0.13"></a>
8
17
  ## [v4.0.13] - 2025-10-07
@@ -992,6 +1001,8 @@ to load the custom bundle file instead.
992
1001
 
993
1002
  ### Features
994
1003
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
1004
+ [v4.1.1]: https://github.com/SAP/ui5-builder/compare/v4.1.0...v4.1.1
1005
+ [v4.1.0]: https://github.com/SAP/ui5-builder/compare/v4.0.13...v4.1.0
995
1006
  [v4.0.13]: https://github.com/SAP/ui5-builder/compare/v4.0.11...v4.0.13
996
1007
  [v4.0.11]: https://github.com/SAP/ui5-builder/compare/v4.0.10...v4.0.11
997
1008
  [v4.0.10]: https://github.com/SAP/ui5-builder/compare/v4.0.9...v4.0.10
package/CONTRIBUTING.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # Contributing to the UI5 CLI
2
2
 
3
- See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md) repository.
3
+ See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/v4/CONTRIBUTING.md) repository.
package/README.md CHANGED
@@ -24,10 +24,10 @@ UI5 Builder documentation can be found here: [ui5.github.io/cli](https://ui5.git
24
24
  The UI5 Builder API Reference can be found here: [`@ui5/builder`](https://ui5.github.io/cli/v4/api/)
25
25
 
26
26
  ## Contributing
27
- Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md).
27
+ Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/v4/CONTRIBUTING.md).
28
28
 
29
29
  ## Support
30
- Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue.
30
+ Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/v4/CONTRIBUTING.md#report-an-issue) on how to report an issue.
31
31
 
32
32
  Please report issues in the main [UI5 CLI](https://github.com/UI5/cli) repository.
33
33
 
@@ -56,6 +56,7 @@ class BundleResolver {
56
56
  * in the resource pool.
57
57
  */
58
58
  const missingModules = Object.create(null);
59
+ const ignoreMissingModules = pool.getIgnoreMissingModules();
59
60
  /**
60
61
  * Names of modules that are included in non-decomposable bundles.
61
62
  * If they occur in the missingModules, then this is not an error.
@@ -123,7 +124,7 @@ class BundleResolver {
123
124
  done = pool.findResourceWithInfo(resourceName)
124
125
  .catch( (err) => {
125
126
  // if the caller provided an error message, log it
126
- if ( msg ) {
127
+ if ( msg && !ignoreMissingModules ) {
127
128
  missingModules[resourceName] ??= [];
128
129
  missingModules[resourceName].push(msg);
129
130
  }
@@ -14,7 +14,7 @@ import semver from "semver";
14
14
  * at runtime.
15
15
  * If a change bundle is created, "sap.ui.fl" is added as a dependency to the manifest.json if not already present -
16
16
  * if the dependency is already listed but lazy-loaded, lazy loading is disabled.
17
- * If minUI5Version >= 1.73 flexibility-bundle.json will be create.
17
+ * If minUI5Version >= 1.73 flexibility-bundle.json is created.
18
18
  * If there are control variants and minUI5Version < 1.73 build will break and throw an error.
19
19
  *
20
20
  * @public
@@ -38,29 +38,34 @@ export default async function({workspace, taskUtil, options = {}}) {
38
38
  pathPrefix = `/resources/${namespace}`;
39
39
  }
40
40
 
41
- function updateJson(data) {
42
- // ensure the existence of the libs section in the dependencies
41
+ function updateJson(data, bBundleCreated) {
43
42
  data["sap.ui5"] = data["sap.ui5"] || {};
44
- data["sap.ui5"].dependencies = data["sap.ui5"].dependencies || {};
45
- const mLibs = data["sap.ui5"].dependencies.libs = data["sap.ui5"].dependencies.libs || {};
46
-
47
- if (mLibs["sap.ui.fl"]) {
48
- log.verbose("sap.ui.fl found in manifest.json");
49
- if (mLibs["sap.ui.fl"].lazy) {
50
- log.verbose("sap.ui.fl 'lazy' attribute found in manifest.json, setting it to false...");
51
- mLibs["sap.ui.fl"].lazy = false;
43
+ // explicit set the flag to inform the runtime if a bundle is present before the preload can be interpreted
44
+ data["sap.ui5"].flexBundle = bBundleCreated;
45
+
46
+ if (bBundleCreated) {
47
+ // ensure the existence of the libs section in the dependencies
48
+ data["sap.ui5"].dependencies = data["sap.ui5"].dependencies || {};
49
+ const mLibs = data["sap.ui5"].dependencies.libs = data["sap.ui5"].dependencies.libs || {};
50
+
51
+ if (mLibs["sap.ui.fl"]) {
52
+ log.verbose("sap.ui.fl found in manifest.json");
53
+ if (mLibs["sap.ui.fl"].lazy) {
54
+ log.verbose("sap.ui.fl 'lazy' attribute found in manifest.json, setting it to false...");
55
+ mLibs["sap.ui.fl"].lazy = false;
56
+ }
57
+ } else {
58
+ log.verbose("sap.ui.fl not found in manifest.json, inserting it...");
59
+ mLibs["sap.ui.fl"] = {};
52
60
  }
53
- } else {
54
- log.verbose("sap.ui.fl not found in manifest.json, inserting it...");
55
- mLibs["sap.ui.fl"] = {};
56
61
  }
57
62
  }
58
63
 
59
- async function updateFLdependency() {
64
+ async function updateManifestWithFlDependencyAndFlexBundleFlag(bBundleCreated) {
60
65
  const manifestResource = await workspace.byPath(`${pathPrefix}/manifest.json`);
61
66
  const manifestContent = JSON.parse(await manifestResource.getString());
62
67
 
63
- updateJson(manifestContent);
68
+ updateJson(manifestContent, bBundleCreated);
64
69
  manifestResource.setString(JSON.stringify(manifestContent, null, "\t"));
65
70
 
66
71
  await workspace.write(manifestResource);
@@ -83,6 +88,9 @@ export default async function({workspace, taskUtil, options = {}}) {
83
88
  log.verbose("Collecting flexibility changes");
84
89
  const allResources = await workspace.byGlob(
85
90
  `${pathPrefix}/changes/*.{change,variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change}`);
91
+
92
+ let bBundleCreated = false;
93
+
86
94
  if (allResources.length > 0) {
87
95
  const versionArray = await readManifestMinUI5Version();
88
96
  const versions = versionArray.map((version) => semver.coerce(version));
@@ -109,9 +117,7 @@ export default async function({workspace, taskUtil, options = {}}) {
109
117
  return workspace.write(resource);
110
118
  }));
111
119
  // Add the sap.ui.fl dependency if a bundle has been created
112
- if (processedResources.length > 0) {
113
- await updateFLdependency();
114
- }
120
+ bBundleCreated = processedResources.length > 0;
115
121
 
116
122
  // Do not write bundled source files to build result
117
123
  if (taskUtil) {
@@ -120,4 +126,7 @@ export default async function({workspace, taskUtil, options = {}}) {
120
126
  });
121
127
  }
122
128
  }
129
+
130
+ // Always append the flexBundle flag to the manifest.json, even if no bundle was created
131
+ await updateManifestWithFlDependencyAndFlexBundleFlag(bBundleCreated);
123
132
  }
@@ -1,3 +1,4 @@
1
+ import semver from "semver";
1
2
  import {getLogger} from "@ui5/logger";
2
3
  const log = getLogger("builder:tasks:bundlers:generateLibraryPreload");
3
4
  import moduleBundler from "../../processors/bundlers/moduleBundler.js";
@@ -32,6 +33,32 @@ function getDefaultLibraryPreloadFilters(namespace, excludes) {
32
33
  return filters;
33
34
  }
34
35
 
36
+ function getExperimentalDefaultLibraryPreloadFilters(namespace, excludes) {
37
+ const filters = [
38
+ `${namespace}/library.js`,
39
+ `!${namespace}/**/*-preload.js`, // exclude all bundles
40
+ `!${namespace}/designtime/`,
41
+ `!${namespace}/**/*.designtime.js`,
42
+ `!${namespace}/**/*.support.js`
43
+ ];
44
+
45
+ if (Array.isArray(excludes)) {
46
+ const allFilterExcludes = negateFilters(excludes);
47
+ // Add configured excludes at the end of filter list
48
+ allFilterExcludes.forEach((filterExclude) => {
49
+ // Allow all excludes (!) and limit re-includes (+) to the library namespace
50
+ if (filterExclude.startsWith("!") || filterExclude.startsWith(`+${namespace}/`)) {
51
+ filters.push(filterExclude);
52
+ } else {
53
+ log.warn(`Configured preload exclude contains invalid re-include: !${filterExclude.substr(1)}. ` +
54
+ `Re-includes must start with the library's namespace ${namespace}`);
55
+ }
56
+ });
57
+ }
58
+
59
+ return filters;
60
+ }
61
+
35
62
  function getBundleDefinition(namespace, excludes) {
36
63
  // Note: This configuration is only used when no bundle definition in ui5.yaml exists (see "skipBundles" parameter)
37
64
 
@@ -106,6 +133,69 @@ function getBundleDefinition(namespace, excludes) {
106
133
  };
107
134
  }
108
135
 
136
+ function getBundleInfoPreloadDefinition(namespace, excludes, coreVersion) {
137
+ const sections = [{
138
+ mode: "preload",
139
+ filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes),
140
+ resolve: true
141
+ },
142
+ {
143
+ mode: "bundleInfo",
144
+ name: `${namespace}/library-content.js`,
145
+ filters: getDefaultLibraryPreloadFilters(namespace, excludes),
146
+ resolve: false,
147
+ resolveConditional: false,
148
+ renderer: true
149
+ }];
150
+
151
+ if (coreVersion) {
152
+ const parsedVersion = semver.parse(coreVersion);
153
+ let targetUi5CoreVersionMajor = parsedVersion.major;
154
+
155
+ // legacy-free versions include changes of the upcoming major version
156
+ // so we should treat them the same as the next major version
157
+ if (
158
+ parsedVersion.prerelease.includes("legacy-free") ||
159
+ parsedVersion.prerelease.includes("legacy-free-SNAPSHOT") // Maven snapshot version
160
+ ) {
161
+ targetUi5CoreVersionMajor += 1;
162
+ }
163
+ if (parsedVersion) {
164
+ if (targetUi5CoreVersionMajor >= 2) {
165
+ // Do not include manifest.json in UI5 2.x and higher to allow for loading it upfront for all libraries
166
+ sections.unshift({
167
+ mode: "provided",
168
+ filters: [
169
+ `${namespace}/manifest.json`,
170
+ ]
171
+ });
172
+ }
173
+ }
174
+ }
175
+
176
+ return {
177
+ name: `${namespace}/library-preload.js`,
178
+ sections,
179
+ };
180
+ }
181
+
182
+ function getContentBundleDefinition(namespace, excludes) {
183
+ return {
184
+ name: `${namespace}/library-content.js`,
185
+ sections: [{
186
+ mode: "provided",
187
+ filters: getExperimentalDefaultLibraryPreloadFilters(namespace, excludes),
188
+ resolve: true
189
+ }, {
190
+ mode: "preload",
191
+ filters: getDefaultLibraryPreloadFilters(namespace, excludes),
192
+ resolve: false,
193
+ resolveConditional: false,
194
+ renderer: true
195
+ }]
196
+ };
197
+ }
198
+
109
199
  function getDesigntimeBundleDefinition(namespace) {
110
200
  return {
111
201
  name: `${namespace}/designtime/library-preload.designtime.js`,
@@ -258,6 +348,7 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
258
348
  }
259
349
  const coreVersion = taskUtil?.getProject("sap.ui.core")?.getVersion();
260
350
  const allowStringBundling = taskUtil?.getProject().getSpecVersion().lt("4.0");
351
+ const createBundleInfoPreload = !!process.env.UI5_CLI_EXPERIMENTAL_BUNDLE_INFO_PRELOAD;
261
352
  const execModuleBundlerIfNeeded = ({options, resources}) => {
262
353
  if (skipBundles.includes(options.bundleDefinition.name)) {
263
354
  log.verbose(`Skipping generation of bundle ${options.bundleDefinition.name}`);
@@ -390,42 +481,99 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
390
481
  const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern);
391
482
  if (libraryNamespaceMatch && libraryNamespaceMatch[1]) {
392
483
  const libraryNamespace = libraryNamespaceMatch[1];
393
- const results = await Promise.all([
394
- execModuleBundlerIfNeeded({
395
- options: {
396
- bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
397
- bundleOptions: {
398
- optimize: true,
399
- ignoreMissingModules: true
400
- }
401
- },
402
- resources
403
- }),
404
- execModuleBundlerIfNeeded({
405
- options: {
406
- bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
407
- bundleOptions: {
408
- optimize: true,
409
- ignoreMissingModules: true,
410
- skipIfEmpty: true
411
- }
412
- },
413
- resources
414
- }),
415
- execModuleBundlerIfNeeded({
416
- options: {
417
- bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
418
- bundleOptions: {
419
- optimize: false,
420
- ignoreMissingModules: true,
421
- skipIfEmpty: true
422
- }
423
- // Note: Although the bundle uses optimize=false, there is
424
- // no moduleNameMapping needed, as support files are excluded from minification.
425
- },
426
- resources
427
- })
428
- ]);
484
+ let results;
485
+ if (!createBundleInfoPreload) {
486
+ // Regular bundling
487
+ results = await Promise.all([
488
+ execModuleBundlerIfNeeded({
489
+ options: {
490
+ bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
491
+ bundleOptions: {
492
+ optimize: true,
493
+ ignoreMissingModules: true
494
+ }
495
+ },
496
+ resources
497
+ }),
498
+ execModuleBundlerIfNeeded({
499
+ options: {
500
+ bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
501
+ bundleOptions: {
502
+ optimize: true,
503
+ ignoreMissingModules: true,
504
+ skipIfEmpty: true
505
+ }
506
+ },
507
+ resources
508
+ }),
509
+ execModuleBundlerIfNeeded({
510
+ options: {
511
+ bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
512
+ bundleOptions: {
513
+ optimize: false,
514
+ ignoreMissingModules: true,
515
+ skipIfEmpty: true
516
+ }
517
+ // Note: Although the bundle uses optimize=false, there is
518
+ // no moduleNameMapping needed, as support files are excluded from minification.
519
+ },
520
+ resources
521
+ })
522
+ ]);
523
+ } else {
524
+ log.info(
525
+ `Using experimental bundling with bundle info preload ` +
526
+ `for library ${libraryNamespace} in project ${projectName}`);
527
+ log.info(`Detected sap.ui.core version is ${coreVersion || "unknown"}`);
528
+ // Experimental bundling with bundle info preload
529
+ results = await Promise.all([
530
+ execModuleBundlerIfNeeded({
531
+ options: {
532
+ bundleDefinition:
533
+ getBundleInfoPreloadDefinition(libraryNamespace, excludes, coreVersion),
534
+ bundleOptions: {
535
+ optimize: true,
536
+ ignoreMissingModules: true
537
+ }
538
+ },
539
+ resources
540
+ }),
541
+ execModuleBundlerIfNeeded({
542
+ options: {
543
+ bundleDefinition: getContentBundleDefinition(libraryNamespace, excludes),
544
+ bundleOptions: {
545
+ optimize: true,
546
+ ignoreMissingModules: true
547
+ }
548
+ },
549
+ resources
550
+ }),
551
+ execModuleBundlerIfNeeded({
552
+ options: {
553
+ bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
554
+ bundleOptions: {
555
+ optimize: true,
556
+ ignoreMissingModules: true,
557
+ skipIfEmpty: true
558
+ }
559
+ },
560
+ resources
561
+ }),
562
+ execModuleBundlerIfNeeded({
563
+ options: {
564
+ bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
565
+ bundleOptions: {
566
+ optimize: false,
567
+ ignoreMissingModules: true,
568
+ skipIfEmpty: true
569
+ }
570
+ // Note: Although the bundle uses optimize=false, there is
571
+ // no moduleNameMapping needed, as support files are excluded from minification.
572
+ },
573
+ resources
574
+ })
575
+ ]);
576
+ }
429
577
  const bundles = Array.prototype.concat.apply([], results).filter(Boolean);
430
578
  return Promise.all(bundles.map(({bundle, sourceMap} = {}) => {
431
579
  if (bundle) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/builder",
3
- "version": "4.0.13",
3
+ "version": "4.1.1",
4
4
  "description": "UI5 CLI - Builder",
5
5
  "author": {
6
6
  "name": "SAP SE",
@@ -129,10 +129,10 @@
129
129
  "escope": "^4.0.0",
130
130
  "espree": "^10.4.0",
131
131
  "graceful-fs": "^4.2.11",
132
- "jsdoc": "^4.0.4",
132
+ "jsdoc": "^4.0.5",
133
133
  "less-openui5": "^0.11.6",
134
134
  "pretty-data": "^0.40.0",
135
- "semver": "^7.7.2",
135
+ "semver": "^7.7.3",
136
136
  "terser": "^5.44.0",
137
137
  "workerpool": "^9.3.4",
138
138
  "xml2js": "^0.6.2"
@@ -141,13 +141,13 @@
141
141
  "@eslint/js": "^9.14.0",
142
142
  "@istanbuljs/esm-loader-hook": "^0.3.0",
143
143
  "@jridgewell/trace-mapping": "^0.3.31",
144
- "@ui5/project": "^4.0.6",
144
+ "@ui5/project": "^4.0.8",
145
145
  "ava": "^6.4.1",
146
146
  "chokidar-cli": "^3.0.0",
147
147
  "cross-env": "^7.0.3",
148
148
  "depcheck": "^1.4.7",
149
149
  "docdash": "^2.0.2",
150
- "eslint": "^9.37.0",
150
+ "eslint": "^9.38.0",
151
151
  "eslint-config-google": "^0.14.0",
152
152
  "eslint-plugin-ava": "^15.1.0",
153
153
  "eslint-plugin-jsdoc": "^52.0.4",