@ui5/builder 5.0.0-alpha.2 → 5.0.0-alpha.4
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 +48 -0
- package/lib/lbt/bundle/Resolver.js +2 -1
- package/lib/lbt/resources/ModuleInfo.js +4 -3
- package/lib/lbt/resources/ResourceFilterList.js +3 -2
- package/lib/lbt/utils/JSTokenizer.js +2 -1
- package/lib/processors/bundlers/moduleBundler.js +5 -1
- package/lib/processors/jsdoc/lib/createIndexFiles.js +1 -1
- package/lib/processors/jsdoc/lib/transformApiJson.js +97 -24
- package/lib/processors/jsdoc/lib/ui5/plugin.js +65 -11
- package/lib/processors/jsdoc/lib/ui5/template/publish.js +29 -1
- package/lib/processors/jsdoc/lib/ui5/template/utils/typeParser.js +1 -1
- package/lib/processors/manifestCreator.js +8 -1
- package/lib/processors/manifestEnhancer.js +6 -0
- package/lib/processors/nonAsciiEscaper.js +3 -3
- package/lib/processors/versionInfoGenerator.js +6 -4
- package/lib/tasks/bundlers/generateLibraryPreload.js +207 -39
- package/lib/tasks/generateResourcesJson.js +4 -1
- package/package.json +9 -30
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
|
|
|
4
4
|
|
|
5
5
|
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v4.0.11...HEAD).
|
|
6
6
|
|
|
7
|
+
## [5.0.0-alpha.4](https://github.com/UI5/cli/compare/builder-v5.0.0-alpha.3...builder-v5.0.0-alpha.4) (2026-03-24)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **builder:** Add dynamic section for API reference during JSDoc build ([47970f7](https://github.com/UI5/cli/commit/47970f74c4adf63625e4f330f19090083f563f82))
|
|
13
|
+
* **builder:** Add hierarchical subsection support for API reference sections ([49a3ef5](https://github.com/UI5/cli/commit/49a3ef569074ff84b5f6dfc2cfc619cd1654c2c8))
|
|
14
|
+
* **builder:** Automatically add [@lends](https://github.com/lends) tag to extend calls during JSdoc build ([3547f42](https://github.com/UI5/cli/commit/3547f425b33663f7e5e2618a6d876fe018058d99))
|
|
15
|
+
* **builder:** In JSDoc build, push the deprecation of namespaces down to the contained APIs ([71c08f2](https://github.com/UI5/cli/commit/71c08f2bb1d2cfee34ab9ca2e9e6042b5e67ae0c))
|
|
16
|
+
* **builder:** Mark cross-references to typedefs in the api.json (SDK variant) ([a474700](https://github.com/UI5/cli/commit/a47470050e804192bfab857f6c224faa6e445e10))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **builder:** Improve JSDoc support for "module:" syntax ([aca05f0](https://github.com/UI5/cli/commit/aca05f0b75561f33842ccb30248ede3a82f2ae46))
|
|
22
|
+
* **builder:** JSDoc: avoid errors when symbol has no events ([6e517e6](https://github.com/UI5/cli/commit/6e517e6c7f480784b93e37e94dcf74e0a689427f))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Dependencies
|
|
26
|
+
|
|
27
|
+
* The following workspace dependencies were updated
|
|
28
|
+
* dependencies
|
|
29
|
+
* @ui5/fs bumped from ^5.0.0-alpha.3 to ^5.0.0-alpha.4
|
|
30
|
+
* @ui5/logger bumped from ^5.0.0-alpha.3 to ^5.0.0-alpha.4
|
|
31
|
+
* devDependencies
|
|
32
|
+
* @ui5/project bumped from ^5.0.0-alpha.3 to ^5.0.0-alpha.4
|
|
33
|
+
|
|
34
|
+
## [5.0.0-alpha.3](https://github.com/UI5/cli/compare/builder-v5.0.0-alpha.2...builder-v5.0.0-alpha.3) (2026-02-13)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Bug Fixes
|
|
38
|
+
|
|
39
|
+
* Change Markdown syntax to [@example](https://github.com/example) in one place ([f7e2e73](https://github.com/UI5/cli/commit/f7e2e732570e3948d87c200ed5d64f65891a866d))
|
|
40
|
+
* Fix gray code blocks in Markdown ([1cdd610](https://github.com/UI5/cli/commit/1cdd61060bd592b5c5f573635444d5ba37a8dc98))
|
|
41
|
+
* Remove unnecessary space ([892ebca](https://github.com/UI5/cli/commit/892ebcadbd9ac4a3c0db84c7fb12c8c1e5daa1b6))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### Dependencies
|
|
45
|
+
|
|
46
|
+
* Bump escape-unicode from 0.2.0 to 0.3.0 ([#1160](https://github.com/UI5/cli/issues/1160)) ([90d07bd](https://github.com/UI5/cli/commit/90d07bdf9703e1072a8453a58302c00ac60ad83e))
|
|
47
|
+
* Bump workerpool from 9.3.4 to 10.0.1 ([#1171](https://github.com/UI5/cli/issues/1171)) ([1f659b4](https://github.com/UI5/cli/commit/1f659b405038f91f6aa62d0cc29c32376e4e24d3))
|
|
48
|
+
* The following workspace dependencies were updated
|
|
49
|
+
* dependencies
|
|
50
|
+
* @ui5/fs bumped from ^5.0.0-alpha.2 to ^5.0.0-alpha.3
|
|
51
|
+
* @ui5/logger bumped from ^5.0.0-alpha.2 to ^5.0.0-alpha.3
|
|
52
|
+
* devDependencies
|
|
53
|
+
* @ui5/project bumped from ^5.0.0-alpha.2 to ^5.0.0-alpha.3
|
|
54
|
+
|
|
7
55
|
## [5.0.0-alpha.2](https://github.com/UI5/cli/compare/builder-v5.0.0-alpha.1...builder-v5.0.0-alpha.2) (2025-12-15)
|
|
8
56
|
|
|
9
57
|
|
|
@@ -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
|
}
|
|
@@ -84,9 +84,10 @@ class ModuleInfo {
|
|
|
84
84
|
* global scope. If a module declares global variables, all of them will be exported
|
|
85
85
|
* with additional code of the form:
|
|
86
86
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
87
|
+
* ```js
|
|
88
|
+
* this["name"] = name;
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
90
91
|
*
|
|
91
92
|
* To avoid this, developers can either add "name" to the <code>ignoreGlobals</code>
|
|
92
93
|
* attribute of a raw-module or they can completely suppress embedding of the module
|
|
@@ -141,10 +141,11 @@ export default class ResourceFilterList {
|
|
|
141
141
|
* and vice versa.
|
|
142
142
|
*
|
|
143
143
|
* Example:
|
|
144
|
-
*
|
|
144
|
+
* ```
|
|
145
145
|
* !sap/ui/core/
|
|
146
146
|
* +sap/ui/core/utils/
|
|
147
|
-
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
148
149
|
* excludes everything from sap/ui/core, but includes everything from the subpackage sap/ui/core/utils/.
|
|
149
150
|
*
|
|
150
151
|
* Note that the filter operates on the full name of each resource. If a resource name
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
*
|
|
30
30
|
* Contains functions to consume tokens on an input string.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
32
|
+
* ```js
|
|
33
33
|
* sap.ui.require(["sap/base/util/JSTokenizer"], function(JSTokenizer){
|
|
34
34
|
* JSTokenizer().parseJS("{test:'123'}"); // {test:'123'}
|
|
35
35
|
* });
|
|
36
|
+
* ```
|
|
36
37
|
*
|
|
37
38
|
* @class sap/base/util/JSTokenizer
|
|
38
39
|
* @since 1.58
|
|
@@ -57,7 +57,10 @@ const log = getLogger("builder:processors:bundlers:moduleBundler");
|
|
|
57
57
|
* denote an arbitrary number of characters or folder names.
|
|
58
58
|
* Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later
|
|
59
59
|
* exclusion overrides an earlier inclusion, and vice versa.
|
|
60
|
-
*
|
|
60
|
+
*
|
|
61
|
+
* <i>Example:</i>
|
|
62
|
+
*
|
|
63
|
+
* ```js
|
|
61
64
|
* // Includes everything from "some/path/to/module/",
|
|
62
65
|
* // but excludes the subfolder "some/path/to/module/to/be/excluded/"
|
|
63
66
|
* const section = {
|
|
@@ -66,6 +69,7 @@ const log = getLogger("builder:processors:bundlers:moduleBundler");
|
|
|
66
69
|
* "!some/path/to/module/to/be/excluded/"
|
|
67
70
|
* ]
|
|
68
71
|
* };
|
|
72
|
+
* ```
|
|
69
73
|
*
|
|
70
74
|
* @property {boolean} [resolve=false] Whether (transitive) dependencies of modules that match the given filters
|
|
71
75
|
* should be resolved and added to the module set
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Node script to create cross-library API index files for use in the UI5 SDKs.
|
|
3
3
|
*
|
|
4
|
-
* (c) Copyright
|
|
4
|
+
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node script to preprocess api.json files for use in the UI5 SDKs.
|
|
3
3
|
*
|
|
4
|
-
* (c) Copyright
|
|
4
|
+
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -47,10 +47,10 @@ const typeParser = new TypeParser();
|
|
|
47
47
|
* @param {string} sLibraryFile Path to the .library file of the library, used to extract further documentation information
|
|
48
48
|
* @param {string|string[]} vDependencyAPIFiles Path of folder that contains api.json files of predecessor libs or
|
|
49
49
|
* an array of paths of those files
|
|
50
|
-
* @param {string}
|
|
50
|
+
* @param {string} sSectionsDir Path to the directory containing the sources for the sections (FAQ, documentation, etc.) in the API Reference
|
|
51
51
|
* @returns {Promise} A Promise that resolves after the transformation has been completed
|
|
52
52
|
*/
|
|
53
|
-
function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
53
|
+
function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, sSectionsDir, options) {
|
|
54
54
|
const fs = options && options.fs || require("fs");
|
|
55
55
|
const returnOutputFiles = options && !!options.returnOutputFiles;
|
|
56
56
|
|
|
@@ -59,7 +59,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
59
59
|
log.info(" output file: " + sOutputFile);
|
|
60
60
|
log.info(" library file: " + sLibraryFile);
|
|
61
61
|
log.info(" dependency dir: " + vDependencyAPIFiles);
|
|
62
|
-
log.info("
|
|
62
|
+
log.info(" sections src dir: " + sSectionsDir);
|
|
63
63
|
if (options && options.fs) {
|
|
64
64
|
log.info("Using custom fs.");
|
|
65
65
|
}
|
|
@@ -156,9 +156,10 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
156
156
|
* UI5Types: ["sap.ui.core.Control"] // skip template if its value is "${0}" (default value)
|
|
157
157
|
* }
|
|
158
158
|
* @param {string} sComplexType
|
|
159
|
+
* @param {Object<string,object>} oAllDependentAPIs Map of entity names to their symbol
|
|
159
160
|
* @returns {{template=: string, UI5Types=: string[]}}
|
|
160
161
|
*/
|
|
161
|
-
function parseUI5Types(sComplexType) {
|
|
162
|
+
function parseUI5Types(sComplexType, oAllDependentAPIs) {
|
|
162
163
|
let oParsed;
|
|
163
164
|
try {
|
|
164
165
|
oParsed = typeParser.parseSimpleTypes(sComplexType, isUI5Type);
|
|
@@ -176,6 +177,14 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
176
177
|
|
|
177
178
|
if (oParsed.simpleTypes?.length) { // it can be empty if none of the included simple types satisfied the filter function
|
|
178
179
|
result.UI5Types = oParsed.simpleTypes;
|
|
180
|
+
|
|
181
|
+
// check whether the type refers to a typedef and set a marker
|
|
182
|
+
if (result.template == null && result.UI5Types.length === 1) {
|
|
183
|
+
const symbol = oAllDependentAPIs[result.UI5Types[0]];
|
|
184
|
+
if (symbol?.kind === "typedef" && Array.isArray(symbol.properties)) {
|
|
185
|
+
result.refersToTypedef = true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
return result;
|
|
@@ -266,6 +275,8 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
266
275
|
* @param {object} oChainObject chain object
|
|
267
276
|
*/
|
|
268
277
|
const transformApiJson = function (oChainObject) {
|
|
278
|
+
const {oAllDependentAPIs} = oChainObject;
|
|
279
|
+
|
|
269
280
|
// Function is a copy from: LibraryInfo.js => LibraryInfo.prototype._getActualComponent => "match" inline method
|
|
270
281
|
function matchComponent(sModuleName, sPattern) {
|
|
271
282
|
sModuleName = sModuleName.toLowerCase();
|
|
@@ -435,7 +446,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
435
446
|
// Types
|
|
436
447
|
oParameter.types = [];
|
|
437
448
|
if (oParameter.type) {
|
|
438
|
-
oParameter.typeInfo = parseUI5Types(oParameter.type);
|
|
449
|
+
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
|
|
439
450
|
// Keep file size in check
|
|
440
451
|
delete oParameter.type;
|
|
441
452
|
}
|
|
@@ -520,7 +531,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
520
531
|
// Type
|
|
521
532
|
if (oSymbol.kind !== "enum") { // enum properties don't have an own type
|
|
522
533
|
if (oProperty.type) {
|
|
523
|
-
oProperty.typeInfo = parseUI5Types(oProperty.type);
|
|
534
|
+
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
|
|
524
535
|
// Keep file size in check
|
|
525
536
|
delete oProperty.type;
|
|
526
537
|
}
|
|
@@ -560,7 +571,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
560
571
|
|
|
561
572
|
// Type
|
|
562
573
|
if (oProperty.type) {
|
|
563
|
-
oProperty.typeInfo = parseUI5Types(oProperty.type);
|
|
574
|
+
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
|
|
564
575
|
// Keep file size in check
|
|
565
576
|
delete oProperty.type;
|
|
566
577
|
}
|
|
@@ -657,7 +668,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
657
668
|
Object.keys(aParams).forEach((sParam) => {
|
|
658
669
|
const sSince = aParams[sParam].since;
|
|
659
670
|
const oDeprecated = aParams[sParam].deprecated;
|
|
660
|
-
const oEvtInSymbol = oSymbol.events
|
|
671
|
+
const oEvtInSymbol = oSymbol.events?.find((e) => e.name === oEvent.name);
|
|
661
672
|
const oParamInSymbol = oEvtInSymbol && oEvtInSymbol.parameters[0] &&
|
|
662
673
|
oEvtInSymbol.parameters[0].parameterProperties &&
|
|
663
674
|
oEvtInSymbol.parameters[0].parameterProperties.getParameters &&
|
|
@@ -687,7 +698,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
687
698
|
|
|
688
699
|
// Link Enabled
|
|
689
700
|
if (oSetting.type) {
|
|
690
|
-
oSetting.typeInfo = parseUI5Types(oSetting.type);
|
|
701
|
+
oSetting.typeInfo = parseUI5Types(oSetting.type, oAllDependentAPIs);
|
|
691
702
|
delete oSetting.type; // Keep file size in check
|
|
692
703
|
}
|
|
693
704
|
|
|
@@ -756,7 +767,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
756
767
|
oEvent.parameters.forEach((oParameter) => {
|
|
757
768
|
|
|
758
769
|
if (oParameter.type) {
|
|
759
|
-
oParameter.typeInfo = parseUI5Types(oParameter.type);
|
|
770
|
+
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
|
|
760
771
|
delete oParameter.type; // Keep file size in check
|
|
761
772
|
}
|
|
762
773
|
|
|
@@ -789,7 +800,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
789
800
|
if (oSymbol.methods) {
|
|
790
801
|
|
|
791
802
|
// Pre-process methods
|
|
792
|
-
methods.buildMethodsModel(oSymbol.methods);
|
|
803
|
+
methods.buildMethodsModel(oSymbol.methods, oAllDependentAPIs);
|
|
793
804
|
|
|
794
805
|
oSymbol.methods.forEach((oMethod) => {
|
|
795
806
|
|
|
@@ -950,6 +961,14 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
950
961
|
}
|
|
951
962
|
|
|
952
963
|
function loadDependencyLibraryFiles (oChainObject) {
|
|
964
|
+
const oAllDependentAPIs = oChainObject.oAllDependentAPIs = Object.create(null);
|
|
965
|
+
// add symbols from current library
|
|
966
|
+
if (oChainObject.fileData.symbols) {
|
|
967
|
+
for (const oSymbol of oChainObject.fileData.symbols) {
|
|
968
|
+
oAllDependentAPIs[oSymbol.name] = oSymbol;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
953
972
|
if (!oChainObject.aDependentLibraryFiles) {
|
|
954
973
|
return oChainObject;
|
|
955
974
|
}
|
|
@@ -978,6 +997,12 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
978
997
|
// In this case we don't add it to the dependency list to skip double iteration.
|
|
979
998
|
if (oData && oChainObject.fileData.library !== oData.library) {
|
|
980
999
|
oDependentAPIs[oData.library] = oData.symbols;
|
|
1000
|
+
|
|
1001
|
+
if (Array.isArray(oData.symbols)) {
|
|
1002
|
+
for (const oSymbol of oData.symbols) {
|
|
1003
|
+
oAllDependentAPIs[oSymbol.name] = oSymbol;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
981
1006
|
}
|
|
982
1007
|
});
|
|
983
1008
|
|
|
@@ -987,22 +1012,69 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
987
1012
|
}
|
|
988
1013
|
|
|
989
1014
|
/**
|
|
990
|
-
* Check for existence of
|
|
991
|
-
* (
|
|
1015
|
+
* Check for existence of sections data
|
|
1016
|
+
* (Sections data must be defined as *.md files in the <code>sSectionsDir</code>)
|
|
992
1017
|
* and add a boolean flag in case it exists
|
|
993
1018
|
*
|
|
994
1019
|
* @param oChainObject chain object
|
|
995
1020
|
*/
|
|
996
|
-
function
|
|
997
|
-
if (!
|
|
1021
|
+
function addFlagsForSectionsData(oChainObject) {
|
|
1022
|
+
if (!sSectionsDir) {
|
|
998
1023
|
return oChainObject;
|
|
999
1024
|
}
|
|
1025
|
+
|
|
1000
1026
|
const slibName = oChainObject.fileData.library;
|
|
1027
|
+
|
|
1001
1028
|
oChainObject.fileData.symbols.forEach(function(symbol) {
|
|
1002
|
-
const
|
|
1003
|
-
if (
|
|
1004
|
-
|
|
1029
|
+
const sComponentPath = symbol.name.replace(slibName, "").replace(/^[.]/, "").replace(/[.]/g, "/");
|
|
1030
|
+
if (!sComponentPath) {
|
|
1031
|
+
return;
|
|
1005
1032
|
}
|
|
1033
|
+
|
|
1034
|
+
const componentDir = path.join(sSectionsDir, sComponentPath);
|
|
1035
|
+
if (fs.existsSync(componentDir)) {
|
|
1036
|
+
try {
|
|
1037
|
+
const dirContents = fs.readdirSync(componentDir, { withFileTypes: true });
|
|
1038
|
+
|
|
1039
|
+
// Separate files and directories in one pass
|
|
1040
|
+
const mdFiles = [];
|
|
1041
|
+
const dirNames = new Set();
|
|
1042
|
+
dirContents.forEach((dirent) => {
|
|
1043
|
+
if (dirent.isFile() && dirent.name.endsWith('.md')) {
|
|
1044
|
+
mdFiles.push(dirent.name.replace(/\.md$/, ''));
|
|
1045
|
+
} else if (dirent.isDirectory()) {
|
|
1046
|
+
dirNames.add(dirent.name);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
// Build customSections array with subsection support
|
|
1051
|
+
const customSections = mdFiles.map(function(sectionName) {
|
|
1052
|
+
// Check if there's a matching directory for this section
|
|
1053
|
+
if (dirNames.has(sectionName)) {
|
|
1054
|
+
try {
|
|
1055
|
+
const subsectionFiles = fs.readdirSync(path.join(componentDir, sectionName), { withFileTypes: true })
|
|
1056
|
+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
|
|
1057
|
+
.map((dirent) => dirent.name.replace(/\.md$/, ''));
|
|
1058
|
+
|
|
1059
|
+
if (subsectionFiles.length > 0) {
|
|
1060
|
+
return { name: sectionName, hasSubsections: true, subsections: subsectionFiles };
|
|
1061
|
+
}
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
log.error('Error scanning subsection directory:', path.join(componentDir, sectionName), error);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return sectionName;
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
if (customSections.length > 0) {
|
|
1070
|
+
symbol.customSections = customSections;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
log.error('Error scanning component sections directory:', componentDir, error);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1006
1078
|
});
|
|
1007
1079
|
return oChainObject;
|
|
1008
1080
|
}
|
|
@@ -2175,7 +2247,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2175
2247
|
* Adjusts methods info so that it can be easily displayed in a table
|
|
2176
2248
|
* @param aMethods - the methods array initially coming from the server
|
|
2177
2249
|
*/
|
|
2178
|
-
buildMethodsModel: function (aMethods) {
|
|
2250
|
+
buildMethodsModel: function (aMethods, oAllDependentAPIs) {
|
|
2179
2251
|
var fnExtractParameterProperties = function (oParameter, aParameters, iDepth, aPhoneName) {
|
|
2180
2252
|
if (oParameter.parameterProperties) {
|
|
2181
2253
|
Object.keys(oParameter.parameterProperties).forEach(function (sProperty) {
|
|
@@ -2185,7 +2257,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2185
2257
|
|
|
2186
2258
|
// Handle types
|
|
2187
2259
|
if (oProperty.type) {
|
|
2188
|
-
oProperty.typeInfo = parseUI5Types(oProperty.type);
|
|
2260
|
+
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
|
|
2189
2261
|
// Keep file size in check
|
|
2190
2262
|
delete oProperty.type;
|
|
2191
2263
|
}
|
|
@@ -2217,7 +2289,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2217
2289
|
if (oMethod.parameters) {
|
|
2218
2290
|
oMethod.parameters.forEach(function (oParameter) {
|
|
2219
2291
|
if (oParameter.type) {
|
|
2220
|
-
oParameter.typeInfo = parseUI5Types(oParameter.type);
|
|
2292
|
+
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
|
|
2221
2293
|
// Keep file size in check
|
|
2222
2294
|
delete oParameter.type;
|
|
2223
2295
|
}
|
|
@@ -2238,7 +2310,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2238
2310
|
// Handle return values
|
|
2239
2311
|
if (oMethod.returnValue && oMethod.returnValue.type) {
|
|
2240
2312
|
// Handle types
|
|
2241
|
-
oMethod.returnValue.typeInfo = parseUI5Types(oMethod.returnValue.type);
|
|
2313
|
+
oMethod.returnValue.typeInfo = parseUI5Types(oMethod.returnValue.type, oAllDependentAPIs);
|
|
2242
2314
|
}
|
|
2243
2315
|
|
|
2244
2316
|
});
|
|
@@ -2375,6 +2447,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2375
2447
|
inputFile: sInputFile,
|
|
2376
2448
|
outputFile: sOutputFile,
|
|
2377
2449
|
libraryFile: sLibraryFile,
|
|
2450
|
+
sectionsDir: sSectionsDir,
|
|
2378
2451
|
aDependentLibraryFiles: Array.isArray(vDependencyAPIFiles) ? vDependencyAPIFiles : null
|
|
2379
2452
|
};
|
|
2380
2453
|
|
|
@@ -2387,7 +2460,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
|
|
|
2387
2460
|
.then(getAPIJSONPromise)
|
|
2388
2461
|
.then(loadDependencyLibraryFiles)
|
|
2389
2462
|
.then(transformApiJson)
|
|
2390
|
-
.then(
|
|
2463
|
+
.then(addFlagsForSectionsData)
|
|
2391
2464
|
.then(createApiRefApiJson);
|
|
2392
2465
|
return p;
|
|
2393
2466
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* JSDoc3 plugin for UI5 documentation generation.
|
|
3
3
|
*
|
|
4
|
-
* (c) Copyright
|
|
4
|
+
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -73,7 +73,8 @@ const path = require('jsdoc/path');
|
|
|
73
73
|
const logger = require('jsdoc/util/logger');
|
|
74
74
|
const pluginConfig = (env.conf && env.conf.templates && env.conf.templates.ui5) || env.opts.sapui5 || {};
|
|
75
75
|
const escope = require("escope");
|
|
76
|
-
const
|
|
76
|
+
const templatePath = path.getResourcePath(env.opts.template) || "./template";
|
|
77
|
+
const {isSemVer} = require(`${templatePath}/utils/versionUtil`);
|
|
77
78
|
|
|
78
79
|
/* ---- logging ---- */
|
|
79
80
|
|
|
@@ -1220,11 +1221,13 @@ function collectClassInfo(extendCall, classDoclet) {
|
|
|
1220
1221
|
if ( classDoclet && classDoclet.augments && classDoclet.augments.length === 1 ) {
|
|
1221
1222
|
baseType = classDoclet.augments[0];
|
|
1222
1223
|
}
|
|
1224
|
+
const jsdocClassName = classDoclet?.kind === "class" ? classDoclet.alias || classDoclet.longname : undefined;
|
|
1225
|
+
|
|
1223
1226
|
if ( isMemberExpression(extendCall.callee) ) {
|
|
1224
1227
|
const baseCandidate = getResolvedObjectName(extendCall.callee.object);
|
|
1225
1228
|
if ( baseCandidate && baseType == null ) {
|
|
1226
1229
|
baseType = baseCandidate;
|
|
1227
|
-
} else if ( baseCandidate !== baseType ) {
|
|
1230
|
+
} else if ( baseCandidate !== toRuntimeMetadataName(baseType) ) {
|
|
1228
1231
|
future(`documented base type '${baseType}' doesn't match technical base type '${baseCandidate}'`);
|
|
1229
1232
|
}
|
|
1230
1233
|
}
|
|
@@ -1283,6 +1286,32 @@ function collectClassInfo(extendCall, classDoclet) {
|
|
|
1283
1286
|
}
|
|
1284
1287
|
|
|
1285
1288
|
const classInfoNode = extendCall.arguments[1];
|
|
1289
|
+
if (classInfoNode?.type === Syntax.ObjectExpression && jsdocClassName && addLeadingCommentNode) {
|
|
1290
|
+
// Take care of the @lends comment only
|
|
1291
|
+
// - when the class info is an object literal (otherwise lends makes no sense)
|
|
1292
|
+
// - when a class name is found (it might differ from the runtime metadata name)
|
|
1293
|
+
// - when the entity is documented as a class (or else '.prototype' would be wrong)
|
|
1294
|
+
const classInfoComment = getLeadingCommentNode(classInfoNode);
|
|
1295
|
+
if (classInfoComment == null) {
|
|
1296
|
+
// If the class info object doesn't have a @lends tag, add it
|
|
1297
|
+
const raw = `/** @lends ${jsdocClassName}.prototype */`;
|
|
1298
|
+
info(`lends comment injected for class '${jsdocClassName}'`);
|
|
1299
|
+
addLeadingCommentNode(classInfoNode, {
|
|
1300
|
+
type: "CommentBlock",
|
|
1301
|
+
raw,
|
|
1302
|
+
value: raw.slice(2,-2),
|
|
1303
|
+
loc: classInfoNode.loc
|
|
1304
|
+
});
|
|
1305
|
+
} else {
|
|
1306
|
+
// Otherwise check whether the name matches the name of the described class
|
|
1307
|
+
const doclet = new Doclet(getRawComment(classInfoComment), {});
|
|
1308
|
+
// The name after the @lends is surprisingly stored in the 'alias' property
|
|
1309
|
+
if (doclet.alias !== `${jsdocClassName}.prototype`) {
|
|
1310
|
+
future(`lends comment '${doclet.alias}' doesn't match documented class name '${jsdocClassName}'`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1286
1315
|
const classInfoMap = createPropertyMap(classInfoNode);
|
|
1287
1316
|
if ( classInfoMap && classInfoMap.metadata && classInfoMap.metadata.value.type !== Syntax.ObjectExpression ) {
|
|
1288
1317
|
warning(`class metadata exists but can't be analyzed. It is not of type 'ObjectExpression', but a '${classInfoMap.metadata.value.type}'.`);
|
|
@@ -1772,7 +1801,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
1772
1801
|
}
|
|
1773
1802
|
|
|
1774
1803
|
function name(prefix, n, _static) {
|
|
1775
|
-
return
|
|
1804
|
+
return jsdocClassName + rname(prefix, n, _static);
|
|
1776
1805
|
}
|
|
1777
1806
|
|
|
1778
1807
|
/*
|
|
@@ -1884,6 +1913,18 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
1884
1913
|
const p = m ? m.index : -1;
|
|
1885
1914
|
const hasSettingsDocs = rawClassComment.indexOf("The supported settings are:") >= 0;
|
|
1886
1915
|
const classAccess = doclet?.access ?? "public";
|
|
1916
|
+
|
|
1917
|
+
// If doclet exists and has an alias or longname, use that when building the fully qualified
|
|
1918
|
+
// names of methods or events.
|
|
1919
|
+
// Do this only when the doclet is of kind "class". Many TypeScript classes unfortunately
|
|
1920
|
+
// use a broken pattern with @namespace (needs to be deprecated in babel-plugin-transform-modules-ui5)
|
|
1921
|
+
const jsdocClassName =
|
|
1922
|
+
(doclet?.kind === "class" ? doclet.alias || doclet.longname : undefined) || oClassInfo.name;
|
|
1923
|
+
if (jsdocClassName !== oClassInfo.name) {
|
|
1924
|
+
info("jsdoc class name differs from runtime metadata name", jsdocClassName, oClassInfo.name);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
const displayClassName = oClassInfo.name;
|
|
1887
1928
|
const visibility = createVisibilityTags(classAccess, doclet?.__ui5?.stakeholders);
|
|
1888
1929
|
const thisClass = 'this'; // oClassInfo.name
|
|
1889
1930
|
|
|
@@ -2007,7 +2048,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2007
2048
|
}
|
|
2008
2049
|
|
|
2009
2050
|
newJSDoc([
|
|
2010
|
-
"Returns a metadata object for class " +
|
|
2051
|
+
"Returns a metadata object for class " + displayClassName + ".",
|
|
2011
2052
|
"",
|
|
2012
2053
|
"@returns {sap.ui.base.Metadata} Metadata object describing this class",
|
|
2013
2054
|
visibility,
|
|
@@ -2018,7 +2059,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2018
2059
|
|
|
2019
2060
|
if ( !oClassInfo["final"] ) {
|
|
2020
2061
|
newJSDoc([
|
|
2021
|
-
"Creates a new subclass of class " +
|
|
2062
|
+
"Creates a new subclass of class " + displayClassName + " with name <code>sClassName</code>",
|
|
2022
2063
|
"and enriches it with the information contained in <code>oClassInfo</code>.",
|
|
2023
2064
|
"",
|
|
2024
2065
|
"<code>oClassInfo</code> might contain the same kind of information as described in {@link " + (oClassInfo.baseType ? oClassInfo.baseType + ".extend" : "sap.ui.base.Object.extend Object.extend") + "}.",
|
|
@@ -2304,7 +2345,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2304
2345
|
"",
|
|
2305
2346
|
info.enableEventBubbling ? "This event bubbles up the control hierarchy." : "",
|
|
2306
2347
|
"",
|
|
2307
|
-
"@name " +
|
|
2348
|
+
"@name " + jsdocClassName + "#" + n,
|
|
2308
2349
|
"@event",
|
|
2309
2350
|
devStateTags,
|
|
2310
2351
|
"@param {sap.ui.base.Event} oControlEvent",
|
|
@@ -2321,10 +2362,10 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2321
2362
|
newJSDoc(lines);
|
|
2322
2363
|
|
|
2323
2364
|
newJSDoc([
|
|
2324
|
-
"Attaches event handler <code>fnFunction</code> to the " + link + " event of this <code>" +
|
|
2365
|
+
"Attaches event handler <code>fnFunction</code> to the " + link + " event of this <code>" + displayClassName + "</code>.",
|
|
2325
2366
|
"",
|
|
2326
2367
|
"When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> if specified, ",
|
|
2327
|
-
"otherwise it will be bound to this <code>" +
|
|
2368
|
+
"otherwise it will be bound to this <code>" + displayClassName + "</code> itself.",
|
|
2328
2369
|
"",
|
|
2329
2370
|
!newStyle && info.doc ? info.doc : "",
|
|
2330
2371
|
"",
|
|
@@ -2333,7 +2374,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2333
2374
|
"@param {function(sap.ui.base.Event):void}",
|
|
2334
2375
|
" fnFunction The function to be called when the event occurs",
|
|
2335
2376
|
"@param {object}",
|
|
2336
|
-
" [oListener] Context object to call the event handler with. Defaults to this <code>" +
|
|
2377
|
+
" [oListener] Context object to call the event handler with. Defaults to this <code>" + displayClassName + "</code> itself",
|
|
2337
2378
|
"",
|
|
2338
2379
|
"@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
|
|
2339
2380
|
devStateTags,
|
|
@@ -2342,7 +2383,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
|
|
|
2342
2383
|
"@function"
|
|
2343
2384
|
]);
|
|
2344
2385
|
newJSDoc([
|
|
2345
|
-
"Detaches event handler <code>fnFunction</code> from the " + link + " event of this <code>" +
|
|
2386
|
+
"Detaches event handler <code>fnFunction</code> from the " + link + " event of this <code>" + displayClassName + "</code>.",
|
|
2346
2387
|
"",
|
|
2347
2388
|
"The passed function and listener object must match the ones used for event registration.",
|
|
2348
2389
|
"",
|
|
@@ -2439,6 +2480,7 @@ function toRuntimeMetadataName(longname) {
|
|
|
2439
2480
|
|
|
2440
2481
|
let isDocComment;
|
|
2441
2482
|
let getLeadingCommentNode;
|
|
2483
|
+
let addLeadingCommentNode;
|
|
2442
2484
|
|
|
2443
2485
|
// JSDoc added the node type <code>Syntax.File</code> with the same change that activated Babylon
|
|
2444
2486
|
// See https://github.com/jsdoc3/jsdoc/commit/ffec4a42291de6d68e6240f304b68d6abb82a869
|
|
@@ -2464,6 +2506,15 @@ if ( Syntax.File === 'File' ) {
|
|
|
2464
2506
|
return undefined;
|
|
2465
2507
|
};
|
|
2466
2508
|
|
|
2509
|
+
addLeadingCommentNode = function(node, comment) {
|
|
2510
|
+
node.leadingComments ??= [];
|
|
2511
|
+
let index = node.leadingComments.length;
|
|
2512
|
+
while (index > 0 && node.leadingComments[index - 1].type !== 'CommentBlock') {
|
|
2513
|
+
index--;
|
|
2514
|
+
}
|
|
2515
|
+
node.leadingComments.splice(index, 0, comment);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2467
2518
|
} else {
|
|
2468
2519
|
|
|
2469
2520
|
// JSDoc versions before 3.5.0
|
|
@@ -2498,6 +2549,9 @@ if ( Syntax.File === 'File' ) {
|
|
|
2498
2549
|
|
|
2499
2550
|
return comment;
|
|
2500
2551
|
};
|
|
2552
|
+
|
|
2553
|
+
// not implemented for espree (no automatic injection of @lends comments)
|
|
2554
|
+
addLeadingCommentNode = undefined;
|
|
2501
2555
|
}
|
|
2502
2556
|
|
|
2503
2557
|
//--- comment related functions that are independent from the JSdoc version
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* JSDoc3 template for UI5 documentation generation.
|
|
3
3
|
*
|
|
4
|
-
* (c) Copyright
|
|
4
|
+
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -231,6 +231,7 @@ function publish(symbolSet) {
|
|
|
231
231
|
|
|
232
232
|
// now resolve relationships
|
|
233
233
|
const aRootNamespaces = createNamespaceTree(allSymbols);
|
|
234
|
+
pushDownDeprecations(allSymbols);
|
|
234
235
|
createInheritanceTree(allSymbols);
|
|
235
236
|
collectMembers(allSymbols);
|
|
236
237
|
mergeEventDocumentation(allSymbols);
|
|
@@ -374,6 +375,33 @@ function makeNamespace(memberof) {
|
|
|
374
375
|
]);
|
|
375
376
|
}
|
|
376
377
|
|
|
378
|
+
function pushDownDeprecations() {
|
|
379
|
+
|
|
380
|
+
function pushDown(oSymbol, sInheritedDeprecation) {
|
|
381
|
+
if (oSymbol == null || !isFirstClassSymbol(oSymbol)) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const sOwnDeprecation = oSymbol.deprecated;
|
|
385
|
+
if (sOwnDeprecation == null && sInheritedDeprecation != null) {
|
|
386
|
+
oSymbol.deprecated = sInheritedDeprecation;
|
|
387
|
+
info(`mark ${oSymbol.longname} as deprecated (${sInheritedDeprecation})`);
|
|
388
|
+
} else if (sOwnDeprecation != null) {
|
|
389
|
+
const info = extractSince(sOwnDeprecation);
|
|
390
|
+
const sinceBecause = info.since ? `As of version ${info.since}, because`: `Because`;
|
|
391
|
+
sInheritedDeprecation = `${sinceBecause} it is part of the deprecated package ${oSymbol.longname}`;
|
|
392
|
+
}
|
|
393
|
+
oSymbol.__ui5.children?.forEach((oChild) => pushDown(oChild, sInheritedDeprecation));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// w/o a library name we can't start with the library namespace
|
|
397
|
+
if (!templateConf.uilib) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// start the push down at the library namespace to ensure locality
|
|
402
|
+
pushDown(lookup(templateConf.uilib));
|
|
403
|
+
}
|
|
404
|
+
|
|
377
405
|
//---- inheritance hierarchy ----------------------------------------------------------------------------
|
|
378
406
|
|
|
379
407
|
/*
|
|
@@ -491,11 +491,18 @@ async function createManifest(
|
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
|
|
494
|
+
const libraryMetadata = {
|
|
495
495
|
i18n: i18n(),
|
|
496
496
|
css: css(),
|
|
497
497
|
content: content()
|
|
498
498
|
};
|
|
499
|
+
|
|
500
|
+
if (process.env.UI5_CLI_EXPERIMENTAL_BUNDLE_INFO_PRELOAD) {
|
|
501
|
+
// Add flag indicating that the (currently experimental) bundleVersion 2 preload bundling is used
|
|
502
|
+
libraryMetadata.bundleVersion = 2;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return libraryMetadata;
|
|
499
506
|
}
|
|
500
507
|
|
|
501
508
|
const sapUI5 = {
|
|
@@ -441,6 +441,12 @@ class ManifestEnhancer {
|
|
|
441
441
|
|
|
442
442
|
if (this.manifest["sap.app"].type === "library") {
|
|
443
443
|
await this.processSapUi5LibraryI18n();
|
|
444
|
+
|
|
445
|
+
if (process.env.UI5_CLI_EXPERIMENTAL_BUNDLE_INFO_PRELOAD) {
|
|
446
|
+
// Add flag indicating that the (currently experimental) bundleVersion 2 preload bundling is used
|
|
447
|
+
this.manifest["sap.ui5"].library ??= {};
|
|
448
|
+
this.manifest["sap.ui5"].library.bundleVersion = 2;
|
|
449
|
+
}
|
|
444
450
|
} else {
|
|
445
451
|
await Promise.all([
|
|
446
452
|
this.processSapAppI18n(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import escapeUnicode from "escape-unicode";
|
|
1
|
+
import {escapeUnicode} from "escape-unicode";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
@@ -57,10 +57,10 @@ const escapeNonAscii = function(string) {
|
|
|
57
57
|
/**
|
|
58
58
|
* Escapes non ASCII characters with unicode escape sequences.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
60
|
+
* ```js
|
|
61
61
|
* const encoding = nonAsciiEscaper.getEncodingFromAlias("ISO-8859-1");
|
|
62
62
|
* nonAsciiEscaper({resources, options: {encoding}});
|
|
63
|
-
*
|
|
63
|
+
* ```
|
|
64
64
|
*
|
|
65
65
|
* @public
|
|
66
66
|
* @function default
|
|
@@ -28,14 +28,14 @@ function getTimestamp() {
|
|
|
28
28
|
* @typedef {Object<string, {lazy: boolean}>} ManifestLibraries
|
|
29
29
|
*
|
|
30
30
|
* sample:
|
|
31
|
-
*
|
|
31
|
+
* ```json
|
|
32
32
|
* {
|
|
33
33
|
* "sap.chart": {
|
|
34
34
|
* "lazy": true
|
|
35
35
|
* },
|
|
36
36
|
* "sap.f": { }
|
|
37
37
|
* }
|
|
38
|
-
*
|
|
38
|
+
* ```
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -366,14 +366,16 @@ const processLibraryInfo = async (libraryInfo) => {
|
|
|
366
366
|
* @param {string} parameters.options.rootProjectVersion Version of the root project
|
|
367
367
|
* @param {module:@ui5/builder/processors/versionInfoGenerator~LibraryInfo[]} parameters.options.libraryInfos Array of
|
|
368
368
|
* objects representing libraries,
|
|
369
|
-
* e.g.
|
|
369
|
+
* e.g.
|
|
370
|
+
* ```json
|
|
370
371
|
* {
|
|
371
372
|
* name: "lib.x",
|
|
372
373
|
* version: "1.0.0",
|
|
373
374
|
* libraryManifest: @ui5/fs/Resource,
|
|
374
375
|
* embeddedManifests: @ui5/fs/Resource[]
|
|
375
376
|
* }
|
|
376
|
-
*
|
|
377
|
+
* ```
|
|
378
|
+
*
|
|
377
379
|
* @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with an array containing the versionInfo resource
|
|
378
380
|
*/
|
|
379
381
|
|
|
@@ -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}`);
|
|
@@ -271,7 +362,9 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
|
|
|
271
362
|
return moduleBundler({options, resources});
|
|
272
363
|
};
|
|
273
364
|
|
|
274
|
-
return nonDbgWorkspace.byGlob(
|
|
365
|
+
return nonDbgWorkspace.byGlob(
|
|
366
|
+
"/resources/**/*.{js,json,xml,html,properties,library,js.map}"
|
|
367
|
+
).then(async (resources) => {
|
|
275
368
|
// Find all libraries and create a library-preload.js bundle
|
|
276
369
|
|
|
277
370
|
let p = Promise.resolve();
|
|
@@ -299,8 +392,8 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
|
|
|
299
392
|
return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.HasDebugVariant);
|
|
300
393
|
}
|
|
301
394
|
});
|
|
302
|
-
unoptimizedResources =
|
|
303
|
-
|
|
395
|
+
unoptimizedResources = await unoptimizedWorkspace.byGlob(
|
|
396
|
+
"/resources/**/*.{js,json,xml,html,properties,library,js.map}");
|
|
304
397
|
|
|
305
398
|
unoptimizedModuleNameMapping = createModuleNameMapping({
|
|
306
399
|
resources: unoptimizedResources,
|
|
@@ -390,42 +483,117 @@ export default async function({workspace, taskUtil, options: {skipBundles = [],
|
|
|
390
483
|
const libraryNamespaceMatch = libraryIndicatorPath.match(libraryNamespacePattern);
|
|
391
484
|
if (libraryNamespaceMatch && libraryNamespaceMatch[1]) {
|
|
392
485
|
const libraryNamespace = libraryNamespaceMatch[1];
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
486
|
+
let results;
|
|
487
|
+
if (!createBundleInfoPreload) {
|
|
488
|
+
// Regular bundling
|
|
489
|
+
results = await Promise.all([
|
|
490
|
+
execModuleBundlerIfNeeded({
|
|
491
|
+
options: {
|
|
492
|
+
bundleDefinition: getBundleDefinition(libraryNamespace, excludes),
|
|
493
|
+
bundleOptions: {
|
|
494
|
+
optimize: true,
|
|
495
|
+
ignoreMissingModules: true
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
resources
|
|
499
|
+
}),
|
|
500
|
+
execModuleBundlerIfNeeded({
|
|
501
|
+
options: {
|
|
502
|
+
bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
|
|
503
|
+
bundleOptions: {
|
|
504
|
+
optimize: true,
|
|
505
|
+
ignoreMissingModules: true,
|
|
506
|
+
skipIfEmpty: true
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
resources
|
|
510
|
+
}),
|
|
511
|
+
execModuleBundlerIfNeeded({
|
|
512
|
+
options: {
|
|
513
|
+
bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
|
|
514
|
+
bundleOptions: {
|
|
515
|
+
optimize: false,
|
|
516
|
+
ignoreMissingModules: true,
|
|
517
|
+
skipIfEmpty: true
|
|
518
|
+
}
|
|
519
|
+
// Note: Although the bundle uses optimize=false, there is
|
|
520
|
+
// no moduleNameMapping needed, as support files are excluded from minification.
|
|
521
|
+
},
|
|
522
|
+
resources
|
|
523
|
+
})
|
|
524
|
+
]);
|
|
525
|
+
} else {
|
|
526
|
+
log.info(
|
|
527
|
+
`Using experimental bundling with bundle info preload ` +
|
|
528
|
+
`for library ${libraryNamespace} in project ${projectName}`);
|
|
529
|
+
log.info(`Detected sap.ui.core version is ${coreVersion || "unknown"}`);
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
if (skipBundles.includes(`${libraryNamespace}/library-preload.js`) &&
|
|
533
|
+
!skipBundles.includes(`${libraryNamespace}/_library-content.js`)) {
|
|
534
|
+
// If the standard preload bundle is skipped, ensure to also skip the content bundle,
|
|
535
|
+
// since they depend on each other
|
|
536
|
+
skipBundles.push(`${libraryNamespace}/_library-content.js`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (skipBundles.includes(`${libraryNamespace}/_library-content.js`) &&
|
|
540
|
+
!skipBundles.includes(`${libraryNamespace}/library-preload.js`)) {
|
|
541
|
+
// If the content bundle is skipped, the default preload bundle must be skipped as well
|
|
542
|
+
throw new Error(
|
|
543
|
+
`A custom bundle '${libraryNamespace}/_library-content.js' has been defined, ` +
|
|
544
|
+
`but it also requires a corresponding custom bundle definition for ` +
|
|
545
|
+
`'${libraryNamespace}/library-preload.js'`);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Experimental bundling with bundle info preload
|
|
549
|
+
results = await Promise.all([
|
|
550
|
+
execModuleBundlerIfNeeded({
|
|
551
|
+
options: {
|
|
552
|
+
bundleDefinition:
|
|
553
|
+
getBundleInfoPreloadDefinition(libraryNamespace, excludes, coreVersion),
|
|
554
|
+
bundleOptions: {
|
|
555
|
+
optimize: true,
|
|
556
|
+
ignoreMissingModules: true
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
resources
|
|
560
|
+
}),
|
|
561
|
+
execModuleBundlerIfNeeded({
|
|
562
|
+
options: {
|
|
563
|
+
bundleDefinition: getContentBundleDefinition(libraryNamespace, excludes),
|
|
564
|
+
bundleOptions: {
|
|
565
|
+
optimize: true,
|
|
566
|
+
ignoreMissingModules: true
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
resources
|
|
570
|
+
}),
|
|
571
|
+
execModuleBundlerIfNeeded({
|
|
572
|
+
options: {
|
|
573
|
+
bundleDefinition: getDesigntimeBundleDefinition(libraryNamespace),
|
|
574
|
+
bundleOptions: {
|
|
575
|
+
optimize: true,
|
|
576
|
+
ignoreMissingModules: true,
|
|
577
|
+
skipIfEmpty: true
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
resources
|
|
581
|
+
}),
|
|
582
|
+
execModuleBundlerIfNeeded({
|
|
583
|
+
options: {
|
|
584
|
+
bundleDefinition: getSupportFilesBundleDefinition(libraryNamespace),
|
|
585
|
+
bundleOptions: {
|
|
586
|
+
optimize: false,
|
|
587
|
+
ignoreMissingModules: true,
|
|
588
|
+
skipIfEmpty: true
|
|
589
|
+
}
|
|
590
|
+
// Note: Although the bundle uses optimize=false, there is
|
|
591
|
+
// no moduleNameMapping needed, as support files are excluded from minification.
|
|
592
|
+
},
|
|
593
|
+
resources
|
|
594
|
+
})
|
|
595
|
+
]);
|
|
596
|
+
}
|
|
429
597
|
const bundles = Array.prototype.concat.apply([], results).filter(Boolean);
|
|
430
598
|
return Promise.all(bundles.map(({bundle, sourceMap} = {}) => {
|
|
431
599
|
if (bundle) {
|
|
@@ -55,7 +55,9 @@ function getCreatorOptions(projectName) {
|
|
|
55
55
|
* Therefore it is also not supported in combination with self-contained build.
|
|
56
56
|
* </p>
|
|
57
57
|
*
|
|
58
|
-
*
|
|
58
|
+
* <i>sample resources.json</i>
|
|
59
|
+
*
|
|
60
|
+
* ```js
|
|
59
61
|
* const resourcesJson = {
|
|
60
62
|
* "_version": "1.1.0",
|
|
61
63
|
* "resources": [
|
|
@@ -95,6 +97,7 @@ function getCreatorOptions(projectName) {
|
|
|
95
97
|
* }
|
|
96
98
|
* ]
|
|
97
99
|
* };
|
|
100
|
+
* ```
|
|
98
101
|
*
|
|
99
102
|
* @public
|
|
100
103
|
* @function default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ui5/builder",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.4",
|
|
4
4
|
"description": "UI5 CLI - Builder",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SAP SE",
|
|
@@ -33,39 +33,19 @@
|
|
|
33
33
|
"npm": ">= 8"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
|
-
"test": "npm run lint && npm run coverage
|
|
36
|
+
"test": "npm run lint && npm run coverage",
|
|
37
37
|
"lint": "eslint ./",
|
|
38
38
|
"unit": "rimraf test/tmp && ava",
|
|
39
39
|
"unit-verbose": "rimraf test/tmp && cross-env UI5_LOG_LVL=verbose ava --verbose --serial",
|
|
40
40
|
"unit-watch": "rimraf test/tmp && ava --watch",
|
|
41
41
|
"unit-inspect": "cross-env UI5_LOG_LVL=verbose ava debug --break",
|
|
42
|
-
"coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\""
|
|
43
|
-
"preversion": "npm test",
|
|
44
|
-
"version": "git-chglog --sort semver --next-tag v$npm_package_version -o CHANGELOG.md v4.0.0.. && git add CHANGELOG.md",
|
|
45
|
-
"prepublishOnly": "git push --follow-tags",
|
|
46
|
-
"release-note": "git-chglog --sort semver -c .chglog/release-config.yml v$npm_package_version",
|
|
47
|
-
"depcheck": "depcheck --ignores @ui5/builder,@istanbuljs/esm-loader-hook,catharsis,rimraf,jsdoc --parsers='**/*.js:es6,**/*.cjs:es6'"
|
|
42
|
+
"coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\""
|
|
48
43
|
},
|
|
49
44
|
"files": [
|
|
50
45
|
"CHANGELOG.md",
|
|
51
46
|
"lib/**",
|
|
52
47
|
"LICENSES/**"
|
|
53
48
|
],
|
|
54
|
-
"ava": {
|
|
55
|
-
"files": [
|
|
56
|
-
"test/lib/**/*.js"
|
|
57
|
-
],
|
|
58
|
-
"watchMode": {
|
|
59
|
-
"ignoreChanges": [
|
|
60
|
-
"test/tmp/**"
|
|
61
|
-
]
|
|
62
|
-
},
|
|
63
|
-
"nodeArguments": [
|
|
64
|
-
"--loader=esmock",
|
|
65
|
-
"--no-warnings"
|
|
66
|
-
],
|
|
67
|
-
"workerThreads": false
|
|
68
|
-
},
|
|
69
49
|
"nyc": {
|
|
70
50
|
"reporter": [
|
|
71
51
|
"lcov",
|
|
@@ -111,10 +91,10 @@
|
|
|
111
91
|
},
|
|
112
92
|
"dependencies": {
|
|
113
93
|
"@jridgewell/sourcemap-codec": "^1.5.5",
|
|
114
|
-
"@ui5/fs": "^5.0.0-alpha.
|
|
115
|
-
"@ui5/logger": "^5.0.0-alpha.
|
|
94
|
+
"@ui5/fs": "^5.0.0-alpha.4",
|
|
95
|
+
"@ui5/logger": "^5.0.0-alpha.4",
|
|
116
96
|
"cheerio": "1.0.0",
|
|
117
|
-
"escape-unicode": "^0.
|
|
97
|
+
"escape-unicode": "^0.3.0",
|
|
118
98
|
"escope": "^4.0.0",
|
|
119
99
|
"espree": "^10.4.0",
|
|
120
100
|
"graceful-fs": "^4.2.11",
|
|
@@ -123,16 +103,15 @@
|
|
|
123
103
|
"pretty-data": "^0.40.0",
|
|
124
104
|
"semver": "^7.7.2",
|
|
125
105
|
"terser": "^5.44.0",
|
|
126
|
-
"workerpool": "^
|
|
106
|
+
"workerpool": "^10.0.1",
|
|
127
107
|
"xml2js": "^0.6.2"
|
|
128
108
|
},
|
|
129
109
|
"devDependencies": {
|
|
130
110
|
"@istanbuljs/esm-loader-hook": "^0.3.0",
|
|
131
111
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
132
|
-
"@ui5/project": "^5.0.0-alpha.
|
|
112
|
+
"@ui5/project": "^5.0.0-alpha.4",
|
|
133
113
|
"ava": "^6.4.1",
|
|
134
|
-
"cross-env": "^
|
|
135
|
-
"depcheck": "^1.4.7",
|
|
114
|
+
"cross-env": "^10.1.0",
|
|
136
115
|
"eslint": "^9.36.0",
|
|
137
116
|
"esmock": "^2.7.3",
|
|
138
117
|
"line-column": "^1.0.2",
|