@sap/cds-compiler 2.5.2 → 2.11.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 (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { setProp } = require('../base/model');
4
4
  const { csnRefs } = require('../model/csnRefs');
5
+ const { isBuiltinType } = require('../compiler/builtins.js')
5
6
  const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
6
7
  const version = require('../../package.json').version;
7
8
 
@@ -11,8 +12,8 @@ const version = require('../../package.json').version;
11
12
  * Generic Callback
12
13
  *
13
14
  * @callback genericCallback
14
- * @param {CSN.Artifact} art
15
- * @param {CSN.FQN} name Artifact Name
15
+ * @param {any} art
16
+ * @param {string} name Artifact Name
16
17
  * @param {string} prop Dictionary Property
17
18
  * @param {CSN.Path} path Location
18
19
  * @param {CSN.Artifact} [dictionary]
@@ -36,7 +37,7 @@ const version = require('../../package.json').version;
36
37
  * @param {CSN.Model} model (Compact) CSN model
37
38
  */
38
39
  function getUtils(model) {
39
- const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
40
+ const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
40
41
 
41
42
  return {
42
43
  getCsnDef,
@@ -58,8 +59,137 @@ function getUtils(model) {
58
59
  inspectRef,
59
60
  artifactRef,
60
61
  effectiveType,
62
+ get$combined,
63
+ getOrigin,
61
64
  };
62
65
 
66
+ /**
67
+ * Compute and return $combined for the given query.
68
+ *
69
+ * @param {CSN.Query} query
70
+ * @returns {object}
71
+ */
72
+ function get$combined(query) {
73
+ const sources = getSources(query);
74
+ return sources;
75
+
76
+ /**
77
+ * Get the union of all elements from the from clause
78
+ * - descend into unions, following the lead query
79
+ * - merge all queries in case of joins
80
+ * - follow subqueries
81
+ *
82
+ * @param {CSN.Query} query Query to check
83
+ * @returns {object} Map of sources
84
+ */
85
+ function getSources(query, isSubquery=false) {
86
+ // Remark CW: better just a while along query.SET.args[0]
87
+ if (query.SET) {
88
+ if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
89
+ return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
90
+
91
+ return getSources(query.SET.args[0], isSubquery);
92
+ }
93
+ else if (query.SELECT) {
94
+ if (query.SELECT.from.args) {
95
+ return walkArgs(query.SELECT.from.args);
96
+ }
97
+ else if (query.SELECT.from.ref) {
98
+ let art = artifactRef(query.SELECT.from);
99
+
100
+ if(art.target)
101
+ art = artifactRef(art.target);
102
+
103
+ if(isSubquery && !query.SELECT.elements)
104
+ throw new Error('Expected subquery to have .elements');
105
+
106
+ return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
107
+ query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
108
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
109
+ }
110
+ else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
111
+ return getSources(query.SELECT.from, true);
112
+ }
113
+ }
114
+
115
+ function walkArgs(args) {
116
+ let elements = Object.create(null);
117
+ for (const arg of args) {
118
+ if (arg.args) {
119
+ elements = mergeElementMaps(elements, walkArgs(arg.args));
120
+ }
121
+ else if (arg.ref) {
122
+ const art = artifactRef(arg);
123
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
124
+ }
125
+ else if (arg.SELECT || arg.SET) {
126
+ elements = mergeElementMaps(elements, getSources(arg));
127
+ }
128
+ }
129
+
130
+ return elements;
131
+ }
132
+
133
+ return {};
134
+
135
+ /**
136
+ * Merge two maps of elements together
137
+ *
138
+ * @param {object} mapA Map a - will be returned
139
+ * @param {object} mapB Map b - will not be returned
140
+ * @returns {object} mapA
141
+ */
142
+ function mergeElementMaps(mapA, mapB) {
143
+ for (const elementName in mapB) {
144
+ if (!mapA[elementName])
145
+ mapA[elementName] = [];
146
+
147
+ mapB[elementName].forEach(e => mapA[elementName].push(e));
148
+ }
149
+
150
+ return mapA;
151
+ }
152
+
153
+ /**
154
+ * Merge elements into an existing map
155
+ *
156
+ * @param {any} existingMap map to merge into - will be returned
157
+ * @param {object} elements elements to merge into the map
158
+ * @param {CSN.Location} $location $location of the elements - where they come from
159
+ * @param {any} [parent] Name of the parent of the elements, alias before ref
160
+ * @param {any} [error_parent] Parent name to use for error messages, ref before alias
161
+ * @returns {object} existingMap
162
+ */
163
+ function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
164
+ for (const elementName in elements) {
165
+ const element = elements[elementName];
166
+ if (!existingMap[elementName])
167
+ existingMap[elementName] = [];
168
+
169
+
170
+ existingMap[elementName].push({
171
+ element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
172
+ });
173
+ }
174
+
175
+ return existingMap;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Return the name part of the artifact name - no namespace etc.
181
+ * @param {string|object} name Absolute name of the artifact
182
+ */
183
+ function getBaseName(name) {
184
+ if (!name)
185
+ return name;
186
+
187
+ if (name.id)
188
+ return name.id.substring( name.id.lastIndexOf('.')+1 );
189
+
190
+ return name.substring( name.lastIndexOf('.')+1 )
191
+ }
192
+ }
63
193
 
64
194
 
65
195
  /**
@@ -329,44 +459,53 @@ function getUtils(model) {
329
459
  }
330
460
  }
331
461
 
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) {
462
+
463
+ /**
464
+ * Resolve to the final type of a type, that means follow type chains, references to other types or
465
+ * elements a.s.o
466
+ * Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
467
+ * Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
468
+ * else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
469
+ * e.g. given the following model
470
+ * type bar: S.foo;
471
+ * type s1 {
472
+ * s: s2;
473
+ * };
474
+ * type s2 {
475
+ * u: type of S.e:t;
476
+ * }
477
+ * service S {
478
+ * type foo: type of S.e:i.j1;
479
+ * entity e {
480
+ * key i: { j1: Integer };
481
+ * t: bar;
482
+ * v: s1;
483
+ * x: blutz.s.u;
484
+ * };
485
+ * type blutz: S.e.v;
486
+ * view V as select from e {
487
+ * 1+1 as i: bar,
488
+ * };
489
+ * type tt: type of V:i;
490
+ * }
491
+ * the following calls will all return 'cds.Integer'
492
+ * getFinalBaseType('S.tt')
493
+ * getFinalBaseType('S.e',['i','j1'])
494
+ * getFinalBaseType('S.e',['t'])
495
+ * getFinalBaseType('S.e',['x'])
496
+ * getFinalBaseType('S.blutz',['s', 'u'])
497
+ * Types are always resolved as far as possible. A type name which has no further definition is simply returned.
498
+ * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
499
+ * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
500
+ * no type (e.g. expr in a view without explicit type) returns 'null'
501
+ *
502
+ * @param {string|object} type Type - either string or ref
503
+ * @param {CSN.Path} path
504
+ * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
505
+ * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
506
+ * @returns
507
+ */
508
+ function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
370
509
  if (!type)
371
510
  return type;
372
511
  if (typeof(type) === 'string') {
@@ -384,24 +523,24 @@ function getUtils(model) {
384
523
  }
385
524
  let definedType = model.definitions[type];
386
525
  if (definedType && definedType.type)
387
- return getFinalBaseType(definedType.type, path, cycleCheck);
526
+ return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
388
527
  else
389
- return getFinalBaseType(definedType, path, cycleCheck);
528
+ return getFinalBaseType(definedType, path, resolved, cycleCheck);
390
529
  }
391
530
  else if (typeof(type) === 'object') {
392
531
  if (type.ref) {
393
532
  // assert type.ref instanceof Array && type.ref.length >= 1
394
- const ref = artifactRef(type);
395
- return getFinalBaseType(ref, path, cycleCheck);
533
+ const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
534
+ return getFinalBaseType(ref, path, resolved, cycleCheck);
396
535
  }
397
536
  else if (type.elements) {
398
537
  if (path.length) {
399
538
  let [e, ...p] = path;
400
- return getFinalBaseType(type.elements[e], p, cycleCheck);
539
+ return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
401
540
  }
402
541
  }
403
542
  else if (type.type)
404
- return (getFinalBaseType(type.type, path, cycleCheck));
543
+ return (getFinalBaseType(type.type, path, resolved, cycleCheck));
405
544
  else if (type.items)
406
545
  return type;
407
546
  else
@@ -414,16 +553,6 @@ function getUtils(model) {
414
553
  }
415
554
  }
416
555
 
417
- // Tell if a type is (directly) a builtin type
418
- // Note that in CSN builtins are not in the definition of the model, so we can only check against their absolute names.
419
- // Builtin types are "cds.<something>", i.e. they are directly in 'cds', but not for example
420
- // in 'cds.foundation'. Also note, that a type might be a ref object, that refers to something else,
421
- // so if you consider type chains don't forget first to resolve to the final type before
422
- function isBuiltinType(type) {
423
- return typeof(type) === 'string' && type.startsWith('cds.') && !type.startsWith('cds.foundation.')
424
- }
425
-
426
-
427
556
  /**
428
557
  * Deeply clone the given CSN model and return it.
429
558
  * In testMode (or with testSortCsn), definitions are sorted.
@@ -493,12 +622,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
493
622
  // Backends rely on the fact that `forEachElement` also goes through all
494
623
  // `elements` of the return type (if structured).
495
624
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
496
- if (construct.returns) {
625
+ if (construct.returns && !iterateOptions.elementsOnly) {
497
626
  forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
498
627
  }
499
628
 
500
629
  path = [...path]; // Copy
501
- const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
630
+ const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
502
631
  propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
503
632
  }
504
633
 
@@ -679,11 +808,25 @@ function forAllQueries(query, callback, path = []){
679
808
  }
680
809
  }
681
810
 
682
- function forAllElements(artifact, artifactName, cb){
811
+ function forAllElements(artifact, artifactName, cb, includeActions = false){
683
812
  if(artifact.elements) {
684
813
  cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
685
814
  }
686
815
 
816
+ if(includeActions && artifact.actions) {
817
+ Object.entries(artifact.actions).forEach( ([actionName, action]) => {
818
+ const path = ['definitions', artifactName, 'actions', actionName];
819
+ if(action.params) {
820
+ Object.entries(action.params).forEach( ([paramName, param]) => {
821
+ if(param.elements)
822
+ cb(param, param.elements, path.concat(['params', paramName, 'elements']));
823
+ });
824
+ }
825
+ if(action.returns && action.returns.elements)
826
+ cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
827
+ });
828
+ }
829
+
687
830
  if(artifact.query) {
688
831
  forAllQueries(artifact.query, (q, p) => {
689
832
  const s = q.SELECT;
@@ -733,7 +876,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true) {
733
876
  * function accepts EDM internal and external options
734
877
  *
735
878
  * @param {CSN.Element} elementCsn
736
- * @param {CSN.Options & CSN.ODataOptions} options EDM specific options
879
+ * @param {ODataOptions} options EDM specific options
737
880
  */
738
881
  function isEdmPropertyRendered(elementCsn, options) {
739
882
  if(options.toOdata)
@@ -783,10 +926,10 @@ function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
783
926
  throw new Error('Unknown naming convention: ' + namingConvention);
784
927
  }
785
928
  else {
786
- const namespace = csn;
787
929
  console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
788
930
  if (namingConvention === 'hdbcds') {
789
- if (namespace) {
931
+ if (csn) {
932
+ const namespace = String(csn);
790
933
  return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
791
934
  }
792
935
  return artifactName;
@@ -922,11 +1065,12 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
922
1065
  *
923
1066
  * @param {object} csn CSN to enrich in-place
924
1067
  * @param {object} customTransformers Map of prop to transform and function to apply
925
- * @param {Function[]} artifactTransformers Transformations to run on the artifacts, like forEachDefinition
926
- * @param {Boolean} skipIgnore Wether to skip _ignore elements or not
1068
+ * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
1069
+ * @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
1070
+ * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
927
1071
  * @returns {object} CSN with transformations applied
928
1072
  */
929
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true ) {
1073
+ function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
930
1074
  const transformers = {
931
1075
  elements: dictionary,
932
1076
  definitions: dictionary,
@@ -959,7 +1103,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
959
1103
  for (let name of Object.getOwnPropertyNames( node )) {
960
1104
  const trans = transformers[name] || standard;
961
1105
  if(customTransformers[name])
962
- customTransformers[name](node, name, node[name], csnPath);
1106
+ customTransformers[name](node, name, node[name], csnPath, parent, prop);
963
1107
 
964
1108
  trans( node, name, node[name], csnPath );
965
1109
  }
@@ -968,6 +1112,9 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
968
1112
  }
969
1113
 
970
1114
  function dictionary( node, prop, dict ) {
1115
+ // Allow skipping dicts like actions in forHanaNew
1116
+ if(options.skipDict && options.skipDict[prop])
1117
+ return;
971
1118
  csnPath.push( prop );
972
1119
  for (let name of Object.getOwnPropertyNames( dict )) {
973
1120
  standard( dict, name, dict[name] );
@@ -980,8 +1127,11 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
980
1127
  function definitions( node, prop, dict ) {
981
1128
  csnPath.push( prop );
982
1129
  for (let name of Object.getOwnPropertyNames( dict )) {
983
- artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
984
- standard( dict, name, dict[name] );
1130
+ const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
1131
+ if(!skip) {
1132
+ artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
1133
+ standard( dict, name, dict[name] );
1134
+ }
985
1135
  }
986
1136
  if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
987
1137
  setProp(node, '$' + prop, dict);
@@ -994,10 +1144,14 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
994
1144
  path.forEach( function step( s, i ) {
995
1145
  if (s && typeof s === 'object') {
996
1146
  csnPath.push( i );
997
- if (s.args)
998
- standard( s, 'args', s.args );
999
- if (s.where)
1000
- standard( s, 'where', s.where );
1147
+ if(options.drillRef) {
1148
+ standard(path, i, s);
1149
+ } else {
1150
+ if (s.args)
1151
+ standard( s, 'args', s.args );
1152
+ if (s.where)
1153
+ standard( s, 'where', s.where );
1154
+ }
1001
1155
  csnPath.pop();
1002
1156
  }
1003
1157
  } );
@@ -1383,7 +1537,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1383
1537
  if (!options.testMode)
1384
1538
  return;
1385
1539
  const sorted = Object.create(null);
1386
- Object.keys(csn.definitions).sort().forEach((name) => {
1540
+ Object.keys(csn.definitions || {}).sort().forEach((name) => {
1387
1541
  sorted[name] = csn.definitions[name];
1388
1542
  });
1389
1543
  csn.definitions = sorted;
@@ -1393,7 +1547,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1393
1547
  * Return an array of non-abstract service names contained in CSN
1394
1548
  *
1395
1549
  * @param {CSN.Model} csn
1396
- * @returns {CSN.Service[]}
1550
+ * @returns {string[]}
1397
1551
  */
1398
1552
  function getServiceNames(csn) {
1399
1553
  let result = [];
@@ -1405,6 +1559,60 @@ function getServiceNames(csn) {
1405
1559
  return result;
1406
1560
  }
1407
1561
 
1562
+ /**
1563
+ * Check wether the artifact is @cds.persistence.skip
1564
+ *
1565
+ * @param {CSN.Artifact} artifact
1566
+ * @returns {Boolean}
1567
+ */
1568
+ function isSkipped(artifact) {
1569
+ return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
1570
+ }
1571
+
1572
+ /**
1573
+ * Walk path in the CSN and return the result.
1574
+ *
1575
+ * @param {CSN.Model} csn
1576
+ * @param {CSN.Path} path
1577
+ * @returns {object} Whatever is at the end of path
1578
+ */
1579
+ function walkCsnPath(csn, path) {
1580
+ /** @type {object} */
1581
+ let obj = csn;
1582
+ for(let i = 0; i < path.length; i++){
1583
+ obj = obj[path[i]];
1584
+ }
1585
+
1586
+ return obj;
1587
+ }
1588
+
1589
+ /**
1590
+ * If provided, get the replacement string for the given magic variable ref.
1591
+ * No validation is done that the ref is actually magic!
1592
+ *
1593
+ * @param {array} ref
1594
+ * @param {CSN.Options} options
1595
+ * @returns {string|null}
1596
+ */
1597
+ function getVariableReplacement(ref, options) {
1598
+ if(options && options.variableReplacements) {
1599
+ let replacement = options.variableReplacements;
1600
+ for(let i = 0; i < ref.length; i++) {
1601
+ replacement = replacement[ref[i]];
1602
+ if(replacement === undefined)
1603
+ return null;
1604
+ }
1605
+
1606
+ if(replacement === undefined)
1607
+ return null; // no valid replacement found
1608
+ else if(typeof replacement === 'string')
1609
+ return replacement; // valid replacement
1610
+ else
1611
+ return null; // $user.foo, but we only have configured $user.foo.bar -> error
1612
+ } else {
1613
+ return null;
1614
+ }
1615
+ }
1408
1616
 
1409
1617
  module.exports = {
1410
1618
  getUtils,
@@ -1444,4 +1652,7 @@ module.exports = {
1444
1652
  getNamespace,
1445
1653
  sortCsnDefinitionsForTests,
1446
1654
  getServiceNames,
1655
+ isSkipped,
1656
+ walkCsnPath,
1657
+ getVariableReplacement
1447
1658
  };
@@ -39,16 +39,20 @@ function enrichCsn( csn, options = {} ) {
39
39
  params: dictionary,
40
40
  enum: dictionary,
41
41
  mixin: dictionary,
42
+ returns: definition,
43
+ items: definition,
42
44
  ref: pathRef,
43
45
  type: simpleRef,
44
46
  targetAspect: simpleRef,
45
47
  target: simpleRef,
46
48
  includes: simpleRef,
49
+ $origin,
47
50
  // TODO: excluding
48
51
  '@': () => { /* ignore annotations */ },
49
52
  }
50
53
  setLocations( csn, false, null );
51
- const { inspectRef, artifactRef, __getCache_forEnrichCsnDebugging } = csnRefs( csn );
54
+ const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
55
+ csnRefs( csn );
52
56
  let $$cacheObjectNumber = 0; // for debugging
53
57
  const csnPath = [];
54
58
  if (csn.definitions)
@@ -79,10 +83,19 @@ function enrichCsn( csn, options = {} ) {
79
83
  csnPath.pop();
80
84
  }
81
85
 
86
+ function definition( parent, prop, obj ) {
87
+ const origin = getOrigin( obj ); // before standard for implicit protos inside
88
+ standard( parent, prop, obj );
89
+ if (obj.$origin === undefined && origin != null)
90
+ obj._origin = refLocation( origin );
91
+ }
92
+
82
93
  function dictionary( parent, prop, dict ) {
94
+ if (!dict) // value null for inheritance interruption
95
+ return;
83
96
  csnPath.push( prop );
84
97
  for (let name of Object.getOwnPropertyNames( dict )) {
85
- standard( dict, name, dict[name] );
98
+ definition( dict, name, dict[name] );
86
99
  }
87
100
  if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
88
101
  parent['$'+prop] = dict;
@@ -119,6 +132,25 @@ function enrichCsn( csn, options = {} ) {
119
132
  // parent['_' + prop] = e.toString(); }
120
133
  }
121
134
 
135
+ function $origin( parent, prop, ref ) {
136
+ if (options.testMode) {
137
+ if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
138
+ parent._origin = refLocation( getOrigin( parent, true ) );
139
+ else if ( ref )
140
+ standard( parent, prop, ref )
141
+ }
142
+ else {
143
+ try {
144
+ if (Array.isArray( ref ) || typeof ref === 'string') // $origin: […], not $origin: {…}
145
+ parent._origin = refLocation( getOrigin( parent, true ) );
146
+ else if ( ref )
147
+ standard( parent, prop, ref )
148
+ } catch (e) {
149
+ parent._origin = e.toString();
150
+ }
151
+ }
152
+ }
153
+
122
154
  function pathRef( parent, prop, path ) {
123
155
  const { links, art, scope, $env } = (() => {
124
156
  if (options.testMode)
@@ -154,10 +186,10 @@ function enrichCsn( csn, options = {} ) {
154
186
  csnPath.pop();
155
187
  }
156
188
 
157
- function _cache_debug( obj ) {
189
+ function _cache_debug( obj, subCache ) {
158
190
  if (options.enrichCsn !== 'DEBUG')
159
191
  return;
160
- const cache = __getCache_forEnrichCsnDebugging( obj );
192
+ const cache = subCache || __getCache_forEnrichCsnDebugging( obj );
161
193
  if (!cache)
162
194
  return;
163
195
  if (cache.$$objectNumber > 0) {
@@ -180,8 +212,20 @@ function enrichCsn( csn, options = {} ) {
180
212
  }
181
213
  else if (name[0] !== '$' || !Object.getPrototypeOf( val )) {
182
214
  // ‹name›: dictionary of CSN nodes,
183
- // ‹$name›: dictionary of cache values if no prototype,
184
- obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
215
+ // ‹$name›: dictionary of cache values if no prototype
216
+ if (name !== '$aliases') {
217
+ obj.$$cacheObject[name] = Object.keys( val ); // TODO: or dict?
218
+ }
219
+ else {
220
+ const sub = Object.create(null);
221
+ for (const n in val) {
222
+ const alias = val[n];
223
+ const c = {};
224
+ _cache_debug( c, alias );
225
+ sub[n] = c.$$cacheObject;
226
+ }
227
+ obj.$$cacheObject[name] = sub;
228
+ }
185
229
  }
186
230
  else if (Array.isArray( val )) {
187
231
  obj.$$cacheObject[name] = val.map( item => {