@ui5/builder 3.0.7 → 3.0.8

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.
@@ -12,7 +12,6 @@
12
12
  "use strict";
13
13
 
14
14
  /* imports */
15
- const template = require('jsdoc/template');
16
15
  const helper = require('jsdoc/util/templateHelper');
17
16
  const fs = require('jsdoc/fs');
18
17
  const doclet = require('jsdoc/doclet');
@@ -77,30 +76,15 @@ const MY_TEMPLATE_NAME = "ui5",
77
76
  }
78
77
  ];
79
78
 
80
- const rSecurityTags = new RegExp(A_SECURITY_TAGS.map(function($) {return $.name.toLowerCase(); }).join('|'), "i");
81
- //debug(A_SECURITY_TAGS.map(function($) {return $.name; }).join('|'));
82
-
83
79
  const templatesConf = (env.conf.templates || {}),
84
- templateConf = templatesConf[MY_TEMPLATE_NAME] || templatesConf[MY_ALT_TEMPLATE_NAME] || {},
85
- pluginConf = templateConf;
80
+ templateConf = templatesConf[MY_TEMPLATE_NAME] || templatesConf[MY_ALT_TEMPLATE_NAME] || {};
86
81
 
87
82
  let conf = {};
88
83
 
89
- let view;
90
-
91
84
  let __symbols;
92
85
  let __longnames;
93
86
  let __missingLongnames = {};
94
87
 
