@sap/cds-compiler 2.4.4 → 2.10.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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. package/lib/utils/string.js +0 -17
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { setProp } = require('../base/model');
4
4
  const { csnRefs } = require('../model/csnRefs');
5
- const { sortCsn } = require('../json/to-csn');
5
+ const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
6
6
  const version = require('../../package.json').version;
7
7
 
8
8
  // Low-level utility functions to work with compact CSN.
@@ -36,7 +36,7 @@ const version = require('../../package.json').version;
36
36
  * @param {CSN.Model} model (Compact) CSN model
37
37
  */
38
38
  function getUtils(model) {
39
- const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
39
+ const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
40
40
 
41
41
  return {
42
42
  getCsnDef,
@@ -52,14 +52,143 @@ function getUtils(model) {
52
52
  getContextOfArtifact,
53
53
  addStringAnnotationTo,
54
54
  getServiceName,
55
- hasBoolAnnotation,
55
+ hasAnnotationValue,
56
56
  cloneWithTransformations,
57
57
  getFinalBaseType,
58
58
  inspectRef,
59
59
  artifactRef,
60
60
  effectiveType,
61
+ get$combined,
62
+ getOrigin,
61
63
  };
62
64
 
65
+ /**
66
+ * Compute and return $combined for the given query.
67
+ *
68
+ * @param {CSN.Query} query
69
+ * @returns {object}
70
+ */
71
+ function get$combined(query) {
72
+ const sources = getSources(query);
73
+ return sources;
74
+
75
+ /**
76
+ * Get the union of all elements from the from clause
77
+ * - descend into unions, following the lead query
78
+ * - merge all queries in case of joins
79
+ * - follow subqueries
80
+ *
81
+ * @param {CSN.Query} query Query to check
82
+ * @returns {object} Map of sources
83
+ */
84
+ function getSources(query, isSubquery=false) {
85
+ // Remark CW: better just a while along query.SET.args[0]
86
+ if (query.SET) {
87
+ if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
88
+ return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
89
+
90
+ return getSources(query.SET.args[0], isSubquery);
91
+ }
92
+ else if (query.SELECT) {
93
+ if (query.SELECT.from.args) {
94
+ return walkArgs(query.SELECT.from.args);
95
+ }
96
+ else if (query.SELECT.from.ref) {
97
+ let art = artifactRef(query.SELECT.from);
98
+
99
+ if(art.target)
100
+ art = artifactRef(art.target);
101
+
102
+ if(isSubquery && !query.SELECT.elements)
103
+ throw new Error('Expected subquery to have .elements');
104
+
105
+ return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
106
+ query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
107
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
108
+ }
109
+ else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
110
+ return getSources(query.SELECT.from, true);
111
+ }
112
+ }
113
+
114
+ function walkArgs(args) {
115
+ let elements = Object.create(null);
116
+ for (const arg of args) {
117
+ if (arg.args) {
118
+ elements = mergeElementMaps(elements, walkArgs(arg.args));
119
+ }
120
+ else if (arg.ref) {
121
+ const art = artifactRef(arg);
122
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
123
+ }
124
+ else if (arg.SELECT || arg.SET) {
125
+ elements = mergeElementMaps(elements, getSources(arg));
126
+ }
127
+ }
128
+
129
+ return elements;
130
+ }
131
+
132
+ return {};
133
+
134
+ /**
135
+ * Merge two maps of elements together
136
+ *
137
+ * @param {object} mapA Map a - will be returned
138
+ * @param {object} mapB Map b - will not be returned
139
+ * @returns {object} mapA
140
+ */
141
+ function mergeElementMaps(mapA, mapB) {
142
+ for (const elementName in mapB) {
143
+ if (!mapA[elementName])
144
+ mapA[elementName] = [];
145
+
146
+ mapB[elementName].forEach(e => mapA[elementName].push(e));
147
+ }
148
+
149
+ return mapA;
150
+ }
151
+
152
+ /**
153
+ * Merge elements into an existing map
154
+ *
155
+ * @param {any} existingMap map to merge into - will be returned
156
+ * @param {object} elements elements to merge into the map
157
+ * @param {CSN.Location} $location $location of the elements - where they come from
158
+ * @param {any} [parent] Name of the parent of the elements, alias before ref
159
+ * @param {any} [error_parent] Parent name to use for error messages, ref before alias
160
+ * @returns {object} existingMap
161
+ */
162
+ function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
163
+ for (const elementName in elements) {
164
+ const element = elements[elementName];
165
+ if (!existingMap[elementName])
166
+ existingMap[elementName] = [];
167
+
168
+
169
+ existingMap[elementName].push({
170
+ element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
171
+ });
172
+ }
173
+
174
+ return existingMap;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Return the name part of the artifact name - no namespace etc.
180
+ * @param {string|object} name Absolute name of the artifact
181
+ */
182
+ function getBaseName(name) {
183
+ if (!name)
184
+ return name;
185
+
186
+ if (name.id)
187
+ return name.id.substring( name.id.lastIndexOf('.')+1 );
188
+
189
+ return name.substring( name.lastIndexOf('.')+1 )
190
+ }
191
+ }
63
192
 
64
193
 
65
194
  /**
@@ -328,45 +457,54 @@ function getUtils(model) {
328
457
  return resultNode;
329
458
  }
330
459
  }
331
-
332
- // Resolve to the final type of a type, that means follow type chains, references to other types or
333
- // elements a.s.o
334
- // Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
335
- // Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
336
- // else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
337
- // e.g. given the following model
338
- // type bar: S.foo;
339
- // type s1 {
340
- // s: s2;
341
- // };
342
- // type s2 {
343
- // u: type of S.e:t;
344
- // }
345
- // service S {
346
- // type foo: type of S.e:i.j1;
347
- // entity e {
348
- // key i: { j1: Integer };
349
- // t: bar;
350
- // v: s1;
351
- // x: blutz.s.u;
352
- // };
353
- // type blutz: S.e.v;
354
- // view V as select from e {
355
- // 1+1 as i: bar,
356
- // };
357
- // type tt: type of V:i;
358
- // }
359
- // the following calls will all return 'cds.Integer'
360
- // getFinalBaseType('S.tt')
361
- // getFinalBaseType('S.e',['i','j1'])
362
- // getFinalBaseType('S.e',['t'])
363
- // getFinalBaseType('S.e',['x'])
364
- // getFinalBaseType('S.blutz',['s', 'u'])
365
- // Types are always resolved as far as possible. A type name which has no further definition is simply returned.
366
- // Composed types (structures, entities, vies, ...) are returned as type objects, if not drilled down into
367
- // the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
368
- // no type (e.g. expr in a view without explicit type) returns 'null'
369
- function getFinalBaseType(type, path = [], cycleCheck = undefined) {
460
+
461
+
462
+ /**
463
+ * Resolve to the final type of a type, that means follow type chains, references to other types or
464
+ * elements a.s.o
465
+ * Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
466
+ * Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
467
+ * else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
468
+ * e.g. given the following model
469
+ * type bar: S.foo;
470
+ * type s1 {
471
+ * s: s2;
472
+ * };
473
+ * type s2 {
474
+ * u: type of S.e:t;
475
+ * }
476
+ * service S {
477
+ * type foo: type of S.e:i.j1;
478
+ * entity e {
479
+ * key i: { j1: Integer };
480
+ * t: bar;
481
+ * v: s1;
482
+ * x: blutz.s.u;
483
+ * };
484
+ * type blutz: S.e.v;
485
+ * view V as select from e {
486
+ * 1+1 as i: bar,
487
+ * };
488
+ * type tt: type of V:i;
489
+ * }
490
+ * the following calls will all return 'cds.Integer'
491
+ * getFinalBaseType('S.tt')
492
+ * getFinalBaseType('S.e',['i','j1'])
493
+ * getFinalBaseType('S.e',['t'])
494
+ * getFinalBaseType('S.e',['x'])
495
+ * getFinalBaseType('S.blutz',['s', 'u'])
496
+ * Types are always resolved as far as possible. A type name which has no further definition is simply returned.
497
+ * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
498
+ * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
499
+ * no type (e.g. expr in a view without explicit type) returns 'null'
500
+ *
501
+ * @param {string|object} type Type - either string or ref
502
+ * @param {CSN.Path} path
503
+ * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
504
+ * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
505
+ * @returns
506
+ */
507
+ function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
370
508
  if (!type)
371
509
  return type;
372
510
  if (typeof(type) === 'string') {
@@ -384,24 +522,24 @@ function getUtils(model) {
384
522
  }
385
523
  let definedType = model.definitions[type];
386
524
  if (definedType && definedType.type)
387
- return getFinalBaseType(definedType.type, path, cycleCheck);
525
+ return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
388
526
  else
389
- return getFinalBaseType(definedType, path, cycleCheck);
527
+ return getFinalBaseType(definedType, path, resolved, cycleCheck);
390
528
  }
391
529
  else if (typeof(type) === 'object') {
392
530
  if (type.ref) {
393
531
  // assert type.ref instanceof Array && type.ref.length >= 1
394
- const ref = artifactRef(type);
395
- return getFinalBaseType(ref, path, cycleCheck);
532
+ const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
533
+ return getFinalBaseType(ref, path, resolved, cycleCheck);
396
534
  }
397
535
  else if (type.elements) {
398
536
  if (path.length) {
399
537
  let [e, ...p] = path;
400
- return getFinalBaseType(type.elements[e], p, cycleCheck);
538
+ return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
401
539
  }
402
540
  }
403
541
  else if (type.type)
404
- return (getFinalBaseType(type.type, path, cycleCheck));
542
+ return (getFinalBaseType(type.type, path, resolved, cycleCheck));
405
543
  else if (type.items)
406
544
  return type;
407
545
  else
@@ -426,15 +564,30 @@ function isBuiltinType(type) {
426
564
 
427
565
  /**
428
566
  * Deeply clone the given CSN model and return it.
429
- * In testMode, definitions are sorted.
567
+ * In testMode (or with testSortCsn), definitions are sorted.
568
+ * Note that annotations are only copied shallowly.
430
569
  *
431
- * @param {object} csn
570
+ * @param {object} csn Top-level CSN. You can pass non-dictionary values.
432
571
  * @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
433
572
  */
434
573
  function cloneCsn(csn, options) {
435
574
  return sortCsn(csn, options);
436
575
  }
437
576
 
577
+ /**
578
+ * Deeply clone the given CSN dictionary and return it.
579
+ * Note that annotations are only copied shallowly.
580
+ * This function does _not_ sort the given dictionary.
581
+ * See cloneCsn() if you want sorted definitions.
582
+ *
583
+ * @param {object} csn
584
+ * @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
585
+ * used and cloneOptions are passed to sort().
586
+ */
587
+ function cloneCsnDictionary(csn, options) {
588
+ return _cloneCsnDictionary(csn, options);
589
+ }
590
+
438
591
  /**
439
592
  * Apply function `callback` to all artifacts in dictionary
440
593
  * `model.definitions`. See function `forEachGeneric` for details.
@@ -478,12 +631,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
478
631
  // Backends rely on the fact that `forEachElement` also goes through all
479
632
  // `elements` of the return type (if structured).
480
633
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
481
- if (construct.returns) {
634
+ if (construct.returns && !iterateOptions.elementsOnly) {
482
635
  forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
483
636
  }
484
637
 
485
638
  path = [...path]; // Copy
486
- const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
639
+ const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
487
640
  propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
488
641
  }
489
642
 
@@ -526,7 +679,9 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
526
679
  if (!Object.prototype.hasOwnProperty.call(dict, name))
527
680
  continue;
528
681
  const dictObj = dict[name];
529
- if(iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind))
682
+ if((iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind))
683
+ || (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
684
+ && iterateOptions.skipArtifact(dictObj, name)))
530
685
  continue;
531
686
  cb( dictObj, name );
532
687
  }
@@ -682,15 +837,30 @@ function forAllElements(artifact, artifactName, cb){
682
837
  }
683
838
 
684
839
  /**
685
- * Returns true if the element has a specific annotation set to the given value.
840
+ * Compare a given annotation value with an expectation value and return
841
+ *
842
+ * | Expected
843
+ * | true | false | null | arb val
844
+ * Anno Val |-------|-------|-------|--------
845
+ * true | true | false | false | false
846
+ * false | false | true | false | false
847
+ * null | false | true | true | false
848
+ * arb val | false | false | false | true/false
849
+ *
850
+ * If the annotation value is 'null', 'true' is returned for the expectation values
851
+ * 'null' and 'false'. Expecting 'null' for an annotation value 'false' returns
852
+ * 'false'.
686
853
  *
687
854
  * @param {CSN.Artifact} artifact
688
855
  * @param {string} annotationName Name of the annotation (including the at-sign)
689
- * @param {any} value
856
+ * @param {any} expected
690
857
  * @returns {boolean}
691
858
  */
692
- function hasBoolAnnotation(artifact, annotationName, value = true) {
693
- return artifact[annotationName] === value;
859
+ function hasAnnotationValue(artifact, annotationName, expected = true) {
860
+ if(expected === false)
861
+ return artifact[annotationName] === expected || artifact[annotationName] === null;
862
+ else
863
+ return artifact[annotationName] === expected;
694
864
  }
695
865
 
696
866
  /**
@@ -890,11 +1060,12 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
890
1060
  *
891
1061
  * @param {object} csn CSN to enrich in-place
892
1062
  * @param {object} customTransformers Map of prop to transform and function to apply
893
- * @param {Function[]} artifactTransformers Transformations to run on the artifacts, like forEachDefinition
894
- * @param {Boolean} skipIgnore Wether to skip _ignore elements or not
1063
+ * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
1064
+ * @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
1065
+ * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
895
1066
  * @returns {object} CSN with transformations applied
896
1067
  */
897
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true ) {
1068
+ function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
898
1069
  const transformers = {
899
1070
  elements: dictionary,
900
1071
  definitions: dictionary,
@@ -927,7 +1098,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
927
1098
  for (let name of Object.getOwnPropertyNames( node )) {
928
1099
  const trans = transformers[name] || standard;
929
1100
  if(customTransformers[name])
930
- customTransformers[name](node, name, node[name], csnPath);
1101
+ customTransformers[name](node, name, node[name], csnPath, parent, prop);
931
1102
 
932
1103
  trans( node, name, node[name], csnPath );
933
1104
  }
@@ -948,8 +1119,11 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
948
1119
  function definitions( node, prop, dict ) {
949
1120
  csnPath.push( prop );
950
1121
  for (let name of Object.getOwnPropertyNames( dict )) {
951
- artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
952
- standard( dict, name, dict[name] );
1122
+ const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
1123
+ if(!skip) {
1124
+ artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
1125
+ standard( dict, name, dict[name] );
1126
+ }
953
1127
  }
954
1128
  if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
955
1129
  setProp(node, '$' + prop, dict);
@@ -962,10 +1136,14 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
962
1136
  path.forEach( function step( s, i ) {
963
1137
  if (s && typeof s === 'object') {
964
1138
  csnPath.push( i );
965
- if (s.args)
966
- standard( s, 'args', s.args );
967
- if (s.where)
968
- standard( s, 'where', s.where );
1139
+ if(options.drillRef) {
1140
+ standard(path, i, s);
1141
+ } else {
1142
+ if (s.args)
1143
+ standard( s, 'args', s.args );
1144
+ if (s.where)
1145
+ standard( s, 'where', s.where );
1146
+ }
969
1147
  csnPath.pop();
970
1148
  }
971
1149
  } );
@@ -1047,7 +1225,7 @@ function setDependencies( csn ) {
1047
1225
  * @returns {boolean}
1048
1226
  */
1049
1227
  function isPersistedOnDatabase(art) {
1050
- return !([ 'entity', 'view' ].includes(art.kind) && (art.abstract || hasBoolAnnotation(art, '@cds.persistence.skip')));
1228
+ return !([ 'entity', 'view' ].includes(art.kind) && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1051
1229
  }
1052
1230
 
1053
1231
  /**
@@ -1160,6 +1338,7 @@ function mergeOptions(...optionsObjects) {
1160
1338
  // Will return the artifact 'name' if it is a top-level artifact itself, and 'undefined'
1161
1339
  // if there is no artifact surrounding 'name' in the model
1162
1340
  // TODO: to be checked by author: still intended behaviour with 'cds' prefix?
1341
+ // TODO: Can this be replaced by getRootArtifactName? Or maybe not rely on namespace-hacking...
1163
1342
  // FIXME: This only works with namespace-hacking, i.e. adding them as artifacts...
1164
1343
  function getTopLevelArtifactNameOf(name, model) {
1165
1344
  let dotIdx = name.indexOf('.');
@@ -1267,22 +1446,13 @@ function copyAnnotations(fromNode, toNode, overwrite=false) {
1267
1446
  if (!Object.hasOwnProperty.call( fromNode, prop ))
1268
1447
  continue;
1269
1448
  if (prop.startsWith('@')) {
1270
- if (toNode[prop] == undefined || overwrite) {
1449
+ if (toNode[prop] === undefined || overwrite) {
1271
1450
  toNode[prop] = fromNode[prop];
1272
1451
  }
1273
1452
  }
1274
1453
  }
1275
1454
  }
1276
1455
 
1277
- // Return true if 'type' is a composition type
1278
- function isComposition(type) {
1279
- if (!type)
1280
- return type;
1281
- if (type._artifact)
1282
- type = type._artifact.name;
1283
- return type.absolute === 'cds.Composition';
1284
- }
1285
-
1286
1456
  function isAspect(node) {
1287
1457
  return node && node.kind === 'aspect';
1288
1458
  }
@@ -1305,59 +1475,7 @@ function forEachPath(node, callback) {
1305
1475
  }
1306
1476
  }
1307
1477
 
1308
- // Add an annotation with absolute name 'absoluteName' (including '@') and string value 'theValue' to 'node'
1309
- function addBoolAnnotationTo(absoluteName, theValue, node) {
1310
- // Sanity check
1311
- if (!absoluteName.startsWith('@')) {
1312
- throw Error('Annotation name should start with "@": ' + absoluteName);
1313
- }
1314
- // Assemble the annotation
1315
- if(isAnnotationAssignable(node, absoluteName)) {
1316
- node[absoluteName] = {
1317
- name: {
1318
- absolute: absoluteName.substring(1),
1319
- location: node.location, // inherit location from main element
1320
- },
1321
- val: theValue,
1322
- literal: 'boolean',
1323
- location: node.location, // inherit location from main element
1324
- };
1325
- }
1326
- }
1327
1478
 
1328
- // Add an annotation with absolute name 'absoluteName' (including '@') and array 'theValue' to 'node'
1329
- function addArrayAnnotationTo(absoluteName, theValue, node) {
1330
- // Sanity check
1331
- if (!absoluteName.startsWith('@')) {
1332
- throw Error(`Annotation name should start with "@": ${ absoluteName}`);
1333
- }
1334
- // Assemble the annotation
1335
- if(isAnnotationAssignable(node, absoluteName)) {
1336
- node[absoluteName] = {
1337
- name: {
1338
- absolute: absoluteName.substring(1),
1339
- location: node.location, // inherit location from main element
1340
- },
1341
- val: theValue,
1342
- literal: 'array',
1343
- location: node.location, // inherit location from main element
1344
- };
1345
- }
1346
- }
1347
-
1348
- /**
1349
- * Check wether an annotation can be assigned or not
1350
- * (must be either undefined or null value)
1351
- * @param {object} node Assignee
1352
- * @param {string} name Annotation name
1353
- * @returns {boolean}
1354
- */
1355
- function isAnnotationAssignable(node, name) {
1356
- if (!name.startsWith('@')) {
1357
- throw Error(`Annotation name should start with "@": ${ name}`);
1358
- }
1359
- return (node[name] === undefined || node[name] && node[name].val === null);
1360
- }
1361
1479
  /**
1362
1480
  * Return true if the artifact has a valid, truthy persistence.exists/skip annotation
1363
1481
  *
@@ -1366,7 +1484,7 @@ function isAnnotationAssignable(node, name) {
1366
1484
  */
1367
1485
  function hasValidSkipOrExists(artifact) {
1368
1486
  return (artifact.kind === 'entity' || artifact.kind === 'view') &&
1369
- (hasBoolAnnotation(artifact, '@cds.persistence.exists', true) || hasBoolAnnotation(artifact, '@cds.persistence.skip', true))
1487
+ (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
1370
1488
 
1371
1489
  }
1372
1490
 
@@ -1420,7 +1538,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1420
1538
  /**
1421
1539
  * Return an array of non-abstract service names contained in CSN
1422
1540
  *
1423
- * @param {CSN.Model} csn
1541
+ * @param {CSN.Model} csn
1424
1542
  * @returns {CSN.Service[]}
1425
1543
  */
1426
1544
  function getServiceNames(csn) {
@@ -1433,10 +1551,37 @@ function getServiceNames(csn) {
1433
1551
  return result;
1434
1552
  }
1435
1553
 
1554
+ /**
1555
+ * Check wether the artifact is @cds.persistence.skip
1556
+ *
1557
+ * @param {CSN.Artifact} artifact
1558
+ * @returns {Boolean}
1559
+ */
1560
+ function isSkipped(artifact) {
1561
+ return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
1562
+ }
1563
+
1564
+ /**
1565
+ * Walk path in the CSN and return the result.
1566
+ *
1567
+ * @param {CSN.Model} csn
1568
+ * @param {CSN.Path} path
1569
+ * @returns {object} Whatever is at the end of path
1570
+ */
1571
+ function walkCsnPath(csn, path) {
1572
+ /** @type {object} */
1573
+ let obj = csn;
1574
+ for(let i = 0; i < path.length; i++){
1575
+ obj = obj[path[i]];
1576
+ }
1577
+
1578
+ return obj;
1579
+ }
1436
1580
 
1437
1581
  module.exports = {
1438
1582
  getUtils,
1439
1583
  cloneCsn,
1584
+ cloneCsnDictionary,
1440
1585
  isBuiltinType,
1441
1586
  assignAll,
1442
1587
  forEachGeneric,
@@ -1446,7 +1591,7 @@ module.exports = {
1446
1591
  forEachRef,
1447
1592
  forAllQueries,
1448
1593
  forAllElements,
1449
- hasBoolAnnotation,
1594
+ hasAnnotationValue,
1450
1595
  isEdmPropertyRendered,
1451
1596
  getArtifactDatabaseNameOf,
1452
1597
  getResultingName,
@@ -1465,13 +1610,12 @@ module.exports = {
1465
1610
  getParentNameOf,
1466
1611
  getLastPartOf,
1467
1612
  copyAnnotations,
1468
- isComposition,
1469
1613
  isAspect,
1470
1614
  forEachPath,
1471
- addBoolAnnotationTo,
1472
- addArrayAnnotationTo,
1473
1615
  hasValidSkipOrExists,
1474
1616
  getNamespace,
1475
1617
  sortCsnDefinitionsForTests,
1476
1618
  getServiceNames,
1619
+ isSkipped,
1620
+ walkCsnPath,
1477
1621
  };