@sap/cds-compiler 2.10.4 → 2.12.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 (103) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +58 -35
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +16 -0
  9. package/lib/api/.eslintrc.json +2 -0
  10. package/lib/api/main.js +10 -36
  11. package/lib/api/options.js +17 -8
  12. package/lib/api/validate.js +30 -3
  13. package/lib/backends.js +12 -13
  14. package/lib/base/dictionaries.js +2 -1
  15. package/lib/base/keywords.js +3 -2
  16. package/lib/base/message-registry.js +64 -11
  17. package/lib/base/messages.js +38 -18
  18. package/lib/base/model.js +6 -4
  19. package/lib/base/optionProcessorHelper.js +148 -86
  20. package/lib/checks/.eslintrc.json +2 -0
  21. package/lib/checks/actionsFunctions.js +2 -1
  22. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  23. package/lib/checks/foreignKeys.js +4 -4
  24. package/lib/checks/managedInType.js +4 -4
  25. package/lib/checks/queryNoDbArtifacts.js +1 -3
  26. package/lib/checks/selectItems.js +4 -0
  27. package/lib/checks/sql-snippets.js +93 -0
  28. package/lib/checks/unknownMagic.js +6 -3
  29. package/lib/checks/validator.js +8 -0
  30. package/lib/compiler/assert-consistency.js +14 -5
  31. package/lib/compiler/base.js +64 -0
  32. package/lib/compiler/builtins.js +62 -16
  33. package/lib/compiler/checks.js +34 -10
  34. package/lib/compiler/definer.js +91 -112
  35. package/lib/compiler/index.js +30 -30
  36. package/lib/compiler/propagator.js +8 -4
  37. package/lib/compiler/resolver.js +279 -63
  38. package/lib/compiler/shared.js +65 -230
  39. package/lib/compiler/utils.js +191 -0
  40. package/lib/edm/annotations/genericTranslation.js +35 -18
  41. package/lib/edm/annotations/preprocessAnnotations.js +1 -1
  42. package/lib/edm/csn2edm.js +4 -3
  43. package/lib/edm/edm.js +8 -8
  44. package/lib/edm/edmPreprocessor.js +61 -59
  45. package/lib/edm/edmUtils.js +14 -15
  46. package/lib/gen/Dictionary.json +82 -40
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +19 -1
  49. package/lib/gen/language.tokens +80 -73
  50. package/lib/gen/languageLexer.interp +27 -1
  51. package/lib/gen/languageLexer.js +925 -826
  52. package/lib/gen/languageLexer.tokens +72 -65
  53. package/lib/gen/languageParser.js +4817 -4102
  54. package/lib/json/from-csn.js +57 -26
  55. package/lib/json/to-csn.js +244 -51
  56. package/lib/language/antlrParser.js +12 -1
  57. package/lib/language/docCommentParser.js +1 -1
  58. package/lib/language/errorStrategy.js +26 -8
  59. package/lib/language/genericAntlrParser.js +106 -30
  60. package/lib/language/language.g4 +200 -70
  61. package/lib/language/multiLineStringParser.js +536 -0
  62. package/lib/main.d.ts +220 -21
  63. package/lib/main.js +6 -3
  64. package/lib/model/api.js +2 -2
  65. package/lib/model/csnRefs.js +218 -86
  66. package/lib/model/csnUtils.js +99 -178
  67. package/lib/model/enrichCsn.js +84 -43
  68. package/lib/model/revealInternalProperties.js +25 -8
  69. package/lib/model/sortViews.js +8 -1
  70. package/lib/modelCompare/compare.js +2 -1
  71. package/lib/optionProcessor.js +33 -18
  72. package/lib/render/.eslintrc.json +1 -2
  73. package/lib/render/DuplicateChecker.js +2 -2
  74. package/lib/render/manageConstraints.js +1 -1
  75. package/lib/render/toCdl.js +202 -82
  76. package/lib/render/toHdbcds.js +194 -135
  77. package/lib/render/toRename.js +7 -10
  78. package/lib/render/toSql.js +91 -51
  79. package/lib/render/utils/common.js +24 -5
  80. package/lib/render/utils/sql.js +6 -4
  81. package/lib/transform/braceExpression.js +4 -2
  82. package/lib/transform/db/applyTransformations.js +189 -0
  83. package/lib/transform/db/associations.js +389 -0
  84. package/lib/transform/db/cdsPersistence.js +150 -0
  85. package/lib/transform/db/constraints.js +275 -119
  86. package/lib/transform/db/draft.js +6 -4
  87. package/lib/transform/db/expansion.js +10 -9
  88. package/lib/transform/db/flattening.js +23 -8
  89. package/lib/transform/db/temporal.js +236 -0
  90. package/lib/transform/db/transformExists.js +106 -25
  91. package/lib/transform/db/views.js +485 -0
  92. package/lib/transform/forHanaNew.js +90 -1036
  93. package/lib/transform/forOdataNew.js +11 -3
  94. package/lib/transform/localized.js +5 -14
  95. package/lib/transform/odata/generateForeignKeyElements.js +2 -2
  96. package/lib/transform/transformUtilsNew.js +34 -20
  97. package/lib/transform/translateAssocsToJoins.js +15 -23
  98. package/lib/transform/universalCsnEnricher.js +217 -47
  99. package/lib/utils/file.js +13 -6
  100. package/lib/utils/term.js +65 -42
  101. package/lib/utils/timetrace.js +55 -27
  102. package/package.json +1 -1
  103. package/lib/transform/db/helpers.js +0 -58
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
@@ -312,25 +504,32 @@ declare namespace compiler {
312
504
  export function parseToExpr(cdl: string, filename?: string, options?: Options): any;
313
505
 
314
506
  /**
315
- * @todo Actual name is "for" which isn't used in the doc as it is a reserved name.
316
- * @alias for
507
+ * @note Actual name is "for" which can't be used directly in the documentation as it is a reserved name.
508
+ * @see for
317
509
  */
318
- export namespace For {
319
- function odata(): any;
510
+ export namespace _for {
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
 
519
+ export { _for as for };
520
+
322
521
  export namespace to {
323
522
  function cdl(csn: CSN, options: Options): object;
324
- function sql(csn: CSN, options: Options): any;
523
+ function sql(csn: CSN, options: SqlOptions): any;
325
524
 
326
- function edm(csn: CSN, options: Options): any;
525
+ function edm(csn: CSN, options: ODataOptions): any;
327
526
  namespace edm {
328
- function all(csn: CSN, options: Options): any;
527
+ function all(csn: CSN, options: ODataOptions): any;
329
528
  }
330
529
 
331
- function edmx(csn: CSN, options: Options): any;
530
+ function edmx(csn: CSN, options: ODataOptions): any;
332
531
  namespace edmx {
333
- function all(csn: CSN, options: Options): any;
532
+ function all(csn: CSN, options: ODataOptions): any;
334
533
  }
335
534
 
336
535
  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,15 @@ 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
+ /** @type {XSN.Model} */
48
+ const model = { sources, options, $functions: {}, $volatileFunctions: {} };
47
49
  const messageFunctions = createMessageFunctions( options, 'parse', model );
48
50
  model.$messageFunctions = messageFunctions;
49
51
 
50
52
  const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
51
53
  messageFunctions );
52
54
  sources[filename] = xsn;
55
+ fns( model );
53
56
  define( model );
54
57
  messageFunctions.throwWithError();
55
58
  return compactModel( model );
@@ -77,8 +80,8 @@ module.exports = {
77
80
  // Compiler
78
81
  version,
79
82
  compile: (...args) => compileX(...args).then( compactModel ), // main function
80
- compileSync: (...args) => compactModel( compileSyncX(...args) ), // main function
81
- compileSources: (...args) => compactModel( compileSourcesX(...args) ), // main function
83
+ compileSync: (filenames, dir, options, fileCache) => compactModel( compileSyncX(filenames, dir, options, fileCache) ), // main function
84
+ compileSources: (sourcesDict, options) => compactModel( compileSourcesX(sourcesDict, options) ), // main function
82
85
  compactModel: csn => csn, // for easy v2 migration
83
86
  CompilationError,
84
87
  sortMessages,
package/lib/model/api.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * - the ‹property name› (might be useful if the same function is used for several props)
16
16
  */
17
17
  const defaultFunctions = {
18
- '@': () => {}, // do not traverse annotation assignments
18
+ '@': () => { /* do not traverse annotation assignments */ },
19
19
  args: dictionary,
20
20
  elements: dictionary,
21
21
  enum: dictionary,
@@ -23,7 +23,7 @@ const defaultFunctions = {
23
23
  actions: dictionary,
24
24
  mixin: dictionary,
25
25
  definitions: dictionary,
26
- '$': () => {}, // do not traverse properties starting with '$'
26
+ '$': () => { /* do not traverse properties starting with '$' */},
27
27
  }
28
28
 
29
29
  /**