@sap/cds-compiler 2.10.2 → 2.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +90 -5
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +3 -1
  4. package/bin/cdsc.js +49 -25
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_BETA.md +10 -0
  8. package/lib/api/.eslintrc.json +2 -0
  9. package/lib/api/main.js +8 -36
  10. package/lib/api/options.js +15 -6
  11. package/lib/api/validate.js +30 -3
  12. package/lib/backends.js +12 -13
  13. package/lib/base/dictionaries.js +2 -1
  14. package/lib/base/keywords.js +3 -2
  15. package/lib/base/message-registry.js +34 -10
  16. package/lib/base/messages.js +38 -18
  17. package/lib/base/model.js +5 -4
  18. package/lib/base/optionProcessorHelper.js +57 -23
  19. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  20. package/lib/checks/selectItems.js +4 -0
  21. package/lib/checks/unknownMagic.js +6 -3
  22. package/lib/compiler/assert-consistency.js +9 -2
  23. package/lib/compiler/base.js +65 -0
  24. package/lib/compiler/builtins.js +62 -16
  25. package/lib/compiler/checks.js +2 -1
  26. package/lib/compiler/definer.js +66 -108
  27. package/lib/compiler/index.js +29 -29
  28. package/lib/compiler/propagator.js +5 -2
  29. package/lib/compiler/resolver.js +225 -58
  30. package/lib/compiler/shared.js +53 -229
  31. package/lib/compiler/utils.js +184 -0
  32. package/lib/edm/annotations/genericTranslation.js +1 -1
  33. package/lib/edm/csn2edm.js +3 -2
  34. package/lib/edm/edmPreprocessor.js +34 -38
  35. package/lib/edm/edmUtils.js +3 -3
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +17 -1
  38. package/lib/gen/language.tokens +79 -73
  39. package/lib/gen/languageLexer.interp +19 -1
  40. package/lib/gen/languageLexer.js +779 -731
  41. package/lib/gen/languageLexer.tokens +71 -65
  42. package/lib/gen/languageParser.js +4668 -4072
  43. package/lib/json/from-csn.js +10 -10
  44. package/lib/json/to-csn.js +228 -47
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +26 -8
  47. package/lib/language/genericAntlrParser.js +73 -14
  48. package/lib/language/language.g4 +79 -3
  49. package/lib/main.d.ts +215 -18
  50. package/lib/main.js +3 -1
  51. package/lib/model/api.js +2 -2
  52. package/lib/model/csnRefs.js +117 -33
  53. package/lib/model/csnUtils.js +65 -133
  54. package/lib/model/enrichCsn.js +62 -37
  55. package/lib/model/revealInternalProperties.js +25 -8
  56. package/lib/model/sortViews.js +8 -1
  57. package/lib/modelCompare/compare.js +2 -1
  58. package/lib/optionProcessor.js +33 -18
  59. package/lib/render/.eslintrc.json +1 -2
  60. package/lib/render/DuplicateChecker.js +1 -1
  61. package/lib/render/toCdl.js +15 -8
  62. package/lib/render/toHdbcds.js +26 -49
  63. package/lib/render/toSql.js +61 -39
  64. package/lib/render/utils/common.js +1 -1
  65. package/lib/transform/db/applyTransformations.js +189 -0
  66. package/lib/transform/db/constraints.js +273 -119
  67. package/lib/transform/db/draft.js +3 -2
  68. package/lib/transform/db/expansion.js +6 -4
  69. package/lib/transform/db/flattening.js +19 -3
  70. package/lib/transform/db/transformExists.js +102 -9
  71. package/lib/transform/db/views.js +485 -0
  72. package/lib/transform/forHanaNew.js +93 -448
  73. package/lib/transform/forOdataNew.js +9 -2
  74. package/lib/transform/localized.js +2 -0
  75. package/lib/transform/odata/structuralPath.js +1 -5
  76. package/lib/transform/transformUtilsNew.js +22 -8
  77. package/lib/transform/translateAssocsToJoins.js +7 -15
  78. package/lib/utils/file.js +11 -5
  79. package/lib/utils/term.js +65 -42
  80. package/lib/utils/timetrace.js +48 -26
  81. package/package.json +1 -1
  82. package/lib/transform/db/helpers.js +0 -58
