@sap/cds-compiler 5.2.0 → 5.3.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 (54) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/cdsc.js +5 -0
  3. package/bin/cdshi.js +8 -8
  4. package/doc/CHANGELOG_BETA.md +9 -4
  5. package/lib/api/validate.js +5 -0
  6. package/lib/base/message-registry.js +25 -1
  7. package/lib/base/messages.js +1 -1
  8. package/lib/base/model.js +0 -1
  9. package/lib/compiler/assert-consistency.js +2 -2
  10. package/lib/compiler/builtins.js +1 -1
  11. package/lib/compiler/checks.js +25 -6
  12. package/lib/compiler/define.js +24 -28
  13. package/lib/compiler/extend.js +11 -13
  14. package/lib/compiler/generate.js +3 -3
  15. package/lib/compiler/populate.js +13 -7
  16. package/lib/compiler/propagator.js +2 -2
  17. package/lib/compiler/resolve.js +58 -60
  18. package/lib/compiler/shared.js +5 -5
  19. package/lib/compiler/tweak-assocs.js +247 -34
  20. package/lib/compiler/utils.js +40 -32
  21. package/lib/compiler/xpr-rewrite.js +44 -58
  22. package/lib/edm/annotations/genericTranslation.js +4 -4
  23. package/lib/edm/csn2edm.js +2 -2
  24. package/lib/edm/edm.js +46 -21
  25. package/lib/edm/edmInboundChecks.js +0 -1
  26. package/lib/edm/edmPreprocessor.js +40 -27
  27. package/lib/edm/edmUtils.js +1 -1
  28. package/lib/gen/BaseParser.js +180 -122
  29. package/lib/gen/CdlParser.js +2226 -2170
  30. package/lib/gen/language.checksum +1 -1
  31. package/lib/gen/language.interp +1 -1
  32. package/lib/gen/languageParser.js +3820 -3777
  33. package/lib/inspect/inspectPropagation.js +1 -1
  34. package/lib/json/from-csn.js +5 -3
  35. package/lib/json/to-csn.js +7 -10
  36. package/lib/language/antlrParser.js +38 -4
  37. package/lib/language/errorStrategy.js +1 -1
  38. package/lib/language/genericAntlrParser.js +4 -4
  39. package/lib/language/multiLineStringParser.js +1 -1
  40. package/lib/main.d.ts +23 -0
  41. package/lib/model/cloneCsn.js +22 -13
  42. package/lib/optionProcessor.js +7 -7
  43. package/lib/parsers/AstBuildingParser.js +155 -37
  44. package/lib/parsers/CdlGrammar.g4 +154 -81
  45. package/lib/parsers/Lexer.js +20 -10
  46. package/lib/render/toCdl.js +23 -18
  47. package/lib/transform/addTenantFields.js +4 -4
  48. package/lib/transform/db/rewriteCalculatedElements.js +11 -5
  49. package/lib/transform/db/transformExists.js +43 -18
  50. package/lib/transform/effective/main.js +1 -1
  51. package/lib/transform/forRelationalDB.js +8 -7
  52. package/lib/utils/moduleResolve.js +1 -1
  53. package/package.json +1 -1
  54. package/share/messages/redirected-to-complex.md +6 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,45 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 5.3.2 - 2024-10-08
11
+
12
+ ### Fixed
13
+
14
+ - to.sql|hdi|hdbcds|effective: Handle subexpressions in conjunction with exists predicate.
15
+
16
+
17
+ ## Version 5.3.0 - 2024-09-25
18
+
19
+ ### Added
20
+
21
+ - compiler:
22
+ - A warning is emitted if a string enum's values are longer than the specified length.
23
+ - ON-condition rewriting has been improved and now supports secondary associations.
24
+ - to.edm(x): Support optional action and function parameters in OData V4. The following rules apply:
25
+ + A parameter declared `not null` without default value is mandatory.
26
+ + A **function** parameter declared `null` without default value is mandatory.
27
+ + An **action** parameter declared `null` without default value is optional as it is equivalent to
28
+ `@Core.OptionalParameter { DefaultValue: null }`.
29
+ + A parameter with a default value is optional and the default value is rendered as
30
+ `@Core.OptionalParameter { DefaultValue: <value> }` regardless of its nullability.
31
+ + `@Core.OptionalParameter: true` can be used to turn a mandatory parameter into an optional parameter
32
+ (especially function parameters) and to signal an unspecified default value in the API if the parameter
33
+ has no default clause.
34
+ + `@Core.OptionalParameter: false` turns the creation of a `@Core.OptionalParameter: { Default: ... }`
35
+ annotation off.
36
+ + A default clause or `@Core.OptionalParameter: <bool>` have no effect on an explicit binding parameter.
37
+ + Mandatory and optional **action** parameters may appear in any order.
38
+ + Optional **function** parameters must not be followed by mandatory parameters.
39
+ - to.edm: Forward `@OpenAPI {...}` into EDM Json with option `--odata-openapi-hints`.
40
+ - cdsc: Option `--transitive-localized-views` was added.
41
+
42
+ ### Fixed
43
+
44
+ - CDL parser: Issue warning if annotation assignments have been written
45
+ at an invalid position inside a type expressions.
46
+ - CDL parser: Issue warning for arrayed parameter with default value.
47
+ - to.cdl: Arrayed parameters with default values were not rendered correctly.
48
+
10
49
  ## Version 5.2.0 - 2024-08-27
