@sap/cds-compiler 2.4.4 → 2.10.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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. package/lib/utils/string.js +0 -17
package/lib/main.d.ts ADDED
@@ -0,0 +1,435 @@
1
+ // Official cds-compiler API.
2
+ // All functions and namespace documented here are available when
3
+ // @sap/cds-compiler is required.
4
+ //
5
+ // These types are improved step by step and use a lot any types at the moment.
6
+
7
+ export = compiler;
8
+
9
+ declare namespace compiler {
10
+
11
+ /**
12
+ * Options used by the core compiler and all backends.
13
+ */
14
+ export type Options = {
15
+ [option: string]: any,
16
+
17
+ /**
18
+ * Compiler and backend messages. Messages can be simple info messages but
19
+ * also warnings and errors. It is highly recommended to fix any warnings
20
+ * and to not ignore them.
21
+ * Errors stop the compilation process.
22
+ */
23
+ messages?: object[]
24
+ /**
25
+ * Dictionary of message-ids and their reclassified severity. This option
26
+ * can be used to increase the severity of messages. The compiler will
27
+ * ignore decreased severities as this may lead to issues during
28
+ * compilation otherwise.
29
+ */
30
+ severities?: { [messageId: string]: MessageSeverity}
31
+ /**
32
+ * Dictionary of beta flag names. This option allows fine-grained control
33
+ * over which beta features should be enabled.
34
+ * For a list of beta flag, please refer to `cdsc --help`.
35
+ *
36
+ * For backwards compatibility, this option may be `true` to indicate that
37
+ * all beta features should be enabled.
38
+ */
39
+ beta?: { [betaFlag: string]: boolean } | boolean
40
+ /**
41
+ * If true, internal consistency checks are enabled and recompilation in
42
+ * backends is disabled.
43
+ *
44
+ * @internal This is an internal option and should not be used by end-users.
45
+ */
46
+ testMode?: boolean
47
+ /**
48
+ * If true, CSN definitions are sorted by name. Implicitly enabled when testMode is true.
49
+ * `testMode` has higher priority, meaning if `testSortCsn` is `false` and `testMode` is true,
50
+ * definitions will still be sorted.
51
+ */
52
+ testSortCsn?: boolean
53
+ /**
54
+ * A JS prototype that will be used for dictionaries created by the compiler.
55
+ * Dictionaries are e.g. "definitions" and "elements".
56
+ */
57
+ dictionaryPrototype?: any
58
+ }
59
+
60
+ /**
61
+ * The compiler's package version.
62
+ * For more details on versioning and SemVer, see `doc/Versioning.md`
63
+ */
64
+ export function version(): string;
65
+
66
+ /**
67
+ * Main function: Compile the sources from the files given by the array of
68
+ * `filenames`. As usual with the `fs` library, relative file names are
69
+ * relative to the working directory `process.cwd()`. With argument `dir`, the
70
+ * file names are relative to `process.cwd()+dir`.
71
+ *
72
+ * This function returns a Promise and can be used with `await`. For an example
73
+ * see `examples/api-usage/`.
74
+ * See function {@link compileSync} or {@link compileSources} for alternative compile
75
+ * functions.
76
+ *
77
+ * The promise is fulfilled if all files could be read and processed without
78
+ * errors. The fulfillment value is a CSN model.
79
+ *
80
+ * If there are errors, the promise is rejected. If there was an invocation
81
+ * error (repeated filenames or if the file could not be read), the rejection
82
+ * value is an {@link InvocationError}. Otherwise, the rejection value is a
83
+ * {@link CompilationError} containing a vector of individual errors.
84
+ *
85
+ * @param filenames Array of files that should be compiled.
86
+ * @param options Compiler options. If you do not set `messages`, they will be printed to console.
87
+ * @param fileCache A dictionary of absolute file names to the file content with values:
88
+ * - false: the file does not exist
89
+ * - true: file exists (fstat), no further knowledge yet - i.e. value will change!
90
+ * - 'string' or Buffer: the file content
91
+ * - { realname: fs.realpath(filename) }: if filename is not canonicalized
92
+ */
93
+ export function compile(filenames: string[], dir?: string, options?: Options, fileCache?: Record<string, any>): Promise<any>;
94
+ export function compileSync(filenames: string[], dir?: string, options?: Options, fileCache?: Record<string, any>): any;
95
+ export function compileSources(sourcesDict: any, options?: Options): any;
96
+
97
+ /**
98
+ * In version 2 of cds-compiler, this is an identity function and
99
+ * is only kept for backwards compatibility.
100
+ *
101
+ * @deprecated
102
+ * @returns The input parameter "csn".
103
+ */
104
+ export function compactModel(csn: CSN): any;
105
+
106
+ export class CompilationError extends Error {
107
+ constructor(messages: any, model: any, text: any, ...args);
108
+ messages: any[];
109
+ toString(): string;
110
+ /**
111
+ * If `options.attachValidNames` is set, this non-enumerable property holds the CSN model.
112
+ * @internal
113
+ */
114
+ model?: CSN;
115
+ /**
116
+ * Used by `cdsc` to indicate whether the message was already printed to stderr.
117
+ * @private
118
+ */
119
+ hasBeenReported: boolean;
120
+ }
121
+
122
+ /**
123
+ * Sort the given messages according to their location. Messages are sorted
124
+ * in ascending order according to their:
125
+ *
126
+ * - file name
127
+ * - start line
128
+ * - start column
129
+ * - end line
130
+ * - end column
131
+ * - semantic location (“home”)
132
+ * - message text
133
+ *
134
+ * If both messages do not have a location, they are sorted by their semantic
135
+ * location and then by their message text. If only one message has a file
136
+ * location, that message is sorted prior to those that don't have one.
137
+ *
138
+ * _Note_: Sorting is done in-place.
139
+ *
140
+ * Example of sorted messages:
141
+ * ```txt
142
+ * A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
143
+ * A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
144
+ * B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
145
+ * B.cds:3:12: Warning id-4: Message text (in entity:“B”/element:“d”)
146
+ * B.cds:3:12: Error id-4: Message text (in entity:“B”/element:“e”)
147
+ * ```
148
+ *
149
+ * If you also want to sort according to message's severity,
150
+ * see {@link sortMessagesSeverityAware}.
151
+ *
152
+ * @returns The same messages array as the input parameter.
153
+ */
154
+ export function sortMessages(messages: CompileMessage[]): CompileMessage[];
155
+
156
+ /**
157
+ * Sort the given messages in severity aware order. Messages are sorted first
158
+ * by severity where 'Error' comes first, then 'Warning' and so forth.
159
+ * Messages of the same severity are sorted the same as by {@link sortMessages}.
160
+ *
161
+ * _Note_: Sorting is done in-place.
162
+ *
163
+ * @returns The same messages array as the input parameter.
164
+ */
165
+ export function sortMessagesSeverityAware(messages: CompileMessage[]): CompileMessage[];
166
+
167
+ /**
168
+ * Removes duplicate messages from the given messages array without destroying
169
+ * references to the array, i.e. removes them in-place.
170
+ *
171
+ * _Note_: Does NOT keep the original order!
172
+ *
173
+ * Two messages are the same if they have the same message hash (see below).
174
+ * If one of the two is more precise, then it replaces the other.
175
+ * A message is more precise if it is contained in the other or if
176
+ * the first does not have an `endLine`/`endCol`.
177
+ *
178
+ * A “message hash” is the string representation of the message. If the
179
+ * message does not have a semantic location (“home”), the message hash
180
+ * is the result of {@link messageString}. If the message has a semantic
181
+ * location, the file location is stripped before being passed to
182
+ * {@link messageString}.
183
+ */
184
+ export function deduplicateMessages(messages: CompileMessage[]): void;
185
+
186
+ /**
187
+ * Returns a message string with file- and semantic location if present in compact
188
+ * form (i.e. one line)
189
+ *
190
+ * Example:
191
+ * ```txt
192
+ * <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
193
+ * ```
194
+ *
195
+ * @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
196
+ * @param noMessageId If true, the message ID will _not_ be part of the string.
197
+ * @param noHome If true, the semantic location will _not_ be part of the string.
198
+ */
199
+ export function messageString(msg: CompileMessage, normalizeFilename?: boolean, noMessageId?: boolean, noHome?: boolean): string;
200
+
201
+ /**
202
+ * Returns a message string with file- and semantic location if present
203
+ * in multiline form.
204
+ * The error (+ message id) will be colored according to their severity if
205
+ * run on a TTY.
206
+ *
207
+ * Example:
208
+ * ```txt
209
+ * Error[message-id]: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
210
+ * |
211
+ * <source>.cds:3:11, at entity:“E”
212
+ * ```
213
+ *
214
+ * @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
215
+ * @param config.noMessageId If true, no messages id (in brackets) will be shown.
216
+ * @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
217
+ * @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
218
+ */
219
+ export function messageStringMultiline(msg: CompileMessage, config?: {
220
+ normalizeFilename?: boolean
221
+ noMessageId?: boolean
222
+ hintExplanation?: boolean
223
+ withLineSpacer?: boolean
224
+ }): string;
225
+
226
+ /**
227
+ * Returns a context (code) string for the given message that is human readable.
228
+ *
229
+ * The message context can be used to indicate to users where an error occurred.
230
+ * The line length is limited to 100 characters. If the message spans more than three
231
+ * lines, only the first three lines are printed and an ellipsis will be appended in the next line.
232
+ * If only one line is to be shown, the affected columns will be highlighted by a caret (`^`).
233
+ * All lines are prepended by a pipe (`|`) and show the corresponding line number.
234
+ *
235
+ * Example Output:
236
+ * ```txt
237
+ * |
238
+ * 13 | num * nu
239
+ * | ^^
240
+ * ```
241
+ *
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.
244
+ */
245
+ export function messageContext(sourceLines: string[], msg: CompileMessage): string;
246
+
247
+ /**
248
+ * Get an explanatory text for a complicated compiler message with ID
249
+ * messageId. This function does a file lookup in `share/messages`.
250
+ * If the message explanation does not exist, an exception is thrown.
251
+ *
252
+ * @throws May throw an ENOENT error if the message explanation cannot be found.
253
+ * @see {@link hasMessageExplanation}
254
+ */
255
+ export function explainMessage(messageId: string): string;
256
+ /**
257
+ * Returns `true` if the given messageId has an explanatory text.
258
+ * Contrary to {@link explainMessage}, this function does not make
259
+ * a file lookup.
260
+ */
261
+ export function hasMessageExplanation(messageId: string): boolean;
262
+
263
+ export class InvocationError extends Error {
264
+ constructor(errs: any, ...args);
265
+ errors: any[]
266
+ }
267
+
268
+ /**
269
+ * Returns true if at least one of the given messages is of severity "Error"
270
+ */
271
+ export function hasErrors(messages: CompileMessage[]): boolean;
272
+
273
+ export function preparedCsnToEdm(csn: CSN, service: string, options: any): any;
274
+ export function preparedCsnToEdmx(csn: CSN, service: string, options: any): any;
275
+
276
+ export namespace parse {
277
+ /**
278
+ * Parse the given CDL in parseCdl mode and return its corresponding CSN representation.
279
+ *
280
+ * @param cdl CDL source as string.
281
+ * @param filename Filename to be used in compiler messages.
282
+ * @param options Compiler options. Note that if `options.messages` is not set, messages will be printed to stderr.
283
+ */
284
+ function cdl(cdl: string, filename: string, options?: Options): any;
285
+
286
+ /**
287
+ * Parse the given CQL and return its corresponding CSN representation.
288
+ *
289
+ * @param cdl CDL source as string.
290
+ * @param filename Filename to be used in compiler messages, default is '<query>.cds'
291
+ * @param options Compiler options. Note that if `options.messages` is not set, messages will be printed to stderr.
292
+ */
293
+ function cql(cdl: string, filename?: string, options?: Options): any;
294
+
295
+ /**
296
+ * Parse the given CDL expression and return its corresponding CSN representation.
297
+ *
298
+ * @param cdl CDL source as string.
299
+ * @param filename Filename to be used in compiler messages, default is '<expr>.cds'
300
+ * @param options Compiler options. Note that if `options.messages` is not set, messages will be printed to stderr.
301
+ */
302
+ function expr(cdl: string, filename?: string, options?: Options): any;
303
+ }
304
+
305
+ /**
306
+ * @deprecated Use {@link parse.cql} instead.
307
+ */
308
+ export function parseToCqn(cdl: string, filename?: string, options?: Options): any;
309
+ /**
310
+ * @deprecated Use {@link parse.expr} instead.
311
+ */
312
+ export function parseToExpr(cdl: string, filename?: string, options?: Options): any;
313
+
314
+ /**
315
+ * @todo Actual name is "for" which isn't used in the doc as it is a reserved name.
316
+ * @alias for
317
+ */
318
+ export namespace For {
319
+ function odata(): any;
320
+ }
321
+
322
+ export namespace to {
323
+ function cdl(csn: CSN, options: Options): object;
324
+ function sql(csn: CSN, options: Options): any;
325
+
326
+ function edm(csn: CSN, options: Options): any;
327
+ namespace edm {
328
+ function all(csn: CSN, options: Options): any;
329
+ }
330
+
331
+ function edmx(csn: CSN, options: Options): any;
332
+ namespace edmx {
333
+ function all(csn: CSN, options: Options): any;
334
+ }
335
+
336
+ function hdbcds(csn: CSN, options: Options): any;
337
+ function hdi(csn: CSN, options: Options): any;
338
+ namespace hdi {
339
+ function migration(csn: CSN, options: Options, beforeImage: any): any;
340
+ }
341
+ }
342
+
343
+ export function getArtifactCdsPersistenceName(artifactName: string, namingConvention: any, csn: CSN): any;
344
+ export function getElementCdsPersistenceName(elemName: string, namingConvention: any): any;
345
+
346
+ /**
347
+ * @private
348
+ */
349
+ export namespace $lsp {
350
+ function compile(filenames: string[], dir?: string, options?: Options, fileCache?: Record<string, any>): Promise<any>;
351
+ function parse(source: string, filename?: string, options?: Options): any;
352
+ }
353
+
354
+ /**
355
+ * CSN object. Not yet specified in this TypeScript declaration file.
356
+ */
357
+ export type CSN = any;
358
+
359
+ export class CompileMessage {
360
+ constructor(location: Location, msg: string, severity?: MessageSeverity, id?: string | null, home?: string | null, moduleName?: string | null);
361
+
362
+ /**
363
+ * Optional ID of the message. Can be used to reclassify messages.
364
+ *
365
+ * @note This property is non-enumerable as message IDs are not finalized, yet.
366
+ */
367
+ messageId?: string
368
+
369
+ severity: MessageSeverity
370
+
371
+ /**
372
+ * @deprecated Use `$location` instead.
373
+ */
374
+ location: Location
375
+
376
+ /**
377
+ * Location information like file and line/column of the message.
378
+ */
379
+ $location: Location & {
380
+ address?: {
381
+ /**
382
+ * Fully qualified name of the affected definition.
383
+ */
384
+ definition?: string
385
+ }
386
+ }
387
+ /**
388
+ * String representation of the message. May be a multi-line message in the future.
389
+ */
390
+ message: string
391
+
392
+ /**
393
+ * A string describing the path to the artifact, e.g. `entity:"E"/element:"x"`.
394
+ */
395
+ home?: string
396
+ /**
397
+ * Array of names that are valid at the specified position.
398
+ * Contains values if the message describes an "artifact not found" message.
399
+ *
400
+ * @internal Only to be used by the LSP implementation for CDS.
401
+ */
402
+ validNames: string[] | null
403
+ /**
404
+ * If `internalMsg` is set, then this property will have an error object with a stack trace.
405
+ */
406
+ error?: Error
407
+
408
+ /**
409
+ * Returns a human readable string of the compiler message. Uses {@link messageString} to render
410
+ * the message without filename normalization and without a message ID.
411
+ */
412
+ toString(): string;
413
+ }
414
+
415
+ /**
416
+ * Severities a compiler message can have.
417
+ */
418
+ export type MessageSeverity = 'Error' | 'Warning' | 'Info' | 'Debug';
419
+
420
+ /**
421
+ * CSN Location, often exposed by `$location` in CSN.
422
+ * Columns and lines are 1-based, i.e. value `0` is an invalid value and
423
+ * indicates absence of the property.
424
+ *
425
+ * All properties are optional, even `file`.
426
+ */
427
+ export type Location = {
428
+ file?: string
429
+ line?: number
430
+ col?: number
431
+ endLine?: number
432
+ endCol?: number
433
+ }
434
+
435
+ }
package/lib/main.js CHANGED
@@ -16,10 +16,12 @@
16
16
  const backends = require('./backends');
