@sap/cds-compiler 2.12.0 → 2.15.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 (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -74,39 +74,39 @@ function createOptionProcessor() {
74
74
  */
75
75
  function command(cmdString) {
76
76
  /** @type {object} */
77
- const command = {
77
+ const cmd = {
78
78
  options: {},
79
79
  positionalArguments: [],
80
- option,
80
+ option: commandOption,
81
81
  positionalArgument: (argumentDefinition) => {
82
- _setPositionalArguments(argumentDefinition, command.positionalArguments);
83
- return command;
82
+ _setPositionalArguments(argumentDefinition, cmd.positionalArguments);
83
+ return cmd;
84
84
  },
85
- help,
85
+ help: commandHelp,
86
86
  ..._parseCommandString(cmdString)
87
87
  };
88
- if (optionProcessor.commands[command.longName]) {
89
- throw new Error(`Duplicate assignment for long command ${command.longName}`);
88
+ if (optionProcessor.commands[cmd.longName]) {
89
+ throw new Error(`Duplicate assignment for long command ${cmd.longName}`);
90
90
  }
91
- optionProcessor.commands[command.longName] = command;
91
+ optionProcessor.commands[cmd.longName] = cmd;
92
92
 
93
- if (command.shortName) {
94
- if (optionProcessor.commands[command.shortName]) {
95
- throw new Error(`Duplicate assignment for short command ${command.shortName}`);
93
+ if (cmd.shortName) {
94
+ if (optionProcessor.commands[cmd.shortName]) {
95
+ throw new Error(`Duplicate assignment for short command ${cmd.shortName}`);
96
96
  }
97
- optionProcessor.commands[command.shortName] = command;
97
+ optionProcessor.commands[cmd.shortName] = cmd;
98
98
  }
99
- return command;
99
+ return cmd;
100
100
 
101
101
  // Command API: Define a command option
102
- function option(optString, validValues, options) {
103
- return _addOption(command, optString, validValues, options);
102
+ function commandOption(optString, validValues, options) {
103
+ return _addOption(cmd, optString, validValues, options);
104
104
  }
105
105
 
106
106
  // Command API: Define the command help text
107
- function help(text) {
108
- command.helpText = text;
109
- return command;
107
+ function commandHelp(text) {
108
+ cmd.helpText = text;
109
+ return cmd;
110
110
  }
111
111
  }
112
112
 
@@ -156,33 +156,33 @@ function createOptionProcessor() {
156
156
  * @private
157
157
  * @see option()
158
158
  */
159
- function _addOption(command, optString, validValues, options) {
159
+ function _addOption(cmd, optString, validValues, options) {
160
160
  const opt = _parseOptionString(optString, validValues);
161
161
  Object.assign(opt, options);
162
162
 
163
- if (command.options[opt.longName]) {
163
+ if (cmd.options[opt.longName]) {
164
164
  throw new Error(`Duplicate assignment for long option ${opt.longName}`);
165
165
  } else if (optionProcessor.options[opt.longName]) {
166
166
  // This path is only taken if optString is for commands
167
167
  optionProcessor.optionClashes.push({
168
168
  option: opt.longName,
169
- description: `Command '${command.longName}' has option clash with general options for: ${opt.longName}`
169
+ description: `Command '${cmd.longName}' has option clash with general options for: ${opt.longName}`
170
170
  });
171
171
  }
172
- command.options[opt.longName] = opt;
172
+ cmd.options[opt.longName] = opt;
173
173
  if (opt.shortName) {
174
- if (command.options[opt.shortName]) {
174
+ if (cmd.options[opt.shortName]) {
175
175
  throw new Error(`Duplicate assignment for short option ${opt.shortName}`);
176
176
  } else if (optionProcessor.options[opt.shortName]) {
177
177
  // This path is only taken if optString is for commands
178
178
  optionProcessor.optionClashes.push({
179
179
  option: opt.shortName,
180
- description: `Command '${command.longName}' has option clash with general options for: ${opt.shortName}`
180
+ description: `Command '${cmd.longName}' has option clash with general options for: ${opt.shortName}`
181
181
  });
182
182
  }
183
- command.options[opt.shortName] = opt;
183
+ cmd.options[opt.shortName] = opt;
184
184
  }
185
- return command;
185
+ return cmd;
186
186
  }
187
187
 
188
188
  // Internal: Parse one command string like "F, toFoo". Return an object like this
@@ -339,47 +339,12 @@ function createOptionProcessor() {
339
339
  // No more options after '--'
340
340
  seenDashDash = true;
341
341
  }
342
+ else if (!seenDashDash && arg.startsWith('--')) {
343
+ i += processOption(i);
344
+ }
342
345
  else if (!seenDashDash && arg.startsWith('-')) {
343
- if (result.command) {
344
- // We already have a command
345
- const opt = optionProcessor.commands[result.command].options[arg];
346
- if (opt) {
347
- // Found as a command option
348
- i += processOption(i, opt, result.command);
349
- } else {
350
- // No command option, try general options as fallback
351
- const opt = optionProcessor.options[arg];
352
- if (opt) {
353
- i += processOption(i, opt, false);
354
- } else {
355
- // Not found at all, put into unknownOptions if it is an option
356
- // for another cdsc command.
357
- // We dig into the other cdsc commands in order to check if
358
- // the option expects a parameter and if so to take the next argument as a value
359
- if (Object.keys(optionProcessor.commands).some(cmd => optionProcessor.commands[cmd].options[arg])) {
360
- const cmd = optionProcessor.commands[
361
- Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg])
362
- ];
363
- i += processOption(i, cmd.options[arg], optionProcessor.commands[result.command], true);
364
- } else { // still add it to the unknownOptions
365
- result.unknownOptions.push(`Unknown option "${arg}" for the command "${result.command}"`);
366
- // if the next argument looks like an argument for this unknown option => skip it
367
- if ((i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cds|json)$'))
368
- i++;
369
- }
370
- }
371
- }
372
- } else {
373
- // We don't have a command
374
- const opt = optionProcessor.options[arg];
375
- if (opt) {
376
- // Found as a general option
377
- i += processOption(i, opt, false);
378
- } else {
379
- // Not found, complain
380
- result.unknownOptions.push(`Unknown option "${arg}"`);
381
- }
382
- }
346
+ splitSingleLetterOption(argv, i); // `-ab` -> `-a -b`
347
+ i += processOption(i);
383
348
  }
384
349
  else {
385
350
  // Command or arg
@@ -407,7 +372,11 @@ function createOptionProcessor() {
407
372
  const missingArg = getCurrentPositionArguments().find((arg) => arg.required && !result.args[arg.name]);
408
373
  if (missingArg) {
409
374
  const forCommand = result.command ? ` for '${ result.command }'` : '';
410
- result.errors.push(`Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`)
375
+ const errorMsg = `Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`;
376
+ if (forCommand)
377
+ result.cmdErrors.push(errorMsg)
378
+ else
379
+ result.errors.push(errorMsg)
411
380
  }
412
381
 
413
382
  return result;
@@ -448,57 +417,127 @@ function createOptionProcessor() {
448
417
 
449
418
  // (Note that this works on 'argv' and 'result' from above).
450
419
  // Process 'argv[i]' as an option.
451
- // Check the option definition in 'opt' to see if a parameter is expected.
420
+ // Check the option definition to see if a parameter is expected.
452
421
  // If so, take it (complain if one is found in 'argv').
453
422
  // Populate 'result.options' with the result. Return the number params found (0 or 1).
454
- function processOption(i, opt, command, unknownOption = false) {
455
- // Does this option expect a parameter?
456
- if (opt.param) {
457
- if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
458
- // There should be a param but isn't - complain
459
- let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
460
- if (command && unknownOption) {
461
- result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
462
- } else if (command) {
463
- error = `${error} of command "${command}"`;
464
- result.cmdErrors.push(error);
465
- } else {
466
- result.errors.push(error);
423
+ function processOption(i) {
424
+ const arg = argv[i];
425
+ let currentCommand = result.command;
426
+
427
+ // First check top-level options
428
+ let currentOption = optionProcessor.options[arg];
429
+ if (currentCommand) {
430
+ // If there is a command and it has an option that overrides it, use it instead.
431
+ const cmdOpt = optionProcessor.commands[currentCommand].options[arg];
432
+ if (cmdOpt)
433
+ currentOption = cmdOpt;
434
+ else if (currentOption)
435
+ // Otherwise, if there exist a top-level option, set 'command' to null.
436
+ currentCommand = null;
437
+ }
438
+
439
+ if (!currentOption)
440
+ return reportUnknown();
441
+
442
+ if (!currentOption.param) {
443
+ setCurrentOption(true);
444
+ return 0;
445
+ }
446
+
447
+ const param = paramForOption(currentOption);
448
+ if (param === null)
449
+ return 0;
450
+ setCurrentOption(param);
451
+ return 1;
452
+
453
+ /**
454
+ * Report that an option is unknown. If the option exists for other
455
+ * commands or if the next argument looks like a param, return 1,
456
+ * otherwise 0, indicating how many argv fields have been consumed.
457
+ *
458
+ * @returns {number}
459
+ */
460
+ function reportUnknown() {
461
+ if (currentCommand)
462
+ result.unknownOptions.push(`Unknown option "${arg}" for the command "${currentCommand}"`);
463
+ else
464
+ result.unknownOptions.push(`Unknown option "${arg}"`);
465
+
466
+ if (currentCommand) {
467
+ // Not found at all. We dig into the other cdsc commands in order to check if
468
+ // the option expects a parameter and if so to take the next argument as a value
469
+ const otherCmd = Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg]);
470
+ const otherCmdOpt = otherCmd && optionProcessor.commands[otherCmd].options[arg];
471
+ if (otherCmdOpt && hasParamForUnknown(otherCmdOpt)) {
472
+ return 1
467
473
  }
468
- return 0;
469
474
  }
470
- else {
471
- // Take the option with the parameter
472
- const value = argv[i + 1];
473
- const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
474
- if (command) {
475
- // if an unknown option for a command => add it to the array and warn about
476
- if (unknownOption) {
477
- result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
478
- } else {
479
- result.options[command][opt.camelName] = value;
480
- if (!isValidOptionValue(opt, value)) {
481
- result.cmdErrors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
482
- }
483
- }
484
- } else {
485
- result.options[opt.camelName] = value;
486
- if (!isValidOptionValue(opt, value)) {
487
- result.errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
488
- }
489
- }
475
+
476
+ if (hasParamForUnknown(null))
490
477
  return 1;
478
+
479
+ return 0;
480
+ }
481
+
482
+ function setCurrentOption(val) {
483
+ if (currentCommand) {
484
+ if (!result.options[currentCommand])
485
+ result.options[currentCommand] = {};
486
+ result.options[currentCommand][currentOption.camelName] = val;
487
+ }
488
+ else {
489
+ result.options[currentOption.camelName] = val;
491
490
  }
492
491
  }
493
- // No parameter, take option as bool
494
- if (command) {
495
- unknownOption
496
- ? result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`)
497
- : result.options[command][opt.camelName] = true;
498
- } else {
499
- result.options[opt.camelName] = true;
492
+
493
+ function reportMissingParam(opt) {
494
+ let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
495
+ if (currentCommand) {
496
+ error = `${error} of command "${currentCommand}"`;
497
+ result.cmdErrors.push(error);
498
+ } else {
499
+ result.errors.push(error);
500
+ }
501
+ }
502
+
503
+ function reportInvalidValue(opt, value) {
504
+ const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
505
+ const errors = currentCommand ? result.cmdErrors : result.errors;
506
+ errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
507
+ }
508
+
509
+ /**
510
+ * Get the value for the option's parameter. If the option does not require one,
511
+ * returns `null`. Reports missing parameters and invalid values.
512
+ *
513
+ * @returns {null|*}
514
+ */
515
+ function paramForOption(opt, reportMissing = true) {
516
+ if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
517
+ if (reportMissing)
518
+ reportMissingParam(opt)
519
+ return null;
520
+ }
521
+
522
+ const value = argv[i + 1];
523
+ if (!isValidOptionValue(opt, value) && reportMissing) {
524
+ reportInvalidValue(opt, value);
525
+ }
526
+ return value;
527
+ }
528
+
529
+ /**
530
+ * Returns true if:
531
+ * - we didn't find an option (opt === null) _or_
532
+ * - we found an option and it requires a param
533
+ * _and_ if the next arg looks like an argument.
534
+ *
535
+ * @param {object|null} opt
536
+ * @returns {boolean}
537
+ */
538
+ function hasParamForUnknown(opt) {
539
+ return ((!opt || opt.param) && (i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cdl|cds|json)$'));
500
540
  }
501
- return 0;
502
541
  }
503
542
  }
504
543
 
@@ -506,16 +545,16 @@ function createOptionProcessor() {
506
545
  // If 'command' is supplied, check only 'options.command', otherwise check
507
546
  // only top-level options
508
547
  // Return an array of complaints (possibly empty)
509
- function verifyOptions(options, command = undefined, silent = false) {
548
+ function verifyOptions(options, commandName = undefined, silent = false) {
510
549
  const result = [];
511
550
  let opts;
512
551
 
513
- if((options.betaMode || options.beta) && !options.testMode && !silent) {
552
+ if ((options.betaMode || options.beta) && !options.testMode && !silent) {
514
553
  const mode = options.beta ? 'beta' : 'beta-mode';
515
554
  result.push(`Option --${mode} was used. This option should not be used in productive scenarios!`)
516
555
  }
517
556
 
518
- if(options) {
557
+ if (options) {
519
558
  [
520
559
  'defaultBinaryLength', 'defaultStringLength',
521
560
  /*'length', 'precision', 'scale'*/
@@ -528,13 +567,13 @@ function createOptionProcessor() {
528
567
  });
529
568
  }
530
569
 
531
- if (command) {
532
- const cmd = optionProcessor.commands[command];
570
+ if (commandName) {
571
+ const cmd = optionProcessor.commands[commandName];
533
572
  if (!cmd) {
534
- throw new Error(`Expected existing command: "${command}"`);
573
+ throw new Error(`Expected existing command: "${cmd}"`);
535
574
  }
536
575
  opts = cmd.options;
537
- options = options[command] || {};
576
+ options = options[cmd] || {};
538
577
  if (typeof options === 'boolean') {
539
578
  // Special case: command without options
540
579
  options = {};
@@ -548,12 +587,12 @@ function createOptionProcessor() {
548
587
  let error;
549
588
  if (!opt) {
550
589
  // Don't report commands in top-level options
551
- if ((command || !optionProcessor.commands[camelName]) && !silent) {
552
- error = `Unknown option "${command ? command + '.' : ''}${camelName}"`;
590
+ if ((commandName || !optionProcessor.commands[camelName]) && !silent) {
591
+ error = `Unknown option "${commandName ? commandName + '.' : ''}${camelName}"`;
553
592
  }
554
593
  } else {
555
594
  const param = options[camelName];
556
- error = verifyOptionParam(param, opt, command ? command + '.' : '');
595
+ error = verifyOptionParam(param, opt, commandName ? commandName + '.' : '');
557
596
  }
558
597
  if (error) {
559
598
  result.push(error);
@@ -596,15 +635,30 @@ function createOptionProcessor() {
596
635
 
597
636
  // Return an array of unique camelNames of the options for the specified command
598
637
  // If invalid command -> an empty array
599
- function camelOptionsForCommand(command) {
600
- if (!command || !optionProcessor.commands[command])
638
+ function camelOptionsForCommand(cmd) {
639
+ if (!cmd || !optionProcessor.commands[cmd])
601
640
  return []
602
- const cmd = optionProcessor.commands[command];
641
+ cmd = optionProcessor.commands[cmd];
603
642
  const names = Object.keys(cmd.options).map(name => cmd.options[name].camelName);
604
643
  return [...new Set(names)];
605
644
  }
606
645
  }
607
646
 
647
+ /**
648
+ * Splits `-abc` into `-a -b -c`. Does this in-place on argv.
649
+ *
650
+ * @param {string[]} argv Argument array
651
+ * @param {number} i Current option index.
652
+ */
653
+ function splitSingleLetterOption(argv, i) {
654
+ const arg = argv[i];
655
+ if (arg.length > 2) { // must be at least `-ab`.
656
+ const rest = argv.slice(i + 1);
657
+ argv.length = i; // trim array
658
+ argv.push(...arg.split('').slice(1).map(a => `-${a}`), ...rest);
659
+ }
660
+ }
661
+
608
662
  /**
609
663
  * Return a camelCase name "fooBar" for a long option "--foo-bar"
610
664
  */
@@ -13,42 +13,22 @@
13
13
  * @param {CSN.Element} member Member to be checked
14
14
  */
15
15
  function checkCoreMediaTypeAllowence(member) {
16
- const allowedCoreMediaTypes = [
17
- 'cds.String',
18
- 'cds.LargeString',
19
- 'cds.hana.VARCHAR',
20
- 'cds.hana.CHAR',
21
- 'cds.Binary',
22
- 'cds.LargeBinary',
23
- 'cds.hana.CLOB',
24
- 'cds.hana.BINARY',
25
- ];
26
- if (member['@Core.MediaType'] && member.type && !allowedCoreMediaTypes.includes(member.type)) {
16
+ const allowedCoreMediaTypes = {
17
+ 'cds.String': 1,
18
+ 'cds.LargeString': 1,
19
+ 'cds.hana.VARCHAR': 1,
20
+ 'cds.hana.CHAR': 1,
21
+ 'cds.Binary': 1,
22
+ 'cds.LargeBinary': 1,
23
+ 'cds.hana.CLOB': 1,
24
+ 'cds.hana.BINARY': 1,
25
+ };
26
+ if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalBaseType(member.type) in allowedCoreMediaTypes)) {
27
27
  this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
28
28
  'Element annotated with “@Core.MediaType” should be of a type mapped to $(NAMES)');
29
29
  }
30
30
  }
31
31
 
32
- /**
33
- * Make sure only one element in a definition is annotated with `@Core.MediaType`
34
- * This is only OData V2 relevant.
35
- *
36
- * @param {CSN.Artifact} artifact Definition to be checked
37
- * @param {string} artifactName The name of the artifact
38
- */
39
- function checkForMultipleCoreMediaTypes(artifact, artifactName) {
40
- if (!this.csnUtils.getServiceName(artifactName))
41
- return;
42
- if (this.options.toOdata && this.options.toOdata.version === 'v2' && artifact.elements) {
43
- const mediaTypeElementsNames = Object.keys(artifact.elements)
44
- .filter(elementName => artifact.elements[elementName]['@Core.MediaType']);
45
- if (mediaTypeElementsNames.length > 1) {
46
- this.error(null, artifact.$path, { names: mediaTypeElementsNames },
47
- `Multiple elements $(NAMES) annotated with “@Core.MediaType”, OData V2 allows only one`);
48
- }
49
- }
50
- }
51
-
52
32
  /**
53
33
  * Check if `@Aggregation.default` is assigned together with `@Analytics.Measure`
54
34
  *
@@ -82,13 +62,12 @@ function checkAtSapAnnotations(node) {
82
62
  function checkReadOnlyAndInsertOnly(artifact, artifactName) {
83
63
  if (!this.csnUtils.getServiceName(artifactName))
84
64
  return;
85
- if (artifact.kind && [ 'entity', 'view' ].includes(artifact.kind) && artifact['@readonly'] && artifact['@insertonly'])
65
+ if (artifact.kind === 'entity' && artifact['@readonly'] && artifact['@insertonly'])
86
66
  this.warning(null, artifact.$path, 'Annotations “@readonly” and “@insertonly” can\'t be assigned in combination');
87
67
  }
88
68
 
89
69
  module.exports = {
90
70
  checkCoreMediaTypeAllowence,
91
- checkForMultipleCoreMediaTypes,
92
71
  checkAnalytics,
93
72
  checkAtSapAnnotations,
94
73
  checkReadOnlyAndInsertOnly,
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { forEachMemberRecursively } = require('../model/csnUtils');
4
-
5
3
  // Only to be used with validator.js - a correct `this` value needs to be provided!
6
4
 
7
5
  /**
@@ -43,35 +41,4 @@ function validateAssociationsInItems(member) {
43
41
  }
44
42
  }
45
43
 
46
- /**
47
- * Check that there are no .items containing .items.
48
- *
49
- * @param {CSN.Artifact} art Artifact
50
- * @param {string} artName Name of the artifact
51
- */
52
- function checkChainedArray(art, artName) {
53
- if (!this.csnUtils.getServiceName(artName))
54
- return;
55
- checkIfItemsOfItems.bind(this)(art);
56
- forEachMemberRecursively(art, checkIfItemsOfItems.bind(this));
57
-
58
- /**
59
- *
60
- * @param {object} construct the construct to be checked
61
- */
62
- function checkIfItemsOfItems(construct) {
63
- const constructType = this.csnUtils.effectiveType(construct);
64
- if (constructType.items) {
65
- if (constructType.items.items) {
66
- this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
67
- return;
68
- }
69
-
70
- const itemsType = this.csnUtils.effectiveType(constructType.items);
71
- if (itemsType.items)
72
- this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
73
- }
74
- }
75
- }
76
-
77
- module.exports = { validateAssociationsInItems, checkChainedArray };
44
+ module.exports = { validateAssociationsInItems };
@@ -13,7 +13,8 @@
13
13
  function validateCdsPersistenceAnnotation(artifact, artifactName, prop, path) {
14
14
  if (artifact.kind === 'entity') {
15
15
  // filter for 'table', 'udf', 'calcview' === true
16
- const TableUdfCv = Object.keys(artifact).filter(p => [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ].includes(p) && artifact[p]);
16
+ const persistenceAnnos = [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ];
17
+ const TableUdfCv = Object.keys(artifact).filter(p => persistenceAnnos.includes(p) && artifact[p]);
17
18
  if (TableUdfCv.length > 1)
18
19
  this.error(null, path, `Annotations ${ TableUdfCv.join(', ') } can't be used in combination`);
19
20
  }
@@ -30,6 +30,7 @@ function enrichCsn( csn ) {
30
30
  type: simpleRef,
31
31
  target: simpleRef,
32
32
  includes: simpleRef,
33
+ columns,
33
34
  // Annotations are ignored.
34
35
  '@': () => { /* ignore annotations */ },
35
36
  };
@@ -40,7 +41,7 @@ function enrichCsn( csn ) {
40
41
  cleanupCallbacks = [];
41
42
  };
42
43
 
43
- const { inspectRef, artifactRef } = csnRefs( csn );
44
+ const { inspectRef, artifactRef, getElement } = csnRefs( csn );
44
45
  const csnPath = [];
45
46
  if (csn.definitions)
46
47
  dictionary( csn, 'definitions', csn.definitions );
@@ -82,6 +83,21 @@ function enrichCsn( csn ) {
82
83
  csnPath.pop();
83
84
  }
84
85
 
86
+ // eslint-disable-next-line jsdoc/require-jsdoc
87
+ function columns( parent, prop, node ) {
88
+ // Establish the link relationships
89
+ parent[prop].forEach((column) => {
90
+ const element = getElement(column);
91
+ if (element) {
92
+ setProp(column, '_element', element);
93
+ cleanupCallbacks.push(() => delete column._element);
94
+ setProp(element, '_column', column);
95
+ cleanupCallbacks.push(() => delete element._column);
96
+ }
97
+ });
98
+ standard(parent, prop, node);
99
+ }
100
+
85
101
  // eslint-disable-next-line jsdoc/require-jsdoc
86
102
  function simpleRef( node, prop ) {
87
103
  setProp(node, '$path', [ ...csnPath ]);
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Only to be used with validator.js - a correct this value needs to be provided!
4
4
 
5
+ const { ModelError } = require('../base/error');
6
+
5
7
  /**
6
8
  * Assert that targets of associations and compositions are entities.
7
9
  *
@@ -22,7 +24,7 @@ function invalidTarget(member) {
22
24
  if (mem.target) {
23
25
  const target = this.csn.definitions[mem.target];
24
26
  if (!target)
25
- throw new Error(`Expected target ${ mem.target }`);
27
+ throw new ModelError(`Expected target ${ mem.target }`);
26
28
  if (target.kind !== 'entity') {
27
29
  const isAssoc = this.csnUtils.getFinalBaseType(member.type) !== 'cds.Composition';
28
30
  this.error(
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
 
4
+ const { ModelError } = require('../base/error');
5
+
4
6
  /**
5
7
  * Trigger a recompilation in case of an association without .keys and without .on
6
8
  *
@@ -10,7 +12,7 @@
10
12
  */
11
13
  function managedWithoutKeys(member, memberName, prop) {
12
14
  if (prop === 'elements' && member.target && !member.keys && !member.on) { // trigger recompilation
13
- throw new Error('Expected association to have either an on-condition or foreign keys.');
15
+ throw new ModelError('Expected association to have either an on-condition or foreign keys.');
14
16
  }
15
17
  }
16
18
 
@@ -29,7 +29,7 @@ function validateSelectItems(query) {
29
29
  }
30
30
  });
31
31
  // .call() with 'this' to ensure we have access to the options
32
- rejectManagedAssociationsAndStructuresForHdbcsNames.call(this, SELECT, SELECT.$path);
32
+ rejectManagedAssociationsAndStructuresForHdbcdsNames.call(this, SELECT, SELECT.$path);
33
33
  }
34
34
 
35
35
 
@@ -41,10 +41,10 @@ function validateSelectItems(query) {
41
41
  * @param {CSN.Artifact} queryArtifact the query artifact which should be checked
42
42
  * @param {CSN.Path} artifactPath the path to that artifact
43
43
  */
44
- function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, artifactPath) {
44
+ function rejectManagedAssociationsAndStructuresForHdbcdsNames(queryArtifact, artifactPath) {
45
45
  if (this.options.transformation === 'hdbcds' && this.options.sqlMapping === 'hdbcds') {
46
46
  forEachGeneric(queryArtifact, 'elements', (selectItem, elemName, prop, elementPath) => {
47
- if (this.csnUtils.isManagedAssociationElement(selectItem))
47
+ if (this.csnUtils.isManagedAssociation(selectItem))
48
48
  this.error('query-unexpected-assoc-hdbcds', elementPath);
49
49
  if (this.csnUtils.isStructured(selectItem))
50
50
  this.error('query-unexpected-structure-hdbcds', elementPath);
@@ -52,4 +52,4 @@ function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, arti
52
52
  }
53
53
  }
54
54
 
55
- module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcsNames };
55
+ module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcdsNames };