@ui5/builder 4.0.10 → 4.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,10 +2,16 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
4
 
5
- A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v4.0.10...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v4.0.11...HEAD).
6
+
7
+ <a name="v4.0.11"></a>
8
+ ## [v4.0.11] - 2025-09-11
9
+ ### Bug Fixes
10
+ - Rename project to UI5 CLI [`2d0cb3a`](https://github.com/SAP/ui5-builder/commit/2d0cb3a222508facec0a4103d048f92c2fd0b41f)
11
+
6
12
 
7
13
  <a name="v4.0.10"></a>
8
- ## [v4.0.10] - 2025-09-01
14
+ ## [v4.0.10] - 2025-09-03
9
15
  ### Bug Fixes
10
16
  - Downgrade cheerio to v1.0.0 to resolve Node compatibility issues [`4dc3a6e`](https://github.com/SAP/ui5-builder/commit/4dc3a6ef3029fca50d6b3ab45ed9667c206c6435)
11
17
  - **Bundler:** Include 'bundleInfo' section in multipart bundles [`e7ab31e`](https://github.com/SAP/ui5-builder/commit/e7ab31e859c5659612f0cc726e0410064f0b3174)
@@ -980,6 +986,7 @@ to load the custom bundle file instead.
980
986
 
981
987
  ### Features
982
988
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
989
+ [v4.0.11]: https://github.com/SAP/ui5-builder/compare/v4.0.10...v4.0.11
983
990
  [v4.0.10]: https://github.com/SAP/ui5-builder/compare/v4.0.9...v4.0.10
984
991
  [v4.0.9]: https://github.com/SAP/ui5-builder/compare/v4.0.8...v4.0.9
985
992
  [v4.0.8]: https://github.com/SAP/ui5-builder/compare/v4.0.7...v4.0.8
package/CONTRIBUTING.md CHANGED
@@ -1,3 +1,3 @@
1
- # Contributing to the UI5 Tooling
1
+ # Contributing to the UI5 CLI
2
2
 
3
- See CONTRIBUTING.md in the [SAP/ui5-tooling](https://github.com/SAP/ui5-tooling/blob/main/CONTRIBUTING.md) repository.
3
+ See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md) repository.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- ![UI5 icon](https://raw.githubusercontent.com/SAP/ui5-tooling/main/docs/images/UI5_logo_wide.png)
1
+ ![UI5 icon](https://raw.githubusercontent.com/UI5/cli/main/docs/images/UI5_logo_wide.png)
2
2
 
3
3
  # ui5-builder
4
4
  > Modules for building UI5 projects
5
- > Part of the [UI5 Tooling](https://github.com/SAP/ui5-tooling)
5
+ > Part of the [UI5 CLI](https://github.com/UI5/cli)
6
6
 
7
7
  [![REUSE status](https://api.reuse.software/badge/github.com/SAP/ui5-builder)](https://api.reuse.software/info/github.com/SAP/ui5-builder)
8
8
  [![Build Status](https://dev.azure.com/sap/opensource/_apis/build/status/SAP.ui5-builder?branchName=main)](https://dev.azure.com/sap/opensource/_build/latest?definitionId=26&branchName=main)
@@ -10,17 +10,17 @@
10
10
  [![Coverage Status](https://coveralls.io/repos/github/SAP/ui5-builder/badge.svg)](https://coveralls.io/github/SAP/ui5-builder)
11
11
 
12
12
  ## Documentation
13
- UI5 Builder documentation can be found here: [sap.github.io/ui5-tooling](https://sap.github.io/ui5-tooling/v4/pages/Builder/)
13
+ UI5 Builder documentation can be found here: [ui5.github.io/cli](https://ui5.github.io/cli/v4/pages/Builder/)
14
14
 
15
- The UI5 Builder API Reference can be found here: [`@ui5/builder`](https://sap.github.io/ui5-tooling/v4/api/)
15
+ The UI5 Builder API Reference can be found here: [`@ui5/builder`](https://ui5.github.io/cli/v4/api/)
16
16
 
17
17
  ## Contributing
18
- Please check our [Contribution Guidelines](https://github.com/SAP/ui5-tooling/blob/main/CONTRIBUTING.md).
18
+ Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md).
19
19
 
20
20
  ## Support
21
- Please follow our [Contribution Guidelines](https://github.com/SAP/ui5-tooling/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue.
21
+ Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue.
22
22
 
23
- Please report issues in the main [UI5 Tooling](https://github.com/SAP/ui5-tooling) repository.
23
+ Please report issues in the main [UI5 CLI](https://github.com/UI5/cli) repository.
24
24
 
25
25
  ## Release History
26
26
  See [CHANGELOG.md](CHANGELOG.md).
package/jsdoc.json CHANGED
@@ -31,11 +31,11 @@
31
31
  }
32
32
  },
33
33
  "openGraph": {
34
- "title": "UI5 Tooling - API Reference",
34
+ "title": "UI5 CLI - API Reference",
35
35
  "type": "website",
36
- "image": "https://sap.github.io/ui5-tooling/v4/images/UI5_logo_wide.png",
37
- "site_name": "UI5 Tooling - API Reference",
38
- "url": "https://sap.github.io/ui5-tooling/"
36
+ "image": "https://ui5.github.io/cli/v4/images/UI5_logo_wide.png",
37
+ "site_name": "UI5 CLI - API Reference",
38
+ "url": "https://ui5.github.io/cli/"
39
39
  },
40
40
  "docdash": {
41
41
  "sectionOrder": [
@@ -49,8 +49,8 @@
49
49
  "Interfaces"
50
50
  ],
51
51
  "meta": {
52
- "title": "UI5 Tooling - API Reference - UI5 Builder",
53
- "description": "UI5 Tooling - API Reference - UI5 Builder",
52
+ "title": "UI5 CLI - API Reference - UI5 Builder",
53
+ "description": "UI5 CLI - API Reference - UI5 Builder",
54
54
  "keyword": "openui5 sapui5 ui5 build development tool api reference"
55
55
  },
56
56
  "search": true,
@@ -182,7 +182,7 @@ class ComponentAnalyzer {
182
182
  } else {
183
183
  log.warn(`Provided OData version "${odataVersion}" in ` +
184
184
  `dataSource "${options.dataSource}" for model "${model}" is unknown. ` +
185
- `You might be using an outdated version of the UI5 Tooling.`);
185
+ `You might be using an outdated version of the UI5 CLI.`);
186
186
  return;
187
187
  }
188
188
  break;
@@ -546,8 +546,8 @@ class BundleBuilder {
546
546
  log.error(
547
547
  "Module " + moduleName + " requires top level scope and can only be embedded as a string " +
548
548
  "(requires 'eval'), which is not supported with specVersion 4.0 and higher. " +
549
- "For more information, see the UI5 Tooling documentation " +
550
- "https://sap.github.io/ui5-tooling/stable/pages/Builder/#javascript-files-requiring-top-level-scope");
549
+ "For more information, see the UI5 CLI documentation " +
550
+ "https://ui5.github.io/cli/stable/pages/Builder/#javascript-files-requiring-top-level-scope");
551
551
  }
552
552
 
553
553
  async checkForStringBundling(moduleName) {
@@ -737,6 +737,14 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
737
737
  oEvent.description = formatters.formatDescriptionSince(oEvent.description, oEvent.since);
738
738
  }
739
739
 
740
+ if (oEvent.references) {
741
+ formatters.formatReferencesInDescription(oEvent);
742
+ }
743
+
744
+ if (oEvent.description) {
745
+ oEvent.description = formatters.formatDescription(oEvent.description);
746
+ }
747
+
740
748
  // Deprecated
741
749
  if (oEvent.deprecated) {
742
750
  oEvent.deprecatedText = formatters.formatEventDeprecated(oEvent.deprecated.since,
@@ -757,6 +765,14 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
757
765
  oParameter.description = formatters.formatDescriptionSince(oParameter.description, oParameter.since);
758
766
  }
759
767
 
768
+ if (oParameter.references) {
769
+ formatters.formatReferencesInDescription(oParameter);
770
+ }
771
+
772
+ if (oParameter.description) {
773
+ oParameter.description = formatters.formatDescription(oParameter.description);
774
+ }
775
+
760
776
  // Deprecated
761
777
  if (oParameter.deprecated) {
762
778
  oParameter.deprecatedText = formatters.formatDeprecated(oParameter.deprecated.since,
@@ -997,7 +1013,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
997
1013
  */
998
1014
  function createApiRefApiJson(oChainObject) {
999
1015
  if (returnOutputFiles) {
1000
- // If requested, return data instead of writing to FS (required by UI5 Tooling/UI5 Builder)
1016
+ // If requested, return data instead of writing to FS (required by UI5 CLI/UI5 Builder)
1001
1017
  return JSON.stringify(oChainObject.parsedData);
1002
1018
  }
1003
1019
  const sOutputDir = path.dirname(oChainObject.outputFile);
@@ -73,6 +73,7 @@ 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 {isSemVer} = require("./template/utils/versionUtil");
76
77
 
77
78
  /* ---- logging ---- */
78
79
 
@@ -871,13 +872,13 @@ function resolveFullyQuantifiedName(node) {
871
872
 
872
873
  const writeExprNode = writeExpr[0].writeExpr;
873
874
 
874
- if ( writeExprNode.type === Syntax.MemberExpression && !writeExprNode.computed && writeExprNode.object.type === Syntax.Identifier ) {
875
- leftMostName = writeExprNode.object.name;
876
-
877
- } else if (writeExprNode.type === Syntax.MemberExpression && writeExprNode.object.type === Syntax.MemberExpression) { // Standalone variable without leading dot notation namespace
875
+ // determine from write expression how to replace the leftmost name
876
+ if (writeExprNode.type === Syntax.MemberExpression) {
877
+ // replacement is a qualified name (its leftmost part will be resolved in the next round)
878
878
  leftMostName = getResolvedObjectName(writeExprNode);
879
879
 
880
880
  } else if (writeExprNode.type === Syntax.Identifier) {
881
+ // leftMostName was an alias only
881
882
  leftMostName = writeExprNode.name;
882
883
 
883
884
  } else {
@@ -886,6 +887,8 @@ function resolveFullyQuantifiedName(node) {
886
887
 
887
888
  if (leftMostName) {
888
889
  originalName = leftMostName + "." + originalName;
890
+ // determine new leftMostName to resolve next
891
+ leftMostName = leftMostName.split(".")[0];
889
892
  }
890
893
  }
891
894
 
@@ -1187,6 +1190,30 @@ function collectVisibilityInfo(settings, doclet, className, n) {
1187
1190
  return visibility;
1188
1191
  }
1189
1192
 
1193
+ function convertDefaultClass(settings, doclet, n) {
1194
+ const node = settings.defaultClass?.value;
1195
+ if ( node == null ) {
1196
+ // check if a @default tag is given (e.g. sap.ui.core.Element#customData)
1197
+ // This is only taken into accout when there is no defaultClass property in the metadata
1198
+ if ( doclet?.defaultvalue != null ) {
1199
+ if ( typeof doclet.defaultvalue === "string" ) {
1200
+ return doclet.defaultvalue;
1201
+ }
1202
+ error(`could not derive defaultClass for aggregation ${n} from default tag ${doclet.defaultvalue}`);
1203
+ }
1204
+ return undefined;
1205
+ }
1206
+ if ( node.type === Syntax.Identifier ) {
1207
+ if ( currentModule.localNames[node.name]?.module ) {
1208
+ return currentModule.localNames[node.name].module;
1209
+ }
1210
+ error(`could not derive defaultClass for aggregation ${n} from identifier ${node.name}`);
1211
+ return "unknown";
1212
+ }
1213
+ error(`could not derive defaultClass for aggregation ${n} from node of type ${node.type}`);
1214
+ return "unknown";
1215
+ }
1216
+
1190
1217
  function collectClassInfo(extendCall, classDoclet) {
1191
1218
 
1192
1219
  let baseType;
@@ -1349,6 +1376,7 @@ function collectClassInfo(extendCall, classDoclet) {
1349
1376
  singularName : settings.singularName ? settings.singularName.value.value : guessSingularName(n),
1350
1377
  cardinality : (settings.multiple && !settings.multiple.value.value) ? "0..1" : "0..n",
1351
1378
  bindable : settings.bindable ? !!convertValue(settings.bindable.value) : false,
1379
+ defaultClass: convertDefaultClass(settings, doclet, n),
1352
1380
  methods: (methods = {
1353
1381
  "get": "get" + N,
1354
1382
  "destroy": "destroy" + N
@@ -1772,6 +1800,31 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1772
1800
  // return s.slice(s.lastIndexOf('.') + 1);
1773
1801
  // }
1774
1802
 
1803
+ /**
1804
+ * Creates all necessary tags to reflect the develpment state
1805
+ * (ui5-experimental-since/experimental, since and/or deprecated).
1806
+ *
1807
+ * If the experimental property of the info object contains only a SemVer,
1808
+ * it is assumed to stem from an @ui5-experimental-since tag, otherwise, if
1809
+ * it's not empty, it is assumed to stem from an experimental tag.
1810
+ * @returns {string[]} List of tags
1811
+ */
1812
+ function createDevelopmentStateTags(info) {
1813
+ const tags = [];
1814
+ if (isSemVer(info.experimental)) {
1815
+ tags.push(`@ui5-experimental-since ${info.experimental}`);
1816
+ } else if (info.experimental) {
1817
+ tags.push(`@experimental ${info.experimental}`);
1818
+ }
1819
+ if (info.since) {
1820
+ tags.push(`@since ${info.since}`);
1821
+ }
1822
+ if (info.deprecation) {
1823
+ tags.push(`@deprecated ${info.deprecation}`);
1824
+ }
1825
+ return tags;
1826
+ }
1827
+
1775
1828
  function createVisibilityTags(access, stakeholders) {
1776
1829
  if ( access === "restricted" ) {
1777
1830
  return [
@@ -1986,6 +2039,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1986
2039
  if ( info.visibility === 'hidden' ) {
1987
2040
  continue;
1988
2041
  }
2042
+ const devStateTags = createDevelopmentStateTags(info);
1989
2043
  const visibilityTags = createVisibilityTagsForSetting(info, classAccess);
1990
2044
 
1991
2045
  // link = newStyle ? "{@link #setting:" + n + " " + n + "}" : "<code>" + n + "</code>";
@@ -1999,9 +2053,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1999
2053
  ? "Default value is <code>" + (info.defaultValue.value === "" ? "empty string" : info.defaultValue.raw) + "</code>."
2000
2054
  : "",
2001
2055
  "@returns {" + info.type + "} Value of property <code>" + n + "</code>",
2002
- info.since ? "@since " + info.since : "",
2003
- info.deprecation ? "@deprecated " + info.deprecation : "",
2004
- info.experimental ? "@experimental " + info.experimental : "",
2056
+ devStateTags,
2005
2057
  visibilityTags,
2006
2058
  "@name " + name("get", n),
2007
2059
  "@function"
@@ -2018,9 +2070,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2018
2070
  : "",
2019
2071
  generateParamTag(n, info.type, "New value for property <code>" + n + "</code>", info.defaultValue),
2020
2072
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2021
- info.since ? "@since " + info.since : "",
2022
- info.deprecation ? "@deprecated " + info.deprecation : "",
2023
- info.experimental ? "@experimental " + info.experimental : "",
2073
+ devStateTags,
2024
2074
  visibilityTags,
2025
2075
  "@name " + name("set", n),
2026
2076
  "@function"
@@ -2033,9 +2083,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2033
2083
  "detailed description of the possible properties of <code>oBindingInfo</code>",
2034
2084
  "@param {sap.ui.base.ManagedObject.PropertyBindingInfo} oBindingInfo The binding information",
2035
2085
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2036
- info.since ? "@since " + info.since : "",
2037
- info.deprecation ? "@deprecated " + info.deprecation : "",
2038
- info.experimental ? "@experimental " + info.experimental : "",
2086
+ devStateTags,
2039
2087
  visibilityTags,
2040
2088
  "@name " + name("bind", n),
2041
2089
  "@function"
@@ -2043,9 +2091,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2043
2091
  newJSDoc([
2044
2092
  "Unbinds property " + link + " from model data.",
2045
2093
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2046
- info.since ? "@since " + info.since : "",
2047
- info.deprecation ? "@deprecated " + info.deprecation : "",
2048
- info.experimental ? "@experimental " + info.experimental : "",
2094
+ devStateTags,
2049
2095
  visibilityTags,
2050
2096
  "@name " + name("unbind", n),
2051
2097
  "@function"
@@ -2058,6 +2104,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2058
2104
  if ( info.visibility === 'hidden' ) {
2059
2105
  continue;
2060
2106
  }
2107
+ const devStateTags = createDevelopmentStateTags(info);
2061
2108
  const visibilityTags = createVisibilityTagsForSetting(info, classAccess);
2062
2109
 
2063
2110
  // link = newStyle ? "{@link #setting:" + n + " " + n + "}" : "<code>" + n + "</code>";
@@ -2069,9 +2116,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2069
2116
  "",
2070
2117
  n === info.defaultAggregation ? "<strong>Note</strong>: this is the default aggregation for " + n + "." : "",
2071
2118
  "@returns {" + makeTypeString(info) + "}",
2072
- info.since ? "@since " + info.since : "",
2073
- info.deprecation ? "@deprecated " + info.deprecation : "",
2074
- info.experimental ? "@experimental " + info.experimental : "",
2119
+ devStateTags,
2075
2120
  visibilityTags,
2076
2121
  "@name " + name("get", n),
2077
2122
  "@function"
@@ -2089,9 +2134,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2089
2134
  " greater than the current size of the aggregation, the " + n1 + " is inserted at",
2090
2135
  " the last position",
2091
2136
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2092
- info.since ? "@since " + info.since : "",
2093
- info.deprecation ? "@deprecated " + info.deprecation : "",
2094
- info.experimental ? "@experimental " + info.experimental : "",
2137
+ devStateTags,
2095
2138
  visibilityTags,
2096
2139
  "@name " + name("insert", n1),
2097
2140
  "@function"
@@ -2102,9 +2145,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2102
2145
  "@param {" + makeTypeString(info, true) + "}",
2103
2146
  " " + varname(n1, info.altTypes ? "variant" : info.type) + " The " + n1 + " to add; if empty, nothing is inserted",
2104
2147
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2105
- info.since ? "@since " + info.since : "",
2106
- info.deprecation ? "@deprecated " + info.deprecation : "",
2107
- info.experimental ? "@experimental " + info.experimental : "",
2148
+ devStateTags,
2108
2149
  visibilityTags,
2109
2150
  "@name " + name("add", n1),
2110
2151
  "@function"
@@ -2114,9 +2155,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2114
2155
  "",
2115
2156
  "@param {int | string | " + makeTypeString(info, true) + "} " + varname(n1, "variant") + " The " + n1 + " to remove or its index or id",
2116
2157
  "@returns {" + makeTypeString(info, true) + "|null} The removed " + n1 + " or <code>null</code>",
2117
- info.since ? "@since " + info.since : "",
2118
- info.deprecation ? "@deprecated " + info.deprecation : "",
2119
- info.experimental ? "@experimental " + info.experimental : "",
2158
+ devStateTags,
2120
2159
  visibilityTags,
2121
2160
  "@name " + name("remove", n1),
2122
2161
  "@function"
@@ -2126,9 +2165,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2126
2165
  "",
2127
2166
  "Additionally, it unregisters them from the hosting UIArea.",
2128
2167
  "@returns {" + makeTypeString(info) + "} An array of the removed elements (might be empty)",
2129
- info.since ? "@since " + info.since : "",
2130
- info.deprecation ? "@deprecated " + info.deprecation : "",
2131
- info.experimental ? "@experimental " + info.experimental : "",
2168
+ devStateTags,
2132
2169
  visibilityTags,
2133
2170
  "@name " + name("removeAll", n),
2134
2171
  "@function"
@@ -2139,9 +2176,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2139
2176
  "@param {" + makeTypeString(info, true) + "}",
2140
2177
  " " + varname(n1, info.altTypes ? "variant" : info.type) + " The " + n1 + " whose index is looked for",
2141
2178
  "@returns {int} The index of the provided control in the aggregation if found, or -1 otherwise",
2142
- info.since ? "@since " + info.since : "",
2143
- info.deprecation ? "@deprecated " + info.deprecation : "",
2144
- info.experimental ? "@experimental " + info.experimental : "",
2179
+ devStateTags,
2145
2180
  visibilityTags,
2146
2181
  "@name " + name("indexOf", n1),
2147
2182
  "@function"
@@ -2151,9 +2186,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2151
2186
  "Sets the aggregated " + link + ".",
2152
2187
  "@param {" + makeTypeString(info) + "} " + varname(n, info.altTypes ? "variant" : info.type) + " The " + n + " to set",
2153
2188
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2154
- info.since ? "@since " + info.since : "",
2155
- info.deprecation ? "@deprecated " + info.deprecation : "",
2156
- info.experimental ? "@experimental " + info.experimental : "",
2189
+ devStateTags,
2157
2190
  visibilityTags,
2158
2191
  "@name " + name("set", n),
2159
2192
  "@function"
@@ -2162,9 +2195,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2162
2195
  newJSDoc([
2163
2196
  "Destroys " + (info.cardinality === "0..n" ? "all " : "") + "the " + n + " in the aggregation " + link + ".",
2164
2197
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2165
- info.since ? "@since " + info.since : "",
2166
- info.deprecation ? "@deprecated " + info.deprecation : "",
2167
- info.experimental ? "@experimental " + info.experimental : "",
2198
+ devStateTags,
2168
2199
  visibilityTags,
2169
2200
  "@name " + name("destroy", n),
2170
2201
  "@function"
@@ -2177,9 +2208,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2177
2208
  "detailed description of the possible properties of <code>oBindingInfo</code>.",
2178
2209
  "@param {sap.ui.base.ManagedObject.AggregationBindingInfo} oBindingInfo The binding information",
2179
2210
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2180
- info.since ? "@since " + info.since : "",
2181
- info.deprecation ? "@deprecated " + info.deprecation : "",
2182
- info.experimental ? "@experimental " + info.experimental : "",
2211
+ devStateTags,
2183
2212
  visibilityTags,
2184
2213
  "@name " + name("bind", n),
2185
2214
  "@function"
@@ -2187,9 +2216,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2187
2216
  newJSDoc([
2188
2217
  "Unbinds aggregation " + link + " from model data.",
2189
2218
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2190
- info.since ? "@since " + info.since : "",
2191
- info.deprecation ? "@deprecated " + info.deprecation : "",
2192
- info.experimental ? "@experimental " + info.experimental : "",
2219
+ devStateTags,
2193
2220
  visibilityTags,
2194
2221
  "@name " + name("unbind", n),
2195
2222
  "@function"
@@ -2202,6 +2229,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2202
2229
  if ( info.visibility === 'hidden' ) {
2203
2230
  continue;
2204
2231
  }
2232
+ const devStateTags = createDevelopmentStateTags(info);
2205
2233
  const visibilityTags = createVisibilityTagsForSetting(info, classAccess);
2206
2234
 
2207
2235
  // link = newStyle ? "{@link #setting:" + n + " " + n + "}" : "<code>" + n + "</code>";
@@ -2214,9 +2242,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2214
2242
  newStyle && info.doc ? info.doc : "",
2215
2243
  "",
2216
2244
  `@returns {${info.cardinality === "0..n" ? "sap.ui.core.ID[]" : "sap.ui.core.ID|null"}}`,
2217
- info.since ? "@since " + info.since : "",
2218
- info.deprecation ? "@deprecated " + info.deprecation : "",
2219
- info.experimental ? "@experimental " + info.experimental : "",
2245
+ devStateTags,
2220
2246
  visibilityTags,
2221
2247
  "@name " + name("get", n),
2222
2248
  "@function"
@@ -2228,9 +2254,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2228
2254
  "",
2229
2255
  "@param {sap.ui.core.ID | " + info.type + "} " + varname(n1, "variant") + " The " + n + " to add; if empty, nothing is inserted",
2230
2256
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2231
- info.since ? "@since " + info.since : "",
2232
- info.deprecation ? "@deprecated " + info.deprecation : "",
2233
- info.experimental ? "@experimental " + info.experimental : "",
2257
+ devStateTags,
2234
2258
  visibilityTags,
2235
2259
  "@name " + name("add", n1),
2236
2260
  "@function"
@@ -2239,9 +2263,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2239
2263
  "Removes an " + n1 + " from the association named " + link + ".",
2240
2264
  "@param {int | sap.ui.core.ID | " + info.type + "} " + varname(n1, "variant") + " The " + n1 + " to be removed or its index or ID",
2241
2265
  "@returns {sap.ui.core.ID|null} The removed " + n1 + " or <code>null</code>",
2242
- info.since ? "@since " + info.since : "",
2243
- info.deprecation ? "@deprecated " + info.deprecation : "",
2244
- info.experimental ? "@experimental " + info.experimental : "",
2266
+ devStateTags,
2245
2267
  visibilityTags,
2246
2268
  "@name " + name("remove", n1),
2247
2269
  "@function"
@@ -2249,9 +2271,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2249
2271
  newJSDoc([
2250
2272
  "Removes all the controls in the association named " + link + ".",
2251
2273
  "@returns {sap.ui.core.ID[]} An array of the removed elements (might be empty)",
2252
- info.since ? "@since " + info.since : "",
2253
- info.deprecation ? "@deprecated " + info.deprecation : "",
2254
- info.experimental ? "@experimental " + info.experimental : "",
2274
+ devStateTags,
2255
2275
  visibilityTags,
2256
2276
  "@name " + name("removeAll", n),
2257
2277
  "@function"
@@ -2261,9 +2281,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2261
2281
  "Sets the associated " + link + ".",
2262
2282
  "@param {sap.ui.core.ID | " + info.type + "} " + varname(n, info.type) + " ID of an element which becomes the new target of this " + n + " association; alternatively, an element instance may be given",
2263
2283
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2264
- info.since ? "@since " + info.since : "",
2265
- info.deprecation ? "@deprecated " + info.deprecation : "",
2266
- info.experimental ? "@experimental " + info.experimental : "",
2284
+ devStateTags,
2267
2285
  visibilityTags,
2268
2286
  "@name " + name("set", n),
2269
2287
  "@function"
@@ -2273,6 +2291,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2273
2291
 
2274
2292
  for (const n in oClassInfo.events ) {
2275
2293
  const info = oClassInfo.events[n];
2294
+ const devStateTags = createDevelopmentStateTags(info);
2276
2295
  const visibilityTags = createVisibilityTagsForSetting(info, classAccess);
2277
2296
 
2278
2297
  //link = newStyle ? "{@link #event:" + n + " " + n + "}" : "<code>" + n + "</code>";
@@ -2287,9 +2306,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2287
2306
  "",
2288
2307
  "@name " + oClassInfo.name + "#" + n,
2289
2308
  "@event",
2290
- info.since ? "@since " + info.since : "",
2291
- info.deprecation ? "@deprecated " + info.deprecation : "",
2292
- info.experimental ? "@experimental " + info.experimental : "",
2309
+ devStateTags,
2293
2310
  "@param {sap.ui.base.Event} oControlEvent",
2294
2311
  "@param {sap.ui.base.EventProvider} oControlEvent.getSource",
2295
2312
  "@param {object} oControlEvent.getParameters"
@@ -2319,10 +2336,8 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2319
2336
  " [oListener] Context object to call the event handler with. Defaults to this <code>" + oClassInfo.name + "</code> itself",
2320
2337
  "",
2321
2338
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2339
+ devStateTags,
2322
2340
  visibilityTags,
2323
- info.since ? "@since " + info.since : "",
2324
- info.deprecation ? "@deprecated " + info.deprecation : "",
2325
- info.experimental ? "@experimental " + info.experimental : "",
2326
2341
  "@name " + name("attach", n),
2327
2342
  "@function"
2328
2343
  ]);
@@ -2336,9 +2351,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2336
2351
  "@param {object}",
2337
2352
  " [oListener] Context object on which the given function had to be called",
2338
2353
  "@returns {" + thisClass + "} Reference to <code>this</code> in order to allow method chaining",
2339
- info.since ? "@since " + info.since : "",
2340
- info.deprecation ? "@deprecated " + info.deprecation : "",
2341
- info.experimental ? "@experimental " + info.experimental : "",
2354
+ devStateTags,
2342
2355
  visibilityTags,
2343
2356
  "@name " + name("detach", n),
2344
2357
  "@function"
@@ -2382,10 +2395,8 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2382
2395
  }
2383
2396
 
2384
2397
  lines.push(
2398
+ devStateTags,
2385
2399
  createVisibilityTags(vis, info.stakeholders),
2386
- info.since ? "@since " + info.since : "",
2387
- info.deprecation ? "@deprecated " + info.deprecation : "",
2388
- info.experimental ? "@experimental " + info.experimental : "",
2389
2400
  "@name " + name("fire", n),
2390
2401
  "@function"
2391
2402
  );
@@ -2407,6 +2418,21 @@ function location(doclet) {
2407
2418
  return " #" + ui5data(doclet).id + "@" + filename + (doclet.meta.lineno != null ? ":" + doclet.meta.lineno : "") + (doclet.synthetic ? "(synthetic)" : "");
2408
2419
  }
2409
2420
 
2421
+ /**
2422
+ * Converts a JSDoc name to a UI5 runtime metadata name.
2423
+ *
2424
+ * Basically, global names in dot notation don't require a conversion. Only when the JSDoc name
2425
+ * is a `module:*` name, the prefix is removed and slashes are converted to dots.
2426
+ *
2427
+ * Note: the conversion in the opposite direction is not well-defined as not every dot has to be
2428
+ * converted to a slash (e.g. not for named exports or subtypes in another module). This
2429
+ * also implies that two different JSDoc names might map to the same UI5 runtime metadata
2430
+ * name. API design should avoid such ambiguities.
2431
+ */
2432
+ function toRuntimeMetadataName(longname) {
2433
+ return longname?.startsWith("module:") ? longname.slice("module:".length).replace(/\//g, ".") : longname;
2434
+ }
2435
+
2410
2436
  // ---- Comment handling ---------------------------------------------------------------------------
2411
2437
 
2412
2438
  // --- comment related functions that depend on the JSdoc version (e.g. on the used parser)
@@ -2602,7 +2628,23 @@ exports.defineTags = function(dictionary) {
2602
2628
  };
2603
2629
 
2604
2630
  /**
2605
- * A sapui5 specific tag to add a disclaimer to a symbol
2631
+ * Override the built-in `since` tag (with the same configuration) to check that
2632
+ * `since` and `ui5-experimental-since` are not used both for the same entity.
2633
+ *
2634
+ * Also see the checks in experimental and ui5-experimental-since.
2635
+ */
2636
+ dictionary.defineTag("since", {
2637
+ mustHaveValue: true,
2638
+ onTagged: function(doclet, tag) {
2639
+ if (isSemVer(doclet.experimental)) {
2640
+ error(`@since and @ui5-experimental-since must not be used both for the same API (${doclet.meta?.lineno})`);
2641
+ }
2642
+ doclet.since = tag.value;
2643
+ }
2644
+ });
2645
+
2646
+ /**
2647
+ * A UI5 custom tag to add a disclaimer to a symbol.
2606
2648
  */
2607
2649
  dictionary.defineTag('disclaimer', {
2608
2650
  // value is optional
@@ -2612,12 +2654,48 @@ exports.defineTags = function(dictionary) {
2612
2654
  });
2613
2655
 
2614
2656
  /**
2615
- * A sapui5 specific tag to mark a symbol as experimental.
2657
+ * A UI5 custom tag to mark a symbol as experimental.
2658
+ *
2659
+ * Note: `experimental` and `ui5-experimental-since` must not used be used within the same doclet.
2660
+ *
2661
+ * @deprecated As of version 1.139, use @ui5-experimental-since
2616
2662
  */
2617
2663
  dictionary.defineTag('experimental', {
2618
2664
  // value is optional
2619
2665
  onTagged: function(doclet, tag) {
2620
- doclet.experimental = tag.value || EMPTY;
2666
+ future(`As of version 1.139, the experimental tag is deprecated and should no longer be used. Use the @ui5-experimental-since tag instead (${doclet.meta?.lineno})`);
2667
+ if (isSemVer(doclet.experimental)) {
2668
+ error(`@experimental and @ui5-experimental-since must not be used both for the same API (${doclet.meta?.lineno})`);
2669
+ }
2670
+ const value = tag.value?.trim();
2671
+ if (isSemVer(value)) {
2672
+ // In some places, @experimental was used with a version only.
2673
+ // Prefix the value to avoid misinterpretation as @ui5-experimental-since
2674
+ doclet.experimental = `As of version ${value}`;
2675
+ } else {
2676
+ doclet.experimental = value || EMPTY;
2677
+ }
2678
+ }
2679
+ });
2680
+
2681
+ /**
2682
+ * A UI5 custom tag to mark a symbol as experimental.
2683
+ *
2684
+ * This tag only allows and expects a since version, no additional text.
2685
+ *
2686
+ * Note: `experimental` and `ui5-experimental-since` must not used be used within the same doclet.
2687
+ */
2688
+ dictionary.defineTag('ui5-experimental-since', {
2689
+ mustHaveValue: true,
2690
+ onTagged: function(doclet, tag) {
2691
+ if (doclet.since || doclet.experimental) {
2692
+ error(`@experimental/@since and @ui5-experimental-since must not be used together for the same API (${doclet.meta?.lineno})`);
2693
+ }
2694
+ const version = tag.value?.trim() ?? "";
2695
+ if (!isSemVer(version)) {
2696
+ error(`@ui5-experimental-since must have a version and only a version, not '${version}' (${doclet.meta?.lineno})`);
2697
+ }
2698
+ doclet.experimental = version;
2621
2699
  }
2622
2700
  });
2623
2701
 
@@ -2680,6 +2758,9 @@ exports.defineTags = function(dictionary) {
2680
2758
  }
2681
2759
  }
2682
2760
  });
2761
+ /**
2762
+ * @deprecated Use `ui5-restricted` instead.
2763
+ */
2683
2764
  dictionary.defineTag('sap-restricted', {
2684
2765
  onTagged: function(doclet, tag) {
2685
2766
  error("Tag @sap-restricted has been deprecated, use @ui5-restricted instead");
@@ -2939,15 +3020,19 @@ exports.handlers = {
2939
3020
  const doclet = doclets[i];
2940
3021
 
2941
3022
  // add metadata to class symbols
2942
- if ( classInfos[doclet.longname] ) {
3023
+ let classInfo = classInfos[doclet.longname];
3024
+ if (classInfo == null && doclet.longname?.startsWith("module:")) {
3025
+ classInfo = classInfos[toRuntimeMetadataName(doclet.longname)];
3026
+ }
3027
+ if ( classInfo ) {
2943
3028
  // debug("class data", doclet.longname, "'" + classInfos[doclet.longname].export + "'");
2944
3029
  if ( doclet.__ui5.export === undefined ) {
2945
- doclet.__ui5.export = classInfos[doclet.longname].export;
3030
+ doclet.__ui5.export = classInfo.export;
2946
3031
  }
2947
3032
 
2948
- doclet.__ui5.metadata = classInfos[doclet.longname];
3033
+ doclet.__ui5.metadata = classInfo;
2949
3034
  // Push the stereotype to the main doclet.__ui5 object since that's where it's read.
2950
- doclet.__ui5.stereotype = classInfos[doclet.longname].stereotype;
3035
+ doclet.__ui5.stereotype = classInfo.stereotype;
2951
3036
 
2952
3037
  // add designtime infos, if configured
2953
3038
  let designtimeModule = doclet.__ui5.metadata.designtime;
@@ -2971,11 +3056,18 @@ exports.handlers = {
2971
3056
  // derive interface implementations from UI5 metadata
2972
3057
  if ( doclet.__ui5.metadata.interfaces && doclet.__ui5.metadata.interfaces.length ) {
2973
3058
  /* eslint-disable no-loop-func */
2974
- doclet.__ui5.metadata.interfaces.forEach((intf) => {
3059
+ doclet.__ui5.metadata.interfaces.forEach((rtmIntf) => {
2975
3060
  doclet.implements = doclet.implements || [];
2976
- if ( doclet.implements.indexOf(intf) < 0 ) {
2977
- info(` @implements ${intf} derived from UI5 metadata (${doclet.longname})`);
2978
- doclet.implements.push(intf);
3061
+ // add an interface only if no "matching" JSDoc interface is present already
3062
+
3063
+ // TODO if the JSDoc name and runtime metadata name of an interface differ,
3064
+ // this today requires redundant documentation. Ideally, the JSDoc plugin
3065
+ // should handle this and convert the runtime metadata name to the JSDoc name.
3066
+ // Unfortunately, at this processing step here, the necessary information is
3067
+ // not yet available (external APIs are loaded only later).
3068
+ if ( doclet.implements.every((jsdocIntf) => toRuntimeMetadataName(jsdocIntf) !== rtmIntf)) {
3069
+ info(` @implements ${rtmIntf} derived from UI5 metadata (${doclet.longname})`);
3070
+ doclet.implements.push(rtmIntf);
2979
3071
  }
2980
3072
  });
2981
3073
  /* eslint-enable no-loop-func */
@@ -2983,12 +3075,16 @@ exports.handlers = {
2983
3075
  }
2984
3076
 
2985
3077
  // add DataType info to typedef symbols
2986
- if ( typeInfos[doclet.longname] ) {
3078
+ let typeInfo = typeInfos[doclet.longname];
3079
+ if (typeInfo == null && doclet.longname?.startsWith("module:")) {
3080
+ typeInfo = typeInfos[toRuntimeMetadataName(doclet.longname)];
3081
+ }
3082
+ if ( typeInfo ) {
2987
3083
  doclet.__ui5.stereotype = 'datatype';
2988
3084
  doclet.__ui5.metadata = {
2989
- basetype: typeInfos[doclet.longname].base,
2990
- pattern: typeInfos[doclet.longname].pattern,
2991
- range: typeInfos[doclet.longname].range
3085
+ basetype: typeInfo.base,
3086
+ pattern: typeInfo.pattern,
3087
+ range: typeInfo.range
2992
3088
  };
2993
3089
  }
2994
3090
 
@@ -3292,11 +3388,17 @@ exports.astNodeVisitor = {
3292
3388
  typeString = 'function';
3293
3389
  // #### BEGIN: MODIFIED BY SAP
3294
3390
  paramTypes = [];
3391
+ // add only one of 'new:' or 'this:' type, 'new:' is preferred as it implies a 'this:'
3295
3392
  if (parsedType.new) {
3296
- paramTypes.push(toTypeString(parsedType.new));
3393
+ paramTypes.push("new:" + toTypeString(parsedType.new));
3394
+ } else if (parsedType.this) {
3395
+ paramTypes.push("this:" + toTypeString(parsedType.this));
3297
3396
  }
3397
+
3298
3398
  if (Array.isArray(parsedType.params)) {
3299
- paramTypes.push(...parsedType.params.map(toTypeString));
3399
+ paramTypes.push(...parsedType.params.map((paramType) => {
3400
+ return toTypeString(paramType) + (paramType.optional ? "=" : "");
3401
+ }));
3300
3402
  }
3301
3403
  if (paramTypes.length || parsedType.result) {
3302
3404
  typeString += `(${paramTypes.join(", ")})`;
@@ -730,10 +730,18 @@ class TypeStringBuilder {
730
730
  str: 'Promise' + this.lt + this.safe(fulfillmentType) + this.gt
731
731
  };
732
732
  }
733
- "function"(paramTypes, returnType) {
733
+ "function"(paramTypes, returnType, thisType, constructorType) {
734
+ paramTypes = paramTypes.map(
735
+ (type) => type.str + (type.optional ? "=" : "")
736
+ );
737
+ if (constructorType != null) {
738
+ paramTypes.unshift(`new:${constructorType.str}`);
739
+ } else if (thisType != null) {
740
+ paramTypes.unshift(`this:${thisType.str}`);
741
+ }
734
742
  return {
735
743
  simpleComponent: false,
736
- str: "function(" + paramTypes.map((type) => type.str).join(',') + ")" + ( returnType ? " : " + this.safe(returnType) : "")
744
+ str: `function(${paramTypes.join(",")})${returnType ? " : " + this.safe(returnType) : ""}`
737
745
  };
738
746
  }
739
747
  structure(structure) {
@@ -889,6 +897,13 @@ function normalizeWS(text) {
889
897
 
890
898
  }
891
899
 
900
+ const rStartsWithValidPropertyName = /^(?:[^'"\s.][^\s.]*|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")/;
901
+
902
+ function isNestedPropertyName(name) {
903
+ const match = rStartsWithValidPropertyName.exec(name);
904
+ return match && match[0].length < name.length && name[match[0].length] === ".";
905
+ }
906
+
892
907
  //---- add on: API JSON -----------------------------------------------------------------
893
908
 
894
909
  function createAPIJSON(symbols, filename) {
@@ -1270,6 +1285,9 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
1270
1285
  if ( aggr.altTypes ) {
1271
1286
  curr.altTypes = aggr.altTypes.slice();
1272
1287
  }
1288
+ if ( aggr.defaultClass ) {
1289
+ attrib("defaultClass", aggr.defaultClass);
1290
+ }
1273
1291
  attrib("cardinality", aggr.cardinality, '0..n');
1274
1292
  attrib("visibility", aggr.visibility, 'public');
1275
1293
  if (aggr.stakeholders) {
@@ -1411,7 +1429,7 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
1411
1429
  continue;
1412
1430
  }
1413
1431
 
1414
- if ( name.indexOf('.') >= 0 ) {
1432
+ if ( isNestedPropertyName(name) ) {
1415
1433
  continue;
1416
1434
  }
1417
1435
 
@@ -1927,9 +1945,8 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
1927
1945
  return obj[0];
1928
1946
  }
1929
1947
 
1930
- function postProcessAPIJSON(api) {
1948
+ function addExportInfo(symbols) {
1931
1949
  const modules = {};
1932
- const symbols = api.symbols;
1933
1950
 
1934
1951
  // collect modules and the symbols that refer to them
1935
1952
  for ( let i = 0; i < symbols.length; i++) {
@@ -2051,6 +2068,13 @@ function postProcessAPIJSON(api) {
2051
2068
  for ( const n in modules ) {
2052
2069
  guessExports(n, modules[n]);
2053
2070
  }
2071
+ }
2072
+
2073
+ function postProcessAPIJSON(api) {
2074
+
2075
+ const symbols = api.symbols;
2076
+
2077
+ addExportInfo(symbols);
2054
2078
 
2055
2079
  function findSymbol(name) {
2056
2080
  if ( name == null || name === '' ) {
@@ -2079,6 +2103,28 @@ function postProcessAPIJSON(api) {
2079
2103
  return (m && m[1]) || "";
2080
2104
  }
2081
2105
 
2106
+ // create a 2-level map of symbol names, first keyed by the exporting module and then by export name
2107
+ const modules = Object.create(null);
2108
+ for (const symbol of Object.values(externalSymbols)) {
2109
+ if ( symbol.module && symbol.export !== undefined ) {
2110
+ modules[symbol.module] ??= Object.create(null);
2111
+ modules[symbol.module][symbol.export] = symbol.name;
2112
+ }
2113
+ }
2114
+ for (const symbol of symbols) {
2115
+ if ( symbol.module && symbol.export !== undefined ) {
2116
+ modules[symbol.module] ??= Object.create(null);
2117
+ modules[symbol.module][symbol.export] = symbol.name;
2118
+ }
2119
+ }
2120
+
2121
+ function findSymbolByModule(module, exportName) {
2122
+ if (module === "unknown") {
2123
+ return "unknown";
2124
+ }
2125
+ return modules[module]?.[exportName] ?? "unknown";
2126
+ }
2127
+
2082
2128
  symbols.forEach((symbol) => {
2083
2129
  // add note for enums which are not exposed by their name
2084
2130
  if (symbol.kind === "enum" && symbol.export) {
@@ -2147,6 +2193,16 @@ function postProcessAPIJSON(api) {
2147
2193
  }
2148
2194
  }
2149
2195
  }
2196
+ /*
2197
+ * Up to here, the defaultClass only contains a module name. Replace it with the corresponding API (type) name.
2198
+ */
2199
+ if (Array.isArray(symbol["ui5-metadata"].aggregations)) {
2200
+ for (const aggr of symbol["ui5-metadata"].aggregations) {
2201
+ if (aggr.defaultClass != null) {
2202
+ aggr.defaultClass = findSymbolByModule(aggr.defaultClass, "");
2203
+ }
2204
+ }
2205
+ }
2150
2206
  });
2151
2207
 
2152
2208
  }
@@ -2267,6 +2323,8 @@ function validateAPIJSON(api) {
2267
2323
  const rValidNames = /^[$A-Z_a-z][$0-9A-Z_a-z]*$/i;
2268
2324
  const rValidModuleNames = /^[$A-Z_a-z][$\-\.0-9A-Z_a-z]*$/i;
2269
2325
 
2326
+ let currentTypeParameters = Object.create(null);
2327
+
2270
2328
  function checkName(name, hint) {
2271
2329
  if ( !rValidNames.test(name) ) {
2272
2330
  naming[name] = naming[name] || [];
@@ -2274,6 +2332,19 @@ function validateAPIJSON(api) {
2274
2332
  }
2275
2333
  }
2276
2334
 
2335
+ /**
2336
+ * Checks the name of a property in an object-like structure.
2337
+ * As in JavaScript, the rules for such names are less strict than
2338
+ * for classes, interfaces or function declarations.
2339
+ */
2340
+ function checkPropertyName(name, hint) {
2341
+ const match = rStartsWithValidPropertyName.exec(name);
2342
+ if ( match == null || match[0].length !== name.length ) {
2343
+ naming[name] ??= [];
2344
+ naming[name].push(hint);
2345
+ }
2346
+ }
2347
+
2277
2348
  function checkModuleName(name, hint) {
2278
2349
  if ( !rValidModuleNames.test(name) ) {
2279
2350
  naming[name] = naming[name] || [];
@@ -2311,6 +2382,9 @@ function validateAPIJSON(api) {
2311
2382
  }
2312
2383
 
2313
2384
  function checkSimpleType(typeName, hint) {
2385
+ if (currentTypeParameters[typeName]) {
2386
+ return;
2387
+ }
2314
2388
  if ( !defined[typeName] ) {
2315
2389
  reportError(typeName, hint);
2316
2390
  }
@@ -2373,8 +2447,20 @@ function validateAPIJSON(api) {
2373
2447
  }
2374
2448
  }
2375
2449
 
2450
+ function checkTypeParameter(typeParam, hint) {
2451
+ checkType(typeParam, `type constraint of ${hint}`);
2452
+ if ( typeParam.default ) {
2453
+ checkType({type: typeParam.default}, `default of ${hint}`);
2454
+ }
2455
+ }
2456
+
2376
2457
  function checkParam(param, prefix, hint) {
2377
- checkName(param.name, `name of param ${prefix}${param.name} of ${hint}`);
2458
+ if (prefix) {
2459
+ // nested parameter properties follow the naming conventions for object properties
2460
+ checkPropertyName(param.name, `name of param ${prefix}${param.name} of ${hint}`);
2461
+ } else {
2462
+ checkName(param.name, `name of param ${prefix}${param.name} of ${hint}`);
2463
+ }
2378
2464
  checkType(param, `param ${prefix}${param.name} of ${hint}`);
2379
2465
  if ( param.parameterProperties ) {
2380
2466
  Object.keys(param.parameterProperties).forEach((sub) =>
@@ -2382,7 +2468,19 @@ function validateAPIJSON(api) {
2382
2468
  }
2383
2469
  }
2384
2470
 
2385
- function checkMethodSignature(method, hint) {
2471
+ function checkMethodSignature(method, hint, isConstructor = false) {
2472
+ const prevTypeParams = currentTypeParameters;
2473
+ if ( !isConstructor && method.typeParameters ) {
2474
+ // Note: type parameters of the constructor are handled on class level
2475
+ currentTypeParameters = Object.create(currentTypeParameters);
2476
+ for (const typeParam of method.typeParameters) {
2477
+ currentTypeParameters[typeParam.name] = typeParam;
2478
+ }
2479
+ // check type parameters in a 2nd loop so that they can depend on each other
2480
+ for (const typeParam of method.typeParameters) {
2481
+ checkTypeParameter(typeParam, `type parameter ${typeParam.name} of ${hint}`);
2482
+ }
2483
+ }
2386
2484
  if ( method.returnValue ) {
2387
2485
  checkType(method.returnValue, `return value of ${hint}`);
2388
2486
  }
@@ -2392,6 +2490,8 @@ function validateAPIJSON(api) {
2392
2490
  if ( method.throws ) {
2393
2491
  method.throws.forEach((ex) => checkType(ex, `exception of ${hint}`));
2394
2492
  }
2493
+ // restore type parameters of outer scope
2494
+ currentTypeParameters = prevTypeParams;
2395
2495
  }
2396
2496
 
2397
2497
  function checkClassAgainstInterface(symbol, oIntfAPI) {
@@ -2451,6 +2551,19 @@ function validateAPIJSON(api) {
2451
2551
  }
2452
2552
 
2453
2553
  function checkClass(symbol) {
2554
+ const prevTypeParams = currentTypeParameters;
2555
+ if ( Object.hasOwn(symbol, "constructor") && symbol.constructor.typeParameters ) {
2556
+ currentTypeParameters = Object.create(currentTypeParameters);
2557
+ for (const typeParam of symbol.constructor.typeParameters) {
2558
+ currentTypeParameters[typeParam.name] = typeParam;
2559
+ }
2560
+ // Check type parameters in a 2nd loop so that they can depend on each other.
2561
+ // As of TypeScript 5.9.2, `class Foo<T1 extends T2, T2 = T1> {}´ is valid
2562
+ for (const typeParam of symbol.constructor.typeParameters) {
2563
+ checkTypeParameter(typeParam, `type parameter ${typeParam.name} of ${symbol.name}`);
2564
+ }
2565
+ }
2566
+
2454
2567
  checkCompoundName(symbol.name, `name of ${symbol.name}`);
2455
2568
  if ( symbol.extends ) {
2456
2569
  checkSimpleType(symbol.extends, `base class of ${symbol.name}`);
@@ -2465,12 +2578,12 @@ function validateAPIJSON(api) {
2465
2578
  });
2466
2579
  }
2467
2580
  if ( Object.hasOwn(symbol, "constructor") ) {
2468
- checkMethodSignature(symbol.constructor, symbol.name + ".constructor");
2581
+ checkMethodSignature(symbol.constructor, symbol.name + ".constructor", /* isConstructor */ true);
2469
2582
  }
2470
2583
  if ( symbol.properties ) {
2471
2584
  symbol.properties.forEach((prop) => {
2472
2585
  const qualifiedName = `${symbol.name}.${prop.name}`;
2473
- checkName(prop.name, `name of ${qualifiedName}`);
2586
+ checkPropertyName(prop.name, `name of ${qualifiedName}`);
2474
2587
  if ( prop.type ) {
2475
2588
  checkType(prop, `type of ${qualifiedName}`);
2476
2589
  }
@@ -2494,6 +2607,8 @@ function validateAPIJSON(api) {
2494
2607
  }
2495
2608
  });
2496
2609
  }
2610
+ // restore type parameters of outer scope
2611
+ currentTypeParameters = prevTypeParams;
2497
2612
  }
2498
2613
 
2499
2614
  api.symbols.forEach((symbol) => {
@@ -414,9 +414,19 @@ function TypeParser(defaultBuilder = new ASTBuilder()) {
414
414
  case "function":
415
415
  const aParamTemplates = [];
416
416
  let aParamsimpleTypes = [];
417
+ if (Object.hasOwn(parsed, "constructor") && parsed.constructor) {
418
+ const types = findSimpleTypes(parsed.constructor);
419
+ aParamTemplates.push("new:" + types.template);
420
+ aParamsimpleTypes = aParamsimpleTypes.concat(types.simpleTypes);
421
+ }
422
+ if (parsed.this) {
423
+ const types = findSimpleTypes(parsed.this);
424
+ aParamTemplates.push("this:" + types.template);
425
+ aParamsimpleTypes = aParamsimpleTypes.concat(types.simpleTypes);
426
+ }
417
427
  parsed.params.forEach(function(paramType) {
418
428
  const types = findSimpleTypes(paramType);
419
- aParamTemplates.push(types.template);
429
+ aParamTemplates.push(types.template + (paramType.optional ? "?" : ""));
420
430
  aParamsimpleTypes = aParamsimpleTypes.concat(types.simpleTypes);
421
431
  });
422
432
  const returnType = parsed.return ? findSimpleTypes(parsed.return) : {simpleTypes: []};
@@ -1,5 +1,12 @@
1
1
  "use strict";
2
2
 
3
+ // [m][.minor][.patch ] [pre-release][ build-info ]
4
+ const rSemVer = /^\d+(?:\.\d+(?:\.\d+)?)?(?:-[\w\-]+)?(?:\+[\w\-]+)?$/;
5
+
6
+ function isSemVer(str) {
7
+ return rSemVer.test(str);
8
+ }
9
+
3
10
  const rSinceVersion = /^([0-9]+(?:\.[0-9]+(?:\.[0-9]+)?)?([-.][0-9A-Z]+)?)(\.$|\.\s+|[,:;]\s*|\s-\s*|\s|$)/i;
4
11
  function _parseVersion(value) {
5
12
  const m = rSinceVersion.exec(value);
@@ -86,6 +93,14 @@ function extractSince(value) {
86
93
  value = String(value);
87
94
  }
88
95
 
96
+ if (isSemVer(value)) {
97
+ return {
98
+ since: value,
99
+ pos: value.length,
100
+ value: ""
101
+ };
102
+ }
103
+
89
104
  const mSinceIndicator = rSinceIndicator.exec(value);
90
105
  if (mSinceIndicator) {
91
106
  const iSinceIndicatorLength = mSinceIndicator[0].length;
@@ -115,6 +130,7 @@ function extractSince(value) {
115
130
  }
116
131
 
117
132
  module.exports = {
133
+ isSemVer,
118
134
  extractSince,
119
135
  extractVersion
120
136
  };
@@ -228,7 +228,7 @@ export default async function({
228
228
  if (sourceMapJson.sections) {
229
229
  // TODO 5.0
230
230
  // Module "@jridgewell/trace-mapping" (used by Terser) can't handle index map sections lacking
231
- // a "names" array. Since this is a common occurrence for UI5 Tooling bundles, we search for
231
+ // a "names" array. Since this is a common occurrence for UI5 CLI bundles, we search for
232
232
  // such cases here and fix them until https://github.com/jridgewell/trace-mapping/pull/29 is
233
233
  // resolved and Terser upgraded the dependency
234
234
 
@@ -130,7 +130,7 @@ const utils = {
130
130
  createTmpDir: async function(projectName) {
131
131
  const sanitizedProjectName = projectName.replace(/[^A-Za-z0-9]/g, "");
132
132
 
133
- const tmpRootPath = path.join(os.tmpdir(), "ui5-tooling");
133
+ const tmpRootPath = path.join(os.tmpdir(), "ui5-cli");
134
134
  await mkdirp(tmpRootPath);
135
135
 
136
136
  // Appending minus sign also because node docs advise to "avoid trailing X characters in prefix"
@@ -61,8 +61,8 @@ export async function getTask(taskName) {
61
61
  if (!taskInfo) {
62
62
  if (getRemovedTaskNames().includes(taskName)) {
63
63
  throw new Error(
64
- `Standard task ${taskName} has been removed in UI5 Tooling 3.0. ` +
65
- `Please see the migration guide at https://sap.github.io/ui5-tooling/updates/migrate-v3/`);
64
+ `Standard task ${taskName} has been removed in UI5 CLI 3.0. ` +
65
+ `Please see the migration guide at https://ui5.github.io/cli/updates/migrate-v3/`);
66
66
  }
67
67
  throw new Error(`taskRepository: Unknown Task ${taskName}`);
68
68
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ui5/builder",
3
- "version": "4.0.10",
4
- "description": "UI5 Tooling - Builder",
3
+ "version": "4.0.11",
4
+ "description": "UI5 CLI - Builder",
5
5
  "author": {
6
6
  "name": "SAP SE",
7
7
  "email": "openui5@sap.com",
@@ -122,8 +122,8 @@
122
122
  },
123
123
  "dependencies": {
124
124
  "@jridgewell/sourcemap-codec": "^1.5.5",
125
- "@ui5/fs": "^4.0.1",
126
- "@ui5/logger": "^4.0.1",
125
+ "@ui5/fs": "^4.0.2",
126
+ "@ui5/logger": "^4.0.2",
127
127
  "cheerio": "1.0.0",
128
128
  "escape-unicode": "^0.2.0",
129
129
  "escope": "^4.0.0",
@@ -133,26 +133,26 @@
133
133
  "less-openui5": "^0.11.6",
134
134
  "pretty-data": "^0.40.0",
135
135
  "semver": "^7.7.2",
136
- "terser": "^5.43.1",
137
- "workerpool": "^9.3.3",
136
+ "terser": "^5.44.0",
137
+ "workerpool": "^9.3.4",
138
138
  "xml2js": "^0.6.2"
139
139
  },
140
140
  "devDependencies": {
141
141
  "@eslint/js": "^9.14.0",
142
142
  "@istanbuljs/esm-loader-hook": "^0.3.0",
143
- "@jridgewell/trace-mapping": "^0.3.30",
143
+ "@jridgewell/trace-mapping": "^0.3.31",
144
144
  "@ui5/project": "^4.0.4",
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.34.0",
150
+ "eslint": "^9.35.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",
154
- "esmock": "^2.7.1",
155
- "globals": "^16.3.0",
154
+ "esmock": "^2.7.2",
155
+ "globals": "^16.4.0",
156
156
  "line-column": "^1.0.2",
157
157
  "nyc": "^17.1.0",
158
158
  "open-cli": "^8.0.0",