17
17
  const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
18
18
  const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
19
- const { sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
19
+ const { traverseCsn } = require('./model/api');
20
+ const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
20
21
 
21
22
  const parseLanguage = require('./language/antlrParser');
22
23
  const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
24
+ const { define } = require('./compiler/definer');
23
25
 
24
26
  // The compiler version (taken from package.json)
25
27
  function version() {
@@ -31,7 +33,6 @@ const {
31
33
  messageString,
32
34
  messageStringMultiline,
33
35
  messageContext,
34
- handleMessages,
35
36
  hasErrors,
36
37
  explainMessage,
37
38
  hasMessageExplanation
@@ -39,19 +40,39 @@ const {
39
40
 
40
41
  const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
41
42
 
43
+ function parseCdl( cdl, filename, options = {} ) {
44
+ options = Object.assign( {}, options, { parseCdl: true } );
45
+ const sources = Object.create(null);
46
+ const model = { sources, options };
47
+ const messageFunctions = createMessageFunctions( options, 'parse', model );
48
+ model.$messageFunctions = messageFunctions;
49
+
50
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
51
+ messageFunctions );
52
+ sources[filename] = xsn;
53
+ define( model );
54
+ messageFunctions.throwWithError();
55
+ return compactModel( model );
56
+ }
57
+
42
58
  function parseCql( cdl, filename = '<query>.cds', options = {} ) {
43
- let xsn = parseLanguage( cdl, filename, Object.assign( {parseOnly:true}, options ), 'query' );
44
- handleMessages( xsn, options );
59
+ const messageFunctions = createMessageFunctions( options, 'parse' );
60
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
61
+ messageFunctions, 'query' );
62
+ messageFunctions.throwWithError();
45
63
  return compactQuery( xsn );
46
64
  }
47
65
 
48
66
  function parseExpr( cdl, filename = '<expr>.cds', options = {} ) {
49
- let xsn = parseLanguage( cdl, filename, Object.assign( {parseOnly:true}, options ), 'expr' );
50
- handleMessages( xsn, options );
67
+ const messageFunctions = createMessageFunctions( options, 'parse' );
68
+ const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
69
+ messageFunctions, 'expr' );
70
+ messageFunctions.throwWithError();
51
71
  return compactExpr( xsn );
52
72
  }
53
73
 
54
74
  // FIXME: The implementation of those functions that delegate to 'backends' should probably move here
75
+ // ATTENTION: Keep in sync with main.d.ts!
55
76
  module.exports = {
56
77
  // Compiler
57
78
  version,
@@ -78,7 +99,7 @@ module.exports = {
78
99
  preparedCsnToEdm : (csn, service, options) => { return backends.preparedCsnToEdm(csn, service, options).edmj},
79
100
 
80
101
  // additional API:
81
- parse: { cql: parseCql, expr: parseExpr }, // preferred names
102
+ parse: { cdl: parseCdl, cql: parseCql, expr: parseExpr }, // preferred names
82
103
  /**
83
104
  * @deprecated Use parse.cql instead
84
105
  */
@@ -91,6 +112,9 @@ module.exports = {
91
112
  getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
92
113
  getElementCdsPersistenceName: getElementDatabaseNameOf,
93
114
 
115
+ // Other API functions:
116
+ traverseCsn,
117
+
94
118
  // INTERNAL functions for the cds-lsp package and friends - before you use
95
119
  // it, you MUST talk with us - there can be potential incompatibilities with
96
120
  // new releases (even having the same major version):
@@ -0,0 +1,78 @@
1
+ // Miscellaneous CSN functions we put into our compiler API
2
+
3
+ // Do not change at will - they are in the compiler API!
4
+
5
+ /**
6
+ * Dictionary of default traversal functions for function `traverseCsn`.
7
+ * It maps CSN property names to functions which are used by default
8
+ * to traverse the CSN node which is the value of the corresponding property.
9
+ * Users specify their own traversal function via argument `userFunctions`.
10
+ *
11
+ * Each function in `userFunctions` and `defaultFunctions` is called with:
12
+ * - `userFunctions`
13
+ * - the current CSN node, i.e. ‹parent node›.‹property name›
14
+ * - the the ‹parent node›
15
+ * - the ‹property name› (might be useful if the same function is used for several props)
16
+ */
17
+ const defaultFunctions = {
18
+ '@': () => {}, // do not traverse annotation assignments
19
+ args: dictionary,
20
+ elements: dictionary,
21
+ enum: dictionary,
22
+ params: dictionary,
23
+ actions: dictionary,
24
+ mixin: dictionary,
25
+ definitions: dictionary,
26
+ '$': () => {}, // do not traverse properties starting with '$'
27
+ }
28
+
29
+ /**
30
+ * Traverse the CSN node `csn`.
31
+ * If `csn` is an array, call it recursively on each array item.
32
+ * If `csn` is an(other) object, call a function on each property:
33
+ * - The property name is a used as key in argument `userFunctions` and the
34
+ * constant `defaultFunctions` above to get the function which is called on
35
+ * the property value, see `defaultFunctions` for details.
36
+ * - If no function is found with the property name, try to find one with the first char.
37
+ * - If still not found, call `traverseCsn` recursively.
38
+ *
39
+ * The functions in `userFunctions` are usually transformer functions, which
40
+ * change the input CSN destructively.
41
+ */
42
+ function traverseCsn( userFunctions, csn ) {
43
+ if (!csn || typeof csn !== 'object')
44
+ return;
45
+ if (Array.isArray( csn )) {
46
+ csn.forEach( node => traverseCsn( userFunctions, node ) );
47
+ }
48
+ else {
49
+ for (const prop of Object.keys( csn )) {
50
+ const func = userFunctions[prop] || defaultFunctions[prop] ||
51
+ userFunctions[prop.charAt(0)] || defaultFunctions[prop.charAt(0)] ||
52
+ traverseCsn;
53
+ func( userFunctions, csn[prop], csn, prop );
54
+ }
55
+ }
56
+ }
57
+ // people might want to have their own traversal function for `elements`, etc:
58
+ traverseCsn.dictionary = dictionary;
59
+
60
+ /**
61
+ * Traverse the CSN dictionary node `csn`.
62
+ * Call `traverseCsn` on each property value in `csn`, passing down `userFunctions`.
63
+ */
64
+ function dictionary( userFunctions, csn ) {
65
+ if (!csn || typeof csn !== 'object')
66
+ return;
67
+ if (Array.isArray( csn )) { // args can be both array and dictionary
68
+ csn.forEach( node => traverseCsn( userFunctions, node ) );
69
+ }
70
+ else {
71
+ for (const name of Object.keys( csn ))
72
+ traverseCsn( userFunctions, csn[name] );
73
+ }
74
+ }
75
+
76
+ module.exports = {
77
+ traverseCsn,
78
+ };