95
- /**
96
- * Maps the symbol 'longname's to the unique filename that contains the documentation of that symbol.
97
- * This map is maintained to deal with names that only differ in case (e.g. the namespace sap.ui.model.type and the class sap.ui.model.Type).
98
- */
99
- let __uniqueFilenames = {};
100
-
101
- /* shortcut for Object.prototype.hasOwnProperty.call(obj, prop) */
102
- const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
103
-
104
88
  function merge(target, source) {
105
89
  if ( source != null ) {
106
90
  // simple single source merge
@@ -121,13 +105,28 @@ function merge(target, source) {
121
105
  }
122
106
 
123
107
  function lookup(key) {
124
- if ( !Object.prototype.hasOwnProperty.call(__longnames, key) ) {
108
+ if ( !Object.hasOwn(__longnames, key) ) {
125
109
  __missingLongnames[key] = (__missingLongnames[key] || 0) + 1;
126
110
  __longnames[key] = __symbols.find((symbol) => symbol.longname === key);
127
111
  }
128
112
  return __longnames[key];
129
113
  }
130
114
 
115
+ function createSymbol(longname, lines = []) {
116
+ const comment = [
117
+ "@name " + longname,
118
+ ... lines
119
+ ];
120
+
121
+ const symbol = new doclet.Doclet("/**\n * " + comment.join("\n * ") + "\n */", {});
122
+ symbol.__ui5 = {};
123
+
124
+ __longnames[longname] = symbol;
125
+ __symbols.push(symbol);
126
+
127
+ return symbol;
128
+ }
129
+
131
130
  const externalSymbols = {};
132
131
 
133
132
  function loadExternalSymbols(apiJsonFolder) {
@@ -186,253 +185,14 @@ function supportsInheritance($) {
186
185
  * In the less perfect documentation build, the criterion 'whose parents are all namespaces' is ignored
187
186
  */
188
187
  function isFirstClassSymbol($) {
189
- return /^(namespace|interface|class|typedef)$/.test($.kind) || ($.kind === 'member' && $.isEnum || isModuleExport($) )/* isNonEmptyNamespace($) */;
188
+ return (
189
+ /^(namespace|interface|class|typedef)$/.test($.kind)
190
+ || $.kind === 'member' && $.isEnum
191
+ || ['function', 'member'].includes($.kind) && isModuleExport($)
192
+ )/* isNonEmptyNamespace($) */;
190
193
  }
191
194
 
192
-
193
- // ---- Version class -----------------------------------------------------------------------------------------------------------------------------------------------------------
194
-
195
- const Version = (function() {
196
-
197
- const rVersion = /^[0-9]+(?:\.([0-9]+)(?:\.([0-9]+))?)?(.*)$/;
198
-
199
- function norm(v) {
200
- v = parseInt(v);
201
- return isNaN(v) ? 0 : v;
202
- }
203
-
204
- function compare(v1, v2) {
205
- if ( v1 !== v2 ) {
206
- return v1 < v2 ? -1 : 1;
207
- }
208
- return 0;
209
- }
210
-
211
- /**
212
- * Creates a Version object from the given version string.
213
- *
214
- * @param {string} versionStr A dot-separated version string
215
- *
216
- * @classdesc Represents a version consisting of major, minor, patch version and suffix,
217
- * e.g. '1.2.7-SNAPSHOT'. All parts after the major version are optional.
218
- * @class
219
- */
220
- class Version {
221
-
222
- constructor(versionStr) {
223
- const match = rVersion.exec(versionStr) || [];
224
-
225
- Object.defineProperty(this, "major", {
226
- enumerable: true,
227
- value: norm(match[0])
228
- });
229
- Object.defineProperty(this, "minor", {
230
- enumerable: true,
231
- value: norm(match[1])
232
- });
233
- Object.defineProperty(this, "patch", {
234
- enumerable: true,
235
- value: norm(match[2])
236
- });
237
- Object.defineProperty(this, "suffix", {
238
- enumerable: true,
239
- value: String(match[3] || "")
240
- });
241
- }
242
-
243
- toMajorMinor() {
244
- return new Version(this.major + "." + this.minor);
245
- }
246
-
247
- toString() {
248
- return this.major + "." + this.minor + "." + this.patch + this.suffix;
249
- }
250
-
251
- compareTo(other) {
252
- return (
253
- this.major - other.major
254
- || this.minor - other.minor
255
- || this.patch - other.patch
256
- || compare(this.suffix, other.suffix)
257
- );
258
- }
259
-
260
- }
261
-
262
- return Version;
263
-
264
- }());
265
-
266
- // ---- Link class --------------------------------------------------------------------------------------------------------------------------------------------------------------
267
-
268
- //TODO move to separate module
269
-
270
- const Link = (function() {
271
-
272
- const missingTypes = {};
273
-
274
- function _makeLink(href, target, tooltip, text) {
275
- return '<a' +
276
- (tooltip ? ' title="' + tooltip + '"' : '') +
277
- ' href="' + href + '"' +
278
- (target ? ' target="' + target + '"' : '') +
279
- '>' + text + '</a>';
280
- }
281
-
282
- class Link {
283
-
284
- toSymbol(longname) {
285
- if ( longname != null ) {
286
- longname = String(longname);
287
- if ( /#constructor$/.test(longname) ) {
288
- if ( !this.innerName ) {
289
- this.innerName = 'constructor';
290
- }
291
- longname = longname.slice(0, -"#constructor".length);
292
- }
293
- this.longname = longname;
294
- }
295
- return this;
296
- }
297
-
298
- withText(text) {
299
- this.text = text;
300
- return this;
301
- }
302
-
303
- withTooltip(text) {
304
- this.tooltip = text;
305
- return this;
306
- }
307
-
308
- toFile(file) {
309
- if ( file != null ) {
310
- this.file = file;
311
- }
312
- return this;
313
- }
314
-
315
- toString() {
316
- let longname = this.longname;
317
-
318
- if (longname) {
319
-
320
- if ( /^(?:(?:ftp|https?):\/\/|\.\.?\/)/.test(longname) ) {
321
- // handle real hyperlinks (TODO should be handled with a different "to" method
322
- return _makeLink(longname, this.targetName, this.tooltip, this.text || longname);
323
- } else if ( /^topic:/.test(longname) ) {
324
- // handle documentation links
325
- longname = conf.topicUrlPattern.replace("{{topic}}", longname.slice("topic:".length));
326
- return _makeLink(longname, this.targetName, this.tooltip, this.text || longname);
327
- } else {
328
- return this._makeSymbolLink(longname);
329
- }
330
-
331
- } else if (this.file) {
332
- return _makeLink(Link.base + this.file, this.targetName, null, this.text || this.file);
333
- }
334
-
335
- return undefined;
336
- }
337
-
338
- _makeSymbolLink(longname) {
339
-
340
- // normalize .prototype. and #
341
- longname = longname.replace(/\.prototype\./g, '#');
342
-
343
- // if it is an internal reference, then don't validate against symbols, just create a link
344
- if ( longname.charAt(0) == "#" ) {
345
-
346
- return _makeLink(longname + (this.innerName ? "#" + this.innerName : ""), this.targetName, this.tooltip, this.text || longname.slice(1));
347
-
348
- }
349
-
350
- let linkTo = lookup(longname);
351
- // if there is no symbol by that name just return the name unaltered
352
- if ( !linkTo ) {
353
-
354
- missingTypes[longname] = true;
355
-
356
- return this.text || longname;
357
-
358
- }
359
-
360
- // it's a full symbol reference (potentially to another file)
361
- let mainSymbol, anchor;
362
- if ( (linkTo.kind === 'member' && !linkTo.isEnum) || linkTo.kind === 'constant' || linkTo.kind === 'function' || linkTo.kind === 'event' ) { // it's a method or property
363
-
364
- mainSymbol = linkTo.memberof;
365
- anchor = ( linkTo.kind === 'event' ? "event:" : "") + Link.symbolNameToLinkName(linkTo);
366
-
367
- } else {
368
-
369
- mainSymbol = linkTo.longname;
370
- anchor = this.innerName;
371
-
372
- }
373
-
374
- return _makeLink(Link.baseSymbols + __uniqueFilenames[mainSymbol] + conf.ext + (anchor ? "#" + anchor : ""), this.targetName, this.tooltip, this.text || longname);
375
- }
376
-
377
- }
378
-
379
- Link.getMissingTypes = function() {
380
- return Object.keys(missingTypes);
381
- };
382
-
383
- Link.symbolNameToLinkName = function(symbol) {
384
- let linker = "";
385
- if ( symbol.scope === 'static' ) {
386
- linker = ".";
387
- } else if (symbol.isInner) {
388
- linker = "-"; // TODO-migrate?
389
- }
390
- return linker + symbol.name;
391
- };
392
-
393
- return Link;
394
-
395
- }());
396
-
397
-
398
-
399
- // ---- publish() - main entry point for JSDoc templates -------------------------------------------------------------------------------------------------------
400
-
401
- /* Called automatically by JsDoc Toolkit. */
402
- function publish(symbolSet) {
403
-
404
- info("entering sapui5 template");
405
-
406
- // create output dir
407
- fs.mkPath(env.opts.destination);
408
-
409
- __symbols = symbolSet().get();
410
-
411
- // if ( __symbols.length < 20000 ) {
412
- // const rawSymbolsFile = path.join(env.opts.destination, "symbols-unpruned-ui5.json");
413
- // info(`writing raw symbols to ${rawSymbolsFile}`);
414
- // fs.writeFileSync(rawSymbolsFile, JSON.stringify(__symbols, filter, "\t"), 'utf8');
415
- // }
416
-
417
- info(`before prune: ${__symbols.length} symbols.`);
418
- symbolSet = helper.prune(symbolSet);
419
- __symbols = symbolSet().get();
420
- info(`after prune: ${__symbols.length} symbols.`);
421
-
422
- __longnames = {};
423
- __symbols.forEach(function($) {
424
- __longnames[$.longname] = $;
425
- });
426
-
427
- if ( templateConf.apiJsonFolder ) {
428
- info(`loading external apis from folder '${templateConf.apiJsonFolder}'`);
429
- loadExternalSymbols(templateConf.apiJsonFolder);
430
- }
431
-
432
- const templatePath = path.join(env.opts.template, 'tmpl/');
433
- info(`using templates from '${templatePath}'`);
434
- view = new template.Template(templatePath);
435
-
195
+ function writeSymbols(symbols, filename, caption) {
436
196
  function filter(key,value) {
437
197
  if ( key === 'meta' ) {
438
198
  //return;
@@ -463,24 +223,48 @@ function publish(symbolSet) {
463
223
  }
464
224
  return value;
465
225
  }
226
+ if ( symbols.length < 20000 ) {
227
+ const symbolsFile = path.join(env.opts.destination, filename);
228
+ info(`writing ${caption} to ${symbolsFile}`);
229
+ fs.writeFileSync(symbolsFile, JSON.stringify(symbols, filter, "\t"), 'utf8');
230
+ }
231
+ }
232
+ // ---- publish() - main entry point for JSDoc templates -------------------------------------------------------------------------------------------------------
466
233
 
467
- // now resolve relationships
468
- const aRootNamespaces = createNamespaceTree();
469
- const hierarchyRoots = createInheritanceTree();
470
- collectMembers();
471
- mergeEventDocumentation();
234
+ /* Called automatically by JsDoc Toolkit. */
235
+ function publish(symbolSet) {
472
236
 
473
- if ( __symbols.length < 20000 ) {
474
- const rawSymbolsFile = path.join(env.opts.destination, "symbols-pruned-ui5.json");
475
- info(`writing raw symbols to ${rawSymbolsFile}`);
476
- fs.writeFileSync(rawSymbolsFile, JSON.stringify(__symbols, filter, "\t"), 'utf8');
477
- }
237
+ info("entering sapui5 template");
238
+
239
+ // create output dir
240
+ fs.mkPath(env.opts.destination);
478
241
 
479
- // used to allow Link to check the details of things being linked to
480
- Link.symbolSet = symbolSet;
242
+ const originalSymbols = symbolSet().get();
243
+ // writeSymbols(originalSymbols, "symbols-unpruned-ui5.json", "raw symbols before prune");
481
244
 
245
+ info(`before prune: ${originalSymbols.length} symbols.`);
246
+ symbolSet = helper.prune(symbolSet);
482
247
  // get an array version of the symbol set, useful for filtering
483
- const symbols = __symbols;
248
+ const allSymbols = __symbols = symbolSet().get();
249
+ info(`after prune: ${allSymbols.length} symbols.`);
250
+
251
+ __longnames = {};
252
+ allSymbols.forEach(function($) {
253
+ __longnames[$.longname] = $;
254
+ });
255
+
256
+ if ( templateConf.apiJsonFolder ) {
257
+ info(`loading external apis from folder '${templateConf.apiJsonFolder}'`);
258
+ loadExternalSymbols(templateConf.apiJsonFolder);
259
+ }
260
+
261
+ // now resolve relationships
262
+ const aRootNamespaces = createNamespaceTree(allSymbols);
263
+ createInheritanceTree(allSymbols);
264
+ collectMembers(allSymbols);
265
+ mergeEventDocumentation(allSymbols);
266
+
267
+ writeSymbols(allSymbols, "symbols-pruned-ui5.json", "raw symbols after prune");
484
268
 
485
269
  // -----
486
270
 
@@ -491,7 +275,7 @@ function publish(symbolSet) {
491
275
  apiXmlFile: path.join(env.opts.destination, "jsapi.xml")
492
276
  },
493
277
  processor : function(conf) {
494
- createAPIXML(symbols, conf.apiXmlFile, {
278
+ createAPIXML(allSymbols, conf.apiXmlFile, {
495
279
  legacyContent: true
496
280
  });
497
281
  }
@@ -502,7 +286,7 @@ function publish(symbolSet) {
502
286
  apiJsonFile: path.join(env.opts.destination, "api.json")
503
287
  },
504
288
  processor : function(conf) {
505
- createAPIJSON(symbols, conf.apiJsonFile);
289
+ createAPIJSON(allSymbols, conf.apiJsonFile);
506
290
  }
507
291
  },
508
292
 
@@ -511,88 +295,16 @@ function publish(symbolSet) {
511
295
  fullXmlFile: path.join(env.opts.destination, "fulljsapi.xml")
512
296
  },
513
297
  processor : function(conf) {
514
- createAPIXML(symbols, conf.fullXmlFile, {
298
+ createAPIXML(allSymbols, conf.fullXmlFile, {
515
299
  roots: aRootNamespaces,
516
300
  omitDefaults : conf.omitDefaultsInFullXml,
517
301
  resolveInheritance: true
518
302
  });
519
303
  }
520
- },
521
-
522
- "apijs" : {
523
- defaults: {
524
- jsapiFile: path.join(env.opts.destination, "api.js")
525
- },
526
- processor: function(conf) {
527
- createAPIJS(symbols, conf.jsapiFile);
528
- }
529
- },
530
-
531
- "full" : {
532
- defaults : {
533
- outdir: path.join(env.opts.destination, "full/"),
534
- contentOnly: false,
535
- hierarchyIndex: true
536
- },
537
- processor: function() {
538
- publishClasses(symbolSet, aRootNamespaces, hierarchyRoots);
539
- }
540
- },
541
-
542
- "public" : {
543
- defaults: {
544
- outdir: path.join(env.opts.destination, "public/"),
545
- filter: function($) { return $.access === 'public' || $.access === 'protected' || $.access == null; },
546
- contentOnly: false,
547
- hierarchyIndex: true
548
- },
549
- processor: function(conf) {
550
- publishClasses(symbolSet, aRootNamespaces, hierarchyRoots);
551
- }
552
- },
553
-
554
- "demokit" : {
555
- defaults: {
556
- outdir: path.join(env.opts.destination, "demokit/"),
557
- filter: function($) { return $.access === 'public' || $.access === 'protected' || $.access == null; },
558
- contentOnly: true,
559
- modulePages: true,
560
- hierarchyIndex: false,
561
- securityIndex: true,
562
- sinceIndex: true,
563
- deprecationIndex: true,
564
- experimentalIndex: true,
565
- suppressAuthor: true,
566
- suppressVersion: true
567
- },
568
- processor: function(conf) {
569
- publishClasses(symbolSet, aRootNamespaces, hierarchyRoots);
570
- }
571
- },
572
-
573
- "demokit-internal" : {
574
- defaults: {
575
- outdir: path.join(env.opts.destination, "demokit-internal/"),
576
- // filter: function($) { return $.access === 'public' || $.access === 'protected' || $.access === 'restricted' || $.access == null; },
577
- contentOnly: true,
578
- modulePages: true,
579
- hierarchyIndex: false,
580
- securityIndex: true,
581
- sinceIndex: true,
582
- deprecationIndex: true,
583
- experimentalIndex: true,
584
- suppressAuthor: true,
585
- suppressVersion: true
586
- },
587
- processor: function(conf) {
588
- publishClasses(symbolSet, aRootNamespaces, hierarchyRoots);
589
- }
590
304
  }
591
305
 
592
306
  };
593
307
 
594
- const now = new Date();
595
-
596
308
  info("start publishing");
597
309
  if ( Array.isArray(templateConf.variants) ) {
598
310
  templateConf.variants.forEach((vVariant) => {
@@ -615,14 +327,7 @@ function publish(symbolSet) {
615
327
  // Note: trailing slash expected for dirs
616
328
  conf = merge({
617
329
  ext: ".html",
618
- filter: function($) { return true; },
619
- templatesDir: "/templates/sapui5/",
620
- symbolsDir: "symbols/",
621
- modulesDir: "modules/",
622
- topicUrlPattern: "../../guide/{{topic}}.html",
623
- srcDir: "symbols/src/",
624
- creationDate : now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDay() + " " + now.getHours() + ":" + now.getMinutes(),
625
- outdir: env.opts.destination
330
+ filter: function($) { return true; }
626
331
  }, PUBLISHING_VARIANTS[vVariant.variant].defaults, templateConf, vVariant);
627
332
 
628
333
  info(`publishing as variant '${vVariant.variant}'`);
@@ -641,31 +346,26 @@ function publish(symbolSet) {
641
346
  });
642
347
  }
643
348
 
644
- let builtinSymbols = templateConf.builtinSymbols;
645
- if ( builtinSymbols ) {
646
- Link.getMissingTypes().filter(($) => builtinSymbols.indexOf($) < 0).sort().forEach(($) => {
647
- // TODO instead of filtering topic: and fiori: links out here, they should be correctly linked in the template
648
- if ( !/\{@link (?:topic:|fiori:)/.test($) ) {
649
- warning(`unresolved reference: ${$}`);
650
- }
651
- });
652
- }
653
349
  info("publishing done.");
654
-
655
350
  }
656
351
 
657
352
  //---- namespace tree --------------------------------------------------------------------------------
658
353
 
659
- /*
660
- * Completes the tree of namespaces. Namespaces for which content is available
661
- * but which have not been documented are created as dummy without documentation.
354
+ /**
355
+ * Completes the tree of namespaces.
356
+ *
357
+ * Namespaces for which content is available but which have not been documented
358
+ * are created as dummy, public namespace with empty documentation.
359
+ *
360
+ * @param {Array<doclet.Doclet>} allSymbols Array of all symbols to be published
361
+ * @returns {Array<doclet.Doclet>} Array of all root namespaces
662
362
  */
663
- function createNamespaceTree() {
363
+ function createNamespaceTree(allSymbols) {
664
364
 
665
- info(`create namespace tree (${__symbols.length} symbols)`);
365
+ info(`create namespace tree (${allSymbols.length} symbols)`);
666
366
 
667
367
  const aRootNamespaces = [];
668
- const aTypes = __symbols.filter((symbol) => isFirstClassSymbol(symbol));
368
+ const aTypes = allSymbols.filter((symbol) => isFirstClassSymbol(symbol));
669
369
 
670
370
  for (let i = 0; i < aTypes.length; i++) { // loop with a for-loop as it can handle concurrent modifications
671
371
 
@@ -676,8 +376,6 @@ function createNamespaceTree() {
676
376
  if ( !parent ) {
677
377
  warning(`create missing namespace '${symbol.memberof}' (referenced by ${symbol.longname})`);
678
378
  parent = makeNamespace(symbol.memberof);
679
- __longnames[symbol.memberof] = parent;
680
- __symbols.push(parent);
681
379
  aTypes.push(parent); // concurrent modification: parent will be processed later in this loop
682
380
  }
683
381
  symbol.__ui5.parent = parent;
@@ -698,17 +396,11 @@ function makeNamespace(memberof) {
698
396
 
699
397
  info(`adding synthetic namespace symbol ${memberof}`);
700
398
 
701
- const comment = [
702
- "@name " + memberof,
399
+ return createSymbol(memberof, [
703
400
  "@namespace",
704
401
  "@synthetic",
705
402
  "@public"
706
- ];
707
-
708
- const symbol = new doclet.Doclet("/**\n * " + comment.join("\n * ") + "\n */", {});
709
- symbol.__ui5 = {};
710
-
711
- return symbol;
403
+ ]);
712
404
  }
713
405
 
714
406
  //---- inheritance hierarchy ----------------------------------------------------------------------------
@@ -724,26 +416,19 @@ function makeNamespace(memberof) {
724
416
  * derived : {Node[]} // subclasses/-types
725
417
  * }
726
418
  *
419
+ * @param {Array<doclet.Doclet>} allSymbols Array of all symbols to be published
420
+ * @returns {Array<doclet.Doclet>} Array of all root types
727
421
  */
728
- function createInheritanceTree() {
729
-
730
- function makeDoclet(longname, lines) {
731
- lines.push("@name " + longname);
732
- const newDoclet = new doclet.Doclet("/**\n * " + lines.join("\n * ") + "\n */", {});
733
- newDoclet.__ui5 = {};
734
- __longnames[longname] = newDoclet;
735
- __symbols.push(newDoclet);
736
- return newDoclet;
737
- }
422
+ function createInheritanceTree(allSymbols) {
738
423
 
739
- info(`create inheritance tree (${__symbols.length} symbols)`);
424
+ info(`create inheritance tree (${allSymbols.length} symbols)`);
740
425
 
741
- const aTypes = __symbols.filter((symbol) => supportsInheritance(symbol));
426
+ const aTypes = allSymbols.filter((symbol) => supportsInheritance(symbol));
742
427
  const aRootTypes = [];
743
428
 
744
429
  let oObject = lookup("Object");
745
430
  if ( !oObject ) {
746
- oObject = makeDoclet("Object", [
431
+ oObject = createSymbol("Object", [
747
432
  "@class",
748
433
  "@synthetic",
749
434
  "@public"
@@ -766,7 +451,7 @@ function createInheritanceTree() {
766
451
  warning(`create missing class ${sClass} (extended by ${sExtendingClass})`);
767
452
  }
768
453
  let oBaseClass = getOrCreateClass(sBaseClass, sClass);
769
- oClass = makeDoclet(sClass, [
454
+ oClass = createSymbol(sClass, [
770
455
  "@extends " + sBaseClass,
771
456
  "@" + sKind,
772
457
  "@synthetic",
@@ -812,7 +497,7 @@ function createInheritanceTree() {
812
497
  } else {
813
498
  warning(`create missing interface ${oClass.implements[j]}`);
814
499
  }
815
- oInterface = makeDoclet(oClass.implements[j], [
500
+ oInterface = createSymbol(oClass.implements[j], [
816
501
  "@interface",
817
502
  "@synthetic",
818
503
  sVisibility === "restricted" ? "@ui5-restricted" : "@" + sVisibility
@@ -868,8 +553,13 @@ function createInheritanceTree() {
868
553
  return aRootTypes;
869
554
  }
870
555
 
871
- function collectMembers() {
872
- __symbols.forEach(function($) {
556
+ /**
557
+ * Attaches each symbol to its parent ('memberof').
558
+ *
559
+ * @param {Array<doclet.Doclet>} allSymbols Array of all symbols to be published
560
+ */
561
+ function collectMembers(allSymbols) {
562
+ allSymbols.forEach(function($) {
873
563
  if ( $.memberof ) {
874
564
  const parent = lookup($.memberof);
875
565
  if ( parent /* && supportsInheritance(parent) */ ) {
@@ -880,11 +570,17 @@ function collectMembers() {
880
570
  });
881
571
  }
882
572
 
883
- function mergeEventDocumentation() {
573
+ /**
574
+ * Searches for JSDoc events that are also described in UI5 metadata
575
+ * and merges the parameter description from the JSDoc event into the UI5 event.
576
+ *
577
+ * @param {Array<doclet.Doclet>} allSymbols Array of all symbols to be published
578
+ */
579
+ function mergeEventDocumentation(allSymbols) {
884
580
 
885
581
  debug("merging JSDoc event documentation into UI5 metadata");
886
582
 
887
- const aTypes = __symbols.filter((symbol) => isaClass(symbol));
583
+ const aTypes = allSymbols.filter((symbol) => isaClass(symbol));
888
584
 
889
585
  aTypes.forEach((symbol) => {
890
586
 
@@ -935,181 +631,6 @@ function mergeEventDocumentation() {
935
631
 
936
632
  // ---- publishing -----------------------------------------------------------------------
937
633
 
938
- function publishClasses(symbols, aRootNamespaces, hierarchyRoots) {
939
-
940
- // create output dir
941
- fs.mkPath(path.join(conf.outdir, conf.symbolsDir));
942
-
943
- // get a list of all the first class symbols in the symbolset
944
- const firstClassSymbols = symbols(function() {
945
- return supportsInheritance(this) && conf.filter(this);
946
- }).order("longname");
947
-
948
- // create unique file names
949
- __uniqueFilenames = {};
950
- let filenames = {};
951
- firstClassSymbols.get().sort(sortByAlias).forEach(function(symbol) {
952
- const filename = escape(symbol.longname.replace(/^module:/, "")).replace(/\//g, "%25");
953
- if ( filenames.hasOwnProperty(filename.toUpperCase()) && (filenames[filename.toUpperCase()].longname !== symbol.longname) ) {
954
- // find an unused filename by appending "-n" where n is an integer > 0
955
- let j = 1;
956
- while (filenames.hasOwnProperty(filename.toUpperCase() + "-" + j)) {
957
- j++;
958
- }
959
- warning(`duplicate symbol names ${filenames[filename.toUpperCase()].longname} and ${symbol.longname}, renaming the latter to ${filename + "-" + j}`);
960
- filename = filename + "-" + j;
961
- }
962
- filenames[filename.toUpperCase()] = symbol;
963
- __uniqueFilenames[symbol.longname] = filename;
964
- });
965
- filenames = null;
966
-
967
- // create a class index, displayed in the left-hand column of every class page
968
- let classTemplate;
969
- if ( !conf.contentOnly ) {
970
- info("create embedded class index");
971
- Link.base = "../";
972
- Link.baseSymbols = "";
973
- classTemplate = 'classWithIndex.html.tmpl';
974
- publish.header = processTemplate("_header.tmpl", firstClassSymbols);
975
- publish.footer = processTemplate("_footer.tmpl", firstClassSymbols);
976
- publish.classesIndex = processTemplate("_navIndex.tmpl", firstClassSymbols); // kept in memory
977
- } else {
978
- let newStyle = !!pluginConf.newStyle;
979
- classTemplate = newStyle ? "class-new.html.tmpl" : "class.html.tmpl";
980
- publish.header = '';
981
- publish.footer = '';
982
- publish.classesIndex = '';
983
-
984
- // instead create an index as XML
985
- Link.base = "";
986
- Link.baseSymbols = conf.symbolsDir;
987
- processTemplateAndSave("index.xml.tmpl", aRootNamespaces, "index.xml");
988
- }
989
-
990
- // create each of the class pages
991
- info("create class/namespace pages");
992
- Link.base = "../";
993
- Link.baseSymbols = "";
994
- firstClassSymbols.each(function(symbol) {
995
- let sOutName = path.join(conf.symbolsDir, __uniqueFilenames[symbol.longname]) + conf.ext;
996
- processTemplateAndSave(classTemplate, symbol, sOutName);
997
- });
998
-
999
- if ( conf.modulePages ) {
1000
- info("create module pages");
1001
- Link.base = "../";
1002
- Link.baseSymbols = "../" + conf.symbolsDir;
1003
- fs.mkPath(path.join(conf.outdir, conf.modulesDir));
1004
- groupByModule(firstClassSymbols.get()).forEach(function(module) {
1005
- let sOutName = path.join(conf.modulesDir, module.name.replace(/\//g, '_')) + conf.ext;
1006
- processTemplateAndSave("module.html.tmpl", module, sOutName);
1007
- });
1008
- }
1009
-
1010
- // regenerate the index with a different link base, used in the overview pages
1011
- info("create global class/namespace index");
1012
- Link.base = "";
1013
- Link.baseSymbols = conf.symbolsDir;
1014
- publish.header = processTemplate("_header.tmpl", firstClassSymbols);
1015
- publish.footer = processTemplate("_footer.tmpl", firstClassSymbols);
1016
- publish.classesIndex = processTemplate("_navIndex.tmpl", firstClassSymbols);
1017
-
1018
- // create the all classes index
1019
- processTemplateAndSave("index.html.tmpl", firstClassSymbols, "index" + conf.ext);
1020
-
1021
- // create the class hierarchy page
1022
- if ( conf.hierarchyIndex ) {
1023
- info("create class hierarchy index");
1024
- Link.base = "";
1025
- Link.baseSymbols = conf.symbolsDir;
1026
- processTemplateAndSave("hierarchy.html.tmpl", hierarchyRoots.filter(conf.filter), "hierarchy" + conf.ext);
1027
- }
1028
-
1029
- if ( conf.sinceIndex ) {
1030
- info("create API by version index");
1031
- Link.base = "";
1032
- Link.baseSymbols = conf.symbolsDir;
1033
- let sinceSymbols = symbols(function() {
1034
- let r = !!this.since && !this.inherited && conf.filter(this);
1035
- if ( r && this.memberof ) {
1036
- let parent = lookup(this.memberof);
1037
- // filter out symbol when parent is filtered out
1038
- if ( !parent || !conf.filter(parent) ) {
1039
- debug(`since index: filtering out ${this.longname}, member of ${this.memberof}`);
1040
- r = false;
1041
- }
1042
- if ( parent && parent.since === this.since ) {
1043
- // r = false;
1044
- }
1045
- }
1046
- return r;
1047
- }).order("longname");
1048
- processTemplateAndSave("since.html.tmpl", sinceSymbols, "since" + conf.ext);
1049
- }
1050
-
1051
- if ( conf.deprecationIndex ) {
1052
- info("create deprecated API index");
1053
- Link.base = "";
1054
- Link.baseSymbols = conf.symbolsDir;
1055
- let deprecatedSymbols = symbols(function() {
1056
- return !!this.deprecated && !this.inherited && conf.filter(this);
1057
- }).order("longname");
1058
- processTemplateAndSave("deprecation.html.tmpl", deprecatedSymbols, "deprecation" + conf.ext);
1059
- }
1060
-
1061
- if ( conf.experimentalIndex ) {
1062
- info("create experimental API index");
1063
- Link.base = "";
1064
- Link.baseSymbols = conf.symbolsDir;
1065
- let experimentalSymbols = symbols(function() {
1066
- return !!this.experimental && !this.inherited && conf.filter(this);
1067
- }).order("longname");
1068
- processTemplateAndSave("experimental.html.tmpl", experimentalSymbols, "experimental" + conf.ext);
1069
- }
1070
-
1071
- if ( conf.securityIndex ) {
1072
- info("create Security Relevant API index");
1073
-
1074
- let securityRelevantSymbols = {};
1075
- A_SECURITY_TAGS.forEach(function(oTagDef) {
1076
- securityRelevantSymbols[oTagDef.name.toLowerCase()] = { tag : oTagDef, symbols: [] };
1077
- });
1078
- symbols().each(function($) {
1079
- let tags = $.tags;
1080
- if ( !$.inherited && conf.filter($) && tags ) {
1081
- for (let i = 0; i < tags.length; i++) {
1082
- if ( rSecurityTags.test(tags[i].title) ) {
1083
- securityRelevantSymbols[tags[i].title.toLowerCase()].symbols.push({ symbol: $, tag : tags[i]});
1084
- }
1085
- }
1086
- }
1087
- });
1088
-
1089
- Link.base = "";
1090
- Link.baseSymbols = conf.symbolsDir;
1091
- processTemplateAndSave("security.html.tmpl", securityRelevantSymbols, "security" + conf.ext);
1092
- }
1093
-
1094
- firstClassSymbols = null;
1095
-
1096
- // copy needed mimes
1097
- info("copy mimes");
1098
- // copy the template's static files to outdir
1099
- let templatePath = env.opts.template;
1100
- let fromDir = path.join(templatePath, 'static');
1101
- let staticFiles = fs.ls(fromDir, 3);
1102
- staticFiles.forEach(function(fileName) {
1103
- let toDir = fs.toDir( fileName.replace(fromDir, conf.outdir) );
1104
- fs.mkPath(toDir);
1105
- fs.copyFileSync(fileName, toDir);
1106
- });
1107
-
1108
- __uniqueFilenames = null;
1109
-
1110
- info("publishing done.");
1111
- }
1112
-
1113
634
  // ---- helper functions for the templates ----
1114
635
 
1115
636
  function sortByAlias(a, b) {
@@ -1142,27 +663,6 @@ function sortByAlias(a, b) {
1142
663
  return 0;
1143
664
  }
1144
665
 
1145
- /*
1146
- function isNonEmptyNamespace($) {
1147
- return $.isNamespace && (
1148
- ($.properties && $.properties.length > 0) ||
1149
- ($.methods && $.methods.length > 0) ||
1150
- ($.augments && $.augments.length > 0) ||
1151
- ($.children && $.children.length > 0));
1152
- };*/
1153
-
1154
- /* Just the first sentence (up to a full stop). Should not break on dotted variable names. */
1155
- function summarize(desc) {
1156
- if ( desc != null ) {
1157
- desc = String(desc).replace(/\s+/g, ' ').
1158
- replace(/"'/g, '&quot;').
1159
- replace(/^(<\/?p>|<br\/?>|\s)+/, '');
1160
-
1161
- const match = /([\w\W]+?\.)[^a-z0-9_$]/i.exec(desc);
1162
- return match ? match[1] : desc;
1163
- }
1164
- }
1165
-
1166
666
  /* Make a symbol sorter by some attribute. */
1167
667
  function makeSortby(/* fields ...*/) {
1168
668
  const aFields = Array.prototype.slice.apply(arguments),
@@ -1215,339 +715,8 @@ function makeSortby(/* fields ...*/) {
1215
715
  };
1216
716
  }
1217
717
 
1218
- /** Pull in the contents of an external file at the given path. */
1219
-
1220
- function processTemplateAndSave(sTemplateName, oData, sOutputName) {
1221
- let sResult = processTemplate(sTemplateName, oData);
1222
- if ( conf.normalizeWhitespace && /\.html$/.test(sOutputName) ) {
1223
- sResult = normalizeWhitespace(sResult);
1224
- }
1225
- const sOutpath = path.join(conf.outdir, sOutputName);
1226
- try {
1227
- fs.mkPath( path.dirname(sOutpath) );
1228
- fs.writeFileSync(sOutpath, sResult, 'utf8');
1229
- } catch (e) {
1230
- error(`failed to write generated file '${sOutpath}': ${e.message || e}`);
1231
- }
1232
- }
1233
-
1234
- function processTemplate(sTemplateName, data) {
1235
- debug(`processing template '${sTemplateName}' for ${data.longname}`);
1236
-
1237
- let result;
1238
-
1239
- try {
1240
- result = view.render(sTemplateName, {
1241
- asPlainSummary: asPlainSummary,
1242
- bySimpleName: bySimpleName,
1243
- childrenOfKind: childrenOfKind,
1244
- conf: conf,
1245
- data: data,
1246
- getConstructorDescription : getConstructorDescription,
1247
- getNSClass: getNSClass,
1248
- groupByVersion: groupByVersion,
1249
- extractSince: extractSince,
1250
- include: processTemplate,
1251
- Link: Link,
1252
- listTypes: listTypes,
1253
- linkTypes: linkTypes,
1254
- makeExample: makeExample,
1255
- makeLinkList: makeLinkList,
1256
- makeLinkToSymbolFile: makeLinkToSymbolFile,
1257
- makeSignature: makeSignature,
1258
- makeSortby: makeSortby,
1259
- publish : publish,
1260
- formatText: formatText,
1261
- simpleNameOf: simpleNameOf,
1262
- sortByAlias: sortByAlias,
1263
- summarize: summarize,
1264
- Version : Version
1265
- });
1266
- } catch (e) {
1267
- if ( e.source ) {
1268
- const filename = path.join(env.opts.destination, sTemplateName + ".js");
1269
- error(`failed to process template, source written to ${filename}`);
1270
- fs.mkPath(path.dirname(filename));
1271
- fs.writeFileSync(filename, e.source, 'utf8');
1272
- }
1273
- error(`error while processing ${sTemplateName}`);
1274
- throw e;
1275
- }
1276
- debug("processing template done.");
1277
- return result;
1278
- }
1279
-
1280
- function groupByVersion(symbols, extractVersion) {
1281
-
1282
- const map = {};
1283
-
1284
- symbols.forEach((symbol) => {
1285
-
1286
- const version = extractVersion(symbol),
1287
- key = String(version);
1288
-
1289
- if ( !map[key] ) {
1290
- map[key] = {
1291
- version,
1292
- symbols : []
1293
- };
1294
- }
1295
- map[key].symbols.push(symbol);
1296
-
1297
- });
1298
-
1299
- const groups = Object.values(map);
1300
-
1301
- return groups.sort((a,b) => {
1302
- if ( !a.version && b.version ) {
1303
- return -1;
1304
- } else if ( a.version && !b.version ) {
1305
- return 1;
1306
- } else if ( a.version && b.version ) {
1307
- return -a.version.compareTo(b.version);
1308
- }
1309
- return 0;
1310
- });
1311
- }
1312
-
1313
- function groupByModule(symbols) {
1314
-
1315
- const map = {};
1316
-
1317
- function add(key, symbol) {
1318
- if ( !map[key] ) {
1319
- map[key] = {
1320
- name: key,
1321
- symbols : []
1322
- };
1323
- }
1324
- if ( map[key].symbols.indexOf(symbol) < 0 ) {
1325
- map[key].symbols.push(symbol);
1326
- }
1327
- }
1328
-
1329
- symbols.forEach((symbol) => {
1330
-
1331
- const key = symbol.__ui5.module;
1332
-
1333
- if ( key ) {
1334
- add(key, symbol);
1335
- if ( symbol.__ui5.members ) {
1336
- symbol.__ui5.members.forEach(function($) {
1337
- if ( !$.inherited && $.__ui5.module && $.__ui5.module !== key && conf.filter($) ) {
1338
- add($.__ui5.module, $);
1339
- }
1340
- });
1341
- }
1342
- }
1343
-
1344
- });
1345
-
1346
- return Object.values(map);
1347
- }
1348
-
1349
-
1350
- const REGEXP_TAG = /<(\/?(?:[A-Z][A-Z0-9_-]*:)?[A-Z][A-Z0-9_-]*)(?:\s[^>]*)?>/gi;
1351
-
1352
- /**
1353
- * Removes unnecessary whitespace from an HTML document:
1354
- * - if the text between two adjacent HTML tags consists of whitespace only, the whole text is removed
1355
- * - otherwise, any sequence of whitespace in the text is reduced to a single blank
1356
- * - inside a <pre> tag, whitespace is preserved
1357
- *
1358
- * Whitespace inside an element tag is not touched (although it could be normalized as well)
1359
- * @param {string} content raw HTML file
1360
- * @returns {string} HTML file with normalized whitespace
1361
- */
1362
- function normalizeWhitespace(content) {
1363
- let compressed = '',
1364
- preformatted = 0,
1365
- p = 0, m;
1366
-
1367
- REGEXP_TAG.lastIndex = 0;
1368
- while ( (m = REGEXP_TAG.exec(content)) ) {
1369
- if ( m.index > p ) {
1370
- let text = content.slice(p, m.index);
1371
- if ( preformatted ) {
1372
- compressed += text;
1373
- // debug(` '${text}' (preformatted)`);
1374
- } else {
1375
- text = text.replace(/\s+/g,' ');
1376
- if ( text.trim() ) {
1377
- compressed += text;
1378
- }
1379
- // debug(` '${text}' (trimmed)`);
1380
- }
1381
- }
1382
-
1383
- compressed += m[0];
1384
- // debug(` '${m[0]}' (tag)`);
1385
- p = m.index + m[0].length;
1386
-
1387
- if ( /^pre$/i.test(m[1]) ) {
1388
- preformatted++;
1389
- } else if ( /^\/pre$/i.test(m[1]) && preformatted ) {
1390
- preformatted--;
1391
- }
1392
-
1393
- }
1394
-
1395
- if ( content.length > p ) {
1396
- let text = content.slice(p, content.length);
1397
- if ( preformatted ) {
1398
- compressed += text;
1399
- // debug(` '${text}' (preformatted)`);
1400
- } else {
1401
- text = text.replace(/\s+/g,' ');
1402
- if ( text.trim() ) {
1403
- compressed += text;
1404
- }
1405
- // debug(` '${text}' (trimmed)`);
1406
- }
1407
- }
1408
-
1409
- return compressed;
1410
- }
1411
-
1412
- function makeLinkToSymbolFile(longname) {
1413
- return Link.baseSymbols + __uniqueFilenames[longname] + conf.ext;
1414
- }
1415
-
1416
- function simpleNameOf(longname) {
1417
- longname = String(longname);
1418
- const p = longname.lastIndexOf('.');
1419
- return p < 0 ? longname : longname.slice(p + 1);
1420
- }
1421
-
1422
- function bySimpleName(a,b) {
1423
- if ( a === b ) {
1424
- return 0;
1425
- }
1426
- const simpleA = simpleNameOf(a);
1427
- const simpleB = simpleNameOf(b);
1428
- if ( simpleA === simpleB ) {
1429
- return a < b ? -1 : 1;
1430
- } else {
1431
- return simpleA < simpleB ? -1 : 1;
1432
- }
1433
- }
1434
-
1435
- /* Build output for displaying function parameters. */
1436
- function makeSignature(params) {
1437
- const r = ['('];
1438
- if ( params ) {
1439
- for (let i = 0, p; (p = params[i]); i++) {
1440
- // ignore @param tags for 'virtual' params that are used to document members of config-like params
1441
- // (e.g. like "@param param1.key ...")
1442
- if (p.name && p.name.indexOf('.') == -1) {
1443
- if (i > 0) {
1444
- r.push(', ');
1445
- }
1446
-
1447
- r.push('<span');
1448
-
1449
- const types = listTypes(p.type, true);
1450
- const desc = asPlainSummary(p.description);
1451
- if ( desc || types ) {
1452
- r.push(' title="');
1453
- if (types) {
1454
- r.push('(');
1455
- r.push(types);
1456
- r.push(') ');
1457
- }
1458
- r.push(desc);
1459
- r.push('"');
1460
- }
1461
-
1462
- r.push('>');
1463
- r.push(p.name);
1464
- r.push('</span>');
1465
- if ( p.optional ) {
1466
- r.push('<i class="help" title="Optional parameter">?</i>');
1467
- }
1468
- }
1469
- }
1470
- }
1471
- r.push(')');
1472
- return r.join('');
1473
- }
1474
-
1475
-
1476
- /*
1477
- * regexp to recognize important places in the text
1478
- *
1479
- * Capturing groups of the RegExp:
1480
- * group 1: begin of a pre block
1481
- * group 2: end of a pre block
1482
- * group 3: begin of a header/ul/ol/table, implicitly ends a paragraph
1483
- * group 4: end of a header/ul/ol/table, implicitly starts a new paragraph
1484
- * group 5: target portion of an inline @link tag
1485
- * group 6: (optional) text portion of an inline link tag
1486
- * group 7: an empty line which implicitly starts a new paragraph
1487
- *
1488
- * [------- <pre> block -------] [----------------------- some flow content -----------------------] [---- an inline {@link ...} tag ----] [---------- an empty line ---------] */
1489
- const rFormatText = /(<pre(?:\s[^>]*)?>)|(<\/pre>)|(<(?:h[\d+]|ul|ol|table)(?:\s[^>]*)?>)|(<\/(?:h[\d+]|ul|ol|table)>)|\{@link\s+([^}\s]+)(?:\s+([^\}]*))?\}|((?:\r\n|\r|\n)[ \t]*(?:\r\n|\r|\n))/gi;
1490
-
1491
- function formatText(text) {
1492
-
1493
- if ( !text ) {
1494
- return '';
1495
- }
1496
-
1497
- let inpre = false,
1498
- paragraphs = 0;
1499
-
1500
- text = String(text).replace(rFormatText, function(match, pre, endpre, flow, endflow, linkTarget, linkText, emptyline) {
1501
- if ( pre ) {
1502
- inpre = true;
1503
- return pre.replace(/<pre>/gi, "<pre class=\"prettyprint\">").replace(/<pre\s+lang="([^"]+)"\s*>/gi, "<pre class=\"prettyprint lang-$1\">");
1504
- } else if ( endpre ) {
1505
- inpre = false;
1506
- } else if ( flow ) {
1507
- if ( !inpre ) {
1508
- paragraphs++;
1509
- return '</p>' + match;
1510
- }
1511
- } else if ( endflow ) {
1512
- if ( !inpre ) {
1513
- paragraphs++;
1514
- return match + '<p>';
1515
- }
1516
- } else if ( emptyline ) {
1517
- if ( !inpre ) {
1518
- paragraphs++;
1519
- return '</p><p>';
1520
- }
1521
- } else if ( linkTarget ) {
1522
- if ( !inpre ) {
1523
- // convert to a hyperlink
1524
- let link = new Link().toSymbol(linkTarget);
1525
- // if link tag contained a replacement text, use it
1526
- if ( linkText && linkText.trim()) {
1527
- link = link.withText(linkText.trim());
1528
- }
1529
- return link.toString();
1530
- }
1531
- }
1532
- return match;
1533
- });
1534
-
1535
- if ( paragraphs > 0 ) {
1536
- text = '<p>' + text + '</p>';
1537
- }
1538
-
1539
- // remove empty paragraphs
1540
- text = text.replace(/<p>\s*<\/p>/g, '');
1541
-
1542
- return text;
1543
- }
1544
-
1545
-
1546
- function childrenOfKind(data, kind) {
1547
- let oResult = {
1548
- own: [],
1549
- borrowed: []
1550
- };
718
+ function getMembersOfKind(data, kind) {
719
+ let oResult = [];
1551
720
  //debug(`calculating kind ${kind} from ${data.longname}`);
1552
721
  //console.log(data);
1553
722
  let fnFilter;
@@ -1575,86 +744,15 @@ function childrenOfKind(data, kind) {
1575
744
 
1576
745
  if ( data.__ui5.members ) {
1577
746
  data.__ui5.members.forEach(function($) {
1578
- if ( fnFilter($) && conf.filter($) ) {
1579
- oResult[$.inherited ? 'borrowed' : 'own'].push($);
747
+ if ( !$.inherited && fnFilter($) && conf.filter($) ) {
748
+ oResult.push($);
1580
749
  }
1581
750
  });
1582
751
  }
1583
- oResult.own.sort(makeSortby("!deprecated","static","name"));
1584
- oResult.borrowed = groupByContributors(data, oResult.borrowed);
1585
752
 
1586
753
  return oResult;
1587
754
  }
1588
755
 
1589
- /**
1590
- * Determines the set of contributors of the given borrowed members.
1591
- * The contributors are sorted according to the inheritance hierarchy:
1592
- * first the base class of symbol, then the base class of the base class etc.
1593
- * Any contributors that can not be found in the hierarchy are appended
1594
- * to the set.
1595
- *
1596
- * @param {Symbol} symbol of which these are the members
1597
- * @param {array} aBorrowedMembers set of borrowed members to determine the contributors for
1598
- * @return {array} sorted array of contributors
1599
- */
1600
- function groupByContributors(symbol, aBorrowedMembers) {
1601
-
1602
- let MAX_ORDER = 1000, // a sufficiently large number
1603
- mContributors = {},
1604
- aSortedContributors = [];
1605
-
1606
- aBorrowedMembers.forEach(function(borrowed) {
1607
- let $ = lookup(borrowed.inherits);
1608
- if ($) {
1609
- if (mContributors[$.memberof] == null) {
1610
- mContributors[$.memberof] = { order : MAX_ORDER, items : [$] };
1611
- } else {
1612
- mContributors[$.memberof].items.push($);
1613
- }
1614
- } else {
1615
- future(`symbol '${borrowed.longname}' has 'inherited' flag set, but inherits missing symbol '${borrowed.inherits}' (skipped)`);
1616
- }
1617
- });
1618
-
1619
- // order contributors according to their distance in the inheritance hierarchy
1620
- let order = 0;
1621
- (function handleAugments(oSymbol) {
1622
- if ( oSymbol.augments ) {
1623
- const aParentsToVisit = [];
1624
- // first assign an order
1625
- for (let i = 0; i < oSymbol.augments.length; i++) {
1626
- if ( mContributors[oSymbol.augments[i]] != null && mContributors[oSymbol.augments[i]].order === MAX_ORDER ) {
1627
- mContributors[oSymbol.augments[i]].order = ++order;
1628
- aParentsToVisit.push(oSymbol.augments[i]);
1629
- }
1630
- }
1631
- // only then dive into parents (breadth first search)
1632
- for (let i = 0; i < aParentsToVisit.length; i++) {
1633
- const oTarget = lookup(aParentsToVisit);
1634
- if ( oTarget ) {
1635
- handleAugments(oTarget);
1636
- }
1637
- }
1638
- }
1639
- }(symbol));
1640
-
1641
- // convert to an array and sort by order
1642
- for (let i in mContributors) {
1643
- aSortedContributors.push(mContributors[i]);
1644
- }
1645
- aSortedContributors.sort((a,b) => a.order - b.order);
1646
-
1647
- return aSortedContributors;
1648
-
1649
- }
1650
-
1651
- function makeLinkList(aSymbols) {
1652
- return aSymbols
1653
- .sort(makeSortby("name"))
1654
- .map(($) => new Link().toSymbol($.longname).withText($.name))
1655
- .join(", ");
1656
- }
1657
-
1658
756
  // ---- type parsing ---------------------------------------------------------------------------------------------
1659
757
 
1660
758
  class ASTBuilder {
@@ -1730,6 +828,7 @@ class ASTBuilder {
1730
828
  }
1731
829
  optional(type) {
1732
830
  type.optional = true;
831
+ return type;
1733
832
  }
1734
833
  repeatable(type) {
1735
834
  type.repeatable = true;
@@ -1809,10 +908,18 @@ class TypeStringBuilder {
1809
908
  structure(structure) {
1810
909
  const r = [];
1811
910
  for ( let fieldName in structure ) {
1812
- if ( structure[fieldName].synthetic ) {
911
+ const typeOfField = structure[fieldName];
912
+ // This builder is called bottom up. Therefore, an optional field
913
+ // has been encoded as a trailing "=" in typeOfField.str already.
914
+ // But for structures, the "=" must be added to the field name instead.
915
+ if ( typeOfField.str.endsWith("=") ) {
916
+ fieldName += "=";
917
+ typeOfField.str = typeOfField.str.slice(0, -1);
918
+ }
919
+ if ( typeOfField.synthetic ) {
1813
920
  r.push(fieldName);
1814
921
  } else {
1815
- r.push(fieldName + ":" + this.safe(structure[fieldName]));
922
+ r.push(fieldName + ":" + this.safe(typeOfField));
1816
923
  }
1817
924
  }
1818
925
  return {
@@ -1957,7 +1064,7 @@ function TypeParser(defaultBuilder = new ASTBuilder()) {
1957
1064
  }
1958
1065
  const optional = token === "=";
1959
1066
  if ( optional ) {
1960
- builder.optional(paramType);
1067
+ paramType = builder.optional(paramType);
1961
1068
  next();
1962
1069
  }
1963
1070
  paramTypes.push(paramType);
@@ -1996,12 +1103,19 @@ function TypeParser(defaultBuilder = new ASTBuilder()) {
1996
1103
  }
1997
1104
  next("symbol");
1998
1105
  let propType;
1106
+ const optional = token === "=";
1107
+ if ( optional ) {
1108
+ next();
1109
+ }
1999
1110
  if ( token === ":" ) {
2000
1111
  next();
2001
1112
  propType = parseTypes();
2002
1113
  } else {
2003
1114
  propType = builder.synthetic(builder.simpleType("any"));
2004
1115
  }
1116
+ if ( optional ) {
1117
+ propType = builder.optional(propType);
1118
+ }
2005
1119
  structure[propName] = propType;
2006
1120
  if ( token === "}" ) {
2007
1121
  break;
@@ -2084,32 +1198,8 @@ function TypeParser(defaultBuilder = new ASTBuilder()) {
2084
1198
  };
2085
1199
  }
2086
1200
 
2087
- class TypeLinkBuilder extends TypeStringBuilder {
2088
- constructor(style, encoded) {
2089
- super();
2090
- this.linkStyle = style;
2091
- this.lt = encoded ? "&lt;" : "<";
2092
- this.gt = encoded ? "&gt;" : ">";
2093
- }
2094
- simpleType(type) {
2095
- if ( this.linkStyle === 'text' ) {
2096
- return super.simpleType(type);
2097
- }
2098
- const link = new Link().toSymbol(type);
2099
- if ( this.linkStyle === 'short' ) {
2100
- link.withText(simpleNameOf(type)).withTooltip(type);
2101
- }
2102
- return {
2103
- str: link.toString()
2104
- };
2105
- }
2106
- }
2107
-
2108
1201
  const typeParser = new TypeParser();
2109
1202
  const _TEXT_BUILDER = new TypeStringBuilder();
2110
- const _TEXT_BUILDER_ENCODED = new TypeLinkBuilder('text', true);
2111
- const _SHORT_BUILDER = new TypeLinkBuilder('short', true);
2112
- const _LONG_BUILDER = new TypeLinkBuilder('long', true);
2113
1203
 
2114
1204
  /*
2115
1205
  function testTypeParser(type) {
@@ -2143,12 +1233,8 @@ function _processTypeString(type, builder) {
2143
1233
  }
2144
1234
  }
2145
1235
 
2146
- function listTypes(type, encoded) {
2147
- return _processTypeString(type, encoded ? _TEXT_BUILDER_ENCODED : _TEXT_BUILDER);
2148
- }
2149
-
2150
- function linkTypes(type, short) {
2151
- return _processTypeString(type, short ? _SHORT_BUILDER : _LONG_BUILDER );
1236
+ function listTypes(type) {
1237
+ return _processTypeString(type, _TEXT_BUILDER);
2152
1238
  }
2153
1239
 
2154
1240
  function isArrayType(type) {
@@ -2166,31 +1252,6 @@ function isArrayType(type) {
2166
1252
  return false;
2167
1253
  }
2168
1254
 
2169
- /**
2170
- * Reduces the given text to a summary and removes all tags links etc. and escapes double quotes.
2171
- * The result therefore should be suitable as content for an HTML tag attribute (e.g. title).
2172
- *
2173
- * @param {string} sText Text to extract a summary from
2174
- * @returns {string} summarized, plain attribute
2175
- */
2176
- function asPlainSummary(sText) {
2177
- return sText ? summarize(sText).replace(/<.*?>/g, '').replace(/\{\@link\s*(.*?)\}/g, '$1').replace(/"/g,"&quot;") : '';
2178
- }
2179
-
2180
- function getNSClass(item) {
2181
- if (item.kind === 'interface') {
2182
- return " interface";
2183
- } else if (item.kind === 'namespace') {
2184
- return " namespace";
2185
- } else if (item.kind === 'typedef' ) {
2186
- return " typedef";
2187
- } else if (item.kind === 'member' && item.isEnum ) {
2188
- return " enum";
2189
- } else {
2190
- return "";
2191
- }
2192
- }
2193
-
2194
1255
  function normalizeLF(text) {
2195
1256
  if ( text == null ) {
2196
1257
  return text;
@@ -3126,7 +2187,7 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
3126
2187
  }
3127
2188
 
3128
2189
  if ( !skipMembers ) {
3129
- const ownProperties = childrenOfKind(symbol, "property").own.sort(sortByAlias);
2190
+ const ownProperties = getMembersOfKind(symbol, "property").sort(sortByAlias);
3130
2191
  if ( ownProperties.length > 0 ) {
3131
2192
  collection("properties");
3132
2193
  for ( let i = 0; i < ownProperties.length; i++ ) {
@@ -3159,7 +2220,7 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
3159
2220
  endCollection("properties");
3160
2221
  }
3161
2222
 
3162
- const ownEvents = childrenOfKind(symbol, 'event').own.sort(sortByAlias);
2223
+ const ownEvents = getMembersOfKind(symbol, 'event').sort(sortByAlias);
3163
2224
  if ( ownEvents.length > 0 ) {
3164
2225
  collection("events");
3165
2226
  for (let i = 0; i < ownEvents.length; i++ ) {
@@ -3209,7 +2270,7 @@ function createAPIJSON4Symbol(symbol, omitDefaults) {
3209
2270
  endCollection("events");
3210
2271
  }
3211
2272
 
3212
- const ownMethods = childrenOfKind(symbol, 'method').own.sort(sortByAlias);
2273
+ const ownMethods = getMembersOfKind(symbol, 'method').sort(sortByAlias);
3213
2274
  // xmlmacro stereotype does not allow methods
3214
2275
  if ( symbol.__ui5.stereotype !== 'xmlmacro' && ownMethods.length > 0 ) {
3215
2276
  collection("methods");
@@ -3738,7 +2799,7 @@ function validateAPIJSON(api) {
3738
2799
  }
3739
2800
  });
3740
2801
  }
3741
- if ( Object.prototype.hasOwnProperty.call(symbol, "constructor") ) {
2802
+ if ( Object.hasOwn(symbol, "constructor") ) {
3742
2803
  checkMethodSignature(symbol.constructor, symbol.name + ".constructor");
3743
2804
  }
3744
2805
  if ( symbol.properties ) {
@@ -3786,7 +2847,7 @@ function validateAPIJSON(api) {
3786
2847
 
3787
2848
  Object.keys(missing).forEach((type) => {
3788
2849
  if ( Array.isArray(missing[type]) ) {
3789
- if ( hasOwn(erroneousTypes, type.toLowerCase()) ) {
2850
+ if ( Object.hasOwn(erroneousTypes, type.toLowerCase()) ) {
3790
2851
  error(`type '${type}' (use '${erroneousTypes[type.toLowerCase()]}' instead)`);
3791
2852
  missing[type].forEach((usage) => error(` ${usage}`));
3792
2853
  } else {
@@ -4628,223 +3689,6 @@ function createAPIXML(symbols, filename, options = {}) {
4628
3689
  fs.writeFileSync(filename, getAsString(), 'utf8');
4629
3690
  }
4630
3691
 
4631
- //---- add on: API JS -----------------------------------------------------------------
4632
-
4633
- function createAPIJS(symbols, filename) {
4634
-
4635
- const REGEXP_ARRAY_TYPE = /^Array\.<(.*)>$/;
4636
-
4637
- const rkeywords = /^(?:abstract|as|boolean|break|byte|case|catch|char|class|continue|const|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|is|long|namespace|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|use|var|void|volatile|while|with)$/;
4638
-
4639
- const output = [];
4640
-
4641
- function isNoKeyword($) { return !rkeywords.test($.name); }
4642
-
4643
- function isAPI($) { return $.access === 'public' || $.access === 'protected' || !$.access; }
4644
-
4645
- function writeln(args) {
4646
- if ( arguments.length ) {
4647
- for (let i = 0; i < arguments.length; i++) {
4648
- output.push(arguments[i]);
4649
- }
4650
- }
4651
- output.push("\n");
4652
- }
4653
-
4654
- function unwrap(docletSrc) {
4655
- if (!docletSrc) {
4656
- return '';
4657
- }
4658
-
4659
- // note: keep trailing whitespace for @examples
4660
- // extra opening/closing stars are ignored
4661
- // left margin is considered a star and a space
4662
- // use the /m flag on regex to avoid having to guess what this platform's newline is
4663
- docletSrc =
4664
- docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
4665
- .replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker
4666
- .replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
4667
- .replace(/\s*\\Z$/g, ''); // remove end-marker
4668
-
4669
- return docletSrc;
4670
- }
4671
-
4672
- function comment($, sMetaType) {
4673
-
4674
- let s = unwrap($.comment.toString());
4675
-
4676
- // remove the @desc tag
4677
- s = s.replace(/(\r\n|\r|\n)/gm, "\n");
4678
- s = s.replace(/^\s*@desc\s*/gm, "");
4679
- s = s.replace(/^\s*@alias[^\r\n]*(\r\n|\r|\n)?/gm, "");
4680
- s = s.replace(/^\s*@name[^\r\n]*(\r\n|\r|\n)?/gm, "");
4681
- s = s.replace(/^\s*@function[^\r\n]*(\r\n|\r|\n)?/gm, "");
4682
- s = s.replace(/^\s*@author[^\r\n]*(\r\n|\r|\n)?/gm, "");
4683
- s = s.replace(/^\s*@synthetic[^\r\n]*(\r\n|\r|\n)?/gm, "");
4684
- s = s.replace(/^\s*<\/p><p>\s*(\r\n|\r|\n)?/gm, "\n");
4685
- // skip empty documentation
4686
- if ( !s ) {
4687
- return;
4688
- }
4689
-
4690
- // for namespaces, enforce the @.memberof tag
4691
- if ( sMetaType === "namespace" && $.memberof && s.indexOf("@memberof") < 0 ) {
4692
- s = s + "\n@memberof " + $.memberof;
4693
- }
4694
-
4695
- writeln("/**\n * " + s.replace(/\n/g, "\n * ") + "\n */");
4696
-
4697
- /*
4698
- writeln("/**");
4699
- writeln(s.split(/\r\n|\r|\n/g).map(function($) { return " * " + $;}).join("\r\n"));
4700
- writeln(" * /");
4701
- */
4702
-
4703
- }
4704
-
4705
- function signature($) {
4706
- const p = $.params, r = [];
4707
- if ( p ) {
4708
- for (let i = 0; i < p.length; i++) {
4709
- // ignore @param tags for 'virtual' params that are used to document members of config-like params
4710
- // (e.g. like "@param param1.key ...")
4711
- if (p[i].name && p[i].name.indexOf('.') < 0) {
4712
- r.push(p[i].name);
4713
- }
4714
- }
4715
- }
4716
- return r.join(',');
4717
- }
4718
-
4719
- function qname(member,parent) {
4720
- let r = member.memberof;
4721
- if ( member.scope !== 'static' ) {
4722
- r += ".prototype";
4723
- }
4724
- return (r ? r + "." : "") + member.name;
4725
- }
4726
-
4727
- const mValues = {
4728
- "boolean" : "false",
4729
- "int" : "0",
4730
- "float" : "0.0",
4731
- "number" : "0.0",
4732
- "string" : "\"\"",
4733
- "object" : "new Object()",
4734
- "function" : "function() {}"
4735
- };
4736
-
4737
- function valueForType(type) {
4738
- if ( type && type.names && type.names[0] ) {
4739
- type = type.names[0];
4740
- if ( REGEXP_ARRAY_TYPE.test(type) || type.indexOf("[]") > 0 ) {
4741
- return "new Array()";
4742
- } else if ( mValues[type] ) {
4743
- return mValues[type];
4744
- } else if ( type.indexOf(".") > 0 ) {
4745
- return "new " + type + "()";
4746
- } else {
4747
- // return "/* unsupported type: " + member.type + " */ null";
4748
- return "null";
4749
- }
4750
- }
4751
- }
4752
-
4753
- function value(member) {
4754
- return valueForType(member.type);
4755
- }
4756
-
4757
- function retvalue(member) {
4758
- //debug(member);
4759
- const r = valueForType(member.type || (member.returns && member.returns.length && member.returns[0] && member.returns[0].type && member.returns[0].type));
4760
- if ( r ) {
4761
- return "return " + r + ";";
4762
- }
4763
- return "";
4764
- }
4765
-
4766
- const sortedSymbols = symbols.slice().filter(($) => isaClass($) && isAPI($) && !$.synthetic).sort(sortByAlias); // sort only a copy(!) of the symbols, otherwise the SymbolSet lookup is broken
4767
- sortedSymbols.forEach((symbol) => {
4768
-
4769
- const sMetaType = (symbol.kind === 'member' && symbol.isEnum) ? 'enum' : symbol.kind;
4770
- if ( sMetaType ) {
4771
-
4772
- writeln("");
4773
- writeln("// ---- " + symbol.longname + " --------------------------------------------------------------------------");
4774
- writeln("");
4775
-
4776
- const ownProperties = childrenOfKind(symbol, 'property').own.filter(isNoKeyword).sort(sortByAlias);
4777
- if ( sMetaType === "class" ) {
4778
- comment(symbol, sMetaType);
4779
- writeln(symbol.longname + " = function(" + signature(symbol) + ") {};");
4780
- for ( let memberId in ownProperties ) {
4781
- const member = ownProperties[memberId];
4782
- comment(member, sMetaType);
4783
- writeln(qname(member, symbol) + " = " + value(member));
4784
- writeln("");
4785
- }
4786
- } else if ( sMetaType === 'namespace' || sMetaType === 'enum' ) {
4787
- //debug(`found namespace ${symbol.longname}`);
4788
- //debug(ownProperties);
4789
- if ( ownProperties.length ) {
4790
- writeln("// dummy function to make Eclipse aware of namespace");
4791
- writeln(symbol.longname + ".toString = function() { return \"\"; };");
4792
- }
4793
- }
4794
-
4795
- const ownEvents = childrenOfKind(symbol, 'event').own.filter(isNoKeyword).sort(sortByAlias);
4796
- if ( ownEvents.length ) {
4797
- for ( let memberId in ownEvents ) {
4798
- const member = ownEvents[memberId];
4799
- comment(member, sMetaType);
4800
- writeln(qname(member, symbol) + " = function(" + signature(member) + ") { " + retvalue(member) + " };");
4801
- writeln("");
4802
- }
4803
- }
4804
-
4805
- const ownMethods = childrenOfKind(symbol, 'method').own.filter(isNoKeyword).sort(sortByAlias);
4806
- if ( ownMethods.length ) {
4807
- for (let memberId in ownMethods ) {
4808
- const member = ownMethods[memberId];
4809
- comment(member, sMetaType);
4810
- writeln(qname(member, symbol) + " = function(" + signature(member) + ") { " + retvalue(member) + " };");
4811
- writeln("");
4812
- }
4813
- }
4814
-
4815
- }
4816
- });
4817
-
4818
- writeln("// ---- static fields of namespaces ---------------------------------------------------------------------");
4819
-
4820
- sortedSymbols.forEach((symbol) => {
4821
-
4822
- const sMetaType = (symbol.kind === 'member' && symbol.isEnum) ? 'enum' : symbol.kind;
4823
-
4824
- if ( sMetaType === 'namespace' || sMetaType === 'enum' ) {
4825
-
4826
- const ownProperties = childrenOfKind(symbol, 'property').own.filter(isNoKeyword).sort(sortByAlias);
4827
- if ( ownProperties.length ) {
4828
- writeln("");
4829
- writeln("// ---- " + symbol.longname + " --------------------------------------------------------------------------");
4830
- writeln("");
4831
-
4832
- for (let memberId in ownProperties ) {
4833
- const member = ownProperties[memberId];
4834
- comment(member, sMetaType);
4835
- writeln(qname(member, symbol) + " = " + value(member) + ";");
4836
- writeln("");
4837
- }
4838
- }
4839
- }
4840
-
4841
- });
4842
-
4843
- fs.mkPath(path.dirname(filename));
4844
- fs.writeFileSync(filename, output.join(""), 'utf8');
4845
- info(` saved as ${filename}`);
4846
- }
4847
-
4848
3692
  // Description + Settings
4849
3693
 
4850
3694
  function getConstructorDescription(symbol) {