@sap/cds-compiler 4.8.0 → 4.9.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 (92) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +14 -1
  6. package/doc/CHANGELOG_BETA.md +19 -0
  7. package/lib/api/main.js +59 -24
  8. package/lib/api/options.js +12 -1
  9. package/lib/api/validate.js +1 -5
  10. package/lib/base/builtins.js +27 -0
  11. package/lib/base/message-registry.js +32 -19
  12. package/lib/base/messages.js +50 -19
  13. package/lib/base/model.js +4 -5
  14. package/lib/checks/actionsFunctions.js +2 -2
  15. package/lib/checks/annotationsOData.js +3 -0
  16. package/lib/checks/defaultValues.js +5 -2
  17. package/lib/checks/queryNoDbArtifacts.js +3 -2
  18. package/lib/checks/validator.js +2 -34
  19. package/lib/compiler/assert-consistency.js +8 -2
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +34 -22
  22. package/lib/compiler/extend.js +33 -10
  23. package/lib/compiler/index.js +0 -1
  24. package/lib/compiler/lsp-api.js +5 -0
  25. package/lib/compiler/propagator.js +21 -18
  26. package/lib/compiler/resolve.js +44 -28
  27. package/lib/compiler/shared.js +60 -20
  28. package/lib/compiler/tweak-assocs.js +13 -88
  29. package/lib/compiler/xpr-rewrite.js +689 -0
  30. package/lib/edm/annotations/genericTranslation.js +80 -60
  31. package/lib/edm/edm.js +4 -4
  32. package/lib/edm/edmInboundChecks.js +33 -0
  33. package/lib/edm/edmPreprocessor.js +9 -6
  34. package/lib/gen/Dictionary.json +129 -14
  35. package/lib/gen/language.checksum +1 -1
  36. package/lib/gen/language.interp +1 -1
  37. package/lib/gen/languageParser.js +1523 -1518
  38. package/lib/json/from-csn.js +13 -4
  39. package/lib/json/to-csn.js +10 -11
  40. package/lib/language/genericAntlrParser.js +14 -6
  41. package/lib/main.d.ts +67 -14
  42. package/lib/main.js +1 -0
  43. package/lib/model/cloneCsn.js +6 -3
  44. package/lib/model/csnRefs.js +12 -7
  45. package/lib/model/csnUtils.js +13 -7
  46. package/lib/model/enrichCsn.js +3 -1
  47. package/lib/model/revealInternalProperties.js +2 -1
  48. package/lib/model/sortViews.js +14 -6
  49. package/lib/modelCompare/compare.js +33 -34
  50. package/lib/optionProcessor.js +27 -2
  51. package/lib/render/DuplicateChecker.js +6 -6
  52. package/lib/render/manageConstraints.js +1 -0
  53. package/lib/render/toCdl.js +3 -1
  54. package/lib/transform/db/applyTransformations.js +33 -0
  55. package/lib/transform/db/constraints.js +1 -1
  56. package/lib/transform/db/expansion.js +8 -3
  57. package/lib/transform/db/groupByOrderBy.js +2 -2
  58. package/lib/transform/db/temporal.js +6 -3
  59. package/lib/transform/db/transformExists.js +2 -2
  60. package/lib/transform/effective/annotations.js +194 -0
  61. package/lib/transform/effective/main.js +6 -8
  62. package/lib/transform/effective/misc.js +31 -10
  63. package/lib/transform/forOdata.js +23 -7
  64. package/lib/transform/forRelationalDB.js +1 -1
  65. package/lib/transform/localized.js +7 -6
  66. package/lib/transform/odata/flattening.js +189 -106
  67. package/lib/transform/odata/toFinalBaseType.js +1 -1
  68. package/lib/transform/odata/typesExposure.js +15 -12
  69. package/lib/transform/parseExpr.js +4 -4
  70. package/lib/transform/transformUtils.js +40 -37
  71. package/lib/transform/translateAssocsToJoins.js +47 -47
  72. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  73. package/package.json +1 -1
  74. package/share/messages/anno-missing-rewrite.md +45 -0
  75. package/share/messages/message-explanations.json +1 -0
  76. package/bin/.eslintrc.json +0 -17
  77. package/lib/api/.eslintrc.json +0 -37
  78. package/lib/checks/.eslintrc.json +0 -31
  79. package/lib/compiler/.eslintrc.json +0 -8
  80. package/lib/edm/.eslintrc.json +0 -46
  81. package/lib/inspect/.eslintrc.json +0 -4
  82. package/lib/json/.eslintrc.json +0 -4
  83. package/lib/language/.eslintrc.json +0 -4
  84. package/lib/model/.eslintrc.json +0 -13
  85. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  86. package/lib/render/.eslintrc.json +0 -22
  87. package/lib/transform/.eslintrc.json +0 -13
  88. package/lib/transform/db/.eslintrc.json +0 -41
  89. package/lib/transform/draft/.eslintrc.json +0 -4
  90. package/lib/transform/effective/.eslintrc.json +0 -4
  91. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  92. package/lib/utils/.eslintrc.json +0 -7
