@ui5/builder 3.0.0-alpha.9 → 3.0.0-beta.2

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.
Files changed (83) hide show
  1. package/.reuse/dep5 +2 -2
  2. package/CHANGELOG.md +59 -1
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +4 -4
  5. package/jsdoc.json +2 -2
  6. package/lib/lbt/UI5ClientConstants.js +7 -8
  7. package/lib/lbt/analyzer/ComponentAnalyzer.js +60 -22
  8. package/lib/lbt/analyzer/FioriElementsAnalyzer.js +31 -20
  9. package/lib/lbt/analyzer/JSModuleAnalyzer.js +119 -77
  10. package/lib/lbt/analyzer/SmartTemplateAnalyzer.js +31 -20
  11. package/lib/lbt/analyzer/XMLCompositeAnalyzer.js +35 -25
  12. package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +71 -18
  13. package/lib/lbt/analyzer/analyzeLibraryJS.js +22 -6
  14. package/lib/lbt/bundle/AutoSplitter.js +10 -11
  15. package/lib/lbt/bundle/Builder.js +33 -36
  16. package/lib/lbt/bundle/BundleDefinition.js +1 -5
  17. package/lib/lbt/bundle/BundleWriter.js +1 -2
  18. package/lib/lbt/bundle/ResolvedBundleDefinition.js +6 -7
  19. package/lib/lbt/bundle/Resolver.js +11 -12
  20. package/lib/lbt/calls/SapUiDefine.js +16 -11
  21. package/lib/lbt/graph/dependencyGraph.js +3 -4
  22. package/lib/lbt/graph/dominatorTree.js +3 -3
  23. package/lib/lbt/graph/topologicalSort.js +3 -3
  24. package/lib/lbt/resources/LibraryFileAnalyzer.js +6 -9
  25. package/lib/lbt/resources/LocatorResource.js +2 -2
  26. package/lib/lbt/resources/LocatorResourcePool.js +3 -3
  27. package/lib/lbt/resources/ModuleInfo.js +2 -3
  28. package/lib/lbt/resources/Resource.js +3 -4
  29. package/lib/lbt/resources/ResourceCollector.js +8 -9
  30. package/lib/lbt/resources/ResourceFilterList.js +38 -40
  31. package/lib/lbt/resources/ResourceInfo.js +1 -1
  32. package/lib/lbt/resources/ResourceInfoList.js +5 -3
  33. package/lib/lbt/resources/ResourcePool.js +15 -21
  34. package/lib/lbt/utils/ASTUtils.js +60 -44
  35. package/lib/lbt/utils/JSTokenizer.js +4 -4
  36. package/lib/lbt/utils/ModuleName.js +10 -20
  37. package/lib/lbt/utils/escapePropertiesFile.js +4 -4
  38. package/lib/lbt/utils/parseUtils.js +5 -11
  39. package/lib/processors/bootstrapHtmlTransformer.js +15 -7
  40. package/lib/processors/bundlers/flexChangesBundler.js +16 -8
  41. package/lib/processors/bundlers/moduleBundler.js +26 -15
  42. package/lib/processors/jsdoc/apiIndexGenerator.js +16 -9
  43. package/lib/processors/jsdoc/jsdocGenerator.js +37 -25
  44. package/lib/processors/jsdoc/lib/{createIndexFiles.js → createIndexFiles.cjs} +0 -0
  45. package/lib/processors/jsdoc/lib/{transformApiJson.js → transformApiJson.cjs} +59 -2
  46. package/lib/processors/jsdoc/lib/ui5/{plugin.js → plugin.cjs} +310 -119
  47. package/lib/processors/jsdoc/lib/ui5/template/{publish.js → publish.cjs} +6 -2
  48. package/lib/processors/jsdoc/lib/ui5/template/utils/{versionUtil.js → versionUtil.cjs} +0 -0
  49. package/lib/processors/jsdoc/sdkTransformer.js +18 -11
  50. package/lib/processors/libraryLessGenerator.js +28 -16
  51. package/lib/processors/manifestCreator.js +15 -14
  52. package/lib/processors/minifier.js +22 -16
  53. package/lib/processors/nonAsciiEscaper.js +17 -7
  54. package/lib/processors/resourceListCreator.js +13 -13
  55. package/lib/processors/stringReplacer.js +13 -6
  56. package/lib/processors/themeBuilder.js +33 -24
  57. package/lib/processors/versionInfoGenerator.js +40 -30
  58. package/lib/tasks/buildThemes.js +18 -11
  59. package/lib/tasks/bundlers/generateBundle.js +23 -12
  60. package/lib/tasks/bundlers/generateComponentPreload.js +23 -12
  61. package/lib/tasks/bundlers/generateFlexChangesBundle.js +16 -8
  62. package/lib/tasks/bundlers/generateLibraryPreload.js +33 -17
  63. package/lib/tasks/bundlers/generateStandaloneAppBundle.js +21 -13
  64. package/lib/tasks/bundlers/utils/createModuleNameMapping.js +7 -7
  65. package/lib/tasks/escapeNonAsciiCharacters.js +12 -5
  66. package/lib/tasks/generateCachebusterInfo.js +17 -9
  67. package/lib/tasks/generateLibraryManifest.js +14 -8
  68. package/lib/tasks/generateResourcesJson.js +15 -9
  69. package/lib/tasks/generateThemeDesignerResources.js +19 -9
  70. package/lib/tasks/generateVersionInfo.js +12 -5
  71. package/lib/tasks/jsdoc/executeJsdocSdkTransformation.js +17 -9
  72. package/lib/tasks/jsdoc/generateApiIndex.js +16 -10
  73. package/lib/tasks/jsdoc/generateJsdoc.js +131 -119
  74. package/lib/tasks/minify.js +13 -6
  75. package/lib/tasks/replaceBuildtime.js +12 -5
  76. package/lib/tasks/replaceCopyright.js +12 -5
  77. package/lib/tasks/replaceVersion.js +12 -5
  78. package/lib/tasks/taskRepository.js +24 -30
  79. package/lib/tasks/transformBootstrapHtml.js +6 -5
  80. package/package.json +44 -31
  81. package/index.js +0 -173
  82. package/lib/processors/bundlers/manifestBundler.js +0 -172
  83. package/lib/tasks/bundlers/generateManifestBundle.js +0 -49