@@ -49,6 +49,8 @@ GenericAntlrParser.prototype = Object.assign(
49
49
  attachLocation,
50
50
  startLocation,
51
51
  tokenLocation,
52
+ multiLineTokenLocation,
53
+ previousTokenAtLocation,
52
54
  combinedLocation,
53
55
  surroundByParens,
54
56
  unaryOpForParens,
@@ -245,9 +247,12 @@ function prepareGenericKeywords( pathItem ) {
245
247
  // TODO: If not just at the beginning, we need a stack for $genericKeywords,
246
248
  // as we can have nested special functions
247
249
  this.$genericKeywords.argFull = Object.keys( spec );
250
+ // @ts-ignore
248
251
  const token = this.getCurrentToken() || { text: '' };
249
- if (spec[token.text.toUpperCase()] === 'argFull')
252
+ if (spec[token.text.toUpperCase()] === 'argFull') {
253
+ // @ts-ignore
250
254
  token.type = this.constructor.GenericArgFull;
255
+ }
251
256
  }
252
257
 
253
258
  // Attach location matched by current rule to node `art`. If a location is
@@ -297,7 +302,7 @@ function tokenLocation( token, endToken, val ) {
297
302
  file: this.filename,
298
303
  line: token.line,
299
304
  col: token.column + 1,
300
- // we only have single-line tokens
305
+ // we only have single-line tokens, except for docComments
301
306
  endLine: endToken.line,
302
307
  endCol: endToken.stop - endToken.start + endToken.column + 2, // after the last char (special for EOF?)
303
308
  };
@@ -306,6 +311,52 @@ function tokenLocation( token, endToken, val ) {
306
311
  return r;
307
312
  }
308
313
 
314
+ /**
315
+ * Return location of `token`. In contrast to `tokenLocation()`, this function
316
+ * can handle multiline tokens.
317
+ */
318
+ function multiLineTokenLocation(token, val) {
319
+ if (!token)
320
+ return undefined;
321
+
322
+ // Count the number of newlines in the token.
323
+ const source = token.source[1].data;
324
+ let newLineCount = 0;
325
+ let lastNewlineIndex = token.start;
326
+ for (let i = token.start; i < token.stop; i++) {
327
+ // Note: We do NOT check for CR, LS, and PS (/[\r\u2028\u2029]/)
328
+ // because ANTLR only uses LF for line break detection.
329
+ if (source[i] === 10) { // ASCII code for '\n'
330
+ newLineCount++;
331
+ lastNewlineIndex = i;
332
+ }
333
+ }
334
+ if (newLineCount === 0)
335
+ // endCol calculation below requires at least one newLine.
336
+ return this.tokenLocation(token, token, val);
337
+
338
+ /** @type {CSN.Location} */
339
+ const r = {
340
+ file: this.filename,
341
+ line: token.line,
342
+ col: token.column + 1,
343
+ endLine: token.line + newLineCount,
344
+ endCol: token.stop - lastNewlineIndex + 1, // after the last char (special for EOF?)
345
+ };
346
+ if (val !== undefined)
347
+ return { location: r, val };
348
+ return r;
349
+ }
350
+
351
+ function previousTokenAtLocation( location ) {
352
+ let k = -1;
353
+ let token = this._input.LT(k);
354
+ while (token.line > location.line ||
355
+ token.line === location.line && token.column >= location.col)
356
+ token = this._input.LT(--k);
357
+ return (token.line === location.line && token.column + 1 === location.col) && token;
358
+ }
359
+
309
360
  // Create a location with location properties `filename` and `start` from
310
361
  // argument `start`, and location property `end` from argument `end`.
311
362
  function combinedLocation( start, end ) {
@@ -342,16 +393,20 @@ function unaryOpForParens( query, val ) {
342
393
  // - would influence the prediction, probably even induce adaptivePredict() calls,
343
394
  // - is only slightly "more declarative" in the grammar.
344
395
  function docComment( node ) {
345
- if (!this.options.docComment)
346
- return;
347
396
  const token = this._input.getHiddenTokenToLeft( this.constructor.DocComment );
348
397
  if (!token)
349
398
  return;
399
+
400
+ // This token is actually used by / assigned to an artifact.
401
+ token.isUsed = true;
402
+
403
+ if (!this.options.docComment)
404
+ return;
350
405
  if (node.doc) {
351
406
  this.warning( 'syntax-duplicate-doc-comment', token, {},
352
407
  'Repeated doc comment - previous doc is replaced' );
353
408
  }
354
- node.doc = this.tokenLocation( token, token, parseDocComment( token.text ) );
409
+ node.doc = this.multiLineTokenLocation( token, parseDocComment( token.text ) );
355
410
  }
356
411
 
357
412
  // Classify token (identifier category) for implicit names,
@@ -435,14 +490,18 @@ function valuePathAst( ref ) {
435
490
  path.length = 1;
436
491
  }
437
492
  const { args, id, location } = path[0];
438
- if (args) {
439
- if (path[0].$syntax !== ':')
440
- return { op: { location, val: 'call' }, func: ref, location: ref.location, args };
441
- }
442
- else if (!path[0].$delimited && functionsWithoutParens.includes( id.toUpperCase() )) {
443
- return { op: { location, val: 'call' }, func: ref, location: ref.location };
444
- }
445
- return ref;
493
+ if (args
494
+ ? path[0].$syntax === ':'
495
+ : path[0].$delimited || !functionsWithoutParens.includes( id.toUpperCase() ))
496
+ return ref;
497
+
498
+ const implicit = this.previousTokenAtLocation( location );
499
+ if (implicit && implicit.isIdentifier)
500
+ implicit.isIdentifier = 'func';
501
+ const op = { location, val: 'call' };
502
+ return (args)
503
+ ? { op, func: ref, location: ref.location, args }
504
+ : { op, func: ref, location: ref.location };
446
505
  }
447
506
 
448
507
  // If a '-' is directly before an unsigned number, consider it part of the number;
@@ -601,7 +660,7 @@ function addDef( parent, env, kind, name, annos, props, location ) {
601
660
  // which could be tested in name search (then no undefined-ref error)
602
661
  return art;
603
662
  }
604
- else if (env === 'artifacts') {
663
+ else if (env === 'artifacts' || env === 'vocabularies') {
605
664
  dictAddArray( parent[env], art.name.id, art );
606
665
  }
607
666
  else if (kind || this.options.parseOnly) {
@@ -498,6 +498,8 @@ projectionSpec returns[ query ] locals[ src ]
498
498
  fromPath[ $src, 'ref']
499
499
  )?
500
500
  ( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
501
+ // ANTLR errors are better if we use ( A )? instead of ( A | ):
502
+ { if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
501
503
  bracedSelectItemListDef[ $query ]?
502
504
  excludingClause[ $query ]?
503
505
  ;
@@ -1499,7 +1501,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1499
1501
  { $art.enum = Object.create(null); }
1500
1502
  enumSymbolDef[ $art ]*
1501
1503
  '}'
1502
- optionalSemi | requiredSemi
1504
+ optionalSemi
1505
+ | requiredSemi
1503
1506
  )
1504
1507
  |
1505
1508
  ':' // with element, e.g. `type T : E:elem enum { ... }`
@@ -1511,7 +1514,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1511
1514
  { $art.enum = Object.create(null); }
1512
1515
  enumSymbolDef[ $art ]*
1513
1516
  '}'
1514
- optionalSemi | requiredSemi
1517
+ optionalSemi
1518
+ | requiredSemi
1515
1519
  )
1516
1520
  |
1517
1521
  { this.docComment( $annos ); }
@@ -1520,7 +1524,8 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1520
1524
  { $art.enum = Object.create(null); }
1521
1525
  enumSymbolDef[ $art ]*
1522
1526
  '}'
1523
- optionalSemi | requiredSemi
1527
+ optionalSemi
1528
+ | requiredSemi
1524
1529
  )
1525
1530
  |
1526
1531
  // TODO: complain if used in anno def?
@@ -1776,6 +1781,62 @@ partitionByClause returns [ expr ]
1776
1781
  ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1777
1782
  ;
1778
1783
 
1784
+ windowFrameClause returns [ wf ]
1785
+ :
1786
+ r=ROWS { $wf = { op: this.tokenLocation($r, null, 'rows' ) , args: [] }}
1787
+ wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
1788
+ ;
1789
+
1790
+ windowFrameExtentSpec returns[ wfe ]
1791
+ :
1792
+ { $wfe = {} }
1793
+ windowFrameStartSpec [ $wfe ]
1794
+ |
1795
+ b=BETWEEN
1796
+ { $wfe = { op: this.tokenLocation( $b, null, 'frameBetween' ), args: [] } }
1797
+ wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
1798
+ AND
1799
+ wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
1800
+ ;
1801
+
1802
+ windowFrameBoundSpec returns [ wfb ]
1803
+ @after{ /* #ATN 1 */ }
1804
+ :
1805
+ // #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
1806
+ // `UNBOUNDED` would then be immediately followed by `PRECEDING`
1807
+ u=UNBOUNDED f=FOLLOWING
1808
+ { $wfb = { op: this.tokenLocation($u, $f, 'unboundedFollowing' ), args: []} }
1809
+ |
1810
+ // #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
1811
+ // `Number` would then be immediately followed by `PRECEDING`
1812
+ n=Number f=FOLLOWING
1813
+ { $wfb = { op: this.tokenLocation($n, $f, 'following' ), args: [ this.numberLiteral( $n ) ]} }
1814
+ |
1815
+ { $wfb = {} }
1816
+ windowFrameStartSpec [ $wfb ]
1817
+ ;
1818
+
1819
+ windowFrameStartSpec [ wf ]
1820
+ :
1821
+ u=UNBOUNDED p=PRECEDING
1822
+ {
1823
+ $wf.op = this.tokenLocation($u, $p, 'unboundedPreceding' );
1824
+ $wf.args = [];
1825
+ }
1826
+ |
1827
+ n=Number p=PRECEDING
1828
+ {
1829
+ $wf.op = this.tokenLocation($p, null, 'preceding' );
1830
+ $wf.args = [ this.numberLiteral( $n ) ];
1831
+ }
1832
+ |
1833
+ c=CURRENT r=ROW
1834
+ {
1835
+ $wf.op = this.tokenLocation($c, $r, 'currentRow' );
1836
+ $wf.args = [];
1837
+ }
1838
+ ;
1839
+
1779
1840
  overClause returns [ over ]
1780
1841
  @after { this.attachLocation($over); }
1781
1842
  :
@@ -1783,6 +1844,7 @@ overClause returns [ over ]
1783
1844
  '('
1784
1845
  ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1785
1846
  ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
1847
+ ( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
1786
1848
  ')'
1787
1849
  ;
1788
1850
 
@@ -1948,6 +2010,8 @@ tableTerm returns [ table ]
1948
2010
  // if we would use rule `ident`, we would either had to make all JOIN
1949
2011
  // kinds reserved or introduce ATN
1950
2012
  )?
2013
+ // ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
2014
+ { if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
1951
2015
  |
1952
2016
  open='('
1953
2017
  // #ATN: The following alternative is not LL1, because both can start with
@@ -2602,6 +2666,7 @@ ident[ category ] returns[ id ]
2602
2666
  | COMPOSITION
2603
2667
  | CONTEXT
2604
2668
  | CROSS
2669
+ | CURRENT
2605
2670
  | DAY
2606
2671
  | DEFAULT
2607
2672
  | DEFINE
@@ -2619,6 +2684,7 @@ ident[ category ] returns[ id ]
2619
2684
  | EXTEND
2620
2685
  | FIRST
2621
2686
  | FLOATING
2687
+ | FOLLOWING
2622
2688
  | FULL
2623
2689
  | FUNCTION
2624
2690
  | GROUP
@@ -2650,10 +2716,13 @@ ident[ category ] returns[ id ]
2650
2716
  | OUTER
2651
2717
  | PARAMETERS
2652
2718
  | PARTITION
2719
+ | PRECEDING
2653
2720
  | PROJECTION
2654
2721
  | REDIRECTED
2655
2722
  | RETURNS
2656
2723
  | RIGHT
2724
+ | ROW
2725
+ | ROWS
2657
2726
  | SECOND
2658
2727
  | SERVICE
2659
2728
  | THEN
@@ -2662,6 +2731,7 @@ ident[ category ] returns[ id ]
2662
2731
  | TO
2663
2732
  | TYPE
2664
2733
  | USING
2734
+ | UNBOUNDED
2665
2735
  | VARIABLE
2666
2736
  | VIEW
2667
2737
  | YEAR
@@ -2763,6 +2833,7 @@ BOTH : [bB][oO][tT][hH] ;
2763
2833
  COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
2764
2834
  CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
2765
2835
  CROSS : [cC][rR][oO][sS][sS] ;
2836
+ CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
2766
2837
  DAY : [dD][aA][yY] ;
2767
2838
  DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
2768
2839
  DEFINE : [dD][eE][fF][iI][nN][eE] ;
@@ -2780,6 +2851,7 @@ EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
2780
2851
  EXTEND : [eE][xX][tT][eE][nN][dD] ;
2781
2852
  FIRST : [fF][iI][rR][sS][tT] ;
2782
2853
  FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
2854
+ FOLLOWING : [fF][oO][lL][lL][oO][wW][iI][nN][gG] ;
2783
2855
  FULL : [fF][uU][lL][lL] ;
2784
2856
  FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
2785
2857
  GROUP : [gG][rR][oO][uU][pP] ;
@@ -2812,10 +2884,13 @@ OUTER : [oO][uU][tT][eE][rR] ;
2812
2884
  // OVER : [oO][vV][eE][rR] ;
2813
2885
  PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
2814
2886
  PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
2887
+ PRECEDING: [pP][rR][eE][cC][eE][dD][iI][nN][gG] ;
2815
2888
  PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
2816
2889
  REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
2817
2890
  RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
2818
2891
  RIGHT : [rR][iI][gG][hH][tT] ;
2892
+ ROW : [rR][oO][wW] ;
2893
+ ROWS : [rR][oO][wW][sS] ;
2819
2894
  SECOND : [sS][eE][cC][oO][nN][dD] ;
2820
2895
  SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
2821
2896
  THEN : [tT][hH][eE][nN] ;
@@ -2823,6 +2898,7 @@ TRAILING : [tT][rR][aA][iI][lL][iI][nN][gG] ;
2823
2898
  TO : [tT][oO] ; // or make reserved? (is in SQL-92)
2824
2899
  TYPE : [tT][yY][pP][eE] ;
2825
2900
  UNION : [uU][nN][iI][oO][nN] ;
2901
+ UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
2826
2902
  USING : [uU][sS][iI][nN][gG] ;
2827
2903
  VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
2828
2904
  VIEW : [vV][iI][eE][wW] ;
package/lib/main.d.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  //
5
5
  // These types are improved step by step and use a lot any types at the moment.
6
6
 
7
+ // Author's note: All "options" interfaces should actually be types. However, due to
8
+ // https://github.com/TypeStrong/typedoc/issues/1519 we can't use
9
+ // intersection types at the moment.
10
+
7
11
  export = compiler;
8
12
 
9
13
  declare namespace compiler {
@@ -11,7 +15,7 @@ declare namespace compiler {
11
15
  /**
12
16
  * Options used by the core compiler and all backends.
13
17
  */
14
- export type Options = {
18
+ export interface Options {
15
19
  [option: string]: any,
16
20
 
17
21
  /**
@@ -57,6 +61,180 @@ declare namespace compiler {
57
61
  dictionaryPrototype?: any
58
62
  }
59
63
 
64
+ /**
65
+ * Options used by OData backends. Includes options for the OData
66
+ * transformer as well as for rendering EDM and EDMX.
67
+ */
68
+ export interface ODataOptions extends Options {
69
+ /**
70
+ * OData version for output files. Either 'v4' or 'v2'.
71
+ *
72
+ * @default 'v4'
73
+ */
74
+ odataVersion?: string | 'v4' | 'v2'
75
+ /**
76
+ * Whether to generate OData as flat or as structured.
77
+ * Structured is only supported for OData v4.
78
+ *
79
+ * @default 'flat'
80
+ */
81
+ odataFormat?: string | 'flat' | 'structured'
82
+ /**
83
+ * Naming mode used by the corresponding SQL.
84
+ *
85
+ * @default 'plain'
86
+ */
87
+ sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
88
+ /**
89
+ * If `true`, `cds.Compositions` are rendered as `edm:NavigationProperty` with the additional
90
+ * attribute `ContainsTarget="true"` and all contained entities (composition targets) have no
91
+ * `edm.EntitySet`.
92
+ *
93
+ * @note Only available for OData v4 EDM(X) rendering.
94
+ * @default false
95
+ */
96
+ odataContainment?: boolean
97
+ /**
98
+ * If `true`, render generated foreign keys for managed associations.
99
+ * By default foreign keys are never visible in structured OData APIs.
100
+ *
101
+ * @note Only available for structured OData v4 EDM(X) rendering.
102
+ * @default false
103
+ */
104
+ odataForeignKeys?: boolean
105
+ /**
106
+ * If `true`, association targets outside of the current service are added as
107
+ * `edm.EntityType` that only exposes their primary keys and have no `edm.EntitySet`.
108
+ * If the original association target is a service member, a corresponding `edm.Schema`
109
+ * representing the namespace of that service is added to `edm.Services`. All association
110
+ * targets that are no service members are collected in an `edm.Schema` with namespace `root`.
111
+ *
112
+ * @note Only valid for structured OData v4 EDM(X) rendering.
113
+ * @default false
114
+ * @since v2.1.0
115
+ */
116
+ odataProxies?: boolean
117
+ /**
118
+ * This option is an extension to `odataProxies`.
119
+ * If `true`, an `edm:Reference` instead of a proxy `edm.EntityType` is rendered for each
120
+ * association target that is a service member outside the current service instead of proxies.
121
+ *
122
+ * @note Only valid for structured OData v4 EDM(X) rendering.
123
+ * @default false
124
+ * @since v2.1.0
125
+ */
126
+ odataXServiceRefs?: boolean
127
+ /**
128
+ * The OData specification requires that all primary keys of the principal must be used as
129
+ * referential constraints. If an association is modelled with only a partial key, no
130
+ * referential constraints are added. If `true`, partial constraints are rendered for
131
+ * backwards compatibility and mocking scenarios. A spec violation warning is raised for
132
+ * each incomplete constraint.
133
+ *
134
+ * @note Only valid for OData v2 CSN transformation.
135
+ * @default false
136
+ * @since v2.2.6
137
+ */
138
+ odataV2PartialConstr?: boolean
139
+ /**
140
+ * Service name for which EDMX or EDM shall be rendered.
141
+ *
142
+ * @note Only available for `to.edmx()` and `to.edm()`. For `to.edmx.all()`
143
+ * and `to.edm.all()`, use `serviceNames` instead.
144
+ *
145
+ * @see serviceNames
146
+ */
147
+ service?: string
148
+ /**
149
+ * Array of service names for which EDMX or EDM shall be rendered.
150
+ * If unspecified, all services are rendered.
151
+ *
152
+ * @note Only available for `to.edmx.all()` and `to.edm.all()`. For `to.edmx()`
153
+ * and `to.edm()`, use `service` instead.
154
+ *
155
+ * @see service
156
+ */
157
+ serviceNames?: string[]
158
+ }
159
+
160
+ /**
161
+ * Options used by SQL `to.sql()` backend.
162
+ *
163
+ * @see to.sql()
164
+ */
165
+ export interface SqlOptions extends Options {
166
+ /**
167
+ * The SQL naming mode decides how names are represented.
168
+ * Among others, this includes whether identifiers are quoted or not (note
169
+ * that "smart quoting" is handled by `sqlDialect`).
170
+ *
171
+ * - `plain`:
172
+ * In this naming mode, dots are replaced by underscores.
173
+ * Names are neither upper-cased nor quoted, unless "smart-quoting" is used.
174
+ * This mode can be used with all SQL dialects.
175
+ * - `quoted`:
176
+ * In this mode, all identifiers are quoted. Dots are not replaced in table
177
+ * and view names but are still replaced by underscores in element names.
178
+ * This mode can only be used with SQL dialect `hana`.
179
+ * - `hdbcds`:
180
+ * This mode uses names that are compatible to SAP HANA CDS.
181
+ * In this mode, all identifiers are quoted. Dots are neither replaced in table
182
+ * nor element names. Namespace identifiers are separated from the remaining
183
+ * identifier by `::`, i.e. the dot is replaced. For example `Ns.Books`
184
+ * becomes `"Ns::Books"`.
185
+ * This mode can only be used with SQL dialect `hana`.
186
+ *
187
+ * @default 'plain'
188
+ */
189
+ sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
190
+ /**
191
+ * Use this option to specify what dialect of SQL you want.
192
+ *
193
+ * Different databases may support different feature sets of SQL.
194
+ * For example, timestamps are handled differently. Furthermore, "smart-quoting"
195
+ * is enabled for `sqlite` and `hana`. This is useful if identifiers
196
+ * collide with reserved keywords.
197
+ *
198
+ * - `plain`:
199
+ * Use this option for best compatibility with standard SQL.
200
+ * Note that "smart-quoting" is not available for this mode.
201
+ * Requires `sqlMapping: 'plain'`.
202
+ * - `sqlite`:
203
+ * This SQL dialect ensures compatibility with SQLite, which may not support
204
+ * all SQL features used in your CDS files. For example, `$at.from`/`$at.to` are
205
+ * handled differently to ensure correctness for SQLite. "smart-quoting"
206
+ * quotes identifiers that are reserved keywords, but does not upper-case them.
207
+ * Requires `sqlMapping: 'plain'`.
208
+ * - `hana`:
209
+ * Use this SQL dialect for best compatibility with SAP HANA.
210
+ * "smart-quoting" upper-cases and quotes identifiers.
211
+ *
212
+ * @default 'plain'
213
+ */
214
+ sqlDialect?: string | 'plain' | 'sqlite' | 'hana'
215
+ /**
216
+ * Object containing magic variables. These magic variables are
217
+ * used as placeholder values.
218
+ *
219
+ * @since 2.11.0
220
+ */
221
+ variableReplacements?: {
222
+ [option: string]: string | object,
223
+ /**
224
+ * Commonly used placeholders for user's name and locale.
225
+ */
226
+ $user?: {
227
+ [option: string]: string | object,
228
+ id?: string
229
+ locale?: string
230
+ },
231
+ /**
232
+ * Commonly used placeholders for session variables.
233
+ */
234
+ $session?: Record<string, string | object>
235
+ }
236
+ }
237
+
60
238
  /**
61
239
  * The compiler's package version.
62
240
  * For more details on versioning and SemVer, see `doc/Versioning.md`
@@ -83,6 +261,7 @@ declare namespace compiler {
83
261
  * {@link CompilationError} containing a vector of individual errors.
84
262
  *
85
263
  * @param filenames Array of files that should be compiled.
264
+ * @param dir Working directory. Relative paths in `filenames` will be resolved relatively to this directory.
86
265
  * @param options Compiler options. If you do not set `messages`, they will be printed to console.
87
266
  * @param fileCache A dictionary of absolute file names to the file content with values:
88
267
  * - false: the file does not exist
@@ -138,7 +317,7 @@ declare namespace compiler {
138
317
  * _Note_: Sorting is done in-place.
139
318
  *
140
319
  * Example of sorted messages:
141
- * ```txt
320
+ * ```
142
321
  * A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
143
322
  * A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
144
323
  * B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
@@ -188,10 +367,11 @@ declare namespace compiler {
188
367
  * form (i.e. one line)
189
368
  *
190
369
  * Example:
191
- * ```txt
370
+ * ```
192
371
  * <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
193
372
  * ```
194
373
  *
374
+ * @param msg Compiler message which shall be stringified.
195
375
  * @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
196
376
  * @param noMessageId If true, the message ID will _not_ be part of the string.
197
377
  * @param noHome If true, the semantic location will _not_ be part of the string.
@@ -201,26 +381,30 @@ declare namespace compiler {
201
381
  /**
202
382
  * Returns a message string with file- and semantic location if present
203
383
  * in multiline form.
204
- * The error (+ message id) will be colored according to their severity if
205
- * run on a TTY.
384
+ * The error (+ message id) can colored according to their severity.
206
385
  *
207
386
  * Example:
208
- * ```txt
209
- * Error[message-id]: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
387
+ * ```
388
+ * Error[message-id]: Can't find type `nu` in this scope
210
389
  * |
211
- * <source>.cds:3:11, at entity:“E”
390
+ * <source>.cds:3:11, at entity:“E”/element:“e
212
391
  * ```
213
392
  *
214
393
  * @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
215
394
  * @param config.noMessageId If true, no messages id (in brackets) will be shown.
216
395
  * @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
217
396
  * @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
397
+ * @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
398
+ * coloring will be used. If 'auto', we will decide based on certain factors such
399
+ * as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
400
+ * unset.
218
401
  */
219
402
  export function messageStringMultiline(msg: CompileMessage, config?: {
220
403
  normalizeFilename?: boolean
221
404
  noMessageId?: boolean
222
405
  hintExplanation?: boolean
223
406
  withLineSpacer?: boolean
407
+ color?: boolean | 'auto'
224
408
  }): string;
225
409
 
226
410
  /**
@@ -233,16 +417,24 @@ declare namespace compiler {
233
417
  * All lines are prepended by a pipe (`|`) and show the corresponding line number.
234
418
  *
235
419
  * Example Output:
236
- * ```txt
420
+ * ```
237
421
  * |
238
422
  * 13 | num * nu
239
423
  * | ^^
240
424
  * ```
241
425
  *
242
- * @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
243
- * @param msg Message whose location is used to print the message context.
426
+ * @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
427
+ * @param msg Message whose location is used to print the message context.
428
+ * @param config Configuration for the message context.
429
+ * @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
430
+ * coloring will be used. If 'auto', we will decide based on certain factors such
431
+ * as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
432
+ * unset.
433
+
244
434
  */
245
- export function messageContext(sourceLines: string[], msg: CompileMessage): string;
435
+ export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
436
+ color?: boolean | 'auto'
437
+ }): string;
246
438
 
247
439
  /**
248
440
  * Get an explanatory text for a complicated compiler message with ID
@@ -316,21 +508,26 @@ declare namespace compiler {
316
508
  * @alias for
317
509
  */
318
510
  export namespace For {
319
- function odata(): any;
511
+ /**
512
+ * Transform the given (generic) CSN into one that is used for OData.
513
+ * Changes include flattening, type resolution and more, according to
514
+ * the provided options.
515
+ */
516
+ function odata(csn: CSN, options: ODataOptions): any;
320
517
  }
321
518
 
322
519
  export namespace to {
323
520
  function cdl(csn: CSN, options: Options): object;
324
- function sql(csn: CSN, options: Options): any;
521
+ function sql(csn: CSN, options: SqlOptions): any;
325
522
 
326
- function edm(csn: CSN, options: Options): any;
523
+ function edm(csn: CSN, options: ODataOptions): any;
327
524
  namespace edm {
328
- function all(csn: CSN, options: Options): any;
525
+ function all(csn: CSN, options: ODataOptions): any;
329
526
  }
330
527
 
331
- function edmx(csn: CSN, options: Options): any;
528
+ function edmx(csn: CSN, options: ODataOptions): any;
332
529
  namespace edmx {
333
- function all(csn: CSN, options: Options): any;
530
+ function all(csn: CSN, options: ODataOptions): any;
334
531
  }
335
532
 
336
533
  function hdbcds(csn: CSN, options: Options): any;
package/lib/main.js CHANGED
@@ -21,6 +21,7 @@ const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, dedupli
21
21
 
22
22
  const parseLanguage = require('./language/antlrParser');
23
23
  const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
24
+ const { fns } = require('./compiler/shared');
24
25
  const { define } = require('./compiler/definer');
25
26
 
26
27
  // The compiler version (taken from package.json)
@@ -43,13 +44,14 @@ const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
43
44
  function parseCdl( cdl, filename, options = {} ) {
44
45
  options = Object.assign( {}, options, { parseCdl: true } );
45
46
  const sources = Object.create(null);
46
- const model = { sources, options };
47
+ const model = { sources, options, $functions: {}, $volatileFunctions: {} };
47
48
  const messageFunctions = createMessageFunctions( options, 'parse', model );
48
49
  model.$messageFunctions = messageFunctions;
49
50
 
50
51
  const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
51
52
  messageFunctions );
52
53
  sources[filename] = xsn;
54
+ fns( model );
53
55
  define( model );
54
56
  messageFunctions.throwWithError();
55
57
  return compactModel( model );