@sap/cds-compiler 2.15.8 → 3.1.0

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 (127) hide show
  1. package/CHANGELOG.md +102 -1590
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +61 -46
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  6. package/doc/CHANGELOG_BETA.md +26 -5
  7. package/doc/CHANGELOG_DEPRECATED.md +55 -1
  8. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  9. package/doc/Versioning.md +20 -1
  10. package/lib/api/.eslintrc.json +2 -2
  11. package/lib/api/main.js +282 -156
  12. package/lib/api/options.js +17 -88
  13. package/lib/api/validate.js +6 -10
  14. package/lib/base/keywords.js +280 -110
  15. package/lib/base/message-registry.js +85 -25
  16. package/lib/base/messages.js +119 -89
  17. package/lib/base/model.js +46 -2
  18. package/lib/base/optionProcessorHelper.js +53 -21
  19. package/lib/checks/actionsFunctions.js +15 -12
  20. package/lib/checks/annotationsOData.js +1 -1
  21. package/lib/checks/cdsPersistence.js +1 -0
  22. package/lib/checks/elements.js +6 -6
  23. package/lib/checks/invalidTarget.js +1 -1
  24. package/lib/checks/nonexpandableStructured.js +1 -1
  25. package/lib/checks/queryNoDbArtifacts.js +2 -1
  26. package/lib/checks/selectItems.js +101 -15
  27. package/lib/checks/types.js +7 -8
  28. package/lib/checks/utils.js +2 -2
  29. package/lib/checks/validator.js +3 -3
  30. package/lib/compiler/assert-consistency.js +78 -21
  31. package/lib/compiler/base.js +6 -4
  32. package/lib/compiler/builtins.js +177 -10
  33. package/lib/compiler/checks.js +1 -1
  34. package/lib/compiler/define.js +28 -23
  35. package/lib/compiler/extend.js +75 -18
  36. package/lib/compiler/finalize-parse-cdl.js +25 -18
  37. package/lib/compiler/index.js +27 -11
  38. package/lib/compiler/moduleLayers.js +7 -0
  39. package/lib/compiler/populate.js +26 -39
  40. package/lib/compiler/propagator.js +12 -7
  41. package/lib/compiler/resolve.js +207 -236
  42. package/lib/compiler/shared.js +100 -93
  43. package/lib/compiler/tweak-assocs.js +13 -20
  44. package/lib/compiler/utils.js +20 -6
  45. package/lib/edm/annotations/preprocessAnnotations.js +12 -13
  46. package/lib/edm/csn2edm.js +35 -37
  47. package/lib/edm/edm.js +22 -13
  48. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  49. package/lib/edm/edmInboundChecks.js +85 -0
  50. package/lib/edm/edmPreprocessor.js +338 -689
  51. package/lib/edm/edmUtils.js +97 -67
  52. package/lib/gen/Dictionary.json +29 -9
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +8 -31
  55. package/lib/gen/language.tokens +105 -114
  56. package/lib/gen/languageLexer.interp +1 -34
  57. package/lib/gen/languageLexer.js +892 -1007
  58. package/lib/gen/languageLexer.tokens +95 -106
  59. package/lib/gen/languageParser.js +20629 -22474
  60. package/lib/inspect/.eslintrc.json +4 -0
  61. package/lib/inspect/index.js +14 -0
  62. package/lib/inspect/inspectModelStatistics.js +81 -0
  63. package/lib/inspect/inspectPropagation.js +189 -0
  64. package/lib/inspect/inspectUtils.js +44 -0
  65. package/lib/json/from-csn.js +74 -69
  66. package/lib/json/to-csn.js +17 -14
  67. package/lib/language/antlrParser.js +2 -2
  68. package/lib/language/docCommentParser.js +61 -38
  69. package/lib/language/errorStrategy.js +52 -40
  70. package/lib/language/genericAntlrParser.js +424 -292
  71. package/lib/language/language.g4 +604 -687
  72. package/lib/language/multiLineStringParser.js +14 -42
  73. package/lib/language/textUtils.js +44 -0
  74. package/lib/main.d.ts +28 -42
  75. package/lib/main.js +104 -81
  76. package/lib/model/api.js +1 -1
  77. package/lib/model/csnRefs.js +57 -30
  78. package/lib/model/csnUtils.js +189 -287
  79. package/lib/model/revealInternalProperties.js +32 -10
  80. package/lib/model/sortViews.js +32 -31
  81. package/lib/modelCompare/compare.js +3 -0
  82. package/lib/optionProcessor.js +91 -57
  83. package/lib/render/.eslintrc.json +1 -1
  84. package/lib/render/DuplicateChecker.js +4 -7
  85. package/lib/render/manageConstraints.js +70 -2
  86. package/lib/render/toCdl.js +387 -367
  87. package/lib/render/toHdbcds.js +20 -16
  88. package/lib/render/toRename.js +44 -22
  89. package/lib/render/toSql.js +81 -59
  90. package/lib/render/utils/common.js +16 -3
  91. package/lib/render/utils/sql.js +20 -19
  92. package/lib/sql-identifier.js +6 -0
  93. package/lib/transform/db/.eslintrc.json +3 -2
  94. package/lib/transform/db/associations.js +43 -35
  95. package/lib/transform/db/cdsPersistence.js +5 -16
  96. package/lib/transform/db/constraints.js +1 -1
  97. package/lib/transform/db/expansion.js +7 -6
  98. package/lib/transform/db/flattening.js +16 -18
  99. package/lib/transform/db/transformExists.js +7 -5
  100. package/lib/transform/db/views.js +3 -3
  101. package/lib/transform/draft/.eslintrc.json +2 -2
  102. package/lib/transform/draft/db.js +6 -6
  103. package/lib/transform/draft/odata.js +6 -7
  104. package/lib/transform/forHanaNew.js +30 -24
  105. package/lib/transform/forOdataNew.js +14 -16
  106. package/lib/transform/localized.js +35 -25
  107. package/lib/transform/odata/toFinalBaseType.js +10 -10
  108. package/lib/transform/odata/typesExposure.js +17 -8
  109. package/lib/transform/odata/utils.js +1 -38
  110. package/lib/transform/transformUtilsNew.js +63 -77
  111. package/lib/transform/translateAssocsToJoins.js +2 -2
  112. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  113. package/lib/transform/universalCsn/coreComputed.js +11 -6
  114. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  115. package/lib/utils/file.js +31 -21
  116. package/lib/utils/moduleResolve.js +0 -1
  117. package/lib/utils/timetrace.js +20 -21
  118. package/package.json +34 -4
  119. package/share/messages/syntax-expected-integer.md +9 -8
  120. package/doc/ApiMigration.md +0 -237
  121. package/doc/CommandLineMigration.md +0 -58
  122. package/doc/ErrorMessages.md +0 -175
  123. package/doc/FioriAnnotations.md +0 -94
  124. package/doc/ODataTransformation.md +0 -273
  125. package/lib/backends.js +0 -529
  126. package/lib/checks/unknownMagic.js +0 -41
  127. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -7,8 +7,8 @@