@@ -53,7 +53,14 @@
53
53
  */
54
54
 
55
55
  /* imports */
56
- const Syntax = require('jsdoc/src/syntax').Syntax;
56
+ const Syntax = {
57
+ // Those are currently not in the syntax.js file.
58
+ ChainExpression: "ChainExpression",
59
+ OptionalMemberExpression: "OptionalMemberExpression",
60
+ OptionalCallExpression: "OptionalCallExpression",
61
+
62
+ ...require("jsdoc/src/syntax").Syntax,
63
+ };
57
64
  const Doclet = require('jsdoc/doclet').Doclet;
58
65
  const fs = require('jsdoc/fs');
59
66
  const path = require('jsdoc/path');
@@ -234,9 +241,8 @@ function resolveModuleName(base, name) {
234
241
  function analyzeModuleDefinition(node) {
235
242
  const args = node.arguments;
236
243
  let arg = 0;
237
- if ( arg < args.length
238
- && args[arg].type === Syntax.Literal && typeof args[arg].value === 'string' ) {
239
- currentModule.name = args[arg].value;
244
+ if ( arg < args.length && isStringLiteral(args[arg]) ) {
245
+ currentModule.name = convertValue(args[arg]);
240
246
  warning(`module explicitly defined a module name '${currentModule.name}'`);
241
247
  const resourceModuleName = getModuleName(currentModule.resource);
242
248
  if (currentModule.name !== resourceModuleName) {
@@ -249,13 +255,19 @@ function analyzeModuleDefinition(node) {
249
255
  currentModule.dependencies = convertValue(args[arg], "string[]");
250
256
  arg++;
251
257
  }
252
- if ( arg < args.length && args[arg].type === Syntax.FunctionExpression ) {
258
+ if ( arg < args.length &&
259
+ [Syntax.FunctionExpression, Syntax.ArrowFunctionExpression].includes(args[arg].type)) {
260
+
253
261
  currentModule.factory = args[arg];
254
262
  arg++;
255
263
  }
256
264
  if ( currentModule.dependencies && currentModule.factory ) {
257
265
  for ( let i = 0; i < currentModule.dependencies.length && i < currentModule.factory.params.length; i++ ) {
258
- const name = currentModule.factory.params[i].name;
266
+ const name =
267
+ (currentModule.factory.params[i].type === Syntax.ObjectPattern)
268
+ ? currentModule.factory.params[i].properties[0].value.name // ObjectPattern means destructuring of the parameter
269
+ : currentModule.factory.params[i].name; // simple Identifier
270
+
259
271
  const module = resolveModuleName(currentModule.module, currentModule.dependencies[i]);
260
272
  debug(` import ${name} from '${module}'`);
261
273
  currentModule.localNames[name] = {
@@ -265,7 +277,7 @@ function analyzeModuleDefinition(node) {
265
277
  }
266
278
  }
267
279
  if ( currentModule.factory ) {
268
- collectShortcuts(currentModule.factory.body);
280
+ collectShortcuts(currentModule.factory);
269
281
  }
270
282
  }
271
283
 
@@ -276,16 +288,19 @@ function analyzeModuleDefinition(node) {
276
288
  *
277
289
  * @param {ASTNode} body AST node of a function body that shall be searched for shortcuts
278
290
  */
279
- function collectShortcuts(body) {
291
+ function collectShortcuts(factory) {
292
+ const body = factory.body;
280
293
 
281
294
  function checkAssignment(name, valueNode) {
295
+ valueNode = resolvePotentialWrapperExpression(valueNode);
296
+
282
297
  if ( valueNode.type === Syntax.Literal ) {
283
298
  currentModule.localNames[name] = {
284
299
  value: valueNode.value,
285
300
  raw: valueNode.raw
286
301
  };
287
302
  debug("compile time constant found ", name, valueNode.value);
288
- } else if ( valueNode.type === Syntax.MemberExpression ) {
303
+ } else if ( isMemberExpression(valueNode) ) {
289
304
  const _import = getLeftmostName(valueNode);
290
305
  const local = _import && currentModule.localNames[_import];
291
306
  const objectName = getObjectName(valueNode);
@@ -297,11 +312,9 @@ function collectShortcuts(body) {
297
312
  debug(` found local shortcut: ${name} ${currentModule.localNames[name]}`);
298
313
  }
299
314
  } else if ( isRequireSyncCall(valueNode) || isProbingRequireCall(valueNode) ) {
300
- if ( valueNode.arguments[0]
301
- && valueNode.arguments[0].type === Syntax.Literal
302
- && typeof valueNode.arguments[0].value === 'string' ) {
315
+ if ( valueNode.arguments[0] && isStringLiteral(valueNode.arguments[0]) ) {
303
316
  currentModule.localNames[name] = {
304
- module: valueNode.arguments[0].value
317
+ module: convertValue(valueNode.arguments[0])
305
318
  // no (or empty) path
306
319
  };
307
320
  debug(` found local import: ${name} = ${valueNode.callee.property.name}('${valueNode.arguments[0].value}')`);
@@ -315,8 +328,8 @@ function collectShortcuts(body) {
315
328
  }
316
329
  }
317
330
 
318
- if ( body.type === Syntax.BlockStatement ) {
319
- body.body.forEach(function ( stmt ) {
331
+ if ( body.type === Syntax.BlockStatement || isArrowFuncExpression(factory) ) {
332
+ const itemsResolver = function ( stmt ) {
320
333
  // console.log(stmt);
321
334
  if ( stmt.type === Syntax.FunctionDeclaration ) {
322
335
  if ( stmt.id && stmt.id.type === Syntax.Identifier && stmt.loc && stmt.loc.start ) {
@@ -336,15 +349,28 @@ function collectShortcuts(body) {
336
349
  && stmt.expression.type === Syntax.AssignmentExpression
337
350
  && stmt.expression.left.type === Syntax.Identifier ) {
338
351
  checkAssignment(stmt.expression.left.name, stmt.expression.right);
339
- } else if ( stmt.type === Syntax.ReturnStatement ) {
340
- if ( stmt.argument && stmt.argument.type === Syntax.Identifier ) {
341
- currentModule.defaultExport = stmt.argument.name;
342
- } else if ( stmt.argument && isExtendCall(stmt.argument) ) {
343
- debug(` found default export class definition: return .extend('${stmt.argument.arguments[0].value}', ...)`);
344
- currentModule.defaultExportClass = stmt.argument.arguments[0].value;
352
+ } else if ( isReturningNode(stmt) ) {
353
+ const stmtArgument = isArrowFuncExpression(stmt)
354
+ ? stmt.body
355
+ : resolvePotentialWrapperExpression(stmt).argument;
356
+
357
+ if ( stmtArgument && stmtArgument.type === Syntax.Identifier ) {
358
+ currentModule.defaultExport = stmtArgument.name;
359
+ } else if ( stmtArgument && stmtArgument.type === Syntax.ClassExpression && stmtArgument.id && stmtArgument.id.type === Syntax.Identifier ) {
360
+ debug(` found default export class definition: return class '${stmtArgument.id.name}'`);
361
+ currentModule.defaultExportClass = stmtArgument.id.name;
362
+ } else if ( stmtArgument && isExtendCall(stmtArgument) ) {
363
+ debug(` found default export class definition: return .extend('${stmtArgument.arguments[0].value}', ...)`);
364
+ currentModule.defaultExportClass = convertValue(stmtArgument.arguments[0]);
345
365
  }
346
366
  }
347
- });
367
+ };
368
+
369
+ if (isArrowFuncExpression(factory)) {
370
+ itemsResolver(factory);
371
+ } else {
372
+ body.body.forEach(itemsResolver);
373
+ }
348
374
  }
349
375
 
350
376
  if ( currentModule.defaultExport && currentModule.localNames[currentModule.defaultExport] ) {
@@ -369,12 +395,12 @@ function guessSingularName(sPluralName) {
369
395
  }
370
396
 
371
397
  function getPropertyKey(prop) {
372
- if ( prop.key.type === Syntax.Identifier ) {
398
+ if ( prop.type === Syntax.SpreadElement ) {
399
+ return;
400
+ } else if ( prop.key.type === Syntax.Identifier && prop.computed !== true ) {
373
401
  return prop.key.name;
374
402
  } else if ( prop.key.type === Syntax.Literal ) {
375
403
  return String(prop.key.value);
376
- } else {
377
- return prop.key.toSource();
378
404
  }
379
405
  }
380
406
 
@@ -432,80 +458,181 @@ function createPropertyMap(node, defaultKey) {
432
458
  return result;
433
459
  }
434
460
 
461
+ /**
462
+ * Resolves potential wrapper expressions like: ChainExpression, AwaitExpression, etc.
463
+ * @param {Node} node
464
+ * @returns {Node} the resolved node
465
+ */
466
+ function resolvePotentialWrapperExpression(node) {
467
+ switch (node && node.type) {
468
+ case Syntax.ChainExpression:
469
+ return node.expression;
470
+
471
+ case Syntax.AwaitExpression:
472
+ return node.argument;
473
+
474
+ case Syntax.ExpressionStatement:
475
+ if ( node.expression.type === Syntax.YieldExpression ) {
476
+ return node.expression.argument.type === Syntax.UpdateExpression
477
+ ? node.expression.argument
478
+ : node.expression;
479
+ }
480
+ default:
481
+ return node;
482
+ }
483
+ }
484
+
485
+ /**
486
+ * Strips the ChainExpression wrapper if such
487
+ *
488
+ * @param {Node} rootNode
489
+ * @param {String} path
490
+ * @returns {Node}
491
+ */
492
+ function stripChainWrappers(rootNode, path) {
493
+ const strip = (node) =>
494
+ node && node.type === Syntax.ChainExpression ? node.expression : node;
495
+
496
+ let curNode = strip(rootNode);
497
+ let chunks = path && path.split(".");
498
+ let name;
499
+
500
+ while (chunks && chunks.length) {
501
+ name = chunks.shift();
502
+ curNode = curNode && strip(curNode[name]);
503
+ }
504
+
505
+ return curNode;
506
+ }
507
+
508
+ function isTemplateLiteralWithoutExpression(node) {
509
+ return (
510
+ node &&
511
+ node.type === Syntax.TemplateLiteral &&
512
+ node.expressions && node.expressions.length === 0 &&
513
+ node.quasis && node.quasis.length === 1
514
+ );
515
+ }
516
+
517
+ /**
518
+ * Checks whether a node is Literal or TemplateLiteral without an expression
519
+ *
520
+ * @param {Node} node
521
+ * @returns {String}
522
+ */
523
+ function isStringLiteral(node) {
524
+ return (
525
+ (node && node.type === Syntax.Literal && typeof node.value === "string")
526
+ || isTemplateLiteralWithoutExpression(node)
527
+ );
528
+ }
529
+
530
+ function isMemberExpression(node) {
531
+ return node && [Syntax.MemberExpression, Syntax.OptionalMemberExpression].includes(node.type);
532
+ }
533
+
534
+ function isCaleeMemberExpression(node) {
535
+ return (
536
+ node &&
537
+ [Syntax.CallExpression, Syntax.OptionalCallExpression].includes(node.type) &&
538
+ isMemberExpression(node.callee)
539
+ );
540
+ }
541
+
435
542
  function isExtendCall(node) {
543
+ node = stripChainWrappers(node);
436
544
 
437
545
  return (
438
546
  node
439
- && node.type === Syntax.CallExpression
440
- && node.callee.type === Syntax.MemberExpression
441
- && node.callee.property.type === Syntax.Identifier
442
- && node.callee.property.name === 'extend'
547
+ && isCaleeMemberExpression(node)
548
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
549
+ && stripChainWrappers(node, "callee.property").name === 'extend'
443
550
  && node.arguments.length >= 2
444
- && node.arguments[0].type === Syntax.Literal
445
- && typeof node.arguments[0].value === "string"
551
+ && isStringLiteral(node.arguments[0])
446
552
  && node.arguments[1].type === Syntax.ObjectExpression
447
553
  );
448
554
 
449
555
  }
450
556
 
557
+ function isArrowFuncExpression(node) {
558
+ return (
559
+ node &&
560
+ node.type === Syntax.ArrowFunctionExpression &&
561
+ node.expression === true
562
+ );
563
+ }
564
+
565
+ /**
566
+ * Checks whether the node is of a "returning" type
567
+ *
568
+ * @param {Node} node
569
+ * @returns {Boolean}
570
+ */
571
+ function isReturningNode(node) {
572
+ return (node && node.type === Syntax.ReturnStatement)
573
+ || (node && node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.YieldExpression)
574
+ || isArrowFuncExpression(node);
575
+ }
576
+
451
577
  function isSapUiDefineCall(node) {
452
578
 
453
579
  return (
454
- node
455
- && node.type === Syntax.CallExpression
456
- && node.callee.type === Syntax.MemberExpression
457
- && node.callee.object.type === Syntax.MemberExpression
458
- && node.callee.object.object.type === Syntax.Identifier
459
- && node.callee.object.object.name === 'sap'
460
- && node.callee.object.property.type === Syntax.Identifier
461
- && node.callee.object.property.name === 'ui'
462
- && node.callee.property.type === Syntax.Identifier
463
- && node.callee.property.name === 'define'
580
+ stripChainWrappers(node)
581
+ && isCaleeMemberExpression(stripChainWrappers(node))
582
+ && isMemberExpression(stripChainWrappers(node, "callee.object"))
583
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
584
+ && stripChainWrappers(node, "callee.property").name === 'define'
585
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
586
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
587
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
588
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
464
589
  );
465
590
 
466
591
  }
467
592
 
468
593
  function isCreateDataTypeCall(node) {
594
+ node = stripChainWrappers(node);
595
+
469
596
  return (
470
597
  node
471
- && node.type === Syntax.CallExpression
472
- && node.callee.type === Syntax.MemberExpression
473
- && getResolvedObjectName(node.callee.object) === "sap.ui.base.DataType"
474
- && node.callee.property.type === Syntax.Identifier
475
- && node.callee.property.name === 'createType'
598
+ && isCaleeMemberExpression(node)
599
+ && getResolvedObjectName(stripChainWrappers(node, "callee.object")) === "sap.ui.base.DataType"
600
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
601
+ && stripChainWrappers(node, "callee.property").name === 'createType'
476
602
  );
477
603
  }
478
604
 
479
605
  function isRequireSyncCall(node) {
606
+ node = stripChainWrappers(node);
607
+
480
608
  return (
481
609
  node
482
- && node.type === Syntax.CallExpression
483
- && node.callee.type === Syntax.MemberExpression
484
- && node.callee.object.type === Syntax.MemberExpression
485
- && node.callee.object.object.type === Syntax.Identifier
486
- && node.callee.object.object.name === 'sap'
487
- && node.callee.object.property.type === Syntax.Identifier
488
- && node.callee.object.property.name === 'ui'
489
- && node.callee.property.type === Syntax.Identifier
490
- && node.callee.property.name === 'requireSync'
610
+ && isCaleeMemberExpression(node)
611
+ && isMemberExpression( stripChainWrappers(node, "callee.object") )
612
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
613
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
614
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
615
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
616
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
617
+ && stripChainWrappers(node, "callee.property").name === 'requireSync'
491
618
  );
492
619
  }
493
620
 
494
621
  function isProbingRequireCall(node) {
622
+ node = stripChainWrappers(node);
623
+
495
624
  return (
496
625
  node
497
- && node.type === Syntax.CallExpression
498
- && node.callee.type === Syntax.MemberExpression
499
- && node.callee.object.type === Syntax.MemberExpression
500
- && node.callee.object.object.type === Syntax.Identifier
501
- && node.callee.object.object.name === 'sap'
502
- && node.callee.object.property.type === Syntax.Identifier
503
- && node.callee.object.property.name === 'ui'
504
- && node.callee.property.type === Syntax.Identifier
505
- && node.callee.property.name === 'require'
626
+ && isCaleeMemberExpression(node)
627
+ && isMemberExpression( stripChainWrappers(node, "callee.object") )
628
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
629
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
630
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
631
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
632
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
633
+ && stripChainWrappers(node, "callee.property").name === 'require'
506
634
  && node.arguments.length === 1
507
- && node.arguments[0].type === Syntax.Literal
508
- && typeof node.arguments[0].value === 'string' // TODO generalize to statically analyzable constants
635
+ && isStringLiteral(node.arguments[0])
509
636
  );
510
637
  }
511
638
 
@@ -521,7 +648,7 @@ function isCompileTimeConstant(node) {
521
648
  }
522
649
 
523
650
  function getObjectName(node) {
524
- if ( node.type === Syntax.MemberExpression && !node.computed && node.property.type === Syntax.Identifier ) {
651
+ if ( isMemberExpression(node) && !node.computed && node.property.type === Syntax.Identifier ) {
525
652
  const prefix = getObjectName(node.object);
526
653
  return prefix ? prefix + "." + node.property.name : null;
527
654
  } else if ( node.type === Syntax.Identifier ) {
@@ -536,7 +663,7 @@ function getObjectName(node) {
536
663
  * returns the leftmost identifier a
537
664
  */
538
665
  function getLeftmostName(node) {
539
- while ( node.type === Syntax.MemberExpression ) {
666
+ while ( isMemberExpression(node) ) {
540
667
  node = node.object;
541
668
  }
542
669
  if ( node.type === Syntax.Identifier ) {
@@ -604,7 +731,7 @@ function convertValueWithRaw(node, type, propertyName) {
604
731
  raw: node.operator + node.argument.raw
605
732
  };
606
733
 
607
- } else if ( node.type === Syntax.MemberExpression && type ) {
734
+ } else if ( isMemberExpression(node) && type ) {
608
735
 
609
736
  // enum value (a.b.c)
610
737
  value = getResolvedObjectName(node);
@@ -674,6 +801,13 @@ function convertValueWithRaw(node, type, propertyName) {
674
801
  };
675
802
  }
676
803
 
804
+ } else if ( isTemplateLiteralWithoutExpression(node) ) {
805
+ let value = node.quasis[0].value || {};
806
+
807
+ return {
808
+ value: value.cooked,
809
+ raw: value.raw,
810
+ };
677
811
  }
678
812
 
679
813
  value = "...see text or source";
@@ -697,10 +831,10 @@ function convertStringArray(node) {
697
831
  }
698
832
  const result = [];
699
833
  for ( let i = 0; i < node.elements.length; i++ ) {
700
- if ( node.elements[i].type !== Syntax.Literal || typeof node.elements[i].value !== 'string' ) {
834
+ if ( !isStringLiteral(node.elements[i]) ) {
701
835
  throw new Error("not a string literal");
702
836
  }
703
- result.push(node.elements[i].value);
837
+ result.push( convertValue(node.elements[i]) );
704
838
  }
705
839
  // console.log(result);
706
840
  return result;
@@ -736,7 +870,7 @@ function collectClassInfo(extendCall, classDoclet) {
736
870
  if ( classDoclet && classDoclet.augments && classDoclet.augments.length === 1 ) {
737
871
  baseType = classDoclet.augments[0];
738
872
  }
739
- if ( extendCall.callee.type === Syntax.MemberExpression ) {
873
+ if ( isMemberExpression(extendCall.callee) ) {
740
874
  const baseCandidate = getResolvedObjectName(extendCall.callee.object);
741
875
  if ( baseCandidate && baseType == null ) {
742
876
  baseType = baseCandidate;
@@ -746,7 +880,7 @@ function collectClassInfo(extendCall, classDoclet) {
746
880
  }
747
881
 
748
882
  const oClassInfo = {
749
- name : extendCall.arguments[0].value,
883
+ name : convertValue(extendCall.arguments[0]),
750
884
  baseType : baseType,
751
885
  interfaces : [],
752
886
  doc : classDoclet && classDoclet.description,
@@ -988,7 +1122,7 @@ function collectClassInfo(extendCall, classDoclet) {
988
1122
  return oClassInfo;
989
1123
  }
990
1124
 
991
- function collectDesigntimeInfo(dtNode) {
1125
+ function collectDesigntimeInfo(dtNodeArgument) {
992
1126
 
993
1127
  function each(node, defaultKey, callback) {
994
1128
  const map = node && createPropertyMap(node.value);
@@ -1010,7 +1144,7 @@ function collectDesigntimeInfo(dtNode) {
1010
1144
 
1011
1145
  let oDesigntimeInfo;
1012
1146
 
1013
- const map = createPropertyMap(dtNode.argument);
1147
+ const map = createPropertyMap(dtNodeArgument);
1014
1148
 
1015
1149
  if (map.annotations) {
1016
1150
 
@@ -1095,12 +1229,19 @@ function determineValueRangeBorder(range, expression, varname, inverse) {
1095
1229
  function determineValueRange(expression, varname, inverse) {
1096
1230
  const range = {};
1097
1231
  if ( expression.type === Syntax.LogicalExpression
1098
- && expression.operator === '&&'
1099
1232
  && expression.left.type === Syntax.BinaryExpression
1100
- && expression.right.type === Syntax.BinaryExpression
1101
- && determineValueRangeBorder(range, expression.left, varname, inverse)
1102
- && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1103
- return range;
1233
+ && expression.right.type === Syntax.BinaryExpression ) {
1234
+
1235
+ if ( expression.operator === "&&"
1236
+ && determineValueRangeBorder(range, expression.left, varname, inverse)
1237
+ && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1238
+ return range;
1239
+ } else if ( ["||", "??"].includes(expression.operator)
1240
+ && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1241
+ || determineValueRangeBorder(range, expression.right, varname, inverse) )) {
1242
+ return range;
1243
+ }
1244
+
1104
1245
  } else if ( expression.type === Syntax.BinaryExpression
1105
1246
  && determineValueRangeBorder(range, expression, varname, inverse) ) {
1106
1247
  return range;
@@ -1113,24 +1254,24 @@ function collectDataTypeInfo(extendCall, classDoclet) {
1113
1254
  let i = 0,
1114
1255
  name, def, base, pattern, range;
1115
1256
 
1116
- if ( i < args.length && args[i].type === Syntax.Literal && typeof args[i].value === 'string' ) {
1117
- name = args[i++].value;
1257
+ if ( i < args.length && isStringLiteral(args[i]) ) {
1258
+ name = convertValue(args[i++]);
1118
1259
  }
1119
1260
  if ( i < args.length && args[i].type === Syntax.ObjectExpression ) {
1120
1261
  def = createPropertyMap(args[i++]);
1121
1262
  }
1122
1263
  if ( i < args.length ) {
1123
- if ( args[i].type === Syntax.Literal && typeof args[i].value === 'string' ) {
1124
- base = args[i++].value;
1125
- } else if ( args[i].type === Syntax.CallExpression
1126
- && args[i].callee.type === Syntax.MemberExpression
1127
- && getResolvedObjectName(args[i].callee.object) === "sap.ui.base.DataType"
1128
- && args[i].callee.property.type === Syntax.Identifier
1129
- && args[i].callee.property.name === 'getType'
1130
- && args[i].arguments.length === 1
1131
- && args[i].arguments[0].type === Syntax.Literal
1132
- && typeof args[i].arguments[0].value === 'string' ) {
1133
- base = args[i++].arguments[0].value;
1264
+ const node = resolvePotentialWrapperExpression(args[i]);
1265
+
1266
+ if ( isStringLiteral(args[i]) ) {
1267
+ base = convertValue(args[i++]);
1268
+ } else if ( isCaleeMemberExpression(node)
1269
+ && getResolvedObjectName(node.callee.object) === "sap.ui.base.DataType"
1270
+ && node.callee.property.type === Syntax.Identifier
1271
+ && node.callee.property.name === 'getType'
1272
+ && node.arguments.length === 1
1273
+ && isStringLiteral(node.arguments[0]) ) {
1274
+ base = convertValue(args[i++].arguments[0]);
1134
1275
  } else {
1135
1276
  future(`could not identify base type of data type '${name}'`);
1136
1277
  }
@@ -1138,25 +1279,31 @@ function collectDataTypeInfo(extendCall, classDoclet) {
1138
1279
  base = "any";
1139
1280
  }
1140
1281
 
1282
+ const isArrowExpression = isArrowFuncExpression(def && def.isValid && def.isValid.value);
1283
+
1141
1284
  if ( def
1142
1285
  && def.isValid
1143
- && def.isValid.value.type === Syntax.FunctionExpression
1286
+ && [Syntax.FunctionExpression, Syntax.ArrowFunctionExpression].includes(def.isValid.value.type)
1144
1287
  && def.isValid.value.params.length === 1
1145
1288
  && def.isValid.value.params[0].type === Syntax.Identifier
1146
- && def.isValid.value.body.body.length === 1 ) {
1289
+ && (isArrowExpression || def.isValid.value.body.body.length === 1) ) {
1290
+
1147
1291
  const varname = def.isValid.value.params[0].name;
1148
- const stmt = def.isValid.value.body.body[0];
1149
- if ( stmt.type === Syntax.ReturnStatement && stmt.argument ) {
1150
- if ( stmt.argument.type === Syntax.CallExpression
1151
- && stmt.argument.callee.type === Syntax.MemberExpression
1152
- && stmt.argument.callee.object.type === Syntax.Literal
1153
- && stmt.argument.callee.object.regex
1154
- && stmt.argument.callee.property.type === Syntax.Identifier
1155
- && stmt.argument.callee.property.name === 'test' ) {
1156
- pattern = stmt.argument.callee.object.regex.pattern;
1292
+ const stmt = isArrowExpression ? def.isValid.value.body : def.isValid.value.body.body[0];
1293
+
1294
+ if ( isReturningNode(stmt) || isArrowExpression ) {
1295
+ const stmtArgument = resolvePotentialWrapperExpression(
1296
+ isArrowExpression ? stmt : stmt.argument
1297
+ );
1298
+ if ( isCaleeMemberExpression(stmtArgument)
1299
+ && stmtArgument.callee.object.type === Syntax.Literal
1300
+ && stmtArgument.callee.object.regex
1301
+ && stmtArgument.callee.property.type === Syntax.Identifier
1302
+ && stmtArgument.callee.property.name === 'test' ) {
1303
+ pattern = stmtArgument.callee.object.regex.pattern;
1157
1304
  // console.log(pattern);
1158
1305
  } else {
1159
- range = determineValueRange(stmt.argument, varname, false);
1306
+ range = determineValueRange(stmtArgument, varname, false);
1160
1307
  }
1161
1308
  } else if ( stmt.type === Syntax.IfStatement
1162
1309
  && stmt.consequent.type === Syntax.BlockStatement
@@ -1741,7 +1888,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1741
1888
  newJSDoc([
1742
1889
  "Removes an " + n1 + " from the association named " + link + ".",
1743
1890
  "@param {int | sap.ui.core.ID | " + info.type + "} " + varname(n1, "variant") + " The " + n1 + " to be removed or its index or ID",
1744
- "@returns {sap.ui.core.ID} The removed " + n1 + " or <code>null</code>",
1891
+ "@returns {sap.ui.core.ID|null} The removed " + n1 + " or <code>null</code>",
1745
1892
  info.since ? "@since " + info.since : "",
1746
1893
  info.deprecation ? "@deprecated " + info.deprecation : "",
1747
1894
  info.experimental ? "@experimental " + info.experimental : "",
@@ -2351,6 +2498,7 @@ exports.handlers = {
2351
2498
  e.doclet.meta.__shortpath = getRelativePath(filepath);
2352
2499
  _ui5data.resource = currentModule.resource;
2353
2500
  _ui5data.module = currentModule.name || currentModule.module;
2501
+ _ui5data.initialLongname = e.doclet.longname;
2354
2502
 
2355
2503
  const localDecl = findLocalDeclaration(e.doclet.meta.lineno, e.doclet.meta.columnno);
2356
2504
  if ( localDecl ) {
@@ -2583,24 +2731,37 @@ exports.astNodeVisitor = {
2583
2731
  }
2584
2732
  }
2585
2733
 
2586
- if ( node.type === Syntax.ExpressionStatement ) {
2587
- if ( isSapUiDefineCall(node.expression) ) {
2588
- analyzeModuleDefinition(node.expression);
2589
- /*
2734
+ if ( [Syntax.ExpressionStatement, Syntax.LogicalExpression].includes(node.type) ) {
2735
+ let nodeToAnalyze;
2736
+ if (isSapUiDefineCall(node.expression)) {
2737
+ nodeToAnalyze = node.expression;
2738
+
2739
+ /*
2590
2740
  } else if ( isJQuerySapDeclareCall(node.expression)
2591
2741
  && node.expression.arguments.length > 0
2592
2742
  && node.expression.arguments[0].type === Syntax.Literal
2593
2743
  && typeof node.expression.arguments[0].value === "string" ) {
2594
2744
  warning(`module has explicit module name ${node.expression.arguments[0].value}`);
2595
2745
  */
2746
+ } else if (isSapUiDefineCall(node.left)) {
2747
+ nodeToAnalyze = node.left;
2748
+ } else if (isSapUiDefineCall(node.right)) {
2749
+ nodeToAnalyze = node.right;
2596
2750
  }
2597
2751
 
2752
+ nodeToAnalyze && analyzeModuleDefinition(
2753
+ resolvePotentialWrapperExpression(nodeToAnalyze)
2754
+ );
2598
2755
  }
2599
2756
 
2600
- if (node.type === Syntax.ReturnStatement && node.argument && node.argument.type === Syntax.ObjectExpression && /\.designtime\.js$/.test(currentSourceName) ) {
2757
+ const isArrowExpression = isArrowFuncExpression(node) && node.body.type === Syntax.ObjectExpression;
2758
+ const nodeArgument = isArrowExpression
2759
+ ? node.body
2760
+ : resolvePotentialWrapperExpression(node).argument;
2761
+ if (isArrowExpression || (isReturningNode(node) && nodeArgument && nodeArgument.type === Syntax.ObjectExpression) && /\.designtime\.js$/.test(currentSourceName) ) {
2601
2762
 
2602
2763
  // assume this node to return designtime metadata. Collect it and remember it by its module name
2603
- const oDesigntimeInfo = collectDesigntimeInfo(node);
2764
+ const oDesigntimeInfo = collectDesigntimeInfo(nodeArgument);
2604
2765
  if ( oDesigntimeInfo ) {
2605
2766
  designtimeInfos[currentModule.module] = oDesigntimeInfo;
2606
2767
  info(`collected designtime info ${currentModule.module}`);
@@ -2630,14 +2791,19 @@ exports.astNodeVisitor = {
2630
2791
  }
2631
2792
  });
2632
2793
 
2633
- } else if ( node.type === Syntax.ReturnStatement && isExtendCall(node.argument) ) {
2794
+ } else if ( isReturningNode(node)
2795
+ && isExtendCall(resolvePotentialWrapperExpression(node).argument || node.body) ) {
2796
+
2797
+ const nodeArgument = isArrowFuncExpression(node)
2798
+ ? node.body
2799
+ : resolvePotentialWrapperExpression(node).argument;
2634
2800
 
2635
2801
  // return Something.extend(...)
2636
2802
 
2637
- const className = node.argument.arguments[0].value;
2638
- const comment = getLeadingCommentNode(node, className) || getLeadingCommentNode(node.argument, className);
2803
+ const className = convertValue(nodeArgument.arguments[0]);
2804
+ const comment = getLeadingCommentNode(node, className) || getLeadingCommentNode(nodeArgument, className);
2639
2805
  // console.log(`ast node with comment ${comment}`);
2640
- processExtendCall(node.argument, comment, true);
2806
+ processExtendCall(nodeArgument, comment, true);
2641
2807
  } else if ( node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.AssignmentExpression ) {
2642
2808
 
2643
2809
  if ( isCreateDataTypeCall(node.expression.right) ) {
@@ -2689,7 +2855,8 @@ exports.astNodeVisitor = {
2689
2855
  /*
2690
2856
  * This function has been copied from jsdoc/lib/jsdoc/tag/type (version 3.6.7)
2691
2857
  * The copy has been enhanced with the changes from https://github.com/jsdoc/jsdoc/pull/1735
2692
- * to retain the full function signature for function types.
2858
+ * to retain the full function signature for function types and with a further change
2859
+ * to retain the full record type structure.
2693
2860
  *
2694
2861
  * JSDoc is copyright (c) 2011-present Michael Mathews micmath@gmail.com and the contributors to JSDoc.
2695
2862
  */
@@ -2731,7 +2898,29 @@ exports.astNodeVisitor = {
2731
2898
  // types.push('Object');
2732
2899
  if (Array.isArray(parsedType.fields)) {
2733
2900
  typeString = `{${parsedType.fields.map(
2734
- ({key,value}) => `${catharsis.stringify(key)}: ${toTypeString(value)}`
2901
+ ({key,value}) => {
2902
+ const keyString = catharsis.stringify(key);
2903
+ if (value) {
2904
+ var propertyPlusType = `${keyString}: ${toTypeString(value)}`;
2905
+ return propertyPlusType;
2906
+ } else {
2907
+ let pos;
2908
+ if (keyString && (pos = keyString.indexOf(":")) > -1) {
2909
+ // When no space is present between colon and type (e.g. the structure looks like "{x:number}" with no space after
2910
+ // the colon), then Catharsis parses this as property name "x:number" with no type given.
2911
+ // In this case give a clear hint that this space is needed and throw an error to prevent such issues from being merged
2912
+ const realName = keyString.substring(0, pos);
2913
+ const realValue = keyString.substring(pos + 1, keyString.length);
2914
+ let message = `Cannot parse the "${keyString}" part of "${parsedType.typeExpression}" in RecordType (log output above may give a hint in which file).\n`;
2915
+ message += `Did you mean to specify a property "${realName}" of type "${realValue}"? Then insert a space after the colon and write "${realName}: ${realValue}".`
2916
+ error(message);
2917
+ return "x: any"; // unparseable property set to "any" - but the JSDoc run will fail now, anyway
2918
+ } else {
2919
+ // only property given without type; this will be turned into type "any" further downstream
2920
+ return keyString;
2921
+ }
2922
+ }
2923
+ }
2735
2924
  ).join(', ')}}`;
2736
2925
  types.push(typeString);
2737
2926
  } else {
@@ -2772,7 +2961,9 @@ exports.astNodeVisitor = {
2772
2961
  const origParse = jsdocType.parse;
2773
2962
  jsdocType.parse = function() {
2774
2963
  const tagInfo = origParse.apply(this, arguments);
2775
- if ( tagInfo && /function/.test(tagInfo.typeExpression) && tagInfo.parsedType ) {
2964
+ // #### BEGIN: MODIFIED BY SAP
2965
+ if ( tagInfo && (/function/.test(tagInfo.typeExpression) || /\{.*\}/.test(tagInfo.typeExpression)) && tagInfo.parsedType ) {
2966
+ // #### END: MODIFIED BY SAP
2776
2967
  // console.info("old typeExpression", tagInfo.typeExpression);
2777
2968
  // console.info("old parse tree", tagInfo.parsedType);
2778
2969
  // console.info("old parse result", tagInfo.type);