@ui5/builder 3.0.0-alpha.1 → 3.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +867 -0
  2. package/index.js +0 -43
  3. package/lib/lbt/analyzer/FioriElementsAnalyzer.js +23 -12
  4. package/lib/lbt/analyzer/JSModuleAnalyzer.js +115 -61
  5. package/lib/lbt/analyzer/SmartTemplateAnalyzer.js +23 -12
  6. package/lib/lbt/analyzer/XMLCompositeAnalyzer.js +29 -19
  7. package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +2 -1
  8. package/lib/lbt/analyzer/analyzeLibraryJS.js +15 -0
  9. package/lib/lbt/bundle/Builder.js +365 -138
  10. package/lib/lbt/bundle/BundleWriter.js +17 -0
  11. package/lib/lbt/bundle/Resolver.js +2 -2
  12. package/lib/lbt/calls/SapUiDefine.js +13 -7
  13. package/lib/lbt/resources/LocatorResource.js +7 -7
  14. package/lib/lbt/resources/LocatorResourcePool.js +8 -4
  15. package/lib/lbt/resources/ResourceCollector.js +22 -15
  16. package/lib/lbt/resources/ResourceInfoList.js +0 -1
  17. package/lib/lbt/resources/ResourcePool.js +7 -6
  18. package/lib/lbt/utils/ASTUtils.js +45 -18
  19. package/lib/lbt/utils/escapePropertiesFile.js +3 -6
  20. package/lib/lbt/utils/parseUtils.js +1 -1
  21. package/lib/processors/bundlers/moduleBundler.js +31 -10
  22. package/lib/processors/jsdoc/lib/createIndexFiles.js +1 -1
  23. package/lib/processors/jsdoc/lib/transformApiJson.js +78 -22
  24. package/lib/processors/jsdoc/lib/ui5/plugin.js +414 -122
  25. package/lib/processors/jsdoc/lib/ui5/template/publish.js +112 -91
  26. package/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js +1 -1
  27. package/lib/processors/manifestCreator.js +8 -45
  28. package/lib/processors/minifier.js +11 -5
  29. package/lib/tasks/buildThemes.js +1 -1
  30. package/lib/tasks/bundlers/generateBundle.js +70 -30
  31. package/lib/tasks/bundlers/generateComponentPreload.js +26 -19
  32. package/lib/tasks/bundlers/generateFlexChangesBundle.js +10 -5
  33. package/lib/tasks/bundlers/generateLibraryPreload.js +113 -94
  34. package/lib/tasks/bundlers/generateManifestBundle.js +8 -10
  35. package/lib/tasks/bundlers/generateStandaloneAppBundle.js +42 -10
  36. package/lib/tasks/bundlers/utils/createModuleNameMapping.js +31 -0
  37. package/lib/tasks/generateCachebusterInfo.js +7 -3
  38. package/lib/tasks/generateLibraryManifest.js +6 -8
  39. package/lib/tasks/generateResourcesJson.js +3 -3
  40. package/lib/tasks/generateThemeDesignerResources.js +118 -2
  41. package/lib/tasks/generateVersionInfo.js +5 -5
  42. package/lib/tasks/jsdoc/generateJsdoc.js +1 -1
  43. package/lib/tasks/minify.js +14 -4
  44. package/lib/tasks/taskRepository.js +1 -13
  45. package/lib/tasks/transformBootstrapHtml.js +6 -1
  46. package/package.json +13 -11
  47. package/lib/builder/BuildContext.js +0 -56
  48. package/lib/builder/ProjectBuildContext.js +0 -57
  49. package/lib/builder/builder.js +0 -419
  50. package/lib/tasks/TaskUtil.js +0 -160
  51. package/lib/types/AbstractBuilder.js +0 -270
  52. package/lib/types/AbstractFormatter.js +0 -66
  53. package/lib/types/AbstractUi5Formatter.js +0 -95
  54. package/lib/types/application/ApplicationBuilder.js +0 -211
  55. package/lib/types/application/ApplicationFormatter.js +0 -227
  56. package/lib/types/application/applicationType.js +0 -15
  57. package/lib/types/library/LibraryBuilder.js +0 -231
  58. package/lib/types/library/LibraryFormatter.js +0 -519
  59. package/lib/types/library/libraryType.js +0 -15
  60. package/lib/types/module/ModuleBuilder.js +0 -7
  61. package/lib/types/module/ModuleFormatter.js +0 -54
  62. package/lib/types/module/moduleType.js +0 -15
  63. package/lib/types/themeLibrary/ThemeLibraryBuilder.js +0 -63
  64. package/lib/types/themeLibrary/ThemeLibraryFormatter.js +0 -90
  65. package/lib/types/themeLibrary/themeLibraryType.js +0 -15
  66. package/lib/types/typeRepository.js +0 -46
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * JSDoc3 plugin for UI5 documentation generation.
3
3
  *