@@ -649,7 +649,8 @@ const schema = compileSchema( {
649
649
  noPrefix: false, // just '@' is no CSN property
650
650
  prop: '@‹anno›', // which property name do messages use for annotation assignments?
651
651
  type: annotation,
652
- inKind: () => true, // allowed in all definitions (including columns and extensions)
652
+ // allowed in all definitions except mixins (including columns and extensions)
653
+ inKind: _kind => true, // TODO(v5): (kind !== 'mixin')
653
654
  schema: {
654
655
  '-expr': { // '-expr' and '-' must not exist top-level
655
656
  prop: '@‹anno›',
@@ -745,7 +746,7 @@ const schema = compileSchema( {
745
746
  ignore: true, type: ignore, // TODO: do we need to do something?
746
747
  },
747
748
  namespace: {
748
- type: stringRef,
749
+ type: namespace,
749
750
  },
750
751
  meta: { // meta information
751
752
  type: ignore, // TODO: should we test s/th here?
@@ -1043,6 +1044,11 @@ function definition( def, spec, xsn, csn, name ) {
1043
1044
  }
1044
1045
  }
1045
1046
 
1047
+ function namespace( ref, spec ) {
1048
+ const ns = stringRef(ref, spec);
1049
+ return ns ? { kind: 'namespace', name: ns } : null;
1050
+ }
1051
+
1046
1052
  function couldNotBeEnumProperty( prop ) {
1047
1053
  // returns true for `value` (which we allow with warning when extending an enum with `elements`)
1048
1054
  const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
@@ -1486,9 +1492,12 @@ function annoValue( val, spec ) {
1486
1492
  function annotation( val, spec, xsn, csn, name ) {
1487
1493
  // not used for the value
1488
1494
  const id = (xsn ? name.substring(1) : name);
1489
- if (!id) { // `"@": …` is already syntax-unknown-property
1495
+ if (!id) // `"@": …` is already syntax-unknown-property
1490
1496
  message( 'syntax-invalid-name', location(true), { '#': '{}' } );
1491
- }
1497
+
1498
+ if (xsn && xsn.kind === 'mixin') // TODO(v5): Remove, adapt spec['@'].inKind
1499
+ message('anno-unexpected-mixin', location(true));
1500
+
1492
1501
  const n = { id, location: location() };
1493
1502
  const r = annoValue( val, spec );
1494
1503
  r.name = n;
@@ -232,15 +232,13 @@ function compactModel( model, options = model.options || {} ) {
232
232
  if (using.length)
233
233
  csn.requires = using;
234
234
  }
235
- if (!isBetaEnabled(options, 'v5preview')) {
236
- // 'namespace' for complete model is 'namespace' of first source
237
- // (not a really useful property at all, avoids XSN inspection by Umbrella)
238
- for (const first in srcDict) {
239
- const { namespace } = srcDict[first];
240
- if (namespace?.path)
241
- csn.namespace = pathName( namespace.path );
242
- break;
243
- }
235
+ // 'namespace' for complete model is 'namespace' of first source
236
+ // (not a really useful property at all, avoids XSN inspection by Umbrella)
237
+ for (const first in srcDict) {
238
+ const { namespace } = srcDict[first];
239
+ if (namespace?.name?.path)
240
+ csn.namespace = pathName( namespace.name.path );
241
+ break;
244
242
  }
245
243
  set( 'definitions', csn, model );
246
244
  if (Object.keys(model.vocabularies || {}).length > 0)
@@ -1010,8 +1008,9 @@ function artifactRef( node, terse ) {
1010
1008
  return undefined;
1011
1009
  // Works also on XSN directly coming from parser and with XSN from CDL->CSN transformation
1012
1010
  // Shortcut for many cases:
1013
- if (terse && node._artifact && !node._artifact._main && terse !== '.path')
1014
- return node._artifact.name.id;
1011
+ const art = node._artifact;
1012
+ if (art && (!art._main || art.kind === '$self') && terse && terse !== '.path')
1013
+ return art.name.id;
1015
1014
  let { path } = node;
1016
1015
  if (!path)
1017
1016
  return undefined; // TODO: complain with strict
@@ -724,11 +724,11 @@ function fragileAlias( ast, safe = false ) {
724
724
  return ast;
725
725
  if (safe || ast.$delimited || !/^[a-zA-Z][a-zA-Z_]+$/.test( ast.id )) {
726
726
  this.warning( 'syntax-deprecated-auto-as', ast.location, { keyword: 'as' },
727
- 'Please add the keyword $(KEYWORD) in front of the alias name' );
727
+ 'Add keyword $(KEYWORD) in front of the alias name' );
728
728
  }
729
729
  else { // configurable error
730
730
  this.message( 'syntax-missing-as', ast.location, { keyword: 'as' },
731
- 'Please add the keyword $(KEYWORD) in front of the alias name' );
731
+ 'Add keyword $(KEYWORD) in front of the alias name' );
732
732
  }
733
733
  return ast;
734
734
  }
@@ -759,10 +759,16 @@ function identAst( token, category, noTokenTypeCheck = false ) {
759
759
 
760
760
  // $delimited is used to complain about ![$self] and other magic vars usage;
761
761
  // we might complain about that already here via @arg{category}
762
- return { id, $delimited: true, location: this.tokenLocation( token ) };
762
+
763
+ const ast = { id, $delimited: true, location: this.tokenLocation( token ) };
764
+ ast.location.tokenIndex = token.tokenIndex;
765
+ return ast;
766
+ }
767
+ if (token.text[0] !== '"') {
768
+ const ast = { id, location: this.tokenLocation(token) };
769
+ ast.location.tokenIndex = token.tokenIndex;
770
+ return ast;
763
771
  }
764
- if (token.text[0] !== '"')
765
- return { id, location: this.tokenLocation( token ) };
766
772
  // delimited:
767
773
  id = id.slice( 1, -1 ).replace( /""/g, '"' );
768
774
  if (!id) {
@@ -773,7 +779,9 @@ function identAst( token, category, noTokenTypeCheck = false ) {
773
779
  // eslint-disable-next-line max-len
774
780
  'Deprecated delimited identifier syntax, use $(DELIMITED) - strings are delimited by single quotes' );
775
781
  }
776
- return { id, $delimited: true, location: this.tokenLocation( token ) };
782
+ const ast = { id, $delimited: true, location: this.tokenLocation( token ) };
783
+ ast.location.tokenIndex = token.tokenIndex;
784
+ return ast;
777
785
  }
778
786
 
779
787
  function pushXprToken( args ) {
package/lib/main.d.ts CHANGED
@@ -150,6 +150,22 @@ declare namespace compiler {
150
150
  * @since v2.12.1
151
151
  */
152
152
  $xsnObjects?: boolean
153
+ /**
154
+ * Internal option for LSP only!
155
+ * If set, each AST gets a `tokenStream` property containing all lexed tokens.
156
+ *
157
+ * @private
158
+ */
159
+ attachTokens?: boolean
160
+ /**
161
+ * Internal option for LSP only!
162
+ * If set, enables some extra checks/work for the CDS LSP.
163
+ * May be implicitly set by `$lsp.<api>` functions.
164
+ *
165
+ * @private
166
+ * @since v4.9
167
+ */
168
+ lspMode?: boolean
153
169
  }
154
170
 
155
171
  /**
@@ -260,13 +276,34 @@ declare namespace compiler {
260
276
  /**
261
277
  * Options used by the `for.effective()` CSN transformation.
262
278
  *
263
- * WORK IN PROGRESS
264
- *
265
279
  * @internal
266
280
  * @see _for.effective()
267
281
  */
268
282
  export interface EffectiveCsnOptions extends SqlOptions {
269
- // TODO
283
+ /**
284
+ * If true, resolve simple type references to their simple base type.
285
+ *
286
+ * @default true
287
+ */
288
+ resolveSimpleTypes?: boolean
289
+ /**
290
+ * If true, transform projections into ordinary views with SELECT.
291
+ *
292
+ * @default true
293
+ */
294
+ resolveProjections?: boolean
295
+ /**
296
+ * If true, remap OData annotations to ABAP annotations.
297
+ *
298
+ * @default false
299
+ */
300
+ remapOdataAnnotations?: boolean
301
+ /**
302
+ * If true, keep '.localized' property in the CSN.
303
+ *
304
+ * @default false
305
+ */
306
+ keepLocalized?: boolean
270
307
  }
271
308
 
272
309
  /**
@@ -547,11 +584,6 @@ declare namespace compiler {
547
584
  * @internal
548
585
  */
549
586
  model?: CSN;
550
- /**
551
- * Used by `cdsc` to indicate whether the message was already printed to stderr.
552
- * @private
553
- */
554
- hasBeenReported: boolean;
555
587
  }
556
588
 
557
589
  /**
@@ -666,16 +698,23 @@ declare namespace compiler {
666
698
  * @param config.noMessageId
667
699
  * If true, will _not_ show the message ID (+ explanation hint) in the output.
668
700
  *
701
+ * @param config.idInBrackets
702
+ * If true, the message ID (if there is one and noMessageId is falsey) will be put in brackets.
703
+ * This will be the default in cds-compiler v5.
704
+ *
669
705
  * @param config.noHome
670
706
  * If true, will _not_ show message's semantic location.
671
707
  *
672
- * @param config.module
708
+ * @param config.moduleForMarker
673
709
  * If set, downgradable error messages will get a '‹↓›' marker, depending on whether
674
- * the message can be downgraded for the given module.
710
+ * the message can be downgraded for the given module. A `‹↑›` is used if the message
711
+ * will be an error in the next major cds-compiler release.
712
+ * Was called `module` in v4.8.0 and earlier.
675
713
  */
676
714
  export function messageString(msg: CompileMessage, config?: {
677
715
  normalizeFilename?: boolean
678
716
  noMessageId?: boolean
717
+ idInBrackets?: boolean
679
718
  noHome?: boolean
680
719
  module?: string
681
720
  }): string;
@@ -713,9 +752,11 @@ declare namespace compiler {
713
752
  * @param config.hintExplanation
714
753
  * If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
715
754
  *
716
- * @param config.module
755
+ * @param config.moduleForMarker
717
756
  * If set, downgradable error messages will get a '‹↓›' marker, depending on whether
718
- * the message can be downgraded for the given module.
757
+ * the message can be downgraded for the given module. A `‹↑›` is used if the message
758
+ * will be an error in the next major cds-compiler release.
759
+ * Was called `module` in v4.8.0 and earlier.
719
760
  *
720
761
  * @param config.sourceMap
721
762
  * A dictionary of filename<->source-code entries. You can pass the fileCache that is used
@@ -742,7 +783,7 @@ declare namespace compiler {
742
783
  normalizeFilename?: boolean
743
784
  noMessageId?: boolean
744
785
  hintExplanation?: boolean
745
- module?: string
786
+ moduleForMarker?: string
746
787
  sourceMap?: Record<string, string>
747
788
  sourceLineMap?: Record<string, number[]>
748
789
  cwd?: string
@@ -857,6 +898,12 @@ declare namespace compiler {
857
898
  * @internal
858
899
  */
859
900
  function effective(csn: CSN, options?: EffectiveCsnOptions): CSN;
901
+ /**
902
+ * Transform the given CSN into one that has the properties required for SEAL
903
+ *
904
+ * @internal
905
+ */
906
+ function seal(csn: CSN, options?: EffectiveCsnOptions): CSN;
860
907
  }
861
908
 
862
909
  export { _for as for };
@@ -1269,7 +1316,13 @@ declare namespace compiler {
1269
1316
  * The functions in `userFunctions` are usually transformer functions, which
1270
1317
  * change the input CSN destructively.
1271
1318
  */
1272
- export function traverseCsn(userFunctions: Record<string, Function>, csn: object|any[]): void;
1319
+ export function traverseCsn(userFunctions: Record<string, TraverseCsnCallback>, csn: object|any[]): void;
1320
+ export type TraverseCsnCallback = (
1321
+ userFunctions: Record<string, TraverseCsnCallback>,
1322
+ value: any,
1323
+ csn: any,
1324
+ prop: string
1325
+ ) => any;
1273
1326
 
1274
1327
  /**
1275
1328
  * CSN Model related functions.
package/lib/main.js CHANGED
@@ -128,6 +128,7 @@ module.exports = {
128
128
  odata: (...args) => snapi.odata(...args),
129
129
  java: (...args) => snapi.java(...args),
130
130
  effective: (...args) => snapi.for_effective(...args),
131
+ seal: (...args) => snapi.for_seal(...args),
131
132
  },
132
133
  to: {
133
134
  cdl: Object.assign((...args) => snapi.cdl(...args), {
@@ -47,9 +47,12 @@ const internalCsnPropertyNames = Object.keys(internalCsnProps);
47
47
  * @see cloneAnnotationValue()
48
48
  * @see cloneCsnDict()
49
49
  *
50
- * @param {object} csn Top-level CSN. You can pass non-dictionary values.
51
- * @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`.
52
- * @param {boolean} sort Whether to sort CSN properties.
50
+ * @param {object} csn
51
+ * Top-level CSN. You can pass non-dictionary values.
52
+ * @param {CSN.Options} options
53
+ * CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`.
54
+ * @param {boolean} sort
55
+ * Whether to sort CSN properties.
53
56
  */
54
57
  function cloneCsn( csn, options, sort ) {
55
58
  if (!csn || typeof csn !== 'object')
@@ -230,7 +230,8 @@ const referenceSemantics = {
230
230
  orderBy_ref: { lexical: query => query, dynamic: 'query' },
231
231
  orderBy_expr: { lexical: query => query, dynamic: 'source' }, // ref in ORDER BY expression
232
232
  orderBy_set_ref: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
233
- // refs in ORDER BY expr in UNION not really allowed - only with table alias (of outer queries) or $self
233
+ // refs in ORDER BY expr in UNION not really allowed
234
+ // only with table alias (of outer queries) or $self
234
235
  orderBy_set_expr: { lexical: query => query.$next, dynamic: false },
235
236
  // default: { lexical: query => query, dynamic: 'source' }
236
237
  };
@@ -409,8 +410,11 @@ function csnRefs( csn, universalReady ) {
409
410
  }
410
411
 
411
412
  function getOriginRaw( art ) {
412
- if (art.type) // TODO: make robust against "linked" = only direct
413
- return artifactRef( art.type, BUILTIN_TYPE );
413
+ if (art.type) { // TODO: make robust against "linked" = only direct
414
+ return (art.type !== '$self' || csn.definitions.$self)
415
+ ? artifactRef( art.type, BUILTIN_TYPE )
416
+ : getCache( boundActionOrMain( art ), '_parent' );
417
+ }
414
418
  if (typeof art.$origin === 'object') // null, […], {…}
415
419
  return getOriginExplicit( art.$origin );
416
420
 
@@ -698,7 +702,8 @@ function csnRefs( csn, universalReady ) {
698
702
  const links = path.map( (_v, idx) => ({ idx }) );
699
703
  // TODO: backends should be changed to enable uncommenting:
700
704
  // if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
701
- // throw new ModelError( `Path item 0=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
705
+ // throw new ModelError( `Path item 0=${ pathId( path[0] )
706
+ // } refers to nothing, scope: ${ scope }`);
702
707
  links[0].art = art;
703
708
  for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
704
709
  parent = navigationEnv( art, staticAssoc );
@@ -1010,8 +1015,8 @@ function queryOrMain( query, main ) {
1010
1015
  * - NOT on a `join` node inside `from`.
1011
1016
  *
1012
1017
  * @param {CSN.Query} query
1013
- * @param {CSN.QuerySelect} fromSelect: for query in `from`
1014
- * @param {CSN.Query} parentQuery: for a sub query (ex those in `from`)
1018
+ * @param {CSN.QuerySelect} fromSelect for query in `from`
1019
+ * @param {CSN.Query} parentQuery for a sub query (ex those in `from`)
1015
1020
  * @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched, parentQuery: CSN.Query) => void} callback
1016
1021
  */
1017
1022
  function traverseQuery( query, fromSelect, parentQuery, callback ) {
@@ -1135,7 +1140,7 @@ function startCsnPath( csnPath, csn ) {
1135
1140
  };
1136
1141
  }
1137
1142
  if (csnPath.length < 2 || head !== 'definitions' && head !== 'vocabularies')
1138
- throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet');
1143
+ throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet' );
1139
1144
  const art = csn[head][csnPath[1]];
1140
1145
  return {
1141
1146
  index: 2, main: art, parent: art, art, query: null,
@@ -6,6 +6,7 @@ const {
6
6
  applyTransformations,
7
7
  applyTransformationsOnNonDictionary,
8
8
  applyTransformationsOnDictionary,
9
+ mergeTransformers,
9
10
  } = require('../transform/db/applyTransformations');
10
11
  const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
11
12
  const { ModelError, CompilerAssertion } = require('../base/error');
@@ -134,7 +135,8 @@ function getUtils( model, universalReady ) {
134
135
  }
135
136
  else if (arg.ref) {
136
137
  const art = artifactRef.from(arg);
137
- elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
138
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location,
139
+ arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
138
140
  }
139
141
  else if (arg.SELECT || arg.SET) {
140
142
  elements = mergeElementMaps(elements, getSources(arg, true));
@@ -670,11 +672,13 @@ function isEdmPropertyRendered( elementCsn, options ) {
670
672
  // V4 struct: on/off
671
673
  if (elementCsn == null)
672
674
  return false;
673
- const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
675
+ const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured')
676
+ ? !!options.odataForeignKeys : true;
674
677
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
675
678
  const isNavigable = elementCsn.target
676
679
  ? (elementCsn['@odata.navigable'] === undefined ||
677
- elementCsn['@odata.navigable'] !== undefined && (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
680
+ elementCsn['@odata.navigable'] !== undefined &&
681
+ (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
678
682
  // Foreign Keys can be ignored
679
683
  if (elementCsn['@odata.foreignKey4'])
680
684
  return isNotIgnored && renderForeignKey;
@@ -1124,7 +1128,7 @@ function moveAnnotationsAndDoc( sourceNode, targetNode, overwrite = false ) {
1124
1128
  * override: Whether to ignore existing annotations.
1125
1129
  * filter: Positive filter. If it returns true, annotations for the referenced artifact
1126
1130
  * will be applied.
1127
- * applyToElements: Wether to apply annotations to elements or only to artifacts
1131
+ * applyToElements: Whether to apply annotations to elements or only to artifacts
1128
1132
  */
1129
1133
  function applyAnnotationsFromExtensions( csn, config ) {
1130
1134
  if (!csn.extensions)
@@ -1189,7 +1193,8 @@ function isAspect( node ) {
1189
1193
  */
1190
1194
  function hasValidSkipOrExists( artifact ) {
1191
1195
  return artifact.kind === 'entity' &&
1192
- (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
1196
+ (hasAnnotationValue(artifact, '@cds.persistence.exists', true) ||
1197
+ hasAnnotationValue(artifact, '@cds.persistence.skip', true));
1193
1198
  }
1194
1199
 
1195
1200
  /**
@@ -1368,7 +1373,8 @@ function functionList( functions, thisArg ) {
1368
1373
  * @returns {boolean}
1369
1374
  */
1370
1375
  function isDollarSelfOrProjectionOperand( arg ) {
1371
- return arg.ref && arg.ref.length === 1 && (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
1376
+ return arg.ref && arg.ref.length === 1 &&
1377
+ (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
1372
1378
  }
1373
1379
 
1374
1380
  /**
@@ -1408,7 +1414,6 @@ function findAnnotationExpression( node, prop ) {
1408
1414
  },
1409
1415
  });
1410
1416
  }
1411
-
1412
1417
  return isExpr;
1413
1418
  }
1414
1419
 
@@ -1430,6 +1435,7 @@ module.exports = {
1430
1435
  applyTransformations,
1431
1436
  applyTransformationsOnNonDictionary,
1432
1437
  applyTransformationsOnDictionary,
1438
+ mergeTransformers,
1433
1439
  setDependencies,
1434
1440
  isPersistedOnDatabase,
1435
1441
  isPersistedAsView,
@@ -207,7 +207,9 @@ function enrichCsn( csn, options = {} ) {
207
207
  }
208
208
 
209
209
  function pathRef( parent, prop, path, inspectionPath = csnPath ) {
210
- const inspection = handleError( err => ((err) ? { scope: err.toString() } : inspectRef( inspectionPath )));
210
+ const inspection = handleError( err => ((err)
211
+ ? { scope: err.toString() }
212
+ : inspectRef( inspectionPath )));
211
213
  const {
212
214
  links, art, scope, $env,
213
215
  } = inspection;
@@ -38,7 +38,8 @@ const kindsRepresentedAsLinks = {
38
38
  // represent table alias in from / join-args property as link:
39
39
  $tableAlias: tableAliasAsLink,
40
40
  // represent "navigation elements" in _combined as links:
41
- $navElement: (art, parent) => art._parent && parent !== art._parent.elements && art._parent.kind !== 'aspect',
41
+ $navElement: (art, parent) => art._parent && parent !== art._parent.elements &&
42
+ art._parent.kind !== 'aspect',
42
43
  // represent mixin in $tableAliases as link:
43
44
  mixin: tableAliasAsLink,
44
45
  // represent $projection as link, as it is just another search name for $self:
@@ -5,8 +5,11 @@ const { ModelError } = require('../base/error');
5
5
 
6
6
  /**
7
7
  * @typedef {Object} Layers
8
- * @property {Array[]} layers - An array of arrays, each subarray encompassing one Layer - L0 being layers[0].
9
- * @property {CSN.Artifact[]} leftover - Any artifacts not sorted into a layer due to unmet dependencies - points to there being some error.
8
+ * @property {Array[]} layers
9
+ * An array of arrays, each subarray encompassing one Layer - L0 being layers[0].
10
+ * @property {CSN.Artifact[]} leftover
11
+ * Any artifacts not sorted into a layer due to unmet dependencies.
12
+ * Points to there being some error.
10
13
  */
11
14
 
12
15
  /**
@@ -22,7 +25,8 @@ const { ModelError } = require('../base/error');
22
25
  */
23
26
  function sortTopologically( csn, _dependents, _dependencies ) {
24
27
  const layers = [];
25
- let { zero, nonZero } = _calculateDepth(Object.entries(csn.definitions), _dependents, _dependencies);
28
+ let { zero, nonZero } = _calculateDepth(Object.entries(csn.definitions),
29
+ _dependents, _dependencies);
26
30
  while (zero.length !== 0) {
27
31
  const currentLayer = [];
28
32
  zero.forEach(([ artifactName, artifact ]) => {
@@ -87,8 +91,10 @@ function _findWithXPointers( definitionsArray, x, _dependents, _dependencies ) {
87
91
  * For ordering, only the FROM clause of views is checked - this requires A2J to
88
92
  * be run beforehand to resolve association usages.
89
93
  *
90
- * @param {{sql: string, csn: CSN.Model}} arg sql: Map of <object name>: "CREATE STATEMENT", csn: Model
91
- * @returns {{name: string, sql: string}[]} Sorted array of artifact name / "CREATE STATEMENTS" pairs
94
+ * @param {{sql: string, csn: CSN.Model}} arg
95
+ * sql: Map of <object name>: "CREATE STATEMENT", csn: Model
96
+ * @returns {{name: string, sql: string}[]}
97
+ * Sorted array of artifact name / "CREATE STATEMENTS" pairs
92
98
  */
93
99
  module.exports = function sortViews({ sql, csn }) {
94
100
  const { cleanup, _dependents, _dependencies } = setDependencies(csn);
@@ -98,7 +104,9 @@ module.exports = function sortViews({ sql, csn }) {
98
104
 
99
105
  const result = [];
100
106
  // keep the "artifact name" - needed for to.hdi sorting
101
- layers.forEach(layer => layer.forEach(objName => result.push({ name: objName, sql: sql[objName], dependents: csn.definitions[objName][_dependents] })));
107
+ layers.forEach(layer => layer.forEach(objName => result.push({
108
+ name: objName, sql: sql[objName], dependents: csn.definitions[objName][_dependents],
109
+ })));
102
110
  // attach sql artifacts which are not considered during the view sorting algorithm
103
111
  // --> this is the case for "ALTER TABLE ADD CONSTRAINT" statements,
104
112
  // because their identifiers are not part of the csn.definitions
@@ -13,6 +13,12 @@ const { forEachKey, forEach } = require('../utils/objectUtils');
13
13
  // used to mark a view as changed so we know to drop-create it
14
14
  const isChanged = Symbol('Marks a view as changed');
15
15
 
16
+ const relevantProperties = {
17
+ doc: true,
18
+ '@sql.prepend': true,
19
+ '@sql.append': true,
20
+ };
21
+
16
22
  /**
17
23
  * Compares two models, in HANA-transformed CSN format, to each other.
18
24
  *
@@ -82,6 +88,33 @@ function getExtensionAndMigrations(beforeModel, options, {
82
88
  }) {
83
89
  return function compareArtifacts(artifact, name) {
84
90
  let hasPrimaryKeyChange = false;
91
+ const otherArtifact = beforeModel.definitions[name];
92
+ const isPersisted = isPersistedAsTable(artifact);
93
+ const isPersistedOther = otherArtifact && isPersistedAsTable(otherArtifact);
94
+
95
+ // to make it easier to know which views to drop-create
96
+ if (isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
97
+ // TODO: Check only on artifact.query/projection BUT: Need to manually check for sql-snippets then!
98
+ artifact[isChanged] = JSON.stringify(artifact) !== JSON.stringify(otherArtifact);
99
+ }
100
+
101
+ // Looking for added entities and added/deleted/changed elements.
102
+ // Parameters: `artifact` from afterModel and `otherArtifact` from beforeModel.
103
+
104
+ if (!isPersisted) // Artifact not persisted in afterModel.
105
+ return;
106
+
107
+ if (!isPersistedOther) {
108
+ extensions[name] = artifact;
109
+ return;
110
+ }
111
+
112
+ // Artifact changed?
113
+
114
+ addElements();
115
+ changePropsOrRemoveOrChangeElements();
116
+ if (hasPrimaryKeyChange)
117
+ changedPrimaryKeys.push(name);
85
118
 
86
119
  function addElements() {
87
120
  const elements = {};
@@ -211,34 +244,6 @@ function getExtensionAndMigrations(beforeModel, options, {
211
244
  if (migration.properties || migration.remove || migration.change || migration.removeConstraints)
212
245
  migrations.push(migration);
213
246
  }
214
-
215
- const otherArtifact = beforeModel.definitions[name];
216
- const isPersisted = isPersistedAsTable(artifact);
217
- const isPersistedOther = otherArtifact && isPersistedAsTable(otherArtifact);
218
-
219
- // to make it easier to know which views to drop-create
220
- if (isPersistedAsView(artifact) && isPersistedAsView(otherArtifact)) {
221
- // TODO: Check only on artifact.query/projection BUT: Need to manually check for sql-snippets then!
222
- artifact[isChanged] = JSON.stringify(artifact) !== JSON.stringify(otherArtifact);
223
- }
224
-
225
- // Looking for added entities and added/deleted/changed elements.
226
- // Parameters: `artifact` from afterModel and `otherArtifact` from beforeModel.
227
-
228
- if (!isPersisted) // Artifact not persisted in afterModel.
229
- return;
230
-
231
- if (!isPersistedOther) {
232
- extensions[name] = artifact;
233
- return;
234
- }
235
-
236
- // Artifact changed?
237
-
238
- addElements();
239
- changePropsOrRemoveOrChangeElements();
240
- if (hasPrimaryKeyChange)
241
- changedPrimaryKeys.push(name);
242
247
  };
243
248
  }
244
249
  /**
@@ -374,12 +379,6 @@ function docCommentChanged(element, otherElement) {
374
379
  return element.doc && !otherElement.doc || otherElement.doc && !element.doc || element.doc && element.doc !== otherElement.doc;
375
380
  }
376
381
 
377
- const relevantProperties = {
378
- doc: true,
379
- '@sql.prepend': true,
380
- '@sql.append': true,
381
- };
382
-
383
382
  /**
384
383
  * Returns whether any type parameters differ between two given elements. Ignores whether types themselves differ (`type` property) and ignores
385
384
  * diff in doc comments.
@@ -155,7 +155,8 @@ optionProcessor
155
155
  manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
156
156
  add / modify referential constraints.
157
157
  inspect [options] <files...> (internal) Inspect the given CDS files.
158
- toEffectiveCsn [options] <files...> (internal) Get an effective CSN for SEAL; requires beta mode
158
+ toEffectiveCsn [options] <files...> (internal) Get an effective CSN; requires beta mode
159
+ forSeal [options] <files...> (internal) Get a SEAL CSN
159
160
 
160
161
  Environment variables
161
162
  NO_COLOR If set, compiler messages (/output) will not be colored.
@@ -546,6 +547,8 @@ optionProcessor.command('toEffectiveCsn')
546
547
  .option('-h, --help')
547
548
  .option('--resolve-simple-types <val>', { valid: ['true', 'false'] } )
548
549
  .option('--resolve-projections <val>', { valid: ['true', 'false'] } )
550
+ .option('--remap-odata-annotations <val>', { valid: ['true', 'false'] } )
551
+ .option('--keep-localized <val>', { valid: ['true', 'false'] } )
549
552
  .positionalArgument('<files...>')
550
553
  .help(`
551
554
  Usage: cdsc toEffectiveCsn [options] <files...>
@@ -560,8 +563,30 @@ optionProcessor.command('toEffectiveCsn')
560
563
  true: (default) resolve simple type references to their simple base type
561
564
  false: do not resolve simple type references
562
565
  --resolve-projections <val> Resolve projections:
563
- true: (default) resolve projections to ordinary views with SELECT
566
+ true: (default) transform projections into ordinary views with SELECT
564
567
  false: leave them as real projections
568
+ --remap-odata-annotations <val> Remap OData annotations to ABAP annotations:
569
+ true: remap annotations
570
+ false:(default) leave them as is
571
+ --keep-localized <val> Keep '.localized' property in the CSN:
572
+ true: property is kept
573
+ false:(default) property is deleted
574
+ `);
575
+
576
+ optionProcessor.command('forSeal')
577
+ .option('-h, --help')
578
+ .option('--remap-odata-annotations <val>', { valid: ['true', 'false'] } )
579
+ .positionalArgument('<files...>')
580
+ .help(`
581
+ Usage: cdsc forSeal [options] <files...>
582
+
583
+ (internal): Get the SEAL CSN model compiled from the provided CDS files.
584
+
585
+ Options
586
+ -h, --help Show this help text
587
+ --remap-odata-annotations <val> Remap OData annotations to ABAP annotations:
588
+ true: (default) remap annotations
589
+ false: leave them as is
565
590
  `);
566
591
 
567
592
  module.exports = {