@ui5/builder 4.0.4 → 4.0.6

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.
@@ -1,13 +1,11 @@
1
1
  /*
2
2
  * JSDoc3 plugin for UI5 documentation generation.
3
3
  *
4
- * (c) Copyright 2009-2024 SAP SE or an SAP affiliate company.
4
+ * (c) Copyright 2025 SAP SE or an SAP affiliate company.
5
5
  * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
6
6
  */
7
7
 
8
- /* global global, env */
9
- /* eslint-env es6,node */
10
- /* eslint strict: [2, "global"] */
8
+ /* global env */
11
9
 
12
10
  'use strict';
13
11
 
@@ -49,8 +47,10 @@
49
47
  * newDoclet
50
48
  *
51
49
  * parseComplete
52
- * remove undocumented/ignored/private doclets or duplicate doclets
50
+ * merge collected class info, DataType info and enum values into doclets
53
51
  *
52
+ * processingComplete
53
+ * remove undocumented/ignored/private doclets or duplicate doclets
54
54
  *
55
55
  * Last but not least, it implements an astNodeVisitor to detect UI5 specific "extend" calls and to create
56
56
  * documentation for the properties, aggregations etc. that are created with the "extend" call.
@@ -189,6 +189,7 @@ const designtimeInfos = Object.create(null);
189
189
  /* ---- private functions ---- */
190
190
 
191
191
  function ui5data(doclet) {
192
+ // eslint-disable-next-line no-return-assign
192
193
  return doclet.__ui5 || (doclet.__ui5 = { id: ++docletUid });
193
194
  }
194
195
 