4
- * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company.
4
+ * (c) Copyright 2009-2022 SAP SE or an SAP affiliate company.
5
5
  * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
6
6
  */
7
7
 
@@ -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,31 +288,33 @@ 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
- if ( typeof local === 'object' && local.module ) {
306
+ const objectName = getObjectName(valueNode);
307
+ if ( objectName && typeof local === 'object' && local.module ) {
292
308
  currentModule.localNames[name] = {
293
309
  module: local.module,
294
- path: getObjectName(valueNode).split('.').slice(1).join('.') // TODO chaining if local has path
310
+ path: objectName.split('.').slice(1).join('.') // TODO chaining if local has path
295
311
  };
296
312
  debug(` found local shortcut: ${name} ${currentModule.localNames[name]}`);
297
313
  }
298
314
  } else if ( isRequireSyncCall(valueNode) || isProbingRequireCall(valueNode) ) {
299
- if ( valueNode.arguments[0]
300
- && valueNode.arguments[0].type === Syntax.Literal
301
- && typeof valueNode.arguments[0].value === 'string' ) {
315
+ if ( valueNode.arguments[0] && isStringLiteral(valueNode.arguments[0]) ) {
302
316
  currentModule.localNames[name] = {
303
- module: valueNode.arguments[0].value
317
+ module: convertValue(valueNode.arguments[0])
304
318
  // no (or empty) path
305
319
  };
306
320
  debug(` found local import: ${name} = ${valueNode.callee.property.name}('${valueNode.arguments[0].value}')`);
@@ -314,8 +328,8 @@ function collectShortcuts(body) {
314
328
  }
315
329
  }
316
330
 
317
- if ( body.type === Syntax.BlockStatement ) {
318
- body.body.forEach(function ( stmt ) {
331
+ if ( body.type === Syntax.BlockStatement || isArrowFuncExpression(factory) ) {
332
+ const itemsResolver = function ( stmt ) {
319
333
  // console.log(stmt);
320
334
  if ( stmt.type === Syntax.FunctionDeclaration ) {
321
335
  if ( stmt.id && stmt.id.type === Syntax.Identifier && stmt.loc && stmt.loc.start ) {
@@ -335,15 +349,28 @@ function collectShortcuts(body) {
335
349
  && stmt.expression.type === Syntax.AssignmentExpression
336
350
  && stmt.expression.left.type === Syntax.Identifier ) {
337
351
  checkAssignment(stmt.expression.left.name, stmt.expression.right);
338
- } else if ( stmt.type === Syntax.ReturnStatement ) {
339
- if ( stmt.argument && stmt.argument.type === Syntax.Identifier ) {
340
- currentModule.defaultExport = stmt.argument.name;
341
- } else if ( stmt.argument && isExtendCall(stmt.argument) ) {
342
- debug(` found default export class definition: return .extend('${stmt.argument.arguments[0].value}', ...)`);
343
- 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]);
344
365
  }
345
366
  }
346
- });
367
+ };
368
+
369
+ if (isArrowFuncExpression(factory)) {
370
+ itemsResolver(factory);
371
+ } else {
372
+ body.body.forEach(itemsResolver);
373
+ }
347
374
  }
348
375
 
349
376
  if ( currentModule.defaultExport && currentModule.localNames[currentModule.defaultExport] ) {
@@ -368,12 +395,12 @@ function guessSingularName(sPluralName) {
368
395
  }
369
396
 
370
397
  function getPropertyKey(prop) {
371
- 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 ) {
372
401
  return prop.key.name;
373
402
  } else if ( prop.key.type === Syntax.Literal ) {
374
403
  return String(prop.key.value);
375
- } else {
376
- return prop.key.toSource();
377
404
  }
378
405
  }
379
406
 
@@ -431,80 +458,180 @@ function createPropertyMap(node, defaultKey) {
431
458
  return result;
432
459
  }
433
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?.type === Syntax.TemplateLiteral &&
511
+ node?.expressions?.length === 0 &&
512
+ node?.quasis?.length === 1
513
+ );
514
+ }
515
+
516
+ /**
517
+ * Checks whether a node is Literal or TemplateLiteral without an expression
518
+ *
519
+ * @param {Node} node
520
+ * @returns {String}
521
+ */
522
+ function isStringLiteral(node) {
523
+ return (
524
+ (node && node.type === Syntax.Literal && typeof node.value === "string")
525
+ || isTemplateLiteralWithoutExpression(node)
526
+ );
527
+ }
528
+
529
+ function isMemberExpression(node) {
530
+ return node && [Syntax.MemberExpression, Syntax.OptionalMemberExpression].includes(node.type);
531
+ }
532
+
533
+ function isCaleeMemberExpression(node) {
534
+ return (
535
+ node &&
536
+ [Syntax.CallExpression, Syntax.OptionalCallExpression].includes(node.type) &&
537
+ isMemberExpression(node.callee)
538
+ );
539
+ }
540
+
434
541
  function isExtendCall(node) {
542
+ node = stripChainWrappers(node);
435
543
 
436
544
  return (
437
545
  node
438
- && node.type === Syntax.CallExpression
439
- && node.callee.type === Syntax.MemberExpression
440
- && node.callee.property.type === Syntax.Identifier
441
- && node.callee.property.name === 'extend'
546
+ && isCaleeMemberExpression(node)
547
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
548
+ && stripChainWrappers(node, "callee.property").name === 'extend'
442
549
  && node.arguments.length >= 2
443
- && node.arguments[0].type === Syntax.Literal
444
- && typeof node.arguments[0].value === "string"
550
+ && isStringLiteral(node.arguments[0])
445
551
  && node.arguments[1].type === Syntax.ObjectExpression
446
552
  );
447
553
 
448
554
  }
449
555
 
556
+ function isArrowFuncExpression(node) {
557
+ return (
558
+ node &&
559
+ node.type === Syntax.ArrowFunctionExpression &&
560
+ node.expression === true
561
+ );
562
+ }
563
+
564
+ /**
565
+ * Checks whether the node is of a "returning" type
566
+ *
567
+ * @param {Node} node
568
+ * @returns {Boolean}
569
+ */
570
+ function isReturningNode(node) {
571
+ return (node && node.type === Syntax.ReturnStatement)
572
+ || (node && node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.YieldExpression)
573
+ || isArrowFuncExpression(node);
574
+ }
575
+
450
576
  function isSapUiDefineCall(node) {
451
577
 
452
578
  return (
453
- node
454
- && node.type === Syntax.CallExpression
455
- && node.callee.type === Syntax.MemberExpression
456
- && node.callee.object.type === Syntax.MemberExpression
457
- && node.callee.object.object.type === Syntax.Identifier
458
- && node.callee.object.object.name === 'sap'
459
- && node.callee.object.property.type === Syntax.Identifier
460
- && node.callee.object.property.name === 'ui'
461
- && node.callee.property.type === Syntax.Identifier
462
- && node.callee.property.name === 'define'
579
+ stripChainWrappers(node)
580
+ && isCaleeMemberExpression(stripChainWrappers(node))
581
+ && isMemberExpression(stripChainWrappers(node, "callee.object"))
582
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
583
+ && stripChainWrappers(node, "callee.property").name === 'define'
584
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
585
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
586
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
587
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
463
588
  );
464
589
 
465
590
  }
466
591
 
467
592
  function isCreateDataTypeCall(node) {
593
+ node = stripChainWrappers(node);
594
+
468
595
  return (
469
596
  node
470
- && node.type === Syntax.CallExpression
471
- && node.callee.type === Syntax.MemberExpression
472
- && getResolvedObjectName(node.callee.object) === "sap.ui.base.DataType"
473
- && node.callee.property.type === Syntax.Identifier
474
- && node.callee.property.name === 'createType'
597
+ && isCaleeMemberExpression(node)
598
+ && getResolvedObjectName(stripChainWrappers(node, "callee.object")) === "sap.ui.base.DataType"
599
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
600
+ && stripChainWrappers(node, "callee.property").name === 'createType'
475
601
  );
476
602
  }
477
603
 
478
604
  function isRequireSyncCall(node) {
605
+ node = stripChainWrappers(node);
606
+
479
607
  return (
480
608
  node
481
- && node.type === Syntax.CallExpression
482
- && node.callee.type === Syntax.MemberExpression
483
- && node.callee.object.type === Syntax.MemberExpression
484
- && node.callee.object.object.type === Syntax.Identifier
485
- && node.callee.object.object.name === 'sap'
486
- && node.callee.object.property.type === Syntax.Identifier
487
- && node.callee.object.property.name === 'ui'
488
- && node.callee.property.type === Syntax.Identifier
489
- && node.callee.property.name === 'requireSync'
609
+ && isCaleeMemberExpression(node)
610
+ && isMemberExpression( stripChainWrappers(node, "callee.object") )
611
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
612
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
613
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
614
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
615
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
616
+ && stripChainWrappers(node, "callee.property").name === 'requireSync'
490
617
  );
491
618
  }
492
619
 
493
620
  function isProbingRequireCall(node) {
621
+ node = stripChainWrappers(node);
622
+
494
623
  return (
495
624
  node
496
- && node.type === Syntax.CallExpression
497
- && node.callee.type === Syntax.MemberExpression
498
- && node.callee.object.type === Syntax.MemberExpression
499
- && node.callee.object.object.type === Syntax.Identifier
500
- && node.callee.object.object.name === 'sap'
501
- && node.callee.object.property.type === Syntax.Identifier
502
- && node.callee.object.property.name === 'ui'
503
- && node.callee.property.type === Syntax.Identifier
504
- && node.callee.property.name === 'require'
625
+ && isCaleeMemberExpression(node)
626
+ && isMemberExpression( stripChainWrappers(node, "callee.object") )
627
+ && stripChainWrappers(node, "callee.object.object").type === Syntax.Identifier
628
+ && stripChainWrappers(node, "callee.object.object").name === 'sap'
629
+ && stripChainWrappers(node, "callee.object.property").type === Syntax.Identifier
630
+ && stripChainWrappers(node, "callee.object.property").name === 'ui'
631
+ && stripChainWrappers(node, "callee.property").type === Syntax.Identifier
632
+ && stripChainWrappers(node, "callee.property").name === 'require'
505
633
  && node.arguments.length === 1
506
- && node.arguments[0].type === Syntax.Literal
507
- && typeof node.arguments[0].value === 'string' // TODO generalize to statically analyzable constants
634
+ && isStringLiteral(node.arguments[0])
508
635
  );
509
636
  }
510
637
 
@@ -520,7 +647,7 @@ function isCompileTimeConstant(node) {
520
647
  }
521
648
 
522
649
  function getObjectName(node) {
523
- if ( node.type === Syntax.MemberExpression && !node.computed && node.property.type === Syntax.Identifier ) {
650
+ if ( isMemberExpression(node) && !node.computed && node.property.type === Syntax.Identifier ) {
524
651
  const prefix = getObjectName(node.object);
525
652
  return prefix ? prefix + "." + node.property.name : null;
526
653
  } else if ( node.type === Syntax.Identifier ) {
@@ -535,7 +662,7 @@ function getObjectName(node) {
535
662
  * returns the leftmost identifier a
536
663
  */
537
664
  function getLeftmostName(node) {
538
- while ( node.type === Syntax.MemberExpression ) {
665
+ while ( isMemberExpression(node) ) {
539
666
  node = node.object;
540
667
  }
541
668
  if ( node.type === Syntax.Identifier ) {
@@ -603,7 +730,7 @@ function convertValueWithRaw(node, type, propertyName) {
603
730
  raw: node.operator + node.argument.raw
604
731
  };
605
732
 
606
- } else if ( node.type === Syntax.MemberExpression && type ) {
733
+ } else if ( isMemberExpression(node) && type ) {
607
734
 
608
735
  // enum value (a.b.c)
609
736
  value = getResolvedObjectName(node);
@@ -673,6 +800,11 @@ function convertValueWithRaw(node, type, propertyName) {
673
800
  };
674
801
  }
675
802
 
803
+ } else if ( isTemplateLiteralWithoutExpression(node) ) {
804
+ return {
805
+ value: node?.quasis?.[0]?.value?.cooked,
806
+ raw: node?.quasis?.[0]?.value?.raw,
807
+ };
676
808
  }
677
809
 
678
810
  value = "...see text or source";
@@ -696,10 +828,10 @@ function convertStringArray(node) {
696
828
  }
697
829
  const result = [];
698
830
  for ( let i = 0; i < node.elements.length; i++ ) {
699
- if ( node.elements[i].type !== Syntax.Literal || typeof node.elements[i].value !== 'string' ) {
831
+ if ( !isStringLiteral(node.elements[i]) ) {
700
832
  throw new Error("not a string literal");
701
833
  }
702
- result.push(node.elements[i].value);
834
+ result.push( convertValue(node.elements[i]) );
703
835
  }
704
836
  // console.log(result);
705
837
  return result;
@@ -735,7 +867,7 @@ function collectClassInfo(extendCall, classDoclet) {
735
867
  if ( classDoclet && classDoclet.augments && classDoclet.augments.length === 1 ) {
736
868
  baseType = classDoclet.augments[0];
737
869
  }
738
- if ( extendCall.callee.type === Syntax.MemberExpression ) {
870
+ if ( isMemberExpression(extendCall.callee) ) {
739
871
  const baseCandidate = getResolvedObjectName(extendCall.callee.object);
740
872
  if ( baseCandidate && baseType == null ) {
741
873
  baseType = baseCandidate;
@@ -745,7 +877,7 @@ function collectClassInfo(extendCall, classDoclet) {
745
877
  }
746
878
 
747
879
  const oClassInfo = {
748
- name : extendCall.arguments[0].value,
880
+ name : convertValue(extendCall.arguments[0]),
749
881
  baseType : baseType,
750
882
  interfaces : [],
751
883
  doc : classDoclet && classDoclet.description,
@@ -987,7 +1119,7 @@ function collectClassInfo(extendCall, classDoclet) {
987
1119
  return oClassInfo;
988
1120
  }
989
1121
 
990
- function collectDesigntimeInfo(dtNode) {
1122
+ function collectDesigntimeInfo(dtNodeArgument) {
991
1123
 
992
1124
  function each(node, defaultKey, callback) {
993
1125
  const map = node && createPropertyMap(node.value);
@@ -1009,7 +1141,7 @@ function collectDesigntimeInfo(dtNode) {
1009
1141
 
1010
1142
  let oDesigntimeInfo;
1011
1143
 
1012
- const map = createPropertyMap(dtNode.argument);
1144
+ const map = createPropertyMap(dtNodeArgument);
1013
1145
 
1014
1146
  if (map.annotations) {
1015
1147
 
@@ -1094,12 +1226,19 @@ function determineValueRangeBorder(range, expression, varname, inverse) {
1094
1226
  function determineValueRange(expression, varname, inverse) {
1095
1227
  const range = {};
1096
1228
  if ( expression.type === Syntax.LogicalExpression
1097
- && expression.operator === '&&'
1098
1229
  && expression.left.type === Syntax.BinaryExpression
1099
- && expression.right.type === Syntax.BinaryExpression
1100
- && determineValueRangeBorder(range, expression.left, varname, inverse)
1101
- && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1102
- return range;
1230
+ && expression.right.type === Syntax.BinaryExpression ) {
1231
+
1232
+ if ( expression.operator === "&&"
1233
+ && determineValueRangeBorder(range, expression.left, varname, inverse)
1234
+ && determineValueRangeBorder(range, expression.right, varname, inverse) ) {
1235
+ return range;
1236
+ } else if ( ["||", "??"].includes(expression.operator)
1237
+ && ( determineValueRangeBorder(range, expression.left, varname, inverse)
1238
+ || determineValueRangeBorder(range, expression.right, varname, inverse) )) {
1239
+ return range;
1240
+ }
1241
+
1103
1242
  } else if ( expression.type === Syntax.BinaryExpression
1104
1243
  && determineValueRangeBorder(range, expression, varname, inverse) ) {
1105
1244
  return range;
@@ -1112,24 +1251,24 @@ function collectDataTypeInfo(extendCall, classDoclet) {
1112
1251
  let i = 0,
1113
1252
  name, def, base, pattern, range;
1114
1253
 
1115
- if ( i < args.length && args[i].type === Syntax.Literal && typeof args[i].value === 'string' ) {
1116
- name = args[i++].value;
1254
+ if ( i < args.length && isStringLiteral(args[i]) ) {
1255
+ name = convertValue(args[i++]);
1117
1256
  }
1118
1257
  if ( i < args.length && args[i].type === Syntax.ObjectExpression ) {
1119
1258
  def = createPropertyMap(args[i++]);
1120
1259
  }
1121
1260
  if ( i < args.length ) {
1122
- if ( args[i].type === Syntax.Literal && typeof args[i].value === 'string' ) {
1123
- base = args[i++].value;
1124
- } else if ( args[i].type === Syntax.CallExpression
1125
- && args[i].callee.type === Syntax.MemberExpression
1126
- && getResolvedObjectName(args[i].callee.object) === "sap.ui.base.DataType"
1127
- && args[i].callee.property.type === Syntax.Identifier
1128
- && args[i].callee.property.name === 'getType'
1129
- && args[i].arguments.length === 1
1130
- && args[i].arguments[0].type === Syntax.Literal
1131
- && typeof args[i].arguments[0].value === 'string' ) {
1132
- base = args[i++].arguments[0].value;
1261
+ const node = resolvePotentialWrapperExpression(args[i]);
1262
+
1263
+ if ( isStringLiteral(args[i]) ) {
1264
+ base = convertValue(args[i++]);
1265
+ } else if ( isCaleeMemberExpression(node)
1266
+ && getResolvedObjectName(node.callee.object) === "sap.ui.base.DataType"
1267
+ && node.callee.property.type === Syntax.Identifier
1268
+ && node.callee.property.name === 'getType'
1269
+ && node.arguments.length === 1
1270
+ && isStringLiteral(node.arguments[0]) ) {
1271
+ base = convertValue(args[i++].arguments[0]);
1133
1272
  } else {
1134
1273
  future(`could not identify base type of data type '${name}'`);
1135
1274
  }
@@ -1137,25 +1276,31 @@ function collectDataTypeInfo(extendCall, classDoclet) {
1137
1276
  base = "any";
1138
1277
  }
1139
1278
 
1279
+ const isArrowExpression = isArrowFuncExpression(def && def.isValid && def.isValid.value);
1280
+
1140
1281
  if ( def
1141
1282
  && def.isValid
1142
- && def.isValid.value.type === Syntax.FunctionExpression
1283
+ && [Syntax.FunctionExpression, Syntax.ArrowFunctionExpression].includes(def.isValid.value.type)
1143
1284
  && def.isValid.value.params.length === 1
1144
1285
  && def.isValid.value.params[0].type === Syntax.Identifier
1145
- && def.isValid.value.body.body.length === 1 ) {
1286
+ && (isArrowExpression || def.isValid.value.body.body.length === 1) ) {
1287
+
1146
1288
  const varname = def.isValid.value.params[0].name;
1147
- const stmt = def.isValid.value.body.body[0];
1148
- if ( stmt.type === Syntax.ReturnStatement && stmt.argument ) {
1149
- if ( stmt.argument.type === Syntax.CallExpression
1150
- && stmt.argument.callee.type === Syntax.MemberExpression
1151
- && stmt.argument.callee.object.type === Syntax.Literal
1152
- && stmt.argument.callee.object.regex
1153
- && stmt.argument.callee.property.type === Syntax.Identifier
1154
- && stmt.argument.callee.property.name === 'test' ) {
1155
- pattern = stmt.argument.callee.object.regex.pattern;
1289
+ const stmt = isArrowExpression ? def.isValid.value.body : def.isValid.value.body.body[0];
1290
+
1291
+ if ( isReturningNode(stmt) || isArrowExpression ) {
1292
+ const stmtArgument = resolvePotentialWrapperExpression(
1293
+ isArrowExpression ? stmt : stmt.argument
1294
+ );
1295
+ if ( isCaleeMemberExpression(stmtArgument)
1296
+ && stmtArgument.callee.object.type === Syntax.Literal
1297
+ && stmtArgument.callee.object.regex
1298
+ && stmtArgument.callee.property.type === Syntax.Identifier
1299
+ && stmtArgument.callee.property.name === 'test' ) {
1300
+ pattern = stmtArgument.callee.object.regex.pattern;
1156
1301
  // console.log(pattern);
1157
1302
  } else {
1158
- range = determineValueRange(stmt.argument, varname, false);
1303
+ range = determineValueRange(stmtArgument, varname, false);
1159
1304
  }
1160
1305
  } else if ( stmt.type === Syntax.IfStatement
1161
1306
  && stmt.consequent.type === Syntax.BlockStatement
@@ -1617,7 +1762,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1617
1762
  "Removes a " + n1 + " from the aggregation " + link + ".",
1618
1763
  "",
1619
1764
  "@param {int | string | " + makeTypeString(info, true) + "} " + varname(n1, "variant") + " The " + n1 + " to remove or its index or id",
1620
- "@returns {" + makeTypeString(info, true) + "} The removed " + n1 + " or <code>null</code>",
1765
+ "@returns {" + makeTypeString(info, true) + "|null} The removed " + n1 + " or <code>null</code>",
1621
1766
  info.since ? "@since " + info.since : "",
1622
1767
  info.deprecation ? "@deprecated " + info.deprecation : "",
1623
1768
  info.experimental ? "@experimental " + info.experimental : "",
@@ -1740,7 +1885,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1740
1885
  newJSDoc([
1741
1886
  "Removes an " + n1 + " from the association named " + link + ".",
1742
1887
  "@param {int | sap.ui.core.ID | " + info.type + "} " + varname(n1, "variant") + " The " + n1 + " to be removed or its index or ID",
1743
- "@returns {sap.ui.core.ID} The removed " + n1 + " or <code>null</code>",
1888
+ "@returns {sap.ui.core.ID|null} The removed " + n1 + " or <code>null</code>",
1744
1889
  info.since ? "@since " + info.since : "",
1745
1890
  info.deprecation ? "@deprecated " + info.deprecation : "",
1746
1891
  info.experimental ? "@experimental " + info.experimental : "",
@@ -1812,7 +1957,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1812
1957
  "",
1813
1958
  "@param {object}",
1814
1959
  " [oData] An application-specific payload object that will be passed to the event handler along with the event object when firing the event",
1815
- "@param {function}",
1960
+ "@param {function(sap.ui.base.Event):void}",
1816
1961
  " fnFunction The function to be called when the event occurs",
1817
1962
  "@param {object}",
1818
1963
  " [oListener] Context object to call the event handler with. Defaults to this <code>" + oClassInfo.name + "</code> itself",
@@ -1830,7 +1975,7 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename,
1830
1975
  "",
1831
1976
  "The passed function and listener object must match the ones used for event registration.",
1832
1977
  "",
1833
- "@param {function}",
1978
+ "@param {function(sap.ui.base.Event):void}",
1834
1979
  " fnFunction The function to be called, when the event occurs",
1835
1980
  "@param {object}",
1836
1981
  " [oListener] Context object on which the given function had to be called",
@@ -2582,24 +2727,37 @@ exports.astNodeVisitor = {
2582
2727
  }
2583
2728
  }
2584
2729
 
2585
- if ( node.type === Syntax.ExpressionStatement ) {
2586
- if ( isSapUiDefineCall(node.expression) ) {
2587
- analyzeModuleDefinition(node.expression);
2588
- /*
2730
+ if ( [Syntax.ExpressionStatement, Syntax.LogicalExpression].includes(node.type) ) {
2731
+ let nodeToAnalyze;
2732
+ if (isSapUiDefineCall(node.expression)) {
2733
+ nodeToAnalyze = node.expression;
2734
+
2735
+ /*
2589
2736
  } else if ( isJQuerySapDeclareCall(node.expression)
2590
2737
  && node.expression.arguments.length > 0
2591
2738
  && node.expression.arguments[0].type === Syntax.Literal
2592
2739
  && typeof node.expression.arguments[0].value === "string" ) {
2593
2740
  warning(`module has explicit module name ${node.expression.arguments[0].value}`);
2594
2741
  */
2742
+ } else if (isSapUiDefineCall(node.left)) {
2743
+ nodeToAnalyze = node.left;
2744
+ } else if (isSapUiDefineCall(node.right)) {
2745
+ nodeToAnalyze = node.right;
2595
2746
  }
2596
2747
 
2748
+ nodeToAnalyze && analyzeModuleDefinition(
2749
+ resolvePotentialWrapperExpression(nodeToAnalyze)
2750
+ );
2597
2751
  }
2598
2752
 
2599
- if (node.type === Syntax.ReturnStatement && node.argument && node.argument.type === Syntax.ObjectExpression && /\.designtime\.js$/.test(currentSourceName) ) {
2753
+ const isArrowExpression = isArrowFuncExpression(node) && node.body.type === Syntax.ObjectExpression;
2754
+ const nodeArgument = isArrowExpression
2755
+ ? node.body
2756
+ : resolvePotentialWrapperExpression(node).argument;
2757
+ if (isArrowExpression || (isReturningNode(node) && nodeArgument && nodeArgument.type === Syntax.ObjectExpression) && /\.designtime\.js$/.test(currentSourceName) ) {
2600
2758
 
2601
2759
  // assume this node to return designtime metadata. Collect it and remember it by its module name
2602
- const oDesigntimeInfo = collectDesigntimeInfo(node);
2760
+ const oDesigntimeInfo = collectDesigntimeInfo(nodeArgument);
2603
2761
  if ( oDesigntimeInfo ) {
2604
2762
  designtimeInfos[currentModule.module] = oDesigntimeInfo;
2605
2763
  info(`collected designtime info ${currentModule.module}`);
@@ -2629,14 +2787,19 @@ exports.astNodeVisitor = {
2629
2787
  }
2630
2788
  });
2631
2789
 
2632
- } else if ( node.type === Syntax.ReturnStatement && isExtendCall(node.argument) ) {
2790
+ } else if ( isReturningNode(node)
2791
+ && isExtendCall(resolvePotentialWrapperExpression(node).argument || node.body) ) {
2792
+
2793
+ const nodeArgument = isArrowFuncExpression(node)
2794
+ ? node.body
2795
+ : resolvePotentialWrapperExpression(node).argument;
2633
2796
 
2634
2797
  // return Something.extend(...)
2635
2798
 
2636
- const className = node.argument.arguments[0].value;
2637
- const comment = getLeadingCommentNode(node, className) || getLeadingCommentNode(node.argument, className);
2799
+ const className = convertValue(nodeArgument.arguments[0]);
2800
+ const comment = getLeadingCommentNode(node, className) || getLeadingCommentNode(nodeArgument, className);
2638
2801
  // console.log(`ast node with comment ${comment}`);
2639
- processExtendCall(node.argument, comment, true);
2802
+ processExtendCall(nodeArgument, comment, true);
2640
2803
  } else if ( node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.AssignmentExpression ) {
2641
2804
 
2642
2805
  if ( isCreateDataTypeCall(node.expression.right) ) {
@@ -2677,3 +2840,132 @@ exports.astNodeVisitor = {
2677
2840
  }
2678
2841
 
2679
2842
  };
2843
+
2844
+ (function() {
2845
+ const jsdocType = require("jsdoc/lib/jsdoc/tag/type");
2846
+ const catharsis = require('catharsis');
2847
+ const TYPES = catharsis.Types;
2848
+
2849
+ const toTypeString = (type) => getTypeStrings(type).join("|");
2850
+
2851
+ /*
2852
+ * This function has been copied from jsdoc/lib/jsdoc/tag/type (version 3.6.7)
2853
+ * The copy has been enhanced with the changes from https://github.com/jsdoc/jsdoc/pull/1735
2854
+ * to retain the full function signature for function types and with a further change
2855
+ * to retain the full record type structure.
2856
+ *
2857
+ * JSDoc is copyright (c) 2011-present Michael Mathews micmath@gmail.com and the contributors to JSDoc.
2858
+ */
2859
+ function getTypeStrings(parsedType, isOutermostType) {
2860
+ let applications;
2861
+ let typeString;
2862
+ let types = [];
2863
+ switch (parsedType.type) {
2864
+ case TYPES.AllLiteral:
2865
+ types.push('*');
2866
+ break;
2867
+ case TYPES.FunctionType:
2868
+ typeString = 'function';
2869
+ // #### BEGIN: MODIFIED BY SAP
2870
+ const paramTypes = [];
2871
+ if (parsedType.new) {
2872
+ paramTypes.push(toTypeString(parsedType.new));
2873
+ }
2874
+ if (Array.isArray(parsedType.params)) {
2875
+ paramTypes.push(...parsedType.params.map(toTypeString));
2876
+ }
2877
+ if (paramTypes.length || parsedType.result) {
2878
+ typeString += `(${paramTypes.join(", ")})`;
2879
+ }
2880
+ if (parsedType.result) {
2881
+ typeString += `:${toTypeString(parsedType.result)}`;
2882
+ }
2883
+ types.push(typeString);
2884
+ // #### END: MODIFIED BY SAP
2885
+ break;
2886
+ case TYPES.NameExpression:
2887
+ types.push(parsedType.name);
2888
+ break;
2889
+ case TYPES.NullLiteral:
2890
+ types.push('null');
2891
+ break;
2892
+ case TYPES.RecordType:
2893
+ // #### BEGIN: MODIFIED BY SAP
2894
+ // types.push('Object');
2895
+ if (Array.isArray(parsedType.fields)) {
2896
+ typeString = `{${parsedType.fields.map(
2897
+ ({key,value}) => {
2898
+ const keyString = catharsis.stringify(key);
2899
+ if (value) {
2900
+ var propertyPlusType = `${keyString}: ${toTypeString(value)}`;
2901
+ return propertyPlusType;
2902
+ } else {
2903
+ let pos;
2904
+ if (keyString && (pos = keyString.indexOf(":")) > -1) {
2905
+ // When no space is present between colon and type (e.g. the structure looks like "{x:number}" with no space after
2906
+ // the colon), then Catharsis parses this as property name "x:number" with no type given.
2907
+ // In this case give a clear hint that this space is needed and throw an error to prevent such issues from being merged
2908
+ const realName = keyString.substring(0, pos);
2909
+ const realValue = keyString.substring(pos + 1, keyString.length);
2910
+ let message = `Cannot parse the "${keyString}" part of "${parsedType.typeExpression}" in RecordType (log output above may give a hint in which file).\n`;
2911
+ message += `Did you mean to specify a property "${realName}" of type "${realValue}"? Then insert a space after the colon and write "${realName}: ${realValue}".`
2912
+ error(message);
2913
+ return "x: any"; // unparseable property set to "any" - but the JSDoc run will fail now, anyway
2914
+ } else {
2915
+ // only property given without type; this will be turned into type "any" further downstream
2916
+ return keyString;
2917
+ }
2918
+ }
2919
+ }
2920
+ ).join(', ')}}`;
2921
+ types.push(typeString);
2922
+ } else {
2923
+ types.push('Object');
2924
+ }
2925
+ // #### END: MODIFIED BY SAP
2926
+ break;
2927
+ case TYPES.TypeApplication:
2928
+ // if this is the outermost type, we strip the modifiers; otherwise, we keep them
2929
+ if (isOutermostType) {
2930
+ applications = parsedType.applications.map(application =>
2931
+ catharsis.stringify(application)).join(', ');
2932
+ typeString = `${getTypeStrings(parsedType.expression)[0]}.<${applications}>`;
2933
+ types.push(typeString);
2934
+ }
2935
+ else {
2936
+ types.push( catharsis.stringify(parsedType) );
2937
+ }
2938
+ break;
2939
+ case TYPES.TypeUnion:
2940
+ parsedType.elements.forEach(element => {
2941
+ types = types.concat( getTypeStrings(element) );
2942
+ });
2943
+ break;
2944
+ case TYPES.UndefinedLiteral:
2945
+ types.push('undefined');
2946
+ break;
2947
+ case TYPES.UnknownLiteral:
2948
+ types.push('?');
2949
+ break;
2950
+ default:
2951
+ // this shouldn't happen
2952
+ throw new Error(`unrecognized type ${parsedType.type} in parsed type: ${parsedType}`);
2953
+ }
2954
+ return types;
2955
+ }
2956
+
2957
+ const origParse = jsdocType.parse;
2958
+ jsdocType.parse = function() {
2959
+ const tagInfo = origParse.apply(this, arguments);
2960
+ // #### BEGIN: MODIFIED BY SAP
2961
+ if ( tagInfo && (/function/.test(tagInfo.typeExpression) || /\{.*\}/.test(tagInfo.typeExpression)) && tagInfo.parsedType ) {
2962
+ // #### END: MODIFIED BY SAP
2963
+ // console.info("old typeExpression", tagInfo.typeExpression);
2964
+ // console.info("old parse tree", tagInfo.parsedType);
2965
+ // console.info("old parse result", tagInfo.type);
2966
+ tagInfo.type = getTypeStrings(tagInfo.parsedType);
2967
+ // console.info("new parse result", tagInfo.type);
2968
+ }
2969
+ return tagInfo;
2970
+ }
2971
+ }());