@sap/cds-compiler 2.10.2 → 2.11.4

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 (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
package/lib/model/api.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * - the ‹property name› (might be useful if the same function is used for several props)
16
16
  */
17
17
  const defaultFunctions = {
18
- '@': () => {}, // do not traverse annotation assignments
18
+ '@': () => { /* do not traverse annotation assignments */ },
19
19
  args: dictionary,
20
20
  elements: dictionary,
21
21
  enum: dictionary,
@@ -23,7 +23,7 @@ const defaultFunctions = {
23
23
  actions: dictionary,
24
24
  mixin: dictionary,
25
25
  definitions: dictionary,
26
- '$': () => {}, // do not traverse properties starting with '$'
26
+ '$': () => { /* do not traverse properties starting with '$' */},
27
27
  }
28
28
 
29
29
  /**
@@ -225,17 +225,19 @@ function csnRefs( csn ) {
225
225
  const cachedType = getCache( art, '_effectiveType' );
226
226
  if (cachedType !== undefined)
227
227
  return cachedType;
228
- else if (!art.type && !art.$origin ||
229
- art.elements || art.target || art.targetAspect || art.enum)
230
- return setCache( art, '_effectiveType', art );
231
228
 
232
229
  const chain = [];
233
- while (getCache( art, '_effectiveType' ) === undefined && (art.type || art.$origin) &&
230
+ let origin;
231
+ while (getCache( art, '_effectiveType' ) === undefined &&
232
+ (origin = cached( art, '_origin', getOriginRaw )) &&
234
233
  !art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
235
234
  chain.push( art );
236
235
  setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
237
- art = (art.$origin) ? getOrigin( art ) : artifactRef( art.type, BUILTIN_TYPE );
236
+ art = origin;
238
237
  }
238
+ if (!chain.length)
239
+ return setCache( art, '_effectiveType', art );
240
+
239
241
  if (getCache( art, '_effectiveType' ) === 0)
240
242
  throw new Error( 'Circular type reference');
241
243
  const type = getCache( art, '_effectiveType' ) || art;
@@ -251,10 +253,16 @@ function csnRefs( csn ) {
251
253
  // here, we do not care whether it is semantically ok to navigate into sub
252
254
  // elements of array items (that is the task of the core compiler /
253
255
  // semantic check)
254
- while (type.items)
256
+ while (type.items) {
257
+ cached( type, '$origin', _a => setImplicitOrigin( type, origin ) );
255
258
  type = effectiveType( type.items );
259
+ }
256
260
  // cannot navigate along targetAspect!
257
- return (type.target) ? csn.definitions[type.target] : type;
261
+ const env = (type.target) ? csn.definitions[type.target] : type;
262
+ const origin = cached( env, '_origin', getOriginRaw );
263
+ if (origin && origin !== BUILTIN_TYPE)
264
+ cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
265
+ return env;
258
266
  }
259
267
 
260
268
  /**
@@ -279,38 +287,109 @@ function csnRefs( csn ) {
279
287
  function artifactPathRef( ref ) {
280
288
  const [ head, ...tail ] = ref.ref;
281
289
  let art = csn.definitions[pathId( head )];
282
- for (const elem of tail)
283
- art = navigationEnv( art ).elements[pathId( elem )];
290
+ for (const elem of tail) {
291
+ const env = navigationEnv( art );
292
+ art = env.elements[pathId( elem )];
293
+ }
284
294
  return art;
285
295
  }
286
296
 
287
- function getOrigin( def ) {
288
- const art = cached( def, '_origin', originPathRef );
289
- if (art)
290
- return art;
291
- throw new Error( 'Undefined origin reference' );
297
+ function getOrigin( art, alsoType ) {
298
+ const origin = cached( art, '_origin', getOriginRaw );
299
+ if (origin && origin !== BUILTIN_TYPE)
300
+ cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
301
+ return art.type && !alsoType ? undefined : origin;
292
302
  }
293
303
 
294
- function originPathRef( def ) {
295
- const [ head, ...tail ] = def.$origin;
296
- let art = csn.definitions[head];
297
- for (const elem of tail)
298
- art = originNavigation( art, elem );
299
- return art;
304
+ function getOriginRaw( art ) {
305
+ if (art.type) // TODO: make robust against "linked" = only direct
306
+ return artifactRef( art.type, BUILTIN_TYPE );
307
+ if (!art.$origin) // implicit $origin should have been set
308
+ return null;
309
+ // art.$origin must not be a string here - shortened refs should already
310
+ // have been used to set the _origin cache
311
+ if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
312
+ return cached( art.$origin, '_origin', getOriginRaw );
313
+ const [ head, ...tail ] = art.$origin;
314
+ let origin = csn.definitions[head];
315
+ // allow shorter $origin ref for actions/functions, just using a string:
316
+ let isAction = art.kind === 'action' || art.kind === 'function';
317
+ for (const elem of tail) {
318
+ origin = originNavigation( origin, elem, isAction );
319
+ isAction = false;
320
+ }
321
+ return origin;
300
322
  }
301
323
 
302
- function originNavigation( art, elem ) {
324
+ function originNavigation( art, elem, isAction ) {
303
325
  if (typeof elem !== 'string') {
304
326
  if (elem.action)
305
- return art.actions[elem.action]
327
+ return art.actions[elem.action];
306
328
  if (elem.param)
307
- return (elem.param ? art.params[elem.param] : art.returns);
329
+ return art.params[elem.param];
330
+ if (elem.returns)
331
+ return art.returns;
308
332
  }
309
- if (art.returns)
333
+ if (isAction)
334
+ return art.actions[elem];
335
+ // TODO: if we use effectiveType(), we might have more implicit prototypes
336
+ // we might then need a function like effectiveArtifact,
337
+ // which cares about actions/params
338
+ // let origin = cached( art, '_origin', getOriginRaw );
339
+ // while (art.items) {
340
+ // cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
341
+ // art = art.items;
342
+ // origin = cached( art, '_origin', getOriginRaw );
343
+ // }
344
+ // if (origin)
345
+ // cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
346
+ // console.log(art)
347
+ return (art.elements || art.enum || targetAspect( art ).elements)[elem];
348
+ }
349
+
350
+ function targetAspect( art ) {
351
+ const { $origin } = art;
352
+ return art.targetAspect ||
353
+ $origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
354
+ art.target;
355
+ }
356
+
357
+ // From the current CSN object, set implicit origin for the next navigation step
358
+ function setImplicitOrigin( art, origin ) {
359
+ setMembersImplicit( art.actions, origin.actions );
360
+ setMembersImplicit( art.params, origin.params );
361
+ if (art.returns) {
310
362
  art = art.returns;
311
- while (art.items)
363
+ if (art.type || typeof art.$origin === 'object') // null, […], {…}
364
+ return true; // not implicit or shortened
365
+ origin = effectiveType( origin.returns );
366
+ setCache( art, '_origin', origin );
367
+ return true;
368
+ }
369
+ while (art.items) {
312
370
  art = art.items;
313
- return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
371
+ if (art.type || typeof art.$origin === 'object') // null, [], {…}
372
+ return true; // not implicit or shortened
373
+ origin = effectiveType( origin.items );
374
+ setCache( art, '_origin', origin );
375
+ }
376
+ setMembersImplicit( art.elements, origin.elements );
377
+ // The enum base type is _not_ where we find the origins of the enum symbols.
378
+ // A derived type of an enum type with individual annotations on a symbol
379
+ // has both 'type' and '$origin'.
380
+ if (!art.type || art.$origin)
381
+ setMembersImplicit( art.enum, origin.enum );
382
+ return true;
383
+ }
384
+
385
+ function setMembersImplicit( members, originMembers ) {
386
+ if (!members)
387
+ return;
388
+ for (const name in members) {
389
+ const elem = members[name];
390
+ if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
391
+ setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
392
+ }
314
393
  }
315
394
 
316
395
  /**
@@ -371,6 +450,9 @@ function csnRefs( csn ) {
371
450
  if (semantics.dynamic === 'global' || ref.global)
372
451
  return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
373
452
 
453
+ const origin = cached( main, '_origin', getOriginRaw )
454
+ if (origin)
455
+ cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
374
456
  cached( main, '$queries', allQueries );
375
457
  let qcache = query && cache.get( query.projection || query );
376
458
  // BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
@@ -387,7 +469,7 @@ function csnRefs( csn ) {
387
469
  while (cache) {
388
470
  const alias = tryAlias && cache.$aliases[head];
389
471
  if (alias)
390
- return resolvePath( path, alias._select || alias, 'alias', cache.$queryNumber );
472
+ return resolvePath( path, alias._select || alias._ref, 'alias', cache.$queryNumber );
391
473
  const mixin = cache._select.mixin && cache._select.mixin[head];
392
474
  if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
393
475
  return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
@@ -405,7 +487,7 @@ function csnRefs( csn ) {
405
487
  // not selecting the corresponding element for a select column works,
406
488
  // because explicit keys can only be provided with explicit redirection
407
489
  // target
408
- const target = csn.definitions[parent.target || parent.cast.target];
490
+ const target = csn.definitions[parent.target || parent.$origin && parent.$origin.target || parent.cast.target];
409
491
  return resolvePath( path, target.elements[head], 'target' );
410
492
  }
411
493
  if (baseEnv) // ref-target (filter condition), expand, inline
@@ -481,7 +563,8 @@ function csnRefs( csn ) {
481
563
  if (query.ref) { // ref in from
482
564
  // console.log('SQ:',query,cache.get(query))
483
565
  const as = query.as || implicitAs( query.ref );
484
- getCache( fromSelect, '$aliases' )[as] = fromRef( query );
566
+ const _ref = fromRef( query );
567
+ getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
485
568
  }
486
569
  else {
487
570
  const qcache = getQueryCache( parentQuery );
@@ -517,7 +600,7 @@ function csnRefs( csn ) {
517
600
  function getQueryCache( parentQuery ) {
518
601
  if (!parentQuery)
519
602
  return { $aliases: Object.create(null) };
520
- const pcache = cache.get( parentQuery );
603
+ const pcache = cache.get( parentQuery.projection || parentQuery );
521
604
  if (!parentQuery.SET) // SELECT / projection: real sub query
522
605
  return { $aliases: Object.create(null), $next: pcache };
523
606
  // the parent query is a SET: that is not a sub query
@@ -607,7 +690,7 @@ function queryOrMain( query, main ) {
607
690
  *
608
691
  * @param {CSN.Query} query
609
692
  * @param {CSN.QuerySelect} fromSelect
610
- * @param {CSN.Query} parentQuery
693
+ * @param {CSN.Query} parentQuery
611
694
  * @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
612
695
  */
613
696
  function traverseQuery( query, fromSelect, parentQuery, callback ) {
@@ -632,8 +715,9 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
632
715
 
633
716
  /**
634
717
  * @param {CSN.QueryFrom} from
635
- * @param {CSN.QuerySelect} select
636
- * @param {(from: CSN.QueryFrom, select: CSN.QuerySelect) => void} callback
718
+ * @param {CSN.QuerySelect} fromSelect
719
+ * @param {CSN.Query} parentQuery
720
+ * @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
637
721
  */
638
722
  function traverseFrom( from, fromSelect, parentQuery, callback ) {
639
723
  if (from.ref) {
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../base/model');
4
3
  const { csnRefs } = require('../model/csnRefs');
4
+ const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
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]
@@ -457,8 +458,8 @@ function getUtils(model) {
457
458
  return resultNode;
458
459
  }
459
460
  }
460
-
461
-
461
+
462
+
462
463
  /**
463
464
  * Resolve to the final type of a type, that means follow type chains, references to other types or
464
465
  * elements a.s.o
@@ -497,12 +498,12 @@ function getUtils(model) {
497
498
  * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
498
499
  * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
499
500
  * no type (e.g. expr in a view without explicit type) returns 'null'
500
- *
501
+ *
501
502
  * @param {string|object} type Type - either string or ref
502
- * @param {CSN.Path} path
503
+ * @param {CSN.Path} path
503
504
  * @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
505
+ * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
506
+ * @returns
506
507
  */
507
508
  function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
508
509
  if (!type)
@@ -552,16 +553,6 @@ function getUtils(model) {
552
553
  }
553
554
  }
554
555
 
555
- // Tell if a type is (directly) a builtin type
556
- // Note that in CSN builtins are not in the definition of the model, so we can only check against their absolute names.
557
- // Builtin types are "cds.<something>", i.e. they are directly in 'cds', but not for example
558
- // in 'cds.foundation'. Also note, that a type might be a ref object, that refers to something else,
559
- // so if you consider type chains don't forget first to resolve to the final type before
560
- function isBuiltinType(type) {
561
- return typeof(type) === 'string' && type.startsWith('cds.') && !type.startsWith('cds.foundation.')
562
- }
563
-
564
-
565
556
  /**
566
557
  * Deeply clone the given CSN model and return it.
567
558
  * In testMode (or with testSortCsn), definitions are sorted.
@@ -817,11 +808,25 @@ function forAllQueries(query, callback, path = []){
817
808
  }
818
809
  }
819
810
 
820
- function forAllElements(artifact, artifactName, cb){
811
+ function forAllElements(artifact, artifactName, cb, includeActions = false){
821
812
  if(artifact.elements) {
822
813
  cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
823
814
  }
824
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
+
825
830
  if(artifact.query) {
826
831
  forAllQueries(artifact.query, (q, p) => {
827
832
  const s = q.SELECT;
@@ -871,7 +876,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true) {
871
876
  * function accepts EDM internal and external options
872
877
  *
873
878
  * @param {CSN.Element} elementCsn
874
- * @param {CSN.Options & CSN.ODataOptions} options EDM specific options
879
+ * @param {ODataOptions} options EDM specific options
875
880
  */
876
881
  function isEdmPropertyRendered(elementCsn, options) {
877
882
  if(options.toOdata)
@@ -921,10 +926,10 @@ function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
921
926
  throw new Error('Unknown naming convention: ' + namingConvention);
922
927
  }
923
928
  else {
924
- const namespace = csn;
925
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.`);
926
930
  if (namingConvention === 'hdbcds') {
927
- if (namespace) {
931
+ if (csn) {
932
+ const namespace = String(csn);
928
933
  return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
929
934
  }
930
935
  return artifactName;
@@ -1048,109 +1053,6 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
1048
1053
  }
1049
1054
  }
1050
1055
 
1051
-
1052
- /**
1053
- * Loop through the model, applying the custom transformations on the node's matching.
1054
- *
1055
- * Each transformer gets:
1056
- * - the parent having the property
1057
- * - the name of the property
1058
- * - the value of the property
1059
- * - the path to the property
1060
- *
1061
- * @param {object} csn CSN to enrich in-place
1062
- * @param {object} customTransformers Map of prop to transform and function to apply
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
1066
- * @returns {object} CSN with transformations applied
1067
- */
1068
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
1069
- const transformers = {
1070
- elements: dictionary,
1071
- definitions: dictionary,
1072
- actions: dictionary,
1073
- params: dictionary,
1074
- enum: dictionary,
1075
- mixin: dictionary,
1076
- ref: pathRef,
1077
- //type: simpleRef,
1078
- //target: simpleRef,
1079
- //includes: simpleRef,
1080
- }
1081
-
1082
- const csnPath = [];
1083
- if (csn.definitions)
1084
- definitions( csn, 'definitions', csn.definitions );
1085
- return csn;
1086
-
1087
- function standard( parent, prop, node ) {
1088
- if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || (typeof prop === 'string' && prop.startsWith('@')) || (skipIgnore && node._ignore))
1089
- return;
1090
-
1091
- csnPath.push( prop );
1092
-
1093
- if (Array.isArray(node)) {
1094
- node.forEach( (n, i) => standard( node, i, n ) );
1095
- }
1096
-
1097
- else {
1098
- for (let name of Object.getOwnPropertyNames( node )) {
1099
- const trans = transformers[name] || standard;
1100
- if(customTransformers[name])
1101
- customTransformers[name](node, name, node[name], csnPath, parent, prop);
1102
-
1103
- trans( node, name, node[name], csnPath );
1104
- }
1105
- }
1106
- csnPath.pop();
1107
- }
1108
-
1109
- function dictionary( node, prop, dict ) {
1110
- csnPath.push( prop );
1111
- for (let name of Object.getOwnPropertyNames( dict )) {
1112
- standard( dict, name, dict[name] );
1113
- }
1114
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1115
- setProp(node, '$' + prop, dict);
1116
- csnPath.pop();
1117
- }
1118
-
1119
- function definitions( node, prop, dict ) {
1120
- csnPath.push( prop );
1121
- for (let name of Object.getOwnPropertyNames( dict )) {
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
- }
1127
- }
1128
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1129
- setProp(node, '$' + prop, dict);
1130
- csnPath.pop();
1131
- }
1132
-
1133
- //Keep looping through the pathRef
1134
- function pathRef( node, prop, path ) {
1135
- csnPath.push( prop );
1136
- path.forEach( function step( s, i ) {
1137
- if (s && typeof s === 'object') {
1138
- csnPath.push( i );
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
- }
1147
- csnPath.pop();
1148
- }
1149
- } );
1150
- csnPath.pop();
1151
- }
1152
- }
1153
-
1154
1056
  const _dependencies = Symbol('_dependencies');
1155
1057
  const _dependents = Symbol('_dependents');
1156
1058
 
@@ -1529,7 +1431,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1529
1431
  if (!options.testMode)
1530
1432
  return;
1531
1433
  const sorted = Object.create(null);
1532
- Object.keys(csn.definitions).sort().forEach((name) => {
1434
+ Object.keys(csn.definitions || {}).sort().forEach((name) => {
1533
1435
  sorted[name] = csn.definitions[name];
1534
1436
  });
1535
1437
  csn.definitions = sorted;
@@ -1539,7 +1441,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1539
1441
  * Return an array of non-abstract service names contained in CSN
1540
1442
  *
1541
1443
  * @param {CSN.Model} csn
1542
- * @returns {CSN.Service[]}
1444
+ * @returns {string[]}
1543
1445
  */
1544
1446
  function getServiceNames(csn) {
1545
1447
  let result = [];
@@ -1553,8 +1455,8 @@ function getServiceNames(csn) {
1553
1455
 
1554
1456
  /**
1555
1457
  * Check wether the artifact is @cds.persistence.skip
1556
- *
1557
- * @param {CSN.Artifact} artifact
1458
+ *
1459
+ * @param {CSN.Artifact} artifact
1558
1460
  * @returns {Boolean}
1559
1461
  */
1560
1462
  function isSkipped(artifact) {
@@ -1563,9 +1465,9 @@ function isSkipped(artifact) {
1563
1465
 
1564
1466
  /**
1565
1467
  * Walk path in the CSN and return the result.
1566
- *
1567
- * @param {CSN.Model} csn
1568
- * @param {CSN.Path} path
1468
+ *
1469
+ * @param {CSN.Model} csn
1470
+ * @param {CSN.Path} path
1569
1471
  * @returns {object} Whatever is at the end of path
1570
1472
  */
1571
1473
  function walkCsnPath(csn, path) {
@@ -1578,6 +1480,34 @@ function walkCsnPath(csn, path) {
1578
1480
  return obj;
1579
1481
  }
1580
1482
 
1483
+ /**
1484
+ * If provided, get the replacement string for the given magic variable ref.
1485
+ * No validation is done that the ref is actually magic!
1486
+ *
1487
+ * @param {array} ref
1488
+ * @param {CSN.Options} options
1489
+ * @returns {string|null}
1490
+ */
1491
+ function getVariableReplacement(ref, options) {
1492
+ if(options && options.variableReplacements) {
1493
+ let replacement = options.variableReplacements;
1494
+ for(let i = 0; i < ref.length; i++) {
1495
+ replacement = replacement[ref[i]];
1496
+ if(replacement === undefined)
1497
+ return null;
1498
+ }
1499
+
1500
+ if(replacement === undefined)
1501
+ return null; // no valid replacement found
1502
+ else if(typeof replacement === 'string')
1503
+ return replacement; // valid replacement
1504
+ else
1505
+ return null; // $user.foo, but we only have configured $user.foo.bar -> error
1506
+ } else {
1507
+ return null;
1508
+ }
1509
+ }
1510
+
1581
1511
  module.exports = {
1582
1512
  getUtils,
1583
1513
  cloneCsn,
@@ -1598,6 +1528,7 @@ module.exports = {
1598
1528
  getUnderscoredName,
1599
1529
  getElementDatabaseNameOf,
1600
1530
  applyTransformations,
1531
+ applyTransformationsOnNonDictionary,
1601
1532
  setDependencies,
1602
1533
  isPersistedOnDatabase,
1603
1534
  generatedByCompilerVersion,
@@ -1618,4 +1549,5 @@ module.exports = {
1618
1549
  getServiceNames,
1619
1550
  isSkipped,
1620
1551
  walkCsnPath,
1552
+ getVariableReplacement
1621
1553
  };