@sap/cds-compiler 4.2.2 → 4.3.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 (66) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +2 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +7 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +18 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +42 -38
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
@@ -2,7 +2,8 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const { searchName, weakLocation } = require('../base/messages');
5
+ const { weakLocation } = require('../base/location');
6
+ const { searchName } = require('../base/messages');
6
7
  const {
7
8
  forEachInOrder,
8
9
  forEachDefinition,
@@ -66,7 +67,7 @@ function extend( model ) {
66
67
  // TMP:
67
68
  function tagIncludes( art ) {
68
69
  if (art.includes)
69
- extensionsDict[art.name.absolute] = [];
70
+ extensionsDict[art.name.id] = [];
70
71
  }
71
72
 
72
73
  //-----------------------------------------------------------------------------
@@ -86,10 +87,10 @@ function extend( model ) {
86
87
  if (!art._main && !art._outer && art._extensions === undefined &&
87
88
  art.name && // TODO: probably just a workaround, check with TODO in getOriginRaw()
88
89
  art.kind !== 'namespace') {
89
- const { absolute } = art.name;
90
- setLink( art, '_extensions', model.$collectedExtensions[absolute]?._extensions || null );
90
+ const { id } = art.name;
91
+ setLink( art, '_extensions', model.$collectedExtensions[id]?._extensions || null );
91
92
  if (art._extensions && !art.builtin) { // keep extensions for builtin in $collectedExtensions
92
- delete model.$collectedExtensions[absolute];
93
+ delete model.$collectedExtensions[id];
93
94
  // TODO: if the extension mechanism has been completed, we could uncomment:
94
95
  // art._extensions.forEach( ext => resolvePath( ext.name, ext.kind, ext )); // for LSP
95
96
  // for now, we do that at the end of createRemainingAnnotateStatements()
@@ -182,8 +183,8 @@ function extend( model ) {
182
183
  return;
183
184
  for (const ext of source.extensions ) {
184
185
  const { name } = ext;
185
- if (name?.absolute && name._artifact === undefined) {
186
- const refCtx = (name.absolute.startsWith( 'localized.' )) ? '_extensions' : ext.kind;
186
+ if (name?.id && name._artifact === undefined) {
187
+ const refCtx = (name.id.startsWith( 'localized.' )) ? '_extensions' : ext.kind;
187
188
  resolvePath( name, refCtx, ext ); // for LSP
188
189
  }
189
190
  }
@@ -361,10 +362,10 @@ function extend( model ) {
361
362
  'You can\'t use EXTEND on the generated $(ART)' );
362
363
  }
363
364
  else if (art.kind !== 'annotate' && !art._outer) { // not with elem extension in targetAspect
364
- const { absolute } = art.name;
365
- const dict = extensionsDict[absolute] || (extensionsDict[absolute] = []);
365
+ const { id } = art.name;
366
+ const dict = extensionsDict[id] || (extensionsDict[id] = []);
366
367
  dict.push( ext ); // TODO: change
367
- // console.log( 'ASI:',prop,art.name,ext,extensionsDict[absolute])
368
+ // console.log( 'ASI:',prop,art.name,ext,extensionsDict[id])
368
369
  }
369
370
  // art[prop] = (art[prop]) ? art[prop].concat( ext[prop] ) : ext[prop];
370
371
  }
@@ -491,7 +492,8 @@ function extend( model ) {
491
492
 
492
493
  function normalizeRef( node ) { // see to-csn.js
493
494
  const ref = pathName( node.path );
494
- return node.variant ? `${ ref }#${ node.variant.id }` : ref;
495
+ // TODO: get rid of name.variant (induces a wrong structure anyway)
496
+ return node.variant ? `${ ref }#${ pathName( node.variant.path ) }` : ref;
495
497
  }
496
498
 
497
499
  // For extendArtifactAfter(): -------------------------------------------------
@@ -507,7 +509,7 @@ function extend( model ) {
507
509
  // - 'ext-unexpected-type-argument' (TODO) if the artifact does not have the prop
508
510
  // - 'ext-invalid-type-argument' if the value is wrong for extend (no overwrite)
509
511
  //
510
- // TODO v4: do not allow `extend … with (precision: …)` alone if original def also has `scale`
512
+ // TODO v5: do not allow `extend … with (precision: …)` alone if original def also has `scale`
511
513
  function applyTypeExtensions( art, ext, prop, scaleDiff ) {
512
514
  // console.log('ATE:',art?.[prop],ext?.[prop],scaleDiff)
513
515
  if (!ext?.[prop])
@@ -652,9 +654,9 @@ function extend( model ) {
652
654
  if (art._main)
653
655
  return annotateFor( art._parent, kindProperties[art.kind].dict, art.name.id );
654
656
 
655
- const { absolute } = art.name;
656
- return model.$collectedExtensions[absolute] ||
657
- annotateCreate( model.$collectedExtensions, absolute );
657
+ const { id } = art.name;
658
+ return model.$collectedExtensions[id] ||
659
+ annotateCreate( model.$collectedExtensions, id );
658
660
  }
659
661
 
660
662
  function annotateCreate( dict, id, parent, prop ) {
@@ -668,9 +670,6 @@ function extend( model ) {
668
670
  setLink( annotate, '_parent', parent );
669
671
  setLink( annotate, '_main', parent._main || parent );
670
672
  }
671
- else {
672
- annotate.name.absolute = id; // TODO later (if all names are sparse): delete absolute
673
- }
674
673
  dict[prop || id] = annotate;
675
674
  return annotate;
676
675
  }
@@ -774,9 +773,9 @@ function extend( model ) {
774
773
  function createSuperAnnotate( annotate ) {
775
774
  const extensions = annotate._extensions;
776
775
  if (extensions && !annotate._main) {
777
- const { absolute } = annotate.name;
778
- const isLocalized = absolute.startsWith( 'localized.' ); // TODO: && anno
779
- const art = model.definitions[absolute];
776
+ const { id } = annotate.name;
777
+ const isLocalized = id.startsWith( 'localized.' ); // TODO: && anno
778
+ const art = model.definitions[id];
780
779
  for (const ext of extensions)
781
780
  checkRemainingMainExtensions( art, ext, isLocalized );
782
781
  if (art?.builtin && art.kind !== 'namespace') { // TODO: do not set `builtin` on cds, cds.hana
@@ -799,7 +798,7 @@ function extend( model ) {
799
798
  }
800
799
 
801
800
  function checkRemainingMainExtensions( art, ext, localized ) {
802
- if (localized) // TODO v4: ignore only for annotate
801
+ if (localized) // TODO v5: ignore only for annotate
803
802
  return;
804
803
  if (!resolvePath( ext.name, ext.kind, ext )) // error for extend, info for annotate
805
804
  return;
@@ -1035,13 +1034,16 @@ function extend( model ) {
1035
1034
  * @param {object} validDict
1036
1035
  */
1037
1036
  function extendNothing( extensions, prop, name, art, validDict ) {
1037
+ // TODO: probably too much magic in the creation of artName…
1038
+ const extMain = { ...(art._main || art) };
1038
1039
  const artName = searchName( art, name, dictKinds[prop] );
1040
+ setLink( artName, '_main', extMain );
1039
1041
  for (const ext of extensions) {
1040
1042
  // TODO: use shared functionality with notFound in resolver.js
1041
1043
  const { location } = ext.name;
1042
- const extName = { ...artName, kind: ext.kind };
1044
+ extMain.kind = ext.kind;
1043
1045
  const msg
1044
- = error( 'extend-undefined', [ location, extName ],
1046
+ = error( 'extend-undefined', [ location, artName ],
1045
1047
  { art: artName },
1046
1048
  {
1047
1049
  std: 'Unknown $(ART) - nothing to extend',
@@ -1210,7 +1212,7 @@ function extend( model ) {
1210
1212
  * Report duplicates in parent[prop] that happen due to multiple includes having the
1211
1213
  * same member. Covers `entity G : E, G {};` but not `entity G : E {}; extend G with F;`.
1212
1214
  *
1213
- * TODO(v4): Make this a hard error; see checkRedefinition(); maybe combine both;
1215
+ * TODO(v5): Make this a hard error; see checkRedefinition(); maybe combine both;
1214
1216
  */
1215
1217
  function checkRedefinitionThroughIncludes( parent, prop ) {
1216
1218
  if (!parent[prop])
@@ -40,7 +40,7 @@ function finalizeParseCdl( model ) {
40
40
  // TODO: why not just use the extensions as they are from the first source?
41
41
  for (const name in late) {
42
42
  for (const ext of late[name]._extensions) {
43
- ext.name.absolute = resolveUncheckedPath( ext.name, '_extensions', ext );
43
+ ext.name.id = resolveUncheckedPath( ext.name, '_extensions', ext );
44
44
  // Initialize members and define annotations in sub-elements.
45
45
  initMembers( ext, ext, ext._block, true );
46
46
  extensions.push( ext );
@@ -14,11 +14,10 @@ const {
14
14
  linkToOrigin,
15
15
  setMemberParent,
16
16
  augmentPath,
17
- splitIntoPath,
18
17
  isDirectComposition,
19
18
  copyExpr,
20
19
  } = require('./utils');
21
- const { weakLocation } = require('../base/messages');
20
+ const { weakLocation } = require('../base/location');
22
21
 
23
22
  function generate( model ) {
24
23
  const { options } = model;
@@ -165,7 +164,7 @@ function generate( model ) {
165
164
  const fioriAnno = art['@fiori.draft.enabled'];
166
165
  const fioriEnabled = fioriAnno && (fioriAnno.val === undefined || fioriAnno.val);
167
166
 
168
- const textsName = `${ art.name.absolute }.texts`;
167
+ const textsName = `${ art.name.id }.texts`;
169
168
  const textsEntity = model.definitions[textsName];
170
169
  const localized = localizedData( art, textsEntity, fioriEnabled );
171
170
  if (!localized)
@@ -278,20 +277,45 @@ function generate( model ) {
278
277
  * @param {boolean} fioriEnabled
279
278
  */
280
279
  function createTextsEntity( base, absolute, textElems, fioriEnabled ) {
281
- const art = useTextsAspect
282
- ? createTextsEntityWithInclude( base, absolute, fioriEnabled )
283
- : createTextsEntityWithDefaultElements( base, absolute, fioriEnabled );
284
- // both functions are rather similar...
285
-
286
280
  const { location } = base.name;
281
+ const art = {
282
+ kind: 'entity',
283
+ name: { id: absolute, location },
284
+ location: base.location,
285
+ elements: Object.create( null ),
286
+ $inferred: 'localized-entity',
287
+ };
288
+ setLink( art, '_block', model.$internal );
289
+ model.definitions[absolute] = art;
290
+ extendArtifactBefore( art ); // having extensions here would be wrong
291
+
292
+ if (!fioriEnabled) {
293
+ // To be compatible, we switch off draft without @fiori.draft.enabled
294
+ setAnnotation( art, '@odata.draft.enabled', art.location, false );
295
+ }
296
+ else {
297
+ const textId = {
298
+ name: { location, id: 'ID_texts' },
299
+ kind: 'element',
300
+ key: { val: true, location },
301
+ type: linkMainArtifact( location, 'cds.UUID' ),
302
+ location,
303
+ };
304
+ dictAdd( art.elements, 'ID_texts', textId );
305
+ }
306
+
307
+ const enrich = useTextsAspect
308
+ ? enrichTextsEntityWithInclude
309
+ : enrichTextsEntityWithDefaultElements;
310
+ enrich( art, base, absolute, fioriEnabled );
287
311
 
288
312
  if (addTextsLanguageAssoc) {
289
313
  const language = {
290
314
  name: { location, id: 'language' },
291
315
  kind: 'element',
292
316
  location,
293
- type: augmentPath( location, 'cds.Association' ),
294
- target: augmentPath( location, 'sap.common.Languages' ),
317
+ type: linkMainArtifact( location, 'cds.Association' ),
318
+ target: linkMainArtifact( location, 'sap.common.Languages' ),
295
319
  on: {
296
320
  op: { val: '=', location },
297
321
  args: [
@@ -362,46 +386,25 @@ function generate( model ) {
362
386
  }
363
387
 
364
388
  /**
365
- * Create the `.texts` entity for the given base artifact.
389
+ * Enrich the `.texts` entity for the given base artifact.
366
390
  * In contrast to createTextsEntityWithDefaultElements(), this one creates
367
391
  * an include for `sap.common.TextsAspect`.
368
392
  *
369
393
  * Does NOT apply the include!
370
394
  *
395
+ * @param {XSN.Artifact} art
371
396
  * @param {XSN.Artifact} base
372
397
  * @param {string} absolute
373
398
  * @param {boolean} fioriEnabled
374
399
  */
375
- function createTextsEntityWithInclude( base, absolute, fioriEnabled ) {
400
+ function enrichTextsEntityWithInclude( art, base, absolute, fioriEnabled ) {
376
401
  const textsAspectName = 'sap.common.TextsAspect';
377
402
  const textsAspect = model.definitions['sap.common.TextsAspect'];
378
- const elements = Object.create( null );
379
- const { location } = base.name;
380
- const art = {
381
- kind: 'entity',
382
- name: { path: splitIntoPath( location, absolute ), absolute, location },
383
- includes: [ createInclude( textsAspectName, base.location ) ],
384
- location: base.location,
385
- elements,
386
- $inferred: 'localized-entity',
387
- };
403
+ const { location } = art.name;
388
404
 
389
- if (!fioriEnabled) {
390
- // To be compatible, we switch off draft without @fiori.draft.enabled
391
- setAnnotation( art, '@odata.draft.enabled', art.location, false );
392
- }
393
- else {
394
- // @fiori.draft.enabled artifacts need default elements ID_texts and locale.
395
- // `locale` is copied from `sap.common.TextsAspect`, but without "key".
396
- const textId = {
397
- name: { location, id: 'ID_texts' },
398
- kind: 'element',
399
- key: { val: true, location },
400
- type: augmentPath( location, 'cds.UUID' ),
401
- location,
402
- };
403
- dictAdd( art.elements, 'ID_texts', textId );
405
+ art.includes = [ createInclude( textsAspectName, base.location ) ];
404
406
 
407
+ if (fioriEnabled) {
405
408
  // "Early" include; only for element `locale`, which has its `key` property
406
409
  // removed (or rather: it is not copied).
407
410
  linkToOrigin( textsAspect.elements.locale, 'locale', art, 'elements', location );
@@ -410,61 +413,31 @@ function generate( model ) {
410
413
  if (addTextsLanguageAssoc && art.elements.language)
411
414
  art.elements.language = undefined; // TODO: Message? Ignore?
412
415
  // TODO: what is this necessary? We do not create a text entity in this case
413
-
414
- setLink( art, '_block', model.$internal );
415
- model.definitions[absolute] = art;
416
- extendArtifactBefore( art ); // having extensions here would be wrong
417
- return art;
418
416
  }
419
417
 
420
418
  /**
419
+ * @param {XSN.Artifact} art
421
420
  * @param {XSN.Artifact} base
422
421
  * @param {string} absolute
423
422
  * @param {boolean} fioriEnabled
424
423
  */
425
- function createTextsEntityWithDefaultElements( base, absolute, fioriEnabled ) {
426
- const elements = Object.create( null );
427
- const { location } = base.name;
428
- const art = {
429
- kind: 'entity',
430
- name: { path: splitIntoPath( location, absolute ), absolute, location },
431
- location: base.location,
432
- elements,
433
- $inferred: 'localized-entity',
434
- };
424
+ function enrichTextsEntityWithDefaultElements( art, base, absolute, fioriEnabled ) {
435
425
  // If there is a type `sap.common.Locale`, then use it as the type for the element `locale`.
436
426
  // If not, use the default `cds.String` with a length of 14.
437
427
  const hasLocaleType = model.definitions['sap.common.Locale']?.kind === 'type';
428
+ const { location } = art.name;
438
429
  const locale = {
439
430
  name: { location, id: 'locale' },
440
431
  kind: 'element',
441
- type: augmentPath( location, hasLocaleType ? 'sap.common.Locale' : 'cds.String' ),
432
+ type: linkMainArtifact( location, hasLocaleType ? 'sap.common.Locale' : 'cds.String' ),
442
433
  location,
443
434
  };
444
435
  if (!hasLocaleType)
445
436
  locale.length = { literal: 'number', val: 14, location };
446
437
 
447
- if (!fioriEnabled) {
438
+ if (!fioriEnabled)
448
439
  locale.key = { val: true, location };
449
- // To be compatible, we switch off draft without @fiori.draft.enabled
450
- setAnnotation( art, '@odata.draft.enabled', art.location, false );
451
- }
452
- else {
453
- const textId = {
454
- name: { location, id: 'ID_texts' },
455
- kind: 'element',
456
- key: { val: true, location },
457
- type: augmentPath( location, 'cds.UUID' ),
458
- location,
459
- };
460
- dictAdd( art.elements, 'ID_texts', textId );
461
- }
462
440
  dictAdd( art.elements, 'locale', locale );
463
-
464
- setLink( art, '_block', model.$internal );
465
- model.definitions[absolute] = art;
466
- extendArtifactBefore( art ); // having extensions here would be wrong
467
- return art;
468
441
  }
469
442
 
470
443
  /**
@@ -482,9 +455,9 @@ function generate( model ) {
482
455
  kind: 'element',
483
456
  location,
484
457
  $inferred: 'localized',
485
- type: augmentPath( location, 'cds.Composition' ),
458
+ type: linkMainArtifact( location, 'cds.Composition' ),
486
459
  cardinality: { targetMax: { literal: 'string', val: '*', location }, location },
487
- target: augmentPath( location, textsName ),
460
+ target: linkMainArtifact( location, textsName ),
488
461
  on: augmentEqual( location, 'texts', keys ),
489
462
  };
490
463
  setMemberParent( texts, 'texts', art, 'elements' );
@@ -497,8 +470,8 @@ function generate( model ) {
497
470
  kind: 'element',
498
471
  location,
499
472
  $inferred: 'localized',
500
- type: augmentPath( location, 'cds.Association' ),
501
- target: augmentPath( location, textsName ),
473
+ type: linkMainArtifact( location, 'cds.Association' ),
474
+ target: linkMainArtifact( location, textsName ),
502
475
  on: augmentEqual( location, 'localized', keys ),
503
476
  };
504
477
  setMemberParent( localized, 'localized', art, 'elements' );
@@ -537,7 +510,7 @@ function generate( model ) {
537
510
  */
538
511
  function hasTruthyProp( art, prop ) {
539
512
  const processed = Object.create( null ); // avoid infloops with circular refs
540
- let name = art.name.absolute; // is ok, since no recursive type possible
513
+ let name = (art._main || art).name.id; // is ok, since no recursive type possible
541
514
  while (art && !processed[name]) {
542
515
  if (art[prop])
543
516
  return art[prop].val;
@@ -546,7 +519,7 @@ function generate( model ) {
546
519
  art = art._origin;
547
520
  if (!art.name) // anonymous aspect
548
521
  return false;
549
- name = art && art.name.absolute;
522
+ name = (art._main || art)?.name?.id;
550
523
  }
551
524
  else if (art.type && art._block && art.type.scope !== 'typeOf') {
552
525
  // TODO: also do something special for TYPE OF inside `art`s own elements
@@ -599,7 +572,7 @@ function generate( model ) {
599
572
  target = resolvePath( origin.targetAspect, 'targetAspect', origin );
600
573
  if (!target || !target.elements)
601
574
  return;
602
- const entityName = `${ base.name.absolute }.${ elem.name.id }`;
575
+ const entityName = `${ base.name.id }.${ elem.name.id }`;
603
576
  const entity = allowAspectComposition( target, elem, keys, entityName ) &&
604
577
  createTargetEntity( target, elem, keys, entityName, base );
605
578
  elem.target = {
@@ -615,7 +588,7 @@ function generate( model ) {
615
588
  // already has an _origin (when the managed composition is included)
616
589
  if (up_)
617
590
  setLink( up_, '_origin', entity.elements.up_ );
618
- model.$compositionTargets[entity.name.absolute] = true;
591
+ model.$compositionTargets[entity.name.id] = true;
619
592
  processAspectComposition( entity );
620
593
  processLocalizedData( entity );
621
594
  }
@@ -674,7 +647,7 @@ function generate( model ) {
674
647
 
675
648
  if (elem.type && !isDirectComposition( elem )) {
676
649
  // Only issue warning for direct usages, not for projections, includes, etc.
677
- // TODO: Make it configurable error; v4: error
650
+ // TODO: Make it configurable error; v5: error
678
651
  warning( 'def-expected-comp-aspect', [ elem.type.location, elem ],
679
652
  { prop: 'Composition of', otherprop: 'Association to' },
680
653
  'Expected $(PROP), but found $(OTHERPROP) for composition of aspect' );
@@ -698,8 +671,7 @@ function generate( model ) {
698
671
  const art = {
699
672
  kind: 'entity',
700
673
  name: {
701
- path: splitIntoPath( location, entityName ),
702
- absolute: entityName,
674
+ id: entityName,
703
675
  // for code navigation (e.g. via `extend`s): point to the element's name
704
676
  location: elem.name.location,
705
677
  },
@@ -724,8 +696,8 @@ function generate( model ) {
724
696
  kind: 'element',
725
697
  location: upLocation,
726
698
  $inferred: 'aspect-composition',
727
- type: augmentPath( upLocation, 'cds.Association' ),
728
- target: augmentPath( upLocation, base.name.absolute ),
699
+ type: linkMainArtifact( upLocation, 'cds.Association' ),
700
+ target: linkMainArtifact( upLocation, base.name.id ),
729
701
  cardinality: {
730
702
  targetMin: { val: 1, literal: 'number', location: upLocation },
731
703
  targetMax: { val: 1, literal: 'number', location: upLocation },
@@ -755,9 +727,11 @@ function generate( model ) {
755
727
  model.definitions[entityName] = art;
756
728
  initArtifact( art );
757
729
 
758
- extendArtifactBefore( art ); // having extensions here would be wrong
730
+ // Apply annotations to generated artifact, prepare (not apply!) element
731
+ // annotations (remark: adding elements is not allowed for generated artifacts):
732
+ extendArtifactBefore( art );
759
733
  // Copy persistence annotations from aspect.
760
- copyPersistenceAnnotations( art, target ); // after chooseAnnotation()
734
+ copyPersistenceAnnotations( art, target ); // after extendArtifactBefore()
761
735
  return art;
762
736
  }
763
737
 
@@ -808,6 +782,12 @@ function generate( model ) {
808
782
  target[anno] = { ...source[anno], $inferred: 'parent-origin' };
809
783
  }
810
784
  }
785
+
786
+ function linkMainArtifact( location, absolute ) {
787
+ const r = { location };
788
+ setArtifactLink( r, model.definitions[absolute] );
789
+ return r;
790
+ }
811
791
  }
812
792
 
813
793
  function augmentEqual( location, assocname, relations, prefix = '' ) {
@@ -105,7 +105,7 @@ function kickStart( model ) {
105
105
  const service = art._service;
106
106
  if (!service)
107
107
  return;
108
- const sname = service.name.absolute;
108
+ const sname = service.name.id;
109
109
  art._ancestors.forEach( expose );
110
110
  return;
111
111
 
@@ -122,14 +122,16 @@ function kickStart( model ) {
122
122
  }
123
123
 
124
124
  function tagCompositionTargets( elem ) {
125
- const type = elem.type && elem.type.path;
126
- if (elem.target && type && type[0] && type[0].id === 'cds.Composition') {
125
+ const { type } = elem;
126
+ if (elem.target && type &&
127
+ (type._artifact === model.definitions['cds.Composition'] ||
128
+ type.path?.[0].id === 'cds.Composition')) {
127
129
  // A target aspect would have already moved to property `targetAspect` in
128
130
  // define.js (hm... more something for kick-start.js...)
129
131
  // TODO: for safety, just use resolveUncheckedPath()
130
132
  const target = resolvePath( elem.target, 'target', elem );
131
133
  if (target)
132
- model.$compositionTargets[target.name.absolute] = true;
134
+ model.$compositionTargets[target.name.id] = true;
133
135
  }
134
136
  forEachGeneric( elem, 'elements', tagCompositionTargets );
135
137
  }
@@ -143,7 +145,7 @@ function kickStart( model ) {
143
145
  for (const def of src.usings) {
144
146
  if (def.usings) // using {...}
145
147
  resolveUsings( def );
146
- if (!def.name || !def.name.absolute)
148
+ if (!def.name || !def.name.id)
147
149
  continue; // using {...}, parse error
148
150
  const art = model.definitions[def.name.absolute];
149
151
  if (art && art.$duplicates)