@@ -232,7 +233,7 @@ function resolveModuleName(base, name) {
232
233
  let stack = base.split('/');
233
234
  stack.pop();
234
235
  name.split('/').forEach((segment, i) => {
235
- if ( segment == '..' ) {
236
+ if ( segment === '..' ) {
236
237
  stack.pop();
237
238
  } else if ( segment === '.' ) {
238
239
  // ignore
@@ -283,7 +284,8 @@ function analyzeModuleDefinition(node) {
283
284
  names = [{original: currentModule.factory.params[i].name}];
284
285
  }
285
286
 
286
- names.forEach(name => {
287
+ /*eslint-disable no-loop-func */
288
+ names.forEach((name) => {
287
289
  const module = resolveModuleName(currentModule.module, currentModule.dependencies[i]);
288
290
  debug(` import ${name.renamed || name.original} from '${module}'`);
289
291
 
@@ -292,6 +294,7 @@ function analyzeModuleDefinition(node) {
292
294
  ...(name.path ? {path: name.path} : {})
293
295
  };
294
296
  });
297
+ /*eslint-enable no-loop-func */
295
298
  }
296
299
  }
297
300
  if ( currentModule.factory ) {
@@ -300,11 +303,11 @@ function analyzeModuleDefinition(node) {
300
303
  }
301
304
 
302
305
  /**
303
- * Searches the given body for variable declarations that can be evaluated statically,
306
+ * Searches the body of the given factory for variable declarations that can be evaluated statically,
304
307
  * either because they refer to known AMD module imports (e.g. shortcut variables)
305
308
  * or because they have a (design time) constant value.
306
309
  *
307
- * @param {ASTNode} body AST node of a function body that shall be searched for shortcuts
310
+ * @param {ASTNode} factory AST node of a factory function whose body that shall be searched for shortcuts
308
311
  */
309
312
  function collectShortcuts(factory) {
310
313
  const body = factory.body;
@@ -414,12 +417,13 @@ function guessSingularName(sPluralName) {
414
417
 
415
418
  function getPropertyKey(prop) {
416
419
  if ( prop.type === Syntax.SpreadElement ) {
417
- return;
420
+ return undefined;
418
421
  } else if ( prop.key.type === Syntax.Identifier && prop.computed !== true ) {
419
422
  return prop.key.name;
420
423
  } else if ( prop.key.type === Syntax.Literal ) {
421
424
  return String(prop.key.value);
422
425
  }
426
+ return undefined;
423
427
  }
424
428
 
425
429
  /**
@@ -456,14 +460,14 @@ function createPropertyMap(node, defaultKey) {
456
460
  return result;
457
461
  }
458
462
 
459
- if ( node.type != Syntax.ObjectExpression ) {
463
+ if ( node.type !== Syntax.ObjectExpression ) {
460
464
  // something went wrong, it's not an object literal
461
465
  warning(`not an object literal: ${node.type}: ${node.value}`);
462
466
  // console.log(node.toSource());
463
467
  return undefined;
464
468
  }
465
469
 
466
- // invariant: node.type == Syntax.ObjectExpression
470
+ // invariant: node.type === Syntax.ObjectExpression
467
471
  result = {};
468
472
  for (let i = 0; i < node.properties.length; i++) {
469
473
  const prop = node.properties[i];
@@ -478,7 +482,7 @@ function createPropertyMap(node, defaultKey) {
478
482
 
479
483
  /**
480
484
  * Resolves potential wrapper expressions like: ChainExpression, AwaitExpression, etc.
481
- * @param {Node} node
485
+ * @param {Node} node the node to unwrap
482
486
  * @returns {Node} the resolved node
483
487
  */
484
488
  function resolvePotentialWrapperExpression(node) {
@@ -495,29 +499,33 @@ function resolvePotentialWrapperExpression(node) {
495
499
  ? node.expression.argument
496
500
  : node.expression;
497
501
  }
502
+ // fall through
498
503
  default:
499
504
  return node;
500
505
  }
501
506
  }
502
507
 
503
508
  /**
504
- * Strips the ChainExpression wrapper if such
509
+ * Navigates in the given tree of AST nodes (node) along the given path of property names.
510
+ * Any reached ChainExpression wrappers are skipped (ignored).
505
511
  *
506
- * @param {Node} rootNode
507
- * @param {String} path
508
- * @returns {Node}
512
+ * @param {Node} rootNode Root of a tree of MemberExpressison nodes
513
+ * @param {string} path Path to navigate along
514
+ * @returns {Node} The target node at the end of the path or undefined
509
515
  */
510
516
  function stripChainWrappers(rootNode, path) {
511
517
  const strip = (node) =>
512
- node && node.type === Syntax.ChainExpression ? node.expression : node;
518
+ (node && node.type === Syntax.ChainExpression ? node.expression : node);
513
519
 
514
520
  let curNode = strip(rootNode);
515
- let chunks = path && path.split(".");
516
- let name;
517
521
 
518
- while (chunks && chunks.length) {
519
- name = chunks.shift();
520
- curNode = curNode && strip(curNode[name]);
522
+ if (path) {
523
+ const chunks = path.split(".");
524
+
525
+ while (chunks.length) {
526
+ const name = chunks.shift();
527
+ curNode = curNode && strip(curNode[name]);
528
+ }
521
529
  }
522
530
 
523
531
  return curNode;
@@ -535,8 +543,8 @@ function isTemplateLiteralWithoutExpression(node) {
535
543
  /**
536
544
  * Checks whether a node is Literal or TemplateLiteral without an expression
537
545
  *
538
- * @param {Node} node
539
- * @returns {String}
546
+ * @param {Node} node AST node to check
547
+ * @returns {boolean} Whether the given AST node represents a constant string
540
548
  */
541
549
  function isStringLiteral(node) {
542
550
  return (
@@ -581,10 +589,10 @@ function isArrowFuncExpression(node) {
581
589
  }
582
590
 
583
591
  /**
584
- * Checks whether the node is of a "returning" type
592
+ * Checks whether the node is of a "returning" type.
585
593
  *
586
- * @param {Node} node
587
- * @returns {Boolean}
594
+ * @param {Node} node A statement node
595
+ * @returns {boolean} Whether the node is a return stmt or a yield expression statement
588
596
  */
589
597
  function isReturningNode(node) {
590
598
  return (node && node.type === Syntax.ReturnStatement)
@@ -695,7 +703,7 @@ function resolveObjectPatternChain (valueNode, keyNode, keyChain) {
695
703
  }
696
704
  } else {
697
705
 
698
- let result = { original: keyNode.name, path: keyChain.join(".") };
706
+ const result = { original: keyNode.name, path: keyChain.join(".") };
699
707
 
700
708
  if (keyNode.name !== valueNode.name) {
701
709
  // Renaming
@@ -712,8 +720,8 @@ function resolveObjectPatternChain (valueNode, keyNode, keyChain) {
712
720
  * Tries to resolve an ENUM, regardless where it is defined and being destructured.
713
721
  *
714
722
  * @param {Node} node
715
- * @param {String} type
716
- * @returns {Object}
723
+ * @param {string} type
724
+ * @returns {{value:any, raw: any} | undefined}
717
725
  */
718
726
  function resolvePotentialEnum(node, type) {
719
727
  let value = resolveFullyQuantifiedName(node);
@@ -726,13 +734,14 @@ function resolvePotentialEnum(node, type) {
726
734
  raw: value
727
735
  };
728
736
  }
737
+ return undefined;
729
738
  }
730
739
 
731
740
  /**
732
- * Returns the {Node} of the destructured argument of a (arrow) function.
741
+ * Returns the `Node` of the destructured argument of a (arrow) function.
733
742
  *
734
743
  * @param {Definition|ParameterDefinition} varDefinition
735
- * @returns {Node}
744
+ * @returns {Node | undefined}
736
745
  */
737
746
  function getFuncArgumentDestructNode(varDefinition) {
738
747
  if (
@@ -745,14 +754,14 @@ function getFuncArgumentDestructNode(varDefinition) {
745
754
  return varDefinition.node.params[varDefinition.index];
746
755
  }
747
756
 
748
- // return undefined;
757
+ return undefined;
749
758
  }
750
759
 
751
760
  /**
752
761
  * Checks whether a variable has been destructured.
753
762
  *
754
763
  * @param {Variable} variable
755
- * @returns
764
+ * @returns {boolean} whether `variable` uses destructuring.
756
765
  */
757
766
  function isVarDestructuring(variable) {
758
767
  const defNode =
@@ -761,7 +770,7 @@ function isVarDestructuring(variable) {
761
770
  ( getFuncArgumentDestructNode(variable.defs[0]) // (arrow) function argument
762
771
  || variable.defs[0].node.id ); // variable definition
763
772
 
764
- return defNode && [Syntax.ObjectPattern, Syntax.ArrayPattern].includes( defNode.type );
773
+ return defNode != null && [Syntax.ObjectPattern, Syntax.ArrayPattern].includes( defNode.type );
765
774
  }
766
775
 
767
776
  /**
@@ -926,7 +935,7 @@ function getObjectName(node) {
926
935
 
927
936
  /*
928
937
  * Checks whether the node is a qualified name (a.b.c) and if so,
929
- * returns the leftmost identifier a
938
+ * returns the leftmost identifier 'a'.
930
939
  */
931
940
  function getLeftmostName(node) {
932
941
  while ( isMemberExpression(node) ) {
@@ -935,7 +944,7 @@ function getLeftmostName(node) {
935
944
  if ( node.type === Syntax.Identifier ) {
936
945
  return node.name;
937
946
  }
938
- // return undefined;
947
+ return undefined;
939
948
  }
940
949
 
941
950
  function getResolvedObjectName(node) {
@@ -1076,7 +1085,7 @@ function convertValueWithRaw(node, type, propertyName) {
1076
1085
  }
1077
1086
 
1078
1087
  } else if ( isTemplateLiteralWithoutExpression(node) ) {
1079
- let value = node.quasis[0].value || {};
1088
+ const value = node.quasis[0].value || {};
1080
1089
 
1081
1090
  return {
1082
1091
  value: value.cooked,
@@ -1145,7 +1154,7 @@ function collectVisibilityInfo(settings, doclet, className, n) {
1145
1154
  let visibility = (settings.visibility && settings.visibility.value.value) || "public";
1146
1155
 
1147
1156
  if (!validVisibilities.has(visibility)) {
1148
- error(`${className}: Invalid visibility '${visibility}' in runtime metadata defined for managed setting '${n}. Valid options are ${Array.from(validVisibilities).join(', ')}.`);
1157
+ error(`${className}: Invalid visibility '${visibility}' in runtime metadata defined for managed setting '${n}'. Valid options are ${Array.from(validVisibilities).join(', ')}.`);
1149
1158
  }
1150
1159
 
1151
1160
  if (doclet?.access) {
@@ -1170,7 +1179,7 @@ function collectVisibilityInfo(settings, doclet, className, n) {
1170
1179
  access = "restricted";
1171
1180
  }
1172
1181
 
1173
- if (visibility == "public" && (access === "restricted" || access === "protected")) {
1182
+ if (visibility === "public" && (access === "restricted" || access === "protected")) {
1174
1183
  visibility = access;
1175
1184
  }
1176
1185
  }
@@ -1220,8 +1229,8 @@ function collectClassInfo(extendCall, classDoclet) {
1220
1229
  function each(node, defaultKey, callback) {
1221
1230
  const map = node && createPropertyMap(node.value);
1222
1231
  if ( map ) {
1223
- for (let n in map ) {
1224
- if ( map.hasOwnProperty(n) ) {
1232
+ for (const n in map) {
1233
+ if ( Object.hasOwn(map, n) ) {
1225
1234
  const doclet = getLeadingDoclet(map[n]);
1226
1235
  const settings = createPropertyMap(map[n].value, defaultKey);
1227
1236
  if ( settings == null ) {
@@ -1454,8 +1463,8 @@ function collectDesigntimeInfo(dtNodeArgument) {
1454
1463
  function each(node, defaultKey, callback) {
1455
1464
  const map = node && createPropertyMap(node.value);
1456
1465
  if ( map ) {
1457
- for (let n in map ) {
1458
- if ( map.hasOwnProperty(n) ) {
1466
+ for (const n in map) {
1467
+ if ( Object.hasOwn(map, n) ) {
1459
1468
  const doclet = getLeadingDoclet(map[n], true);
1460
1469
  const settings = createPropertyMap(map[n].value, defaultKey);
1461
1470
  if ( settings == null ) {
@@ -1645,7 +1654,7 @@ function collectDataTypeInfo(extendCall, classDoclet) {
1645
1654
  && stmt.alternate.body[0].argument
1646
1655
  && stmt.alternate.body[0].argument.type === Syntax.Literal
1647
1656
  && typeof stmt.alternate.body[0].argument.value === 'boolean'
1648
- && stmt.consequent.body[0].argument.value !== typeof stmt.alternate.body[0].argument.value ) {
1657
+ && stmt.consequent.body[0].argument.value !== stmt.alternate.body[0].argument.value ) {
1649
1658
  const inverse = stmt.alternate.body[0].argument.value;
1650
1659
  range = determineValueRange(stmt.test, varname, inverse);
1651
1660
  } else {
@@ -1679,8 +1688,8 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1679
1688
  if ( !obj ) {
1680
1689
  return true;
1681
1690
  }
1682
- for (let n in obj) {
1683
- if ( obj.hasOwnProperty(n) ) {
1691
+ for (const n in obj) {
1692
+ if ( Object.hasOwn(obj, n) ) {
1684
1693
  return false;
1685
1694
  }
1686
1695
  }
@@ -1868,8 +1877,8 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1868
1877
  if ( !isEmpty(oClassInfo.properties) ) {
1869
1878
  lines.push("<li>Properties");
1870
1879
  lines.push("<ul>");
1871
- for (let n in oClassInfo.properties) {
1872
- lines.push("<li>{@link " + rname("get", n) + " " + n + "} : " + oClassInfo.properties[n].type + (oClassInfo.properties[n].defaultValue !== null && oClassInfo.properties[n].defaultValue.value !== null ? " (default: " + oClassInfo.properties[n].defaultValue.raw + ")" : "") + (oClassInfo.defaultProperty == n ? " (default)" : "") + "</li>");
1880
+ for (const n in oClassInfo.properties) {
1881
+ lines.push("<li>{@link " + rname("get", n) + " " + n + "} : " + oClassInfo.properties[n].type + (oClassInfo.properties[n].defaultValue !== null && oClassInfo.properties[n].defaultValue.value !== null ? " (default: " + oClassInfo.properties[n].defaultValue.raw + ")" : "") + (oClassInfo.defaultProperty === n ? " (default)" : "") + "</li>");
1873
1882
  }
1874
1883
  lines.push("</ul>");
1875
1884
  lines.push("</li>");
@@ -1877,9 +1886,9 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1877
1886
  if ( !isEmpty(oClassInfo.aggregations) ) {
1878
1887
  lines.push("<li>Aggregations");
1879
1888
  lines.push("<ul>");
1880
- for (let n in oClassInfo.aggregations) {
1889
+ for (const n in oClassInfo.aggregations) {
1881
1890
  if ( oClassInfo.aggregations[n].visibility !== "hidden" ) {
1882
- lines.push("<li>{@link " + rname("get", n) + " " + n + "} : " + makeTypeString(oClassInfo.aggregations[n]) + (oClassInfo.defaultAggregation == n ? " (default)" : "") + "</li>");
1891
+ lines.push("<li>{@link " + rname("get", n) + " " + n + "} : " + makeTypeString(oClassInfo.aggregations[n]) + (oClassInfo.defaultAggregation === n ? " (default)" : "") + "</li>");
1883
1892
  }
1884
1893
  }
1885
1894
  lines.push("</ul>");
@@ -1888,7 +1897,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1888
1897
  if ( !isEmpty(oClassInfo.associations) ) {
1889
1898
  lines.push("<li>Associations");
1890
1899
  lines.push("<ul>");
1891
- for (let n in oClassInfo.associations) {
1900
+ for (const n in oClassInfo.associations) {
1892
1901
  lines.push("<li>{@link " + rname("get", n) + " " + n + "} : (sap.ui.core.ID | " + oClassInfo.associations[n].type + ")" + (oClassInfo.associations[n].cardinality === "0..n" ? "[]" : "") + "</li>");
1893
1902
  }
1894
1903
  lines.push("</ul>");
@@ -1897,7 +1906,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1897
1906
  if ( !isEmpty(oClassInfo.events) ) {
1898
1907
  lines.push("<li>Events");
1899
1908
  lines.push("<ul>");
1900
- for (let n in oClassInfo.events) {
1909
+ for (const n in oClassInfo.events) {
1901
1910
  lines.push("<li>{@link " + "#event:" + n + " " + n + "} : fnListenerFunction or [fnListenerFunction, oListenerObject] or [oData, fnListenerFunction, oListenerObject]</li>");
1902
1911
  }
1903
1912
  lines.push("</ul>");
@@ -1972,7 +1981,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1972
1981
  ]);
1973
1982
  }
1974
1983
 
1975
- for (let n in oClassInfo.properties ) {
1984
+ for (const n in oClassInfo.properties ) {
1976
1985
  const info = oClassInfo.properties[n];
1977
1986
  if ( info.visibility === 'hidden' ) {
1978
1987
  continue;
@@ -2044,7 +2053,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2044
2053
  }
2045
2054
  }
2046
2055
 
2047
- for (let n in oClassInfo.aggregations ) {
2056
+ for (const n in oClassInfo.aggregations ) {
2048
2057
  const info = oClassInfo.aggregations[n];
2049
2058
  if ( info.visibility === 'hidden' ) {
2050
2059
  continue;
@@ -2067,7 +2076,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2067
2076
  "@name " + name("get", n),
2068
2077
  "@function"
2069
2078
  ]);
2070
- if ( info.cardinality == "0..n" ) {
2079
+ if ( info.cardinality === "0..n" ) {
2071
2080
  const n1 = info.singularName;
2072
2081
  newJSDoc([
2073
2082
  "Inserts a " + n1 + " into the aggregation " + link + ".",
@@ -2188,7 +2197,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2188
2197
  }
2189
2198
  }
2190
2199
 
2191
- for (let n in oClassInfo.associations ) {
2200
+ for (const n in oClassInfo.associations ) {
2192
2201
  const info = oClassInfo.associations[n];
2193
2202
  if ( info.visibility === 'hidden' ) {
2194
2203
  continue;
@@ -2262,7 +2271,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2262
2271
  }
2263
2272
  }
2264
2273
 
2265
- for (let n in oClassInfo.events ) {
2274
+ for (const n in oClassInfo.events ) {
2266
2275
  const info = oClassInfo.events[n];
2267
2276
  const visibilityTags = createVisibilityTagsForSetting(info, classAccess);
2268
2277
 
@@ -2285,7 +2294,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2285
2294
  "@param {sap.ui.base.EventProvider} oControlEvent.getSource",
2286
2295
  "@param {object} oControlEvent.getParameters"
2287
2296
  ];
2288
- for (let pName in info.parameters ) {
2297
+ for (const pName in info.parameters ) {
2289
2298
  lines.push(
2290
2299
  "@param {" + (info.parameters[pName].type || "") + "} oControlEvent.getParameters." + pName + " " + (info.parameters[pName].doc || "")
2291
2300
  );
@@ -2351,7 +2360,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
2351
2360
  "@param {object} [mParameters] Parameters to pass along with the event"
2352
2361
  );
2353
2362
  if ( !isEmpty(info.parameters) ) {
2354
- for (let pName in info.parameters) {
2363
+ for (const pName in info.parameters) {
2355
2364
  lines.push(
2356
2365
  "@param {" + (info.parameters[pName].type || "any") + "} [mParameters." + pName + "] " + (info.parameters[pName].doc || "")
2357
2366
  );
@@ -2426,6 +2435,7 @@ if ( Syntax.File === 'File' ) {
2426
2435
  return leadingComments[leadingComments.length - 1];
2427
2436
  }
2428
2437
  }
2438
+ return undefined;
2429
2439
  };
2430
2440
 
2431
2441
  } else {
@@ -2573,7 +2583,7 @@ function preprocessComment(e) {
2573
2583
  // HACK: override cli.exit() to avoid that JSDoc3 exits the VM
2574
2584
  if ( pluginConfig.noExit ) {
2575
2585
  info("disabling exit() call");
2576
- require( path.join(global.env.dirname, 'cli') ).exit = function(retval) {
2586
+ require( path.join(env.dirname, 'cli') ).exit = function(retval) {
2577
2587
  info(`cli.exit(): do nothing (ret val=${retval})`);
2578
2588
  };
2579
2589
  }
@@ -2915,53 +2925,20 @@ exports.handlers = {
2915
2925
  currentSource = e.source;
2916
2926
  },
2917
2927
 
2928
+ /**
2929
+ * Event `parseComplete` is fired by JSDoc after all files have been parsed,
2930
+ * but before inheritance, mixins or borrows are processed.
2931
+ *
2932
+ * We use this event to merge our additional data into the doclets collected by JSDoc.
2933
+ * The merge must happen before doclets are cloned during the prcessing of augments or borrows.
2934
+ */
2918
2935
  parseComplete : function(e) {
2919
-
2920
2936
  const doclets = e.doclets;
2921
- const rAnonymous = /^<anonymous>(~|$)/;
2922
-
2923
- // remove undocumented symbols, ignored symbols, anonymous functions and their members, scope members
2924
- let l = doclets.length, i, j;
2925
- for (i = 0, j = 0; i < l; i++) {
2926
-
2927
- const doclet = doclets[i];
2928
- if ( !doclet.undocumented &&
2929
- !doclet.ignore &&
2930
- !(doclet.memberof && rAnonymous.test(doclet.memberof)) &&
2931
- doclet.longname.indexOf("~") < 0 ) {
2932
- doclets[j++] = doclet;
2933
- }
2934
- }
2935
- if ( j < l ) {
2936
- doclets.splice(j, l - j);
2937
- info(`removed ${l - j} undocumented, ignored or anonymous symbols`);
2938
- l = j;
2939
- }
2940
-
2941
- // sort doclets by name, synthetic, lineno, uid
2942
- // 'ignore' is a combination of criteria, see function above
2943
- debug("sorting doclets by name");
2944
- doclets.sort((a, b) => {
2945
- if ( a.longname === b.longname ) {
2946
- if ( a.synthetic === b.synthetic ) {
2947
- if ( a.meta && b.meta && a.meta.filename == b.meta.filename ) {
2948
- if ( a.meta.lineno !== b.meta.lineno ) {
2949
- return a.meta.lineno < b.meta.lineno ? -1 : 1;
2950
- }
2951
- }
2952
- return a.__ui5.id - b.__ui5.id;
2953
- }
2954
- return a.synthetic && !b.synthetic ? -1 : 1;
2955
- }
2956
- return a.longname < b.longname ? -1 : 1;
2957
- });
2958
- debug("sorting doclets by name done.");
2959
-
2960
- for (i = 0, j = 0; i < l; i++) {
2961
-
2937
+ const l = doclets.length;
2938
+ for (let i = 0; i < l; i++) {
2962
2939
  const doclet = doclets[i];
2963
2940
 
2964
- // add metadata to symbol
2941
+ // add metadata to class symbols
2965
2942
  if ( classInfos[doclet.longname] ) {
2966
2943
  // debug("class data", doclet.longname, "'" + classInfos[doclet.longname].export + "'");
2967
2944
  if ( doclet.__ui5.export === undefined ) {
@@ -3005,6 +2982,7 @@ exports.handlers = {
3005
2982
  }
3006
2983
  }
3007
2984
 
2985
+ // add DataType info to typedef symbols
3008
2986
  if ( typeInfos[doclet.longname] ) {
3009
2987
  doclet.__ui5.stereotype = 'datatype';
3010
2988
  doclet.__ui5.metadata = {
@@ -3014,6 +2992,7 @@ exports.handlers = {
3014
2992
  };
3015
2993
  }
3016
2994
 
2995
+ // add enum values to enum keys (for enum symbols)
3017
2996
  if ( (doclet.kind === 'member' || doclet.kind === 'constant') && doclet.isEnum && Array.isArray(doclet.properties) ) {
3018
2997
  // determine unique enum identifier from key set
3019
2998
  let enumID = doclet.properties.map(function(prop) {
@@ -3036,31 +3015,96 @@ exports.handlers = {
3036
3015
  }
3037
3016
  }
3038
3017
  }
3018
+ }
3019
+ },
3020
+
3021
+ /**
3022
+ * Event `processingComplete` is fired by JSDoc after all files have been parsed,
3023
+ * and after inheritance, mixins and borrows have been processed.
3024
+ *
3025
+ * The `e.doclets` contains the symbols that will be given to templates for publishing.
3026
+ *
3027
+ * We use this event to remove symbols that are not of interest:
3028
+ * - undocumented When JSDoc finds a class, function, object or member without a
3029
+ * JSDoc comment, it creates a doclet with a truthy `undocumented`
3030
+ * property
3031
+ * - ignore A symbol that has been marked with `@ignore` in the source code
3032
+ * - anonymous JSDoc could not infer a name for the symbol, neither from source
3033
+ * code nor from JSDoc comments
3034
+ * - local Local entities (e.g. local vars) can't be addressed from the outside
3035
+ * and therefore are generally not considered as API in UI5
3036
+ * - duplicates This plugin generates doclets for the accessor methods of
3037
+ * managed properties, aggregations, events, associations.
3038
+ * Developers might have created JSDoc comments for the same methods,
3039
+ * either because they have overridden them in code or because they
3040
+ * wanted to detail the method contract. If such duplicate doclets
3041
+ * are detected, the developer created doclets are preferred. If
3042
+ * multiple developer created doclets for the same entity exist in the
3043
+ * same file, the last one wins. If multiple doclets exists across
3044
+ * files, the one created last wins (but usually, this indicates a
3045
+ * copy & paste error)
3046
+ *
3047
+ * The cleanup is done in `processingComplete` as the processing of `@augments` or
3048
+ * `@borrows` tags might have created new non-interesting symbols.
3049
+ */
3050
+ processingComplete(e) {
3051
+ const doclets = e.doclets;
3052
+
3053
+ // sort doclets by name, synthetic, lineno, uid for easier detection of duplicates
3054
+ debug("sorting doclets by name");
3055
+ doclets.sort((a, b) => {
3056
+ if ( a.longname === b.longname ) {
3057
+ if ( a.synthetic === b.synthetic ) {
3058
+ if ( a.meta && b.meta && a.meta.filename === b.meta.filename ) {
3059
+ if ( a.meta.lineno !== b.meta.lineno ) {
3060
+ return a.meta.lineno < b.meta.lineno ? -1 : 1;
3061
+ }
3062
+ }
3063
+ return a.__ui5.id - b.__ui5.id;
3064
+ }
3065
+ return a.synthetic && !b.synthetic ? -1 : 1;
3066
+ }
3067
+ return a.longname < b.longname ? -1 : 1;
3068
+ });
3069
+ debug("sorting doclets by name done.");
3070
+
3071
+ // cleanup doclets
3072
+ const rAnonymous = /^<anonymous>(~|$)/;
3073
+ const l = doclets.length;
3074
+ let j = 0;
3075
+ for (let i = 0; i < l; i++) {
3076
+ const doclet = doclets[i];
3077
+
3078
+ // skip undocumented, ignored, anonymous entities as well as local entities
3079
+ if (doclet.undocumented
3080
+ || doclet.ignore
3081
+ || (doclet.memberof && rAnonymous.test(doclet.memberof))
3082
+ || doclet.longname.includes("~") ) {
3083
+ continue;
3084
+ }
3039
3085
 
3040
3086
  // check for duplicates: last one wins
3041
3087
  if ( j > 0 && doclets[j - 1].longname === doclet.longname ) {
3042
3088
  if ( !doclets[j - 1].synthetic && !doclet.__ui5.updatedDoclet ) {
3043
- // replacing synthetic comments or updating comments are trivial case. Just log non-trivial duplicates
3089
+ // replacing synthetic comments or updating comments are trivial cases. Just log non-trivial duplicates
3044
3090
  debug(`ignoring duplicate doclet for ${doclet.longname}: ${location(doclet)} overrides ${location(doclets[j - 1])}`);
3045
3091
  }
3046
3092
  doclets[j - 1] = doclet;
3047
- } else {
3048
- doclets[j++] = doclet;
3093
+ continue;
3049
3094
  }
3095
+
3096
+ doclets[j++] = doclet;
3050
3097
  }
3051
3098
 
3052
3099
  if ( j < l ) {
3053
3100
  doclets.splice(j, l - j);
3054
- info(`removed ${l - j} duplicate symbols - ${doclets.length} remaining`);
3101
+ info(`processingComplete: removed ${l - j} undocumented, ignored, anonymous, local or duplicate symbols - ${doclets.length} remaining`);
3055
3102
  }
3056
3103
 
3057
3104
  if ( pluginConfig.saveSymbols ) {
3058
-
3059
3105
  fs.mkPath(env.opts.destination);
3060
- fs.writeFileSync(path.join(env.opts.destination, "symbols-parseComplete.json"), JSON.stringify(e.doclets, null, "\t"), 'utf8');
3061
-
3106
+ fs.writeFileSync(path.join(env.opts.destination, "symbols-processingComplete.json"), JSON.stringify(e.doclets, null, "\t"), 'utf8');
3062
3107
  }
3063
-
3064
3108
  }
3065
3109
  };
3066
3110
 
@@ -3122,9 +3166,11 @@ exports.astNodeVisitor = {
3122
3166
  nodeToAnalyze = node.right;
3123
3167
  }
3124
3168
 
3125
- nodeToAnalyze && analyzeModuleDefinition(
3126
- resolvePotentialWrapperExpression(nodeToAnalyze)
3127
- );
3169
+ if (nodeToAnalyze) {
3170
+ analyzeModuleDefinition(
3171
+ resolvePotentialWrapperExpression(nodeToAnalyze)
3172
+ );
3173
+ }
3128
3174
  }
3129
3175
 
3130
3176
  const isArrowExpression = isArrowFuncExpression(node) && node.body.type === Syntax.ObjectExpression;
@@ -3197,7 +3243,7 @@ exports.astNodeVisitor = {
3197
3243
 
3198
3244
  let programBodyStart;
3199
3245
  if ( node.program.body.length >= 1 ) {
3200
- programBodyStart = node.program.body[0].start
3246
+ programBodyStart = node.program.body[0].start;
3201
3247
  } else {
3202
3248
  // File has no code at all
3203
3249
  programBodyStart = Infinity;
@@ -3236,6 +3282,7 @@ exports.astNodeVisitor = {
3236
3282
  function getTypeStrings(parsedType, isOutermostType) {
3237
3283
  let applications;
3238
3284
  let typeString;
3285
+ let paramTypes;
3239
3286
  let types = [];
3240
3287
  switch (parsedType.type) {
3241
3288
  case TYPES.AllLiteral:
@@ -3244,7 +3291,7 @@ exports.astNodeVisitor = {
3244
3291
  case TYPES.FunctionType:
3245
3292
  typeString = 'function';
3246
3293
  // #### BEGIN: MODIFIED BY SAP
3247
- const paramTypes = [];
3294
+ paramTypes = [];
3248
3295
  if (parsedType.new) {
3249
3296
  paramTypes.push(toTypeString(parsedType.new));
3250
3297
  }
@@ -3291,7 +3338,7 @@ exports.astNodeVisitor = {
3291
3338
  const realName = keyString.substring(0, pos);
3292
3339
  const realValue = keyString.substring(pos + 1, keyString.length);
3293
3340
  let message = `Cannot parse the "${keyString}" part of "${parsedType.typeExpression}" in RecordType (log output above may give a hint in which file).\n`;
3294
- message += `Did you mean to specify a property "${realName}" of type "${realValue}"? Then insert a space after the colon and write "${realName}: ${realValue}".`
3341
+ message += `Did you mean to specify a property "${realName}" of type "${realValue}"? Then insert a space after the colon and write "${realName}: ${realValue}".`;
3295
3342
  error(message);
3296
3343
  return "x: any"; // unparseable property set to "any" - but the JSDoc run will fail now, anyway
3297
3344
  } else {
@@ -3310,17 +3357,16 @@ exports.astNodeVisitor = {
3310
3357
  case TYPES.TypeApplication:
3311
3358
  // if this is the outermost type, we strip the modifiers; otherwise, we keep them
3312
3359
  if (isOutermostType) {
3313
- applications = parsedType.applications.map(application =>
3360
+ applications = parsedType.applications.map((application) =>
3314
3361
  catharsis.stringify(application)).join(', ');
3315
3362
  typeString = `${getTypeStrings(parsedType.expression)[0]}.<${applications}>`;
3316
3363
  types.push(typeString);
3317
- }
3318
- else {
3364
+ } else {
3319
3365
  types.push( catharsis.stringify(parsedType) );
3320
3366
  }
3321
3367
  break;
3322
3368
  case TYPES.TypeUnion:
3323
- parsedType.elements.forEach(element => {
3369
+ parsedType.elements.forEach((element) => {
3324
3370
  types = types.concat( getTypeStrings(element) );
3325
3371
  });
3326
3372
  break;
@@ -3350,5 +3396,5 @@ exports.astNodeVisitor = {
3350
3396
  // console.info("new parse result", tagInfo.type);
3351
3397
  }
3352
3398
  return tagInfo;
3353
- }
3399
+ };
3354
3400
  }());