7
7
 
8
8
  const { searchName, weakLocation } = require('../base/messages');
9
9
  const {
10
- isDeprecatedEnabled, isBetaEnabled,
11
- forEachGeneric, forEachInOrder,
10
+ isDeprecatedEnabled,
11
+ forEachGeneric, forEachInOrder, forEachDefinition,
12
12
  } = require('../base/model');
13
13
  const { dictAdd } = require('../base/dictionaries');
14
14
  const { kindProperties, dictKinds } = require('./base');
@@ -33,6 +33,7 @@ function extend( model ) {
33
33
  const {
34
34
  resolvePath,
35
35
  resolveUncheckedPath,
36
+ checkAnnotate,
36
37
  defineAnnotations,
37
38
  attachAndEmitValidNames,
38
39
  checkDefinitions,
@@ -47,15 +48,16 @@ function extend( model ) {
47
48
 
48
49
  applyExtensions();
49
50
 
50
- const commonLanguagesEntity // TODO: remove beta after a grace period
51
- = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
52
- model.definitions['sap.common.Languages'];
51
+ const commonLanguagesEntity = options.addTextsLanguageAssoc &&
52
+ model.definitions['sap.common.Languages'];
53
53
  const addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
54
54
  commonLanguagesEntity.elements.code);
55
55
  Object.keys( model.definitions ).forEach( processArtifact );
56
56
 
57
57
  lateExtensions( false );
58
58
 
59
+ compositionChildPersistence();
60
+
59
61
  /**
60
62
  * Process "composition of" artifacts.
61
63
  *
@@ -88,6 +90,25 @@ function extend( model ) {
88
90
  }
89
91
  }
90
92
 
93
+ /**
94
+ * Copy `@cds.persistence.skip` and `@cds.persistence.skip` from parent to child
95
+ * for managed compositions. This needs to be done after extensions, i.e. annotations,
96
+ * have been applied or `annotate E.comp` would not have an effect on `E.comp.subComp`.
97
+ */
98
+ function compositionChildPersistence() {
99
+ const processed = new WeakSet();
100
+ forEachDefinition(model, processCompositionPersistence);
101
+
102
+ function processCompositionPersistence(def) {
103
+ if (def.$inferred === 'composition-entity' && !processed.has(def)) {
104
+ if (def._parent)
105
+ processCompositionPersistence(def._parent);
106
+ copyPersistenceAnnotations(def, def._parent, options);
107
+ processed.add(def);
108
+ }
109
+ }
110
+ }
111
+
91
112
  // extend ------------------------------------------------------------------
92
113
 
93
114
  /**
@@ -159,6 +180,10 @@ function extend( model ) {
159
180
  checkDefinitions( ext, art, 'actions');
160
181
  checkDefinitions( ext, art, 'params');
161
182
  checkDefinitions( ext, art, 'columns');
183
+ if (ext.includes)
184
+ applyIncludes( ext, art ); // emits error if `includes` is set
185
+ if (ext.kind === 'annotate')
186
+ checkAnnotate( ext, art );
162
187
  defineAnnotations( ext, art, ext._block, ext.kind );
163
188
  }
164
189
  return true;
@@ -218,6 +243,8 @@ function extend( model ) {
218
243
  art.includes = [ ...ext.includes ];
219
244
  applyIncludes( ext, art );
220
245
  }
246
+ if (ext.kind === 'annotate')
247
+ checkAnnotate( ext, art );
221
248
  defineAnnotations( ext, art, ext._block, ext.kind );
222
249
  // TODO: do we allow to add elements with array of {...}? If yes, adapt
223
250
  initMembers( ext, art, ext._block ); // might set _extend, _annotate
@@ -369,9 +396,13 @@ function extend( model ) {
369
396
  for (const ext of exts) {
370
397
  delete ext.name.path[0]._artifact; // get message for root
371
398
  // TODO: make resolvePath('extend'/'annotate') ignore namespaces
372
- if (resolvePath( ext.name, ext.kind, ext )) { // should issue error/info
399
+ // Don't try to apply annotations in the `localized.` namespace.
400
+ // That's done in `localized.js`.
401
+ if (!name.startsWith('localized.') &&
402
+ resolvePath( ext.name, ext.kind, ext )) { // should issue error/info
373
403
  // should issue error for cds extensions (annotate ok)
374
404
  if (art.kind === 'namespace') {
405
+ // TODO: Emit error if namespace is extended by non-definitions.
375
406
  info( 'anno-namespace', [ ext.name.location, ext ], {},
376
407
  'Namespaces can\'t be annotated' );
377
408
  }
@@ -431,6 +462,12 @@ function extend( model ) {
431
462
  * @param {XSN.Artifact} art
432
463
  */
433
464
  function applyIncludes( ext, art ) {
465
+ if (kindProperties[art.kind].include !== true) {
466
+ error('extend-unexpected-include', [ ext.includes[0]?.location, ext ], { kind: art.kind },
467
+ 'Can\'t extend $(KIND) with includes');
468
+ return;
469
+ }
470
+
434
471
  if (!art._ancestors)
435
472
  setLink( art, '_ancestors', [] ); // recursive array of includes
436
473
  for (const ref of ext.includes) {
@@ -492,17 +529,16 @@ function extend( model ) {
492
529
  const fioriAnno = art['@fiori.draft.enabled'];
493
530
  const fioriEnabled = fioriAnno && (fioriAnno.val === undefined || fioriAnno.val);
494
531
 
495
- const textsName = (isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ))
496
- ? `${ art.name.absolute }_texts`
497
- : `${ art.name.absolute }.texts`;
532
+ const textsName = `${ art.name.absolute }.texts`;
498
533
  const textsEntity = model.definitions[textsName];
499
534
  const localized = localizedData( art, textsEntity, fioriEnabled );
500
535
  if (!localized)
501
536
  return;
502
537
  if (textsEntity) // expanded localized data in source
503
538
  return; // -> make it idempotent
504
- createTextsEntity( art, textsName, localized, fioriEnabled );
539
+ const newTextsEntity = createTextsEntity( art, textsName, localized, fioriEnabled );
505
540
  addTextsAssociations( art, textsName, localized );
541
+ copyPersistenceAnnotations(newTextsEntity, art, options);
506
542
  }
507
543
 
508
544
  /**
@@ -636,8 +672,6 @@ function extend( model ) {
636
672
  };
637
673
  dictAdd( art.elements, 'ID_texts', textId );
638
674
  }
639
- if (isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ))
640
- setLink( art, '_base', base );
641
675
 
642
676
  dictAdd( art.elements, 'locale', locale );
643
677
  if (addTextsLanguageAssoc) {
@@ -695,6 +729,8 @@ function extend( model ) {
695
729
  }
696
730
  if (fioriEnabled)
697
731
  annotateWith( art, '@assert.unique.locale', art.location, assertUniqueValue, 'array' );
732
+
733
+ return art;
698
734
  }
699
735
 
700
736
  /**
@@ -808,9 +844,7 @@ function extend( model ) {
808
844
  target = resolvePath( origin.targetAspect, 'compositionTarget', origin );
809
845
  if (!target || !target.elements)
810
846
  return;
811
- const entityName = (isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ))
812
- ? `${ base.name.absolute }_${ elem.name.id }`
813
- : `${ base.name.absolute }.${ elem.name.id }`;
847
+ const entityName = `${ base.name.absolute }.${ elem.name.id }`;
814
848
  const entity = allowAspectComposition( target, elem, keys, entityName ) &&
815
849
  createTargetEntity( target, elem, keys, entityName, base );
816
850
  elem.target = {
@@ -931,7 +965,7 @@ function extend( model ) {
931
965
  // By default, 'up_' is a managed primary key association.
932
966
  // If 'up_' shall be rendered unmanaged, infer the parent
933
967
  // primary keys and add the ON condition
934
- if (isDeprecatedEnabled( options, 'unmanagedUpInComponent' )) {
968
+ if (isDeprecatedEnabled( options, '_unmanagedUpInComponent' )) {
935
969
  addProxyElements( art, keys, 'aspect-composition', target.name && location,
936
970
  'up__', '@odata.containment.ignore' );
937
971
  up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
@@ -942,8 +976,6 @@ function extend( model ) {
942
976
  // even if target cardinality is 1..1
943
977
  up.notNull = { location, val: true };
944
978
  }
945
- if (isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ))
946
- setLink( art, '_base', base._base || base );
947
979
 
948
980
  dictAdd( art.elements, 'up_', up);
949
981
  addProxyElements( art, target.elements, 'aspect-composition', target.name && location );
@@ -951,6 +983,10 @@ function extend( model ) {
951
983
  setLink( art, '_block', model.$internal );
952
984
  model.definitions[entityName] = art;
953
985
  initArtifact( art );
986
+
987
+ // Copy persistence annotations from aspect.
988
+ copyPersistenceAnnotations(art, target, options);
989
+
954
990
  return art;
955
991
  }
956
992
 
@@ -972,6 +1008,27 @@ function extend( model ) {
972
1008
  }
973
1009
  }
974
1010
 
1011
+ /**
1012
+ * Copy the annotations `@cds.persistence.skip`/`@cds.persistence.exists` from
1013
+ * source to target if present on source but not target.
1014
+ *
1015
+ * @param {object} target
1016
+ * @param {object} source
1017
+ * @param {CSN.Options} options
1018
+ */
1019
+ function copyPersistenceAnnotations(target, source, options) {
1020
+ if (!source)
1021
+ return;
1022
+ // Copy @cds.persistence.skip/exists annotation.
1023
+ const noCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
1024
+ const existsAnno = '@cds.persistence.exists';
1025
+ const skipAnno = '@cds.persistence.skip';
1026
+ if (!noCopyExists && source[existsAnno] && !target[existsAnno])
1027
+ target[existsAnno] = source[existsAnno];
1028
+ if (source[skipAnno] && !target[skipAnno])
1029
+ target[skipAnno] = source[skipAnno];
1030
+ }
1031
+
975
1032
  function augmentEqual( location, assocname, relations, prefix = '' ) {
976
1033
  const args = relations.map( eq );
977
1034
  return (args.length === 1)
@@ -2,6 +2,7 @@
2
2
 
3
3
  'use strict';
4
4
 
5
+ const { dictAddArray } = require('../base/dictionaries');
5
6
  const { forEachGeneric, forEachMember } = require('../base/model');
6
7
  const { setLink, setArtifactLink } = require('./utils');
7
8
 
@@ -33,7 +34,7 @@ function finalizeParseCdl( model ) {
33
34
  for (const ext of extensionsDict[name]) {
34
35
  ext.name.absolute = resolveUncheckedPath( ext.name, 'extend', ext );
35
36
  // Define annotations of this top-level extension
36
- defineAnnotations( ext, ext, ext._block );
37
+ defineAnnotations( ext, ext, ext._block, 'extend' );
37
38
  mergeAnnotatesForSameArtifact( ext );
38
39
  // Initialize members and define annotations in sub-elements.
39
40
  initMembers( ext, ext, ext._block, true );
@@ -81,8 +82,12 @@ function finalizeParseCdl( model ) {
81
82
  for (const include of artifact.includes || [])
82
83
  resolveUncheckedPath(include, 'include', main);
83
84
 
85
+ // define.js takes care that `target` is a ref and
86
+ // `targetAspect` is a structure.
84
87
  if (artifact.target)
85
88
  resolveUncheckedPath(artifact.target, 'target', main);
89
+ if (artifact.targetAspect)
90
+ resolveTypesForParseCdl(artifact.targetAspect, main);
86
91
 
87
92
  if (artifact.from) {
88
93
  const { from } = artifact;
@@ -92,12 +97,6 @@ function finalizeParseCdl( model ) {
92
97
  resolveTypesForParseCdl(from, main);
93
98
  }
94
99
 
95
- if (artifact.targetAspect) {
96
- if (artifact.targetAspect.path)
97
- resolveUncheckedPath(artifact.targetAspect, 'target', main);
98
- resolveTypesForParseCdl(artifact.targetAspect, main);
99
- }
100
-
101
100
  // Recursively go through all XSN properties. There are a few that need to be
102
101
  // handled specifically. Refer to the code below this loop for details.
103
102
  for (const prop in artifact) {
@@ -157,8 +156,6 @@ function finalizeParseCdl( model ) {
157
156
  * @param {XSN.Artifact} user
158
157
  */
159
158
  function resolveTypeUnchecked(artWithType, user) {
160
- if (!artWithType.type)
161
- return;
162
159
  const root = artWithType.type.path && artWithType.type.path[0];
163
160
  if (!root) // parse error
164
161
  return;
@@ -186,7 +183,7 @@ function finalizeParseCdl( model ) {
186
183
  let struct = artWithType;
187
184
  while (struct.kind === 'element')
188
185
  struct = struct._parent;
189
- if (struct.kind === 'select' || struct !== user._main) {
186
+ if (struct.kind === 'select' || struct.kind === 'annotation' || struct !== user._main) {
190
187
  message( 'type-unexpected-typeof', [ artWithType.type.location, user ],
191
188
  { keyword: 'type of', '#': struct.kind } );
192
189
  return;
@@ -221,17 +218,27 @@ function finalizeParseCdl( model ) {
221
218
 
222
219
  forEachMember(ext, sub => mergeAnnotatesForSameArtifact(sub));
223
220
 
224
- if (ext.$annotations && Array.isArray(ext.$duplicates)) {
225
- const annotates = ext.$duplicates.filter(val => (val.kind === 'annotate'));
226
- for (const dup of annotates) {
227
- ext.$annotations.push(...dup.$annotations);
228
- delete dup.$annotations;
221
+ // do not do a complex merge:
222
+ if (isComplexExtension( ext ) ||
223
+ !Array.isArray( ext.$duplicates ) || ext.$duplicates.some( isComplexExtension ))
224
+ return;
225
+ for (const dup of ext.$duplicates) {
226
+ for (const prop in dup) {
227
+ if (prop.charAt(0) === '@')
228
+ dictAddArray( ext, prop, dup[prop] );
229
229
  }
230
- ext.$duplicates = ext.$duplicates.filter(val => (val.kind !== 'annotate'));
231
- if (ext.$duplicates.length === 0)
232
- delete ext.$duplicates;
233
230
  }
231
+ delete ext.$duplicates;
234
232
  }
235
233
  }
236
234
 
235
+ /**
236
+ * We only de-duplicate an extend/annotate `ext` in function
237
+ * mergeAnnotatesForSameArtifact() if the extend/annotate is simple, i.e. has
238
+ * no members like elements.
239
+ */
240
+ function isComplexExtension( ext ) {
241
+ return ext.kind !== 'annotate' || ext.elements || ext.parameters || ext.actions;
242
+ }
243
+
237
244
  module.exports = finalizeParseCdl;
@@ -33,6 +33,7 @@ const check = require('./checks');
33
33
 
34
34
  const { emptyWeakLocation } = require('../base/location');
35
35
  const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
36
+ const { checkRemovedDeprecatedFlags } = require('../base/model');
36
37
  const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
37
38
  const { cdsFs } = require('../utils/file');
38
39
 
@@ -83,6 +84,8 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
83
84
  return parseCsn.parse( source, filename, options, messageFunctions );
84
85
  if (options.fallbackParser) // any other value: like 'cdl' (historic reasons)
85
86
  return parseLanguage( source, filename, options, messageFunctions );
87
+ if (source.startsWith('{')) // Source may be JSON.
88
+ return parseCsn.parse( source, filename, options, messageFunctions );
86
89
 
87
90
  const model = { location: { file: filename } };
88
91
  messageFunctions.error( 'file-unknown-ext', emptyWeakLocation(filename),
@@ -224,6 +227,7 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
224
227
  * @param {string} [dir=""] Base directory. All files are resolved relatively
225
228
  * to this directory
226
229
  * @param {object} [options={}] Compilation options.
230
+ * @param {object} [fileCache]
227
231
  * @returns {XSN.Model} Augmented CSN
228
232
  */
229
233
  function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.create(null) ) {
@@ -235,7 +239,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
235
239
  const model = { sources: a.sources, options };
236
240
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
237
241
 
238
- let asts = [];
242
+ const asts = [];
239
243
  const errors = [];
240
244
  a.files.forEach( val => readAndParseSync( val, (err, ast) => {
241
245
  if (err)
@@ -253,17 +257,16 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
253
257
  if (!options.parseOnly && !options.parseCdl) {
254
258
  while (asts.length) {
255
259
  const fileNames = readDependenciesSync( asts );
256
- asts = [];
257
- // TODO: check the following eslint error
258
- // eslint-disable-next-line no-loop-func
259
- fileNames.forEach( (fileName) => {
260
+ asts.length = 0;
261
+ // Push dependencies to `ast`. Only works because readAndParseSync() is synchronous.
262
+ for (const fileName of fileNames) {
260
263
  readAndParseSync(fileName, ( err, ast ) => {
261
264
  if (err)
262
265
  throw err;
263
266
  if (ast)
264
267
  asts.push( ast );
265
268
  });
266
- } );
269
+ }
267
270
  }
268
271
  }
269
272
 
@@ -378,6 +381,15 @@ function compileSourcesX( sourcesDict, options = {} ) {
378
381
  ast.location = { file: filename };
379
382
  assertConsistency( ast, options );
380
383
  }
384
+
385
+ for (const dep of sources[filename].dependencies || []) {
386
+ if (!dep.realname) {
387
+ // `realname` is used by setLayers(). For compileSources(), we don't resolve
388
+ // the USING paths and use the literal instead, which may be part of the
389
+ // source dictionary.
390
+ dep.realname = dep.val;
391
+ }
392
+ }
381
393
  }
382
394
  moduleLayers.setLayers( sources );
383
395
 
@@ -391,14 +403,15 @@ function compileSourcesX( sourcesDict, options = {} ) {
391
403
  * @param {object} options Options
392
404
  * @returns {object} XSN
393
405
  *
394
- * TODO: probaby issue message api-recompiled-csn there.
406
+ * TODO: probably issue message api-recompiled-csn there.
395
407
  */
396
408
  function recompileX( csn, options ) {
397
- options = { ...options, parseCdl: false, $recompile: true };
398
- // TODO: $recompile: true should be enough
399
409
  // Explicitly set parseCdl to false because backends cannot handle it
400
- // Explicitly delete all toCsn options:
410
+ options = { ...options, parseCdl: false, $recompile: true };
411
+ // Reset csnFlavor: Use client style (default)
412
+ delete options.csnFlavor;
401
413
  delete options.toCsn;
414
+ // TODO: $recompile: true should be enough
402
415
 
403
416
  const file = csn.$location && csn.$location.file &&
404
417
  csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
@@ -428,6 +441,9 @@ function compileDoX( model ) {
428
441
  const { throwWithError } = model.$messageFunctions;
429
442
  if (!options.testMode)
430
443
  model.meta = {}; // provide initial central meta object
444
+
445
+ checkRemovedDeprecatedFlags( options, model.$messageFunctions );
446
+
431
447
  if (options.parseOnly) {
432
448
  throwWithError();
433
449
  return model;
@@ -500,7 +516,7 @@ function processFilenamesSync( filenames, dir ) {
500
516
  // Resolve possible symbolic link; if the file does not exist
501
517
  // we just continue using the original name because readFile()
502
518
  // already handles non-existent files.
503
- name = fs.realpathSync(name);
519
+ name = fs.realpathSync.native(name);
504
520
  }
505
521
  catch (e) {
506
522
  // Ignore the not-found (ENOENT) error
@@ -53,6 +53,12 @@ function layer( art ) {
53
53
  return art && art._layerRepresentative;
54
54
  }
55
55
 
56
+ function realname( art ) {
57
+ while (art && art.kind !== 'source')
58
+ art = art._block;
59
+ return art && art.realname || '';
60
+ }
61
+
56
62
  function compareLayer( a, b ) {
57
63
  while (a && a.kind !== 'source')
58
64
  a = a._block;
@@ -64,5 +70,6 @@ function compareLayer( a, b ) {
64
70
  module.exports = {
65
71
  setLayers,
66
72
  layer,
73
+ realname,
67
74
  compareLayer,
68
75
  };
@@ -73,22 +73,16 @@ function populate( model ) {
73
73
  /** @type {any} may also be a boolean */
74
74
  let newAutoExposed = [];
75
75
 
76
- // behavior depending on option `deprecated`:
77
- const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
78
- // TODO: we should get rid of noElementsExpansion soon; both
79
- // beta.nestedProjections and beta.universalCsn do not work with it.
80
76
  const scopedRedirections
81
- = enableExpandElements &&
82
- !isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ) &&
83
- !isDeprecatedEnabled( options, 'shortAutoexposed' ) &&
84
- !isDeprecatedEnabled( options, 'longAutoexposed' ) &&
85
- !isDeprecatedEnabled( options, 'noInheritedAutoexposeViaComposition' ) &&
86
- !isDeprecatedEnabled( options, 'noScopedRedirections' );
77
+ = !isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
78
+ !isDeprecatedEnabled( options, '_longAutoexposed' ) &&
79
+ !isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ) &&
80
+ !isDeprecatedEnabled( options, '_noScopedRedirections' );
87
81
  const autoexposeViaComposition
88
- = (isDeprecatedEnabled( options, 'noInheritedAutoexposeViaComposition' ))
82
+ = (isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ))
89
83
  ? 'Composition'
90
84
  : true;
91
- const redirectInSubQueries = isDeprecatedEnabled( options, 'redirectInSubQueries' );
85
+ const redirectInSubQueries = isDeprecatedEnabled( options, '_redirectInSubQueries' );
92
86
 
93
87
  forEachDefinition( model, traverseElementEnvironments );
94
88
  while (newAutoExposed.length) {
@@ -167,7 +161,7 @@ function populate( model ) {
167
161
  // console.log(message( null, art.location, art, {}, 'Info','FT').toString())
168
162
  const chain = [];
169
163
  while (art && !('_effectiveType' in art) &&
170
- (art.type || art._origin || art.value && art.value.path) &&
164
+ (art.type || art._origin || art.value?.path || art.value?.type) &&
171
165
  // TODO: really stop at art.enum? See #8942
172
166
  !art.target && !art.enum && !art.elements && !art.items) {
173
167
  chain.push( art );
@@ -197,7 +191,7 @@ function populate( model ) {
197
191
  let eType = art;
198
192
  if (eType._outer)
199
193
  eType = effectiveType( eType._outer );
200
- // collect the "latest" cardinality (calculate lazyly if necessary)
194
+ // collect the "latest" cardinality (calculate lazily if necessary)
201
195
  let cardinality = art.cardinality ||
202
196
  art._effectiveType && (() => getCardinality( art._effectiveType ));
203
197
  let prev = art;
@@ -224,10 +218,12 @@ function populate( model ) {
224
218
  return art._origin;
225
219
  if (art.type)
226
220
  return resolveType( art.type, art );
221
+ if (art.value?.type)
222
+ return resolveType( art.value.type, art );
227
223
  // console.log( 'EXPR-IN', art.kind, refString(art.name) )
228
224
  if (!art._main || !art.value || !art.value.path)
229
225
  return undefined;
230
- if (art._pathHead && art.value) {
226
+ if (art._pathHead && art.value.path) {
231
227
  setLink( art, '_origin', resolvePath( art.value, 'expr', art, null ) );
232
228
  return art._origin;
233
229
  }
@@ -248,7 +244,9 @@ function populate( model ) {
248
244
  let struct = user;
249
245
  while (struct.kind === 'element')
250
246
  struct = struct._parent;
251
- if (struct.kind === 'select') {
247
+ if (struct.kind === 'select' || struct.kind === 'annotation') {
248
+ // `type of` in annotation definitions can't work, because csn type refs
249
+ // always refer to definitions.
252
250
  message( 'type-unexpected-typeof', [ ref.location, user ],
253
251
  { keyword: 'type of', '#': struct.kind } );
254
252
  // we actually refer to an element in _combined; TODO: return null if
@@ -295,7 +293,7 @@ function populate( model ) {
295
293
 
296
294
 
297
295
  function expandItems( art, origin, eType ) {
298
- if (!enableExpandElements || art.items)
296
+ if (art.items)
299
297
  return false;
300
298
  if (isInParents( art, eType )) {
301
299
  art.items = 0; // circular
@@ -312,8 +310,6 @@ function populate( model ) {
312
310
  }
313
311
 
314
312
  function expandElements( art, struct, eType ) {
315
- if (!enableExpandElements)
316
- return false;
317
313
  if (art.elements || art.kind === '$tableAlias' ||
318
314
  // no element expansions for "non-proper" types like
319
315
  // entities (as parameter types) etc:
@@ -347,7 +343,7 @@ function populate( model ) {
347
343
  }
348
344
 
349
345
  function expandEnum( art, origin ) {
350
- if (!enableExpandElements || art.enum)
346
+ if (art.enum)
351
347
  return false;
352
348
  const ref = art.type || art.value || art.name;
353
349
  const location = weakLocation( ref && ref.location || art.location );
@@ -469,14 +465,11 @@ function populate( model ) {
469
465
  }
470
466
  }
471
467
  }
472
- // Without element expansion, we can't merge nested elements.
473
- if (art.kind === 'entity' || enableExpandElements) {
474
- for (const id in art.elements$) {
475
- const selem = art.elements$[id]; // specified element
476
- if (!selem.$replacement) {
477
- error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
478
- 'Element $(ID) does not result from the query' );
479
- }
468
+ for (const id in art.elements$) {
469
+ const selem = art.elements$[id]; // specified element
470
+ if (!selem.$replacement) {
471
+ error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
472
+ 'Element $(ID) does not result from the query' );
480
473
  }
481
474
  }
482
475
  }
@@ -490,8 +483,6 @@ function populate( model ) {
490
483
  forEachGeneric( query, '$tableAliases', resolveTabRef );
491
484
 
492
485
  initFromColumns( query, query.columns );
493
- // TODO: already in definer: complain about EXCLUDING with no wildcard
494
- // (would have been automatically with a good CDL syntax: `* without (...)`)
495
486
  if (query.excludingDict) {
496
487
  for (const name in query.excludingDict)
497
488
  resolveExcluding( name, query._combined, query.excludingDict, query );
@@ -1055,8 +1046,6 @@ function populate( model ) {
1055
1046
  // which is not a context/service/namespace, or the definition itself.
1056
1047
  // If inside service, it is the direct child of the (most inner) service.
1057
1048
  function definitionScope( art ) {
1058
- if (art._base) // with deprecated.generatedEntityNameWithUnderscore
1059
- return art._base;
1060
1049
  let base = art;
1061
1050
  while (art._parent) {
1062
1051
  if (art._parent.kind === 'service')
@@ -1112,7 +1101,7 @@ function populate( model ) {
1112
1101
  return false;
1113
1102
  }
1114
1103
  // no @cds.autoexpose or @cds.autoexpose:null
1115
- // TODO: introduce deprecated.noInheritedAutoexposeViaComposition
1104
+ // TODO: introduce deprecated._noInheritedAutoexposeViaComposition
1116
1105
  art.$autoexpose = model.$compositionTargets[art.name.absolute]
1117
1106
  ? autoexposeViaComposition
1118
1107
  : null;
@@ -1121,17 +1110,15 @@ function populate( model ) {
1121
1110
 
1122
1111
  function autoExposedName( target, service, elemScope ) {
1123
1112
  const { absolute } = target.name;
1124
- if (isDeprecatedEnabled( options, 'shortAutoexposed' )) {
1113
+ if (isDeprecatedEnabled( options, '_shortAutoexposed' )) {
1125
1114
  const parent = definitionScope( target )._parent;
1126
1115
  const name = (parent) ? absolute.substring( parent.name.absolute.length + 1 ) : absolute;
1127
- // no need for dedot here (as opposed to deprecated.longAutoexposed), as
1116
+ // no need for dedot here (as opposed to deprecated._longAutoexposed), as
1128
1117
  // the name for dependent entities have already been created using `_` then
1129
1118
  return `${ service.name.absolute }.${ name }`;
1130
1119
  }
1131
- if (isDeprecatedEnabled( options, 'longAutoexposed' )) {
1132
- const dedot = isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' );
1133
- return `${ service.name.absolute }.${ dedot ? absolute.replace( /\./g, '_' ) : absolute }`;
1134
- }
1120
+ if (isDeprecatedEnabled( options, '_longAutoexposed' ))
1121
+ return `${ service.name.absolute }.${ absolute }`;
1135
1122
  const base = definitionScope( target );
1136
1123
  if (base === target)
1137
1124
  return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf('.') + 1 ) }`;
@@ -1,4 +1,10 @@
1
- //
1
+ // Propagate properties in XSN
2
+
3
+ // See also internalDoc/PropagatedCsn.md.
4
+ // As opposed to that document, the propagator here works on XSN, not CSN.
5
+ // We also do not deep-copy member dictionaries here, but create proxy members
6
+ // which get their properties via propagation: we use function `onlyViaParent`
7
+ // if that property would not be propagated otherwise.
2
8
 
3
9
  'use strict';
4
10
 
@@ -11,6 +17,7 @@ const {
11
17
  const { setLink, linkToOrigin, withAssociation } = require('./utils');
12
18
  // const { refString } = require( '../base/messages')
13
19
 
20
+ // Note that propagation here is also used for deep-copying (function `onlyViaParent`)
14
21
  function propagate( model ) {
15
22
  const props = {
16
23
  '@com.sap.gtt.core.CoreModel.Indexable': never,
@@ -56,9 +63,8 @@ function propagate( model ) {
56
63
  returns,
57
64
  };
58
65
  const { options } = model;
59
- const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
60
66
  // eslint-disable-next-line max-len
61
- const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, 'oldVirtualNotNullPropagation' );
67
+ const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
62
68
 
63
69
  forEachDefinition( model, run );
64
70
 
@@ -189,7 +195,7 @@ function propagate( model ) {
189
195
  if (!type || type._main)
190
196
  return false;
191
197
  // We do not consider the $expand status, as elements are already expanded
192
- // by the resolve(), and if not due to deprecated.noElementsExpansion
198
+ // by the resolve()
193
199
  run( type );
194
200
  return type[prop];
195
201
  }
@@ -313,11 +319,10 @@ function propagate( model ) {
313
319
 
314
320
  function items( prop, target, source ) {
315
321
  // usually considered expensive, except:
316
- // - array of Entity (smooth upgrade: array of String(3), array of DerivedScalar)
322
+ // - array of Entity
317
323
  const line = availableAtType( prop, target, source );
318
324
  if (!line ||
319
- line.type && line.type._artifact && line.type._artifact.kind === 'entity' ||
320
- !line.elements && !line.enum && !line.items && !enableExpandElements)
325
+ line.type && line.type._artifact && line.type._artifact.kind === 'entity')
321
326
  returns( prop, target, source, true );
322
327
  }
323
328
  }