11
50
 
12
51
  ### Added
package/bin/cdsc.js CHANGED
@@ -217,6 +217,11 @@ function cdsc_main() {
217
217
  if (cmdLine.options.stdin)
218
218
  cmdLine.options.fallbackParser ??= 'auto!';
219
219
 
220
+ if (cmdLine.options[cmdLine.command]?.transitiveLocalizedViews) {
221
+ cmdLine.options.fewerLocalizedViews = !cmdLine.options[cmdLine.command].transitiveLocalizedViews
222
+ delete cmdLine.options[cmdLine.command].transitiveLocalizedViews;
223
+ }
224
+
220
225
  parseSeverityOptions(cmdLine);
221
226
 
222
227
  // Do the work for the selected command
package/bin/cdshi.js CHANGED
@@ -47,20 +47,20 @@ function highlight( err, buf ) {
47
47
  console.error( 'ERROR:', err.toString() );
48
48
  return;
49
49
  }
50
- const parser = compiler.parseX( buf, 'hi.cds', options ).tokenStream;
51
- // ts is parser with new parser
52
- const { tokens, lexer } = parser;
50
+ const { tokenStream } = compiler.parseX( buf, 'hi.cds', options );
51
+ const { tokens, lexer } = tokenStream;
53
52
  if (!buf.length || !tokens || !tokens.length)
54
53
  return;
54
+
55
55
  const chars = [ ...buf ];
56
56
  for (const tok of tokens) {
57
- const { location } = tok;
58
- const start = lexer.characterPos( location.line, location.col );
57
+ if (tok.type === 'Comment') // but interpret DocComment!
58
+ continue;
59
+ const { location, start } = tok;
59
60
  if (start < 0)
60
61
  continue;
61
62
  const stop = lexer.characterPos( location.endLine, location.endCol ) - 1;
62
- const cat = tok.parsed;
63
- // console.log(tok.location.toString(),tok.text,tok.parsed,stop > start)
63
+ const cat = tok.parsedAs;
64
64
  if (!cat) {
65
65
  if (stop > start) {
66
66
  chars[start] = (cat !== 0 ? '\x0f' : '\x16'); // ^O / ^V (ERROR)
@@ -73,7 +73,7 @@ function highlight( err, buf ) {
73
73
  else if (cat !== 'keyword' && cat !== 'token') {
74
74
  if (cat !== 'ref' || chars[start] !== '$')
75
75
  chars[start] = categoryChars[cat] || cat.charAt(0);
76
- if (stop > start) // stop in ANTLR at last char, not behind
76
+ if (stop > start)
77
77
  chars[start + 1] = '_';
78
78
  }
79
79
  }
@@ -8,6 +8,11 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 5.3.0 - 2024-09-25
12
+
13
+ ### Removed `optionalActionFunctionParameters`
14
+
15
+ It is now enabled by default.
11
16
 
12
17
  ## Version 5.0.0 - 2024-05-29
13
18
 
@@ -17,19 +22,19 @@ It is now enabled by default.
17
22
 
18
23
  ## Version 4.9.0 - 2024-04-25
19
24
 
20
- ## Removed `odataAnnotationExpressions`
25
+ ### Removed `odataAnnotationExpressions`
21
26
 
22
27
  It is now enabled by default.
23
28
 
24
- ## Removed `odataPathsInAnnotationExpressions`
29
+ ### Removed `odataPathsInAnnotationExpressions`
25
30
 
26
31
  It is now enabled by default.
27
32
 
28
- ## Removed `annotationExpressions`
33
+ ### Removed `annotationExpressions`
29
34
 
30
35
  It is now enabled by default.
31
36
 
32
- ## Added `temporalRawProjection`
37
+ ### Added `temporalRawProjection`
33
38
 
34
39
  Enables revocation of temporal where clause in projections of temporal entities if the
35
40
  `@cds.valid { from, to }` annotations on the projection elements have falsy values.
@@ -118,6 +118,11 @@ const validators = {
118
118
  expected: () => 'type boolean|number',
119
119
  found: val => `type ${ typeof val }`,
120
120
  },
121
+ withLocations: {
122
+ validate: val => typeof val === 'boolean' || val === 'withEndPosition',
123
+ expected: () => 'type boolean|"withEndPosition"',
124
+ found: val => `type ${ typeof val }`,
125
+ },
121
126
  dictionaryPrototype: {
122
127
  validate: () => true,
123
128
  },
@@ -158,6 +158,7 @@ const centralMessages = {
158
158
  'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
159
159
  'syntax-missing-as': { severity: 'Error', configurableFor: true },
160
160
  'syntax-missing-proj-semicolon': { severity: 'Warning', errorFor: [ 'v6' ] },
161
+ 'syntax-unexpected-after': { severity: 'Warning', errorFor: [ 'v6' ] },
161
162
  'syntax-unexpected-many-one': { severity: 'Error', configurableFor: true }, // TODO: remove `configurableFor` soon, latest v6
162
163
  'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
163
164
  'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
@@ -359,6 +360,11 @@ const centralMessageTexts = {
359
360
  std: 'Annotations can\'t be used in a column with $(CODE)',
360
361
  doc: 'Doc comments can\'t be used in a column with $(CODE)',
361
362
  },
363
+ 'syntax-unexpected-after': {
364
+ std: 'Unexpected $(KEYWORD) after annotation assignment',
365
+ many: 'Unexpected $(KEYWORD) after array type',
366
+ enum: 'Unexpected annotation assignment after enum type',
367
+ },
362
368
  'syntax-unexpected-many-one': 'Replace $(CODE) with $(DELIMITED) to avoid an ambiguity with managed compositions of anonymous aspects',
363
369
  'syntax-invalid-name': {
364
370
  std: 'Identifier must consist of at least one character', // only via delimited id
@@ -704,6 +710,10 @@ const centralMessageTexts = {
704
710
  'inline-expand': 'The ON-condition is not rewritten in nested projections - provide an explicit ON-condition',
705
711
  'secondary': 'The ON-condition is not rewritten due to multiple associations in this path - provide an explicit ON-condition',
706
712
  },
713
+ 'rewrite-not-projected': {
714
+ std: 'Projected association $(NAME) uses non-projected element $(ELEMREF)',
715
+ element: 'Projected association $(NAME) uses non-projected element $(ELEMREF) of $(ART)',
716
+ },
707
717
  'type-unsupported-rewrite': {
708
718
  std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
709
719
  'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported'
@@ -753,6 +763,13 @@ const centralMessageTexts = {
753
763
  'onCond': 'Unexpected $(KEYWORD) on an association/composition with ON-condition; $(KEYWORD) requires exactly one foreign key',
754
764
  'targetAspect': 'Unexpected $(KEYWORD) on composition of aspect'
755
765
  },
766
+ 'type-expecting-service-target': {
767
+ std: 'Expecting service entity $(TARGET)',
768
+ ref: 'Expecting service entity $(TARGET); its element $(ID) referred to at line $(LINE), column $(COL) is not from an element with the same name in the provided model target',
769
+ key: 'Expecting service entity $(TARGET); its key element $(ID) is not from a key element with the same name in the provided model target',
770
+ missing: 'Expecting service entity $(TARGET); it does not have the key element $(ID) of the provided model target',
771
+ order: 'Expecting service entity $(TARGET); its key elements are in a different order than those of the provided model target',
772
+ },
756
773
 
757
774
  'anno-builtin': 'Builtin types should not be annotated nor extended. Use custom type instead',
758
775
  'ext-undefined-def': 'Artifact $(ART) has not been found',
@@ -988,6 +1005,13 @@ const centralMessageTexts = {
988
1005
  foreignKeys: 'Foreign key $(ID) does not result from the query',
989
1006
  },
990
1007
 
1008
+ // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
1009
+ 'redirected-to-complex': {
1010
+ std: 'Redirection involves the complex view $(ART); add an explicit ON-condition/foreign keys to redirection',
1011
+ target: 'The redirected target $(ART) is a complex view; add an explicit ON-condition/foreign keys to redirection',
1012
+ targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD); add an explicit ON-condition/foreign keys to redirection',
1013
+ },
1014
+
991
1015
  'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
992
1016
 
993
1017
  'ref-ambiguous': {
@@ -1118,7 +1142,7 @@ const centralMessageTexts = {
1118
1142
  'odata-key-recursive': 'Unexpected recursive key $(NAME)',
1119
1143
  'odata-key-uuid-default-anno': 'Expected element of type $(TYPE) to be annotated with $(ANNO) when used as primary key in $(ID)',
1120
1144
  'odata-ignoring-param-default': {
1121
- 'std': 'Ignoring default value on parameter',
1145
+ 'std': 'Ignoring default value',
1122
1146
  'xpr': 'Ignoring unexpected expression as default value',
1123
1147
  'colitem': 'Ignoring unexpected default value for a structured or collection like parameter',
1124
1148
  },
@@ -908,7 +908,7 @@ function transformArg( arg, r, args, texts ) {
908
908
  return quoted( arg );
909
909
  if (arg._artifact)
910
910
  arg = arg._artifact;
911
- if (arg._outer)
911
+ while (arg._outer) // nested 'items'
912
912
  arg = arg._outer;
913
913
  if (args['#'] || args.member )
914
914
  return shortArtName( arg );
package/lib/base/model.js CHANGED
@@ -21,7 +21,6 @@ const availableBetaFlags = {
21
21
  mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
22
22
  enableUniversalCsn: true,
23
23
  odataTerms: true,
24
- optionalActionFunctionParameters: true, // not supported by runtime, yet.
25
24
  effectiveCsn: true,
26
25
  tenantVariable: true,
27
26
  calcAssoc: true,
@@ -122,7 +122,6 @@ function assertConsistency( model, stage ) {
122
122
  'dependencies', // for USING..FROM
123
123
  'kind', // TODO: remove from parser
124
124
  'meta',
125
- '@sql_mapping', // TODO: it is time that a 'header' attribute replaces 'version'
126
125
  '$withLocalized',
127
126
  '$sources',
128
127
  'tokenStream',
@@ -578,6 +577,7 @@ function assertConsistency( model, stage ) {
578
577
  'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
579
578
  '_outer', '_effectiveType', '$effectiveSeqNo', 'notNull', '_parent',
580
579
  '_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
580
+ 'localized', // really? see #13135
581
581
  '$calcDepElement',
582
582
  '$syntax', '_extensions',
583
583
  '_status', '_redirected',
@@ -1041,7 +1041,7 @@ function assertConsistency( model, stage ) {
1041
1041
  // TODO
1042
1042
  // else if (spec.instanceOf && spec.instanceOf !== 'ignore' &&
1043
1043
  // Object.getPrototypeOf( node ) !== spec.instanceOf.prototype)
1044
- // eslint-disable-next-line max-len
1044
+ // eslint-disable-next-line @stylistic/js/max-len
1045
1045
  // throw new InternalConsistencyError( `Expected object of class ${ spec.instanceOf.name } but found ${ found }${ at( [ null, parent ], prop, name ) }` );
1046
1046
  }
1047
1047
 
@@ -219,7 +219,7 @@ const dateRegEx = /^(-?\d{4})-(\d{1,2})-(\d{1,2})$/;
219
219
  // YYYY - MM - dd
220
220
  const timeRegEx = /^T?(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?(?:Z|[+-]\d{2}(?::\d{2})?)?$/;
221
221
  // T HH : mm : ss TZD
222
- // eslint-disable-next-line max-len
222
+ // eslint-disable-next-line @stylistic/js/max-len
223
223
  const timestampRegEx = /^(-?\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(\.\d{1,7})?)?(?:Z|[+-]\d{2}(?::\d{2})?)?$/;
224
224
  // YYYY - MM - dd T HH : mm : ss . fraction TZD
225
225
  const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?[ \t]*$/i;
@@ -33,6 +33,8 @@ function check( model ) {
33
33
  error, warning, info, message,
34
34
  } = model.$messageFunctions;
35
35
 
36
+ const { getOrigin } = model.$functions;
37
+
36
38
  checkSapCommonLocale( model );
37
39
  checkSapCommonTextsAspects( model );
38
40
 
@@ -400,13 +402,18 @@ function check( model ) {
400
402
 
401
403
  const expectedType = isNumeric ? 'number' : 'string';
402
404
 
405
+ let art = enumNode;
406
+ while (art?._effectiveType && art.length === undefined)
407
+ art = getOrigin( art );
408
+ const maxLength = art.length?.val ?? model.options.defaultStringLength;
409
+
403
410
  // Do not check elements that don't have a value at all or are
404
411
  // references to other enum elements. There are other checks for that.
405
412
  const hasWrongType = element => element.value &&
406
413
  (element.value.literal !== expectedType) &&
407
414
  (element.value.literal !== 'enum');
408
415
 
409
- for (const key of Object.keys( enumNode.enum )) {
416
+ for (const key in enumNode.enum) {
410
417
  const element = enumNode.enum[key];
411
418
  if (hasWrongType( element )) {
412
419
  const actualType = element.value.literal;
@@ -418,6 +425,18 @@ function check( model ) {
418
425
  string: 'Expected string value for enum element $(NAME) but was $(PROP)',
419
426
  } );
420
427
  }
428
+ else if (isString && maxLength !== undefined) {
429
+ const value = element.value?.val ?? element.name.id;
430
+ if (value.length > maxLength) {
431
+ const loc = element.value?.location ?? element.name.location;
432
+ warning( 'def-invalid-value', [ loc, element ], {
433
+ '#': element.value ? 'std' : 'implicit', name: element.name.id, value: maxLength,
434
+ }, {
435
+ std: 'Enum value $(NAME) exceeds specified length $(VALUE)',
436
+ implicit: 'Implicit enum value $(NAME) exceeds specified length $(VALUE)',
437
+ } );
438
+ }
439
+ }
421
440
  }
422
441
  }
423
442
 
@@ -850,16 +869,16 @@ function check( model ) {
850
869
  // One argument must be "$self" and the other an assoc
851
870
  if (xpr.op.val === '=' && xpr.args.length === 2) {
852
871
  // Tree-ish expression from the compiler (not augmented)
853
- // eslint-disable-next-line max-len
872
+ // eslint-disable-next-line @stylistic/js/max-len
854
873
  return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[1] ) ||
855
- // eslint-disable-next-line max-len
874
+ // eslint-disable-next-line @stylistic/js/max-len
856
875
  isAssociationOperand( xpr.args[1] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
857
876
  }
858
877
  else if (xpr.args.length === 3 && xpr.args[1].val === '=') {
859
878
  // Tree-ish expression from the compiler (not augmented)
860
- // eslint-disable-next-line max-len
879
+ // eslint-disable-next-line @stylistic/js/max-len
861
880
  return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[2] ) ||
862
- // eslint-disable-next-line max-len
881
+ // eslint-disable-next-line @stylistic/js/max-len
863
882
  isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
864
883
  }
865
884
 
@@ -1054,7 +1073,7 @@ function check( model ) {
1054
1073
  value.literal !== 'timestamp' && value.literal !== 'string') {
1055
1074
  // Hm, actually date and time cannot be mixed
1056
1075
  warning( null, loc, { type, anno },
1057
- // eslint-disable-next-line max-len
1076
+ // eslint-disable-next-line @stylistic/js/max-len
1058
1077
  'A date/time value or a string is required for type $(TYPE) for annotation $(ANNO)' );
1059
1078
  }
1060
1079
  }
@@ -174,7 +174,6 @@ function define( model ) {
174
174
  shuffleArray,
175
175
  initArtifact,
176
176
  initMembers,
177
- checkDefinitions, // TODO: remove
178
177
  initSelectItems,
179
178
  } );
180
179
 
@@ -188,13 +187,12 @@ function define( model ) {
188
187
  function doDefine() {
189
188
  if (options.deprecated &&
190
189
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
191
- warning( 'api-deprecated-option', {},
192
- { prop: 'deprecated', '#': (options.beta ? 'beta' : 'std') }, {
193
- // TODO: make the text scarier in future versions
194
- std: 'With option $(PROP), many newer features are disabled',
195
- // eslint-disable-next-line max-len
196
- beta: 'With option $(PROP), beta features and many other newer features are disabled',
197
- } );
190
+ warning( 'api-deprecated-option', {}, {
191
+ prop: 'deprecated', '#': (options.beta ? 'beta' : 'std'),
192
+ }, {
193
+ std: 'With option $(PROP), recent features are disabled',
194
+ beta: 'With option $(PROP), beta features and other recent features are disabled',
195
+ } );
198
196
  }
199
197
  model.definitions = Object.create( null );
200
198
  setLink( model, '_entities', [] ); // for entities with includes
@@ -317,7 +315,7 @@ function define( model ) {
317
315
  if (artifacts[using])
318
316
  continue;
319
317
  // TODO: enable optional locations
320
- const location = a.name.path && a.name.path[0].location || a.location;
318
+ const location = a.name.path?.[0]?.location || a.location;
321
319
  const absolute = prefix + using;
322
320
  artifacts[using] = {
323
321
  kind: 'using', // !, not namespace - we do not know artifact yet
@@ -350,7 +348,7 @@ function define( model ) {
350
348
  return;
351
349
  decl.extern.id = pathName( path );
352
350
  if (!decl.name)
353
- decl.name = { ...path[path.length - 1], $inferred: 'as' };
351
+ decl.name = { ...path.at(-1), $inferred: 'as' };
354
352
  const name = decl.name.id;
355
353
  // TODO: check name: no "."
356
354
  const found = src.artifacts[name];
@@ -365,7 +363,7 @@ function define( model ) {
365
363
  function addNamespace( namespace, src ) {
366
364
  // create using for own namespace:
367
365
  // TODO: should we really do that (in v6)? See also initNamespaceAndUsing().
368
- const last = namespace.path[namespace.path.length - 1];
366
+ const last = namespace.path.at(-1);
369
367
  const { id } = last;
370
368
  if (src.artifacts[id] || last.id.includes( '.' ))
371
369
  // not used as we have a definition/using with that name, or dotted last path id
@@ -437,8 +435,8 @@ function define( model ) {
437
435
  } );
438
436
  if (parent.kind !== 'extend')
439
437
  return;
440
- if (parent.columns) // TODO: sub queries? expand/inline?
441
- parent.columns.forEach( c => setLink( c, '_block', parent._block ) );
438
+ // TODO: sub queries? expand/inline?
439
+ parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
442
440
  if (parent.scale && !parent.precision) {
443
441
  // TODO: where could we store the location of the name?
444
442
  error( 'syntax-missing-type-property', [ parent.scale.location ],
@@ -794,7 +792,7 @@ function define( model ) {
794
792
  setMemberParent( table, query.name.id, query ); // sets _parent,_main
795
793
  initSubQuery( table ); // init sub queries in ON
796
794
  const aliases = Object.keys( table.$tableAliases || {} );
797
- // Use first tabalias name on the right side of the join to name the
795
+ // Use first table alias name on the right side of the join to name the
798
796
  // (internal) query, should only be relevant for --raw-output, not for
799
797
  // user messages or references - TODO: correct if join on left?
800
798
  table.name.id = aliases[1] || aliases[0] || '<unknown>';
@@ -919,16 +917,15 @@ function define( model ) {
919
917
  else {
920
918
  // a late syntax error (this code also runs with parse-cdl), i.e.
921
919
  // no semantic loc (wouldn't be available for expand/inline anyway)
922
- error( 'syntax-duplicate-wildcard', [ col.location, null ],
923
- {
924
- '#': (wildcard.location.col ? 'col' : 'std'),
925
- prop: '*',
926
- line: wildcard.location.line,
927
- col: wildcard.location.col,
928
- }, {
929
- std: 'You have provided a $(PROP) already in line $(LINE)',
930
- col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
931
- } );
920
+ error( 'syntax-duplicate-wildcard', [ col.location, null ], {
921
+ '#': (wildcard.location.col ? 'col' : 'std'),
922
+ prop: '*',
923
+ line: wildcard.location.line,
924
+ col: wildcard.location.col,
925
+ }, {
926
+ std: 'You have provided a $(PROP) already in line $(LINE)',
927
+ col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
928
+ } );
932
929
  // TODO: extra text variants for expand/inline? - probably not
933
930
  col.val = null; // do not consider it for expandWildcard()
934
931
  }
@@ -1073,7 +1070,7 @@ function define( model ) {
1073
1070
  // We do not want to complain separately about all element properties:
1074
1071
  error( 'ext-unexpected-element', [ e.location, construct ],
1075
1072
  { name: e.name.id, code: 'extend … with enum' },
1076
- // eslint-disable-next-line max-len
1073
+ // eslint-disable-next-line @stylistic/js/max-len
1077
1074
  'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
1078
1075
  // Don't emit 'ext-expecting-enum' if this error is emitted.
1079
1076
  return;
@@ -1094,7 +1091,7 @@ function define( model ) {
1094
1091
 
1095
1092
  function initAnonymousAspect() {
1096
1093
  // TODO: main?
1097
- const inEntity = parent._main && parent._main.kind === 'entity';
1094
+ const inEntity = parent._main?.kind === 'entity';
1098
1095
  // TODO: also allow indirectly (component in component in entity)?
1099
1096
  setLink( targetAspect, '_outer', obj );
1100
1097
  setLink( targetAspect, '_parent', parent._parent );
@@ -1205,7 +1202,7 @@ function define( model ) {
1205
1202
  if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
1206
1203
  const $self = main.$tableAliases?.$self ||
1207
1204
  main.kind === 'extend' && { name: { id: '$self' } };
1208
- // remark: an extend has no "table alias" `$self` (relevant for parse-cdl)
1205
+ // remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
1209
1206
  setLink( type, '_artifact', $self );
1210
1207
  setLink( path[0], '_artifact', $self );
1211
1208
  }
@@ -1258,7 +1255,6 @@ function define( model ) {
1258
1255
  return false;
1259
1256
  }
1260
1257
  }
1261
- //
1262
1258
  else if (parent.kind === 'action' || parent.kind === 'function') {
1263
1259
  error( 'ext-unexpected-action', [ construct.location, construct ], { '#': parent.kind }, {
1264
1260
  std: 'Actions and functions can\'t be extended, only annotated', // TODO: → ext-unsupported
@@ -543,7 +543,7 @@ function extend( model ) {
543
543
  {
544
544
  std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
545
545
  array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
546
- // eslint-disable-next-line max-len
546
+ // eslint-disable-next-line @stylistic/js/max-len
547
547
  struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
548
548
  boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
549
549
  null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
@@ -640,9 +640,9 @@ function extend( model ) {
640
640
  }
641
641
  else if (extVal < artVal + (scaleDiff || 0)) {
642
642
  const number = artVal + (scaleDiff || 0);
643
- error( 'ext-invalid-type-property', [ ext[prop].location, ext ],
644
- // eslint-disable-next-line object-curly-newline
645
- { '#': (scaleDiff ? 'scale' : 'number'), prop, number, otherprop: 'scale' } );
643
+ error( 'ext-invalid-type-property', [ ext[prop].location, ext ], {
644
+ '#': (scaleDiff ? 'scale' : 'number'), prop, number, otherprop: 'scale',
645
+ } );
646
646
  }
647
647
  else {
648
648
  art[prop] = ext[prop];
@@ -1170,14 +1170,11 @@ function extend( model ) {
1170
1170
  // TODO: use shared functionality with notFound in resolver.js
1171
1171
  const { location } = ext.name;
1172
1172
  extMain.kind = ext.kind;
1173
- const msg
1174
- = error( 'extend-undefined', [ location, artName ],
1175
- { art: artName },
1176
- {
1177
- std: 'Unknown $(ART) - nothing to extend',
1178
- element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
1179
- action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
1180
- } );
1173
+ const msg = error( 'extend-undefined', [ location, artName ], { art: artName }, {
1174
+ std: 'Unknown $(ART) - nothing to extend',
1175
+ element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',
1176
+ action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',
1177
+ } );
1181
1178
  attachAndEmitValidNames( msg, validDict );
1182
1179
  }
1183
1180
  }
@@ -1192,6 +1189,7 @@ function extend( model ) {
1192
1189
  *
1193
1190
  * @param {XSN.Definition} art
1194
1191
  * @param {XSN.Artifact} target
1192
+ * @param {string[]} [justResolveCyclic]
1195
1193
  * @returns {boolean}
1196
1194
  */
1197
1195
  function canApplyIncludes( art, target, justResolveCyclic ) {
@@ -1272,7 +1270,7 @@ function extend( model ) {
1272
1270
  *
1273
1271
  * @param {XSN.Extension} ext
1274
1272
  * @param {XSN.Artifact} art
1275
- * @param {string} prop: 'elements' or 'actions'
1273
+ * @param {string} prop 'elements' or 'actions'
1276
1274
  */
1277
1275
  function includeMembers( ext, art, prop ) {
1278
1276
  // TODO two kind of messages:
@@ -103,7 +103,7 @@ function generate( model ) {
103
103
  const lang = textsAspect.elements.language;
104
104
  error( 'def-unexpected-element', [ lang.name.location, lang ],
105
105
  { option: 'addTextsLanguageAssoc', art: textsAspect, name: 'language' },
106
- // eslint-disable-next-line max-len
106
+ // eslint-disable-next-line @stylistic/js/max-len
107
107
  '$(ART) is not used because option $(OPTION) conflicts with existing element $(NAME); remove either option or element' );
108
108
  hasError = true;
109
109
  }
@@ -244,7 +244,7 @@ function generate( model ) {
244
244
  (fioriEnabled && art.elements.ID_texts)) {
245
245
  // TODO if we have too much time: check all elements of texts entity for safety
246
246
  warning( null, [ art.name.location, art ], { art: textsEntity },
247
- // eslint-disable-next-line max-len
247
+ // eslint-disable-next-line @stylistic/js/max-len
248
248
  'Texts entity $(ART) can\'t be created as there is another definition with that name' );
249
249
  info( null, [ textsEntity.name.location, textsEntity ], { art },
250
250
  'Texts entity for $(ART) can\'t be created with this definition' );
@@ -639,7 +639,7 @@ function generate( model ) {
639
639
  }
640
640
  if (model.definitions[entityName]) {
641
641
  error( null, [ location, elem ], { art: entityName },
642
- // eslint-disable-next-line max-len
642
+ // eslint-disable-next-line @stylistic/js/max-len
643
643
  'Target entity $(ART) can\'t be created as there is another definition with this name' );
644
644
  return false;
645
645
  }
@@ -632,6 +632,12 @@ function populate( model ) {
632
632
  }
633
633
  }
634
634
 
635
+ /**
636
+ * Set type properties of specified elements on the inferred artifact, but only
637
+ * assign them if their values differs from the inferred ones (for better locations).
638
+ *
639
+ * @param {XSN.Artifact} art
640
+ */
635
641
  function setSpecifiedElementTypeProperties( art ) {
636
642
  for (const prop in art.typeProps$) {
637
643
  let o = art;
@@ -972,14 +978,14 @@ function populate( model ) {
972
978
  // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
973
979
  info( 'wildcard-excluding-many', [ sibling.name.location, query ],
974
980
  { id, keyword: 'excluding' },
975
- // eslint-disable-next-line max-len
981
+ // eslint-disable-next-line @stylistic/js/max-len
976
982
  'This select item replaces $(ID) from two or more sources. Add $(ID) to $(KEYWORD) to silence this message' );
977
983
  }
978
984
  else {
979
985
  // ID published! Used in stakeholder project; if renamed, add to oldMessageIds
980
986
  info( 'wildcard-excluding-one', [ sibling.name.location, query ],
981
987
  { id, alias: navElem._parent.name.id, keyword: 'excluding' },
982
- // eslint-disable-next-line max-len
988
+ // eslint-disable-next-line @stylistic/js/max-len
983
989
  'This select item replaces $(ID) from table alias $(ALIAS). Add $(ID) to $(KEYWORD) to silence this message' );
984
990
  }
985
991
  }
@@ -1106,9 +1112,9 @@ function populate( model ) {
1106
1112
  // art: definitionScope( target ), - TODO extra debug info in message
1107
1113
  sorted_arts: exposed,
1108
1114
  }, {
1109
- // eslint-disable-next-line max-len
1115
+ // eslint-disable-next-line @stylistic/js/max-len
1110
1116
  std: 'Replace target $(TARGET) by one of $(SORTED_ARTS); can\'t auto-redirect this association if multiple projections exist in this service',
1111
- // eslint-disable-next-line max-len
1117
+ // eslint-disable-next-line @stylistic/js/max-len
1112
1118
  two: 'Replace target $(TARGET) by $(SORTED_ARTS) or $(SECOND); can\'t auto-redirect this association if multiple projections exist in this service',
1113
1119
  } );
1114
1120
  // continuation semantics: no auto-redirection
@@ -1130,11 +1136,11 @@ function populate( model ) {
1130
1136
  anno: 'cds.redirection.target',
1131
1137
  sorted_arts: exposed,
1132
1138
  }, {
1133
- // eslint-disable-next-line max-len
1139
+ // eslint-disable-next-line @stylistic/js/max-len
1134
1140
  std: 'Add $(ANNO) to one of $(SORTED_ARTS) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
1135
- // eslint-disable-next-line max-len
1141
+ // eslint-disable-next-line @stylistic/js/max-len
1136
1142
  two: 'Add $(ANNO) to either $(SORTED_ARTS) or $(SECOND) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
1137
- // eslint-disable-next-line max-len
1143
+ // eslint-disable-next-line @stylistic/js/max-len
1138
1144
  justOne: 'Remove $(ANNO) from all but one of $(SORTED_ARTS) to have a unique redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
1139
1145
  } );
1140
1146
  }
@@ -74,7 +74,7 @@ function propagate( model ) {
74
74
 
75
75
  const { options } = model;
76
76
  const { rewriteAnnotationsRefs } = xprRewriteFns( model );
77
- // eslint-disable-next-line max-len
77
+ // eslint-disable-next-line @stylistic/js/max-len
78
78
  const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
79
79
  const { warning, throwWithError } = model.$messageFunctions;
80
80
 
@@ -368,7 +368,7 @@ function propagate( model ) {
368
368
  const art = item && item._artifact;
369
369
  if (art && art.virtual && art.virtual.val) {
370
370
  warning( 'def-missing-virtual', [ item.location, elem ], { art, keyword: 'virtual' },
371
- // eslint-disable-next-line max-len
371
+ // eslint-disable-next-line @stylistic/js/max-len
372
372
  'Prepend $(KEYWORD) to current select item - referred element $(ART) is virtual which is not inherited' );
373
373
  return;
374
374
  }