@sap/cds-compiler 2.13.8 → 3.0.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 +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  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 -296
  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
@@ -6,11 +6,12 @@
6
6
 
7
7
  const { hasErrors, makeMessageFunction } = require('../base/messages');
8
8
  const { setProp } = require('../base/model');
9
- const { csnRefs } = require('../model/csnRefs');
10
9
 
11
10
  const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
12
- const { cloneCsn, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
11
+ const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
12
+ const { typeParameters, isBuiltinType } = require('../compiler/builtins');
13
13
  const { ModelError } = require("../base/error");
14
+ const { forEach } = require('../utils/objectUtils');
14
15
 
15
16
  // Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
16
17
  // Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
@@ -19,20 +20,18 @@ const { ModelError } = require("../base/error");
19
20
  // TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
20
21
  function getTransformers(model, options, pathDelimiter = '_') {
21
22
  const { error, warning, info } = makeMessageFunction(model, options);
23
+ const csnUtils = getUtils(model);
22
24
  const {
23
25
  getCsnDef,
24
- getFinalBaseType,
26
+ getFinalBaseTypeWithProps,
25
27
  hasAnnotationValue,
26
28
  inspectRef,
27
29
  isStructured,
28
- } = getUtils(model);
29
-
30
- const {
31
30
  effectiveType,
32
- } = csnRefs(model);
33
-
31
+ } = csnUtils;
34
32
 
35
33
  return {
34
+ csnUtils,
36
35
  resolvePath,
37
36
  flattenPath,
38
37
  addDefaultTypeFacets,
@@ -44,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
44
43
  copyTypeProperties,
45
44
  isAssociationOperand,
46
45
  isDollarSelfOrProjectionOperand,
47
- getFinalBaseType,
48
46
  createExposingProjection,
49
47
  createAndAddDraftAdminDataProjection,
50
48
  createScalarElement,
@@ -242,23 +240,21 @@ function getTransformers(model, options, pathDelimiter = '_') {
242
240
  // for type of 'x' -> elem.type is an object, not a string -> use directly
243
241
  let elemType;
244
242
  if (!elem.elements) // structures do not have final base type
245
- elemType = getFinalBaseType(elem.type);
243
+ elemType = getFinalBaseTypeWithProps(elem.type);
246
244
 
247
245
  const struct = elemType ? elemType.elements : elem.elements;
248
246
 
249
247
  // Collect all child elements (recursively) into 'result'
250
248
  // TODO: Do not report collisions in the generated elements here, but instead
251
- // leave that work to the receiver of this result
249
+ // leave that work to the receiver of this result
252
250
  let result = Object.create(null);
253
251
  const addGeneratedFlattenedElement = (e, eName) => {
254
- if(result[eName]){
255
- error(null, pathInCsn, { name: eName },
256
- 'Generated element $(NAME) conflicts with other generated element')
257
- } else {
252
+ if (result[eName])
253
+ error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
254
+ else
258
255
  result[eName] = e;
259
- }
260
256
  }
261
- Object.entries(struct).forEach(([childName, childElem]) => {
257
+ forEach(struct, (childName, childElem) => {
262
258
  if (isStructured(childElem)) {
263
259
  // Descend recursively into structured children
264
260
  let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
@@ -276,7 +272,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
276
272
  } else {
277
273
  // Primitive child - clone it and restore its cross references
278
274
  let flatElemName = elemName + pathDelimiter + childName;
279
- let flatElem = cloneCsn(childElem, options);
275
+ let flatElem = cloneCsnNonDict(childElem, options);
280
276
  // Don't take over notNull from leaf elements
281
277
  delete flatElem.notNull;
282
278
  setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
@@ -285,8 +281,12 @@ function getTransformers(model, options, pathDelimiter = '_') {
285
281
  });
286
282
 
287
283
  // Fix all collected flat elements (names, annotations, properties, origin ..)
288
- Object.values(result).forEach(flatElem => {
289
- // Copy annotations from struct (not overwriting, because deep annotations should have precedence)
284
+ forEach(result, (name, flatElem) => {
285
+ // Copy annotations from struct (not overwriting, because deep annotations should have precedence).
286
+ // Attention:
287
+ // This has historic reasons. We don't copy doc-comments because copying annotations
288
+ // is questionable to begin with. Only selected annotations should have been copied,
289
+ // if at all.
290
290
  copyAnnotations(elem, flatElem, false);
291
291
  // Copy selected type properties
292
292
  const props = ['key', 'virtual', 'masked', 'viaAll'];
@@ -394,82 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
394
394
  }
395
395
 
396
396
  /**
397
- * Replace the type of 'node' with its final base type (in contrast to the compiler,
398
- * also unravel derived enum types, i.e. take the final base type of the enum's base type.
399
- * Similar with associations and compositions (we probably need a _baseType link)
397
+ * Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
398
+ * set the `type` property to the builtin if scalar or delete it if structured/arrayed.
400
399
  *
401
- * @param {CSN.Artifact} node
400
+ * @param {object} nodeWithType
402
401
  * @param {WeakMap} [resolved] WeakMap containing already resolved refs
403
402
  * @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
404
- * @returns {void}
405
403
  */
406
- function toFinalBaseType(node, resolved, keepLocalized=false) {
407
- // Nothing to do if no type (or if array/struct type)
408
- if (!node || !node.type) return;
409
- // In case of a ref -> Follow the ref
410
- if (node.type && node.type.ref) {
411
- const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
412
- if(finalBaseType === null)
413
- throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
414
- if(finalBaseType.elements) {
415
- // This changes the order - to be discussed!
416
- node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
417
- delete node.type; // delete the type reference as edm processing does not expect it
418
- } else if(finalBaseType.items) {
419
- // This changes the order - to be discussed!
420
- node.items = cloneCsn(finalBaseType.items, options); // copy items
421
- delete node.type;
422
- } else {
423
- node.type=finalBaseType;
424
- }
404
+ function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
405
+ const type = nodeWithType?.type;
406
+ if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
425
407
  return;
426
408
  }
427
- // .. or builtin already
428
- if (node.type && isBuiltinType(node.type)) return;
409
+ // The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
410
+ // it to indicate that an artifact has been processed.
411
+ resolved.set(nodeWithType, nodeWithType);
429
412
 
430
- // The type might already be a full fledged type def (array of)
431
- let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
432
- // Nothing to do if type is an array or a struct type
433
- if (typeDef.items || typeDef.elements) {
434
- // Expand structured types on entity elements for flat OData
435
- if(!(options.transformation === 'hdbcds' || options.toSql))
436
- return;
413
+ // Nothing to copy from builtin.
414
+ if (typeof type === 'string' && isBuiltinType(type))
415
+ return;
416
+
417
+ let typeRef = null;
418
+ if (resolved.has(type)) {
419
+ typeRef = resolved.get(type)?.art
420
+ // The cached entry may not be resolved, yet.
421
+ if (typeRef.type && !isBuiltinType(typeRef.type))
422
+ typeRef = getFinalBaseTypeWithProps(typeRef.type);
423
+ } else {
424
+ typeRef = getFinalBaseTypeWithProps(type);
425
+ }
437
426
 
438
- // cloneCsn only works correctly if we start "from the top"
439
- const cloneTypeDef = cloneCsn(typeDef, options);
440
- // With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
441
- if(typeDef.items) {
442
- delete node.type;
443
- if(!node.items)
444
- Object.assign(node, {items: cloneTypeDef.items});
427
+ if (typeRef.elements || typeRef.items) {
428
+ // Copy elements/items and we're finished. No need to look up actual base type,
429
+ // since it must also be structured and must contain at least as many elements,
430
+ // if not more (in client style CSN).
431
+ if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
432
+ nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
433
+ delete nodeWithType.type;
445
434
  }
446
- if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
447
- if(!typeDef.items)
448
- delete node.type;
449
- if(!node.elements)
450
- Object.assign(node, {elements: cloneTypeDef.elements});
435
+ if (typeRef.items) {
436
+ nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
437
+ delete nodeWithType.type;
451
438
  }
452
-
453
-
454
439
  return;
455
440
  }
456
- // if the declared element is an enum, these values are with priority
457
- if (!node.enum && typeDef.enum) {
458
- const clone = cloneCsnDictionary(typeDef.enum, options);
459
- Object.assign(node, { enum: clone });
441
+
442
+ if (typeRef.enum && nodeWithType.enum === undefined)
443
+ nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
444
+
445
+ // Copy type and type arguments (+ localized)
446
+
447
+ for (const param of typeParameters.list) {
448
+ if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
449
+ nodeWithType[param] = typeRef[param];
450
+ }
460
451
  }
461
- if (node.length === undefined && typeDef.length !== undefined)
462
- Object.assign(node, { length: typeDef.length });
463
- if (node.precision === undefined && typeDef.precision !== undefined)
464
- Object.assign(node, { precision: typeDef.precision });
465
- if (node.scale === undefined && typeDef.scale !== undefined)
466
- Object.assign(node, { scale: typeDef.scale });
467
- if (node.srid === undefined && typeDef.srid !== undefined)
468
- Object.assign(node, { srid: typeDef.srid });
469
- if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
470
- Object.assign(node, { localized: typeDef.localized });
471
- node.type = typeDef.type;
472
- toFinalBaseType(node);
452
+ if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
453
+ nodeWithType.localized = typeRef.localized;
454
+ if (typeRef.type)
455
+ nodeWithType.type = typeRef.type;
473
456
  }
474
457
 
475
458
  // Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
@@ -899,7 +882,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
899
882
  let elements = artifact.elements;
900
883
  if (elements) {
901
884
  path.push('elements', null);
902
- Object.entries(elements).forEach(([name, obj]) => {
885
+ forEach(elements, (name, obj) => {
903
886
  path[path.length - 1] = name;
904
887
  recurseElements(obj, path, callback);
905
888
  });
@@ -44,7 +44,7 @@ function translateAssocsToJoinsCSN(csn, options){
44
44
  }
45
45
 
46
46
  // If A2J reports error - end! Continuing with a broken CSN makes no sense
47
- makeMessageFunction(model, options).throwWithError();
47
+ makeMessageFunction(model, options).throwWithAnyError();
48
48
  // FIXME: Move this somewhere more appropriate
49
49
  const compact = compactModel(model, compileOptions);
50
50
  return compact;
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
57
57
  const options = model.options || inputOptions;
58
58
 
59
59
  // create JOINs for foreign key paths
60
- const noJoinForFK = options.forHana ? !options.forHana.joinfk : true;
60
+ const noJoinForFK = options.forHana ? !options.joinfk : true;
61
61
 
62
62
  // Note: This is called from the 'forHana' transformations, so it is controlled by its options)
63
- const pathDelimiter = (options.forHana && options.forHana.names === 'hdbcds') ? '.' : '_';
63
+ const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
64
64
 
65
65
  forEachDefinition(model, prepareAssociations);
66
66
  forEachDefinition(model, transformQueries);
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
1818
1818
  // or that are parameters ($parameters or escaped paths (':')
1819
1819
  //path.length && path[ path.length-1 ]._artifact
1820
1820
  const art = path && path.length && path[path.length-1]._artifact;
1821
+
1822
+ // regardless of the position in the query, ignore paths that have virtual path steps
1823
+ if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
1824
+ return path;
1821
1825
  if(art && !internalArtifactKinds.includes(art.kind))
1822
1826
  {
1823
1827
  if(env.callback)
@@ -21,11 +21,11 @@
21
21
  "sonarjs/no-duplicate-string": "off"
22
22
  },
23
23
  "parserOptions": {
24
- "ecmaVersion": 2018,
24
+ "ecmaVersion": 2020,
25
25
  "sourceType": "script"
26
26
  },
27
27
  "env": {
28
- "es6": true,
28
+ "es2020": true,
29
29
  "node": true
30
30
  },
31
31
  "settings": {
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
12
12
  */
13
13
  function setCoreComputedOnViews(csn) {
14
14
  const {
15
- artifactRef, getColumn, getElement,
15
+ artifactRef, getColumn, getElement, getOrigin,
16
16
  } = getUtils(csn, 'init-all');
17
17
 
18
18
  forEachDefinition(csn, (artifact) => {
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
57
57
  const column = getColumn(element);
58
58
  if (column)
59
59
  return column;
60
- return getElementFromFrom(name, base.from);
60
+ const from = getElementFromFrom(name, base.from);
61
+ if (from)
62
+ return from;
63
+ // For .expand/.inline, we can find it via origin
64
+ // Although I would have expected to find it via getColumn...
65
+ const origin = getOrigin(element);
66
+ if (origin)
67
+ return origin;
68
+ throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
61
69
  }
62
70
 
63
71
  /**
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
87
95
  return getElementFromFrom(name, base.SET.args[0]);
88
96
  }
89
97
  else if (base.args && base.join) {
90
- const result = checkJoinSources(base.args, name);
91
- if (!result)
92
- throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
93
- return result;
98
+ return checkJoinSources(base.args, name);
94
99
  }
95
100
 
96
101
  throw new Error(JSON.stringify(base));
@@ -137,9 +142,11 @@ function setCoreComputedOnViews(csn) {
137
142
  * @returns {boolean}
138
143
  */
139
144
  function needsCoreComputed(column) {
140
- return column && ( column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET || column.ref && column.ref[0] in {
141
- $at: 1, $now: 1, $user: 1, $session: 1,
142
- });
145
+ return column &&
146
+ (
147
+ column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET ||
148
+ column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
149
+ );
143
150
  }
144
151
 
145
152
  /**
@@ -29,10 +29,7 @@ module.exports = (csn, options) => {
29
29
  const definitionPropagationRules = {
30
30
  '@cds.autoexpose': onlyViaArtifact,
31
31
  '@fiori.draft.enabled': onlyViaArtifact,
32
- '@': (prop, target, source) => {
33
- if (source[prop] !== null)
34
- target[prop] = source[prop];
35
- },
32
+ '@': nullStopsPropagation,
36
33
  // Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
37
34
  elements: onlyTypeDef,
38
35
  '@cds.persistence.exists': skip,
@@ -48,7 +45,7 @@ module.exports = (csn, options) => {
48
45
  '@cds.autoexposed': skip,
49
46
  '@cds.redirection.target': skip,
50
47
  type: always,
51
- doc: always,
48
+ doc: nullStopsPropagation,
52
49
  length: always,
53
50
  precision: always,
54
51
  scale: always,
@@ -94,6 +91,10 @@ module.exports = (csn, options) => {
94
91
  }, // overwrite from defProps
95
92
  kind: skip,
96
93
  val: always,
94
+ type: notWithItemsOrElements,
95
+ target: notWithItemsOrElements,
96
+ keys: notWithItemsOrElements,
97
+ cardinality: notWithItemsOrElements,
97
98
  };
98
99
 
99
100
  generate();
@@ -288,7 +289,11 @@ module.exports = (csn, options) => {
288
289
  if (returns.target)
289
290
  calculateForeignKeys(returns);
290
291
  },
291
- items: (parent, prop, items) => propagateMemberPropsFromOrigin(items, { '@': true, doc: true }),
292
+ items: (parent, prop, items) => {
293
+ // items in items must be propagated
294
+ // `specialItemsRule()` does not cover this case
295
+ propagateMemberPropsFromOrigin(items, { '@': true, doc: true }, { items: onlyWithTypeRef });
296
+ },
292
297
  elements: (parent, prop, elements) => {
293
298
  forEachValue(elements, (e) => {
294
299
  // within query elements we have to propagate the `enum` prop
@@ -358,13 +363,31 @@ module.exports = (csn, options) => {
358
363
  return !origin;
359
364
  }
360
365
  }
366
+
367
+ /**
368
+ * Some properties must only be copied over if the target
369
+ * is a type reference pointing to an element of a type or
370
+ * an aspect.
371
+ *
372
+ * @param {string} prop
373
+ * @param {CSN.Element} target
374
+ * @param {CSN.Element} source
375
+ */
376
+ function onlyWithTypeRef(prop, target, source) {
377
+ const typeIsTypeRef = Boolean(typeof target.type === 'object' && target.type.ref);
378
+ if (typeIsTypeRef) {
379
+ const referencedArtifact = csn.definitions[target.type.ref[0]];
380
+ if (referencedArtifact.kind in { type: true, aspect: true })
381
+ target[prop] = source[prop];
382
+ }
383
+ }
361
384
  }
362
385
 
363
386
  /**
364
387
  * Identify the sources of the passed object and propagate the relevant
365
388
  * properties/annotations along it's $origin chain.
366
389
  *
367
- * @param {Object} art Target object for propagation
390
+ * @param {object} art Target object for propagation
368
391
  */
369
392
  function propagateOnArtifactLevel(art) {
370
393
  // check if art was already processed by the status flag
@@ -415,7 +438,7 @@ module.exports = (csn, options) => {
415
438
  copyProperties(definition.$origin, definition, getDefinitionPropagationRuleFor);
416
439
 
417
440
  // We need to propagate .elements to type artifacts - but our direct origin might not have .elements,
418
- // because they are not propagated to members. We check if our root had elements (so we know that we should have some aswell)
441
+ // because they are not propagated to members. We check if our root had elements (so we know that we should have some as well)
419
442
  // and then walk the origin-chain until we find the first .elements
420
443
  if (definition.kind === 'type' && root.elements && !definition.elements) {
421
444
  const firstOriginWithElements = getFirstOriginWithElements(source);
@@ -580,7 +603,7 @@ module.exports = (csn, options) => {
580
603
  * @param {Function} getCustomRule getter for the `memberProps` or `defProps`
581
604
  * which shall be used for retrieving custom rules
582
605
  * @param {object} [except=null] array of properties which should not be propagated
583
- * @param {object} [force=null] Force propagation of the contained keys via rule "always"
606
+ * @param {object} [force=null] Force propagation of the contained keys via a custom rule.
584
607
  */
585
608
  function copyProperties(from, to, getCustomRule, except = null, force = null) {
586
609
  const keys = Object.keys(from);
@@ -589,7 +612,7 @@ function copyProperties(from, to, getCustomRule, except = null, force = null) {
589
612
  if (except && !(force && force[key]) && (key.charAt(0) in except || key in except))
590
613
  continue;
591
614
  if (!(key in to)) {
592
- const func = force && force[key] ? always : getCustomRule(key);
615
+ const func = force && force[key] ? force[key] : getCustomRule(key);
593
616
  if (func)
594
617
  func(key, to, from);
595
618
  }
@@ -680,6 +703,19 @@ function notWithTypeOrigin(prop, target, source) {
680
703
  target[prop] = source[prop];
681
704
  }
682
705
 
706
+ /**
707
+ * The value `null` tells us to skip the propagation of the property.
708
+ * This is the case e.g. for `doc` or for annotations.
709
+ *
710
+ * @param {string} prop
711
+ * @param {CSN.Element} target
712
+ * @param {CSN.Element} source
713
+ */
714
+ function nullStopsPropagation(prop, target, source) {
715
+ if (source[prop] !== null)
716
+ target[prop] = source[prop];
717
+ }
718
+
683
719
  /**
684
720
  * Special propagation rules for .items - depending on the exact type of .items and the
685
721
  * way it was referenced (type of, direct type, direct many), we need to propagate (or not).
@@ -700,6 +736,20 @@ function specialItemsRules(prop, target, source) {
700
736
  target[prop] = source[prop];
701
737
  }
702
738
 
739
+ /**
740
+ * Don't propagate certain properties if the target already has a .items or .elements
741
+ *
742
+ * This happens with .expand/.inline
743
+ *
744
+ * @param {string} prop
745
+ * @param {CSN.Element} target
746
+ * @param {CSN.Element} source
747
+ */
748
+ function notWithItemsOrElements(prop, target, source) {
749
+ if (!target.items && !target.elements || !source.target)
750
+ target[prop] = source[prop];
751
+ }
752
+
703
753
  /**
704
754
  * Some properties must not be copied over if the type of this member
705
755
  * is a reference to another element.
package/lib/utils/file.js CHANGED
@@ -61,12 +61,12 @@ function cdsFs(fileCache, enableTrace) {
61
61
  };
62
62
 
63
63
  function realpath(path, cb) {
64
- return fs.realpath(path, cb);
64
+ return fs.realpath.native(path, cb);
65
65
  }
66
66
 
67
67
  function realpathSync(path, cb) {
68
68
  try {
69
- cb(null, fs.realpathSync(path));
69
+ cb(null, fs.realpathSync.native(path));
70
70
  }
71
71
  catch (err) {
72
72
  cb(err, null);
@@ -87,7 +87,7 @@ function cdsFs(fileCache, enableTrace) {
87
87
  }
88
88
  let body = fileCache[filename];
89
89
  if (body && typeof body === 'object' && body.realname) {
90
- filename = body.realname; // use fs.realpath name
90
+ filename = body.realname; // use fs.realpath.native name
91
91
  body = fileCache[filename];
92
92
  }
93
93
  if (body !== undefined && body !== true) { // true: we just know it is there
@@ -28,13 +28,18 @@ const extensions = [ '.cds', '.csn', '.json' ];
28
28
  * - Why a global? The Umbrella could pass it as an option.
29
29
  *
30
30
  * @param {string} modulePath
31
+ * @param {CSN.Options} options
31
32
  * @returns {string}
32
33
  */
33
- function adaptCdsModule(modulePath) {
34
- // eslint-disable-next-line
35
- if (global['cds'] && global['cds'].home && modulePath.startsWith( '@sap/cds/' ))
34
+ function adaptCdsModule(modulePath, options = {}) {
35
+ if (modulePath.startsWith( '@sap/cds/' )) {
36
+ if (options.cdsHome)
37
+ return options.cdsHome + modulePath.slice(8);
36
38
  // eslint-disable-next-line
37
- return global['cds'].home + modulePath.slice(8)
39
+ if (global['cds'] && global['cds'].home)
40
+ // eslint-disable-next-line
41
+ return global['cds'].home + modulePath.slice(8);
42
+ }
38
43
  return modulePath;
39
44
  }
40
45
 
@@ -42,6 +47,7 @@ function adaptCdsModule(modulePath) {
42
47
  * @param {object} dep
43
48
  * @param {object} fileCache
44
49
  * @param {CSN.Options} options
50
+ * @param {object} messageFunctions
45
51
  */
46
52
  function resolveModule( dep, fileCache, options, messageFunctions ) {
47
53
  const _fs = cdsFs(fileCache, options.traceFs);
@@ -58,7 +64,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
58
64
  realpath: _fs.realpath,
59
65
  };
60
66
  return new Promise( (fulfill, reject) => {
61
- const lookupPath = adaptCdsModule(dep.module);
67
+ const lookupPath = adaptCdsModule(dep.module, options);
62
68
  resolveCDS( lookupPath, opts, (err, res) => {
63
69
  // console.log('RESOLVE', dep, res, err)
64
70
  if (err) {
@@ -105,6 +111,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
105
111
  * @param {object} dep
106
112
  * @param {object} fileCache
107
113
  * @param {CSN.Options} options
114
+ * @param {object} messageFunctions
108
115
  */
109
116
  function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
110
117
  const _fs = cdsFs(fileCache, options.traceFs);
@@ -118,7 +125,7 @@ function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
118
125
 
119
126
  let result = null;
120
127
  let error = null;
121
- const lookupPath = adaptCdsModule(dep.module);
128
+ const lookupPath = adaptCdsModule(dep.module, options);
122
129
 
123
130
  resolveCDS( lookupPath, opts, (err, res) => {
124
131
  if (err)
@@ -14,47 +14,43 @@ class StopWatch {
14
14
  */
15
15
  constructor(id) {
16
16
  this.id = id;
17
- // TODO: If we require Node 12, use process.hrtime.bigint()
18
- // as process.hrtime() is deprecated.
19
- // eslint-disable-next-line no-multi-assign
20
- this.startTime = this.lapTime = process.hrtime();
17
+ this.startTime = process.hrtime.bigint();
18
+ this.lapTime = this.startTime;
21
19
  }
22
20
 
23
21
  /**
24
- * Start watch.
25
- */
22
+ * Start watch.
23
+ */
26
24
  start() {
27
- // eslint-disable-next-line no-multi-assign
28
- this.startTime = this.lapTime = process.hrtime();
25
+ this.startTime = process.hrtime.bigint();
26
+ this.lapTime = this.startTime;
29
27
  }
30
28
 
31
29
  /**
32
- * Stop and return delta T
30
+ * Stop and return delta T in nanoseconds,
33
31
  * but do not set start time
34
32
  */
35
33
  stop() {
36
- return process.hrtime(this.startTime);
34
+ const endTime = process.hrtime.bigint();
35
+ return endTime - this.startTime;
37
36
  }
38
37
 
39
- /**
40
- * return lap time
41
- */
42
38
  lap() {
43
- const dt = process.hrtime(this.lapTime);
44
- this.lapTime = process.hrtime();
39
+ const endTime = process.hrtime.bigint();
40
+ const dt = endTime - this.startTime;
41
+ this.lapTime = process.hrtime.bigint();
45
42
  return dt;
46
43
  }
47
44
 
48
- // stop as sec.ns float
49
45
  stopInFloatSecs() {
50
46
  const dt = this.stop();
51
- return dt[0] + dt[1] / 1000000000;
47
+ return dt / BigInt(1000000000);
52
48
  }
53
49
 
54
50
  // lap as sec.ns float
55
51
  lapInFloatSecs() {
56
52
  const dt = this.lap();
57
- return dt[0] + dt[1] / 1000000000;
53
+ return dt / BigInt(1000000000);
58
54
  }
59
55
  }
60
56
 
@@ -63,7 +59,7 @@ class StopWatch {
63
59
  *
64
60
  * Results are logged to stderr
65
61
  *
66
- * To enable time tracing, set CDSC_TIMETRACE to true in the environment
62
+ * To enable time tracing, set CDSC_TIMETRACING to true in the environment
67
63
  *
68
64
  * @class TimeTracer
69
65
  */
@@ -107,10 +103,13 @@ class TimeTracer {
107
103
  stop() {
108
104
  try {
109
105
  const current = this.traceStack.pop();
110
- const dT = current.stop();
106
+ const dt = current.stop();
111
107
  const base = `${ ' '.repeat(this.traceStack.length * 2) }${ current.id } took:`;
108
+ const sec = (dt / BigInt(1000000000)).toString();
109
+ // first, get remaining ns, then convert to ms.
110
+ const msec = ((dt % BigInt(1000000000)) / BigInt(1000000)).toString();
112
111
  // eslint-disable-next-line no-console
113
- console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, dT[0], dT[1] / 1000000);
112
+ console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, sec, msec );
114
113
  }
115
114
  catch (e) {
116
115
  // eslint-disable-next-line no-console