@sap/cds-compiler 2.5.2 → 2.11.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 (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
@@ -1,5 +1,17 @@
1
1
  // Main XSN-based compiler functions
2
2
 
3
+ // ...
4
+
5
+ // How functions are shared across the Core Compiler sub modules:
6
+
7
+ // - Shared XSN-related functions which do not use a context are in utils.js,
8
+ // they are `require`d as usual at the beginning of sub modules.
9
+ // - The XSN is the only context which context-dependent functions can depend on.
10
+ // - Sharing such a function is by adding it to `‹xsn›.$functions`,
11
+ // e.g. `resolvePath` and similar will be attached to the XSN.
12
+ // - Functions which might be overwritten in a next sub module
13
+ // are added to `‹xsn›.$volatileFunctions`, currently just `environment`.
14
+
3
15
  'use strict';
4
16
 
5
17
  const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
@@ -8,6 +20,7 @@ const parseCsn = require('../json/from-csn');
8
20
 
9
21
  const assertConsistency = require('./assert-consistency');
10
22
  const moduleLayers = require('./moduleLayers');
23
+ const { fns } = require('./shared');
11
24
  const { define } = require('./definer');
12
25
  const resolve = require('./resolver');
13
26
  const propagator = require('./propagator');
@@ -15,7 +28,7 @@ const check = require('./checks');
15
28
 
16
29
 
17
30
  const { emptyWeakLocation } = require('../base/location');
18
- const { handleMessages, makeMessageFunction } = require('../base/messages');
31
+ const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
19
32
  const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
20
33
  const { cdsFs } = require('../utils/file');
21
34
 
@@ -50,25 +63,27 @@ class ArgumentError extends Error {
50
63
  * @param {string} source Source code of the file.
51
64
  * @param {string} filename Filename including its extension, e.g. "file.cds"
52
65
  * @param {object} options Compile options
66
+ * @param {object} messageFunctions If not provided, parse errors will not lead to an exception
53
67
  */
54
- function parseX( source, filename, options = {} ) {
68
+ function parseX( source, filename, options = {}, messageFunctions ) {
69
+ if (!messageFunctions)
70
+ messageFunctions = createMessageFunctions( options, 'parse' );
55
71
  const ext = path.extname( filename ).toLowerCase();
56
72
  if ([ '.json', '.csn' ].includes(ext) || options.fallbackParser === 'csn!')
57
- return parseCsn.parse( source, filename, options );
73
+ return parseCsn.parse( source, filename, options, messageFunctions );
58
74
  if ([ '.cds', '.hdbcds', '.hdbdd', '.cdl' ].includes(ext))
59
- return parseLanguage( source, filename, options );
75
+ return parseLanguage( source, filename, options, messageFunctions );
60
76
  if (options.fallbackParser === 'csn')
61
- return parseCsn.parse( source, filename, options );
77
+ return parseCsn.parse( source, filename, options, messageFunctions );
62
78
  if (options.fallbackParser) // any other value: like 'cdl' (historic reasons)
63
- return parseLanguage( source, filename, options );
79
+ return parseLanguage( source, filename, options, messageFunctions );
64
80
 
65
81
  const model = { location: { file: filename } };
66
- const { error } = makeMessageFunction( model, options, 'compile' );
67
- error( 'file-unknown-ext', emptyWeakLocation(filename),
68
- { file: ext && ext.slice(1), '#': !ext && 'none' }, {
69
- std: 'Unknown file extension $(FILE)',
70
- none: 'No file extension',
71
- } );
82
+ messageFunctions.error( 'file-unknown-ext', emptyWeakLocation(filename),
83
+ { file: ext && ext.slice(1), '#': !ext && 'none' }, {
84
+ std: 'Unknown file extension $(FILE)',
85
+ none: 'No file extension',
86
+ } );
72
87
  return model;
73
88
  }
74
89
 
@@ -108,9 +123,9 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
108
123
  // fileCache = Object.assign( Object.create(null), fileCache );
109
124
  dir = path.resolve(dir);
110
125
  const a = processFilenames( filenames, dir );
111
- a.fileContentDict = Object.create(null);
112
126
 
113
127
  const model = { sources: a.sources, options };
128
+ model.$messageFunctions = createMessageFunctions( options, 'compile', model );
114
129
  let all = promiseAllDoNotRejectImmediately( a.files.map(readAndParse) );
115
130
 
116
131
  all = all
@@ -146,8 +161,7 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
146
161
  }
147
162
  else {
148
163
  try {
149
- a.fileContentDict[filename] = source;
150
- const ast = parseX( source, rel, options );
164
+ const ast = parseX( source, rel, options, model.$messageFunctions );
151
165
  a.sources[filename] = ast;
152
166
  ast.location = { file: rel };
153
167
  ast.dirname = path.dirname( filename );
@@ -187,8 +201,10 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
187
201
  }
188
202
  // create promises after all usingFroms have been collected, as the
189
203
  // Promise executor is called immediately with `new`:
190
- for (const module in dependencies)
191
- promises.push( resolveModule( dependencies[module], fileCache, options ) );
204
+ for (const module in dependencies) {
205
+ promises.push( resolveModule( dependencies[module], fileCache, options,
206
+ model.$messageFunctions ) );
207
+ }
192
208
  }
193
209
  if (!promises.length)
194
210
  return [];
@@ -215,9 +231,9 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
215
231
  // absolute file names - they start with `/` or `\` or similar
216
232
  dir = path.resolve(dir);
217
233
  const a = processFilenames( filenames, dir );
218
- a.fileContentDict = Object.create(null);
219
234
 
220
235
  const model = { sources: a.sources, options };
236
+ model.$messageFunctions = createMessageFunctions( options, 'compile', model );
221
237
 
222
238
  let asts = [];
223
239
  const errors = [];
@@ -275,8 +291,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
275
291
  }
276
292
  else {
277
293
  try {
278
- a.fileContentDict[filename] = source;
279
- const ast = parseX( source, rel, options );
294
+ const ast = parseX( source, rel, options, model.$messageFunctions );
280
295
  a.sources[filename] = ast;
281
296
  ast.location = { file: rel };
282
297
  ast.dirname = path.dirname( filename );
@@ -307,8 +322,10 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
307
322
  }
308
323
  // create promises after all usingFroms have been collected, as the
309
324
  // Promise executor is called immediately with `new`:
310
- for (const module in dependencies)
311
- fileNames.push( resolveModuleSync( dependencies[module], fileCache, options ) );
325
+ for (const module in dependencies) {
326
+ fileNames.push( resolveModuleSync( dependencies[module], fileCache, options,
327
+ model.$messageFunctions ) );
328
+ }
312
329
  }
313
330
  if (!fileNames.length)
314
331
  return [];
@@ -341,11 +358,12 @@ function compileSourcesX( sourcesDict, options = {} ) {
341
358
  sourcesDict = { '<stdin>.cds': sourcesDict };
342
359
  const sources = Object.create(null);
343
360
  const model = { sources, options };
361
+ model.$messageFunctions = createMessageFunctions( options, 'compile', model );
344
362
 
345
363
  for (const filename in sourcesDict) {
346
364
  const source = sourcesDict[filename];
347
365
  if (typeof source === 'string') {
348
- const ast = parseX( source, filename, options );
366
+ const ast = parseX( source, filename, options, model.$messageFunctions );
349
367
  sources[filename] = ast;
350
368
  ast.location = { file: filename };
351
369
  assertConsistency( ast, options );
@@ -359,6 +377,38 @@ function compileSourcesX( sourcesDict, options = {} ) {
359
377
  return compileDoX( model );
360
378
  }
361
379
 
380
+ /**
381
+ * Recompile the given CSN
382
+ *
383
+ * @param {object} csn Input CSN to recompile to XSN
384
+ * @param {object} options Options
385
+ * @returns {object} XSN
386
+ *
387
+ * TODO: probaby issue message api-recompiled-csn there.
388
+ */
389
+ function recompileX( csn, options ) {
390
+ options = { ...options, parseCdl: false, $recompile: true };
391
+ // TODO: $recompile: true should be enough
392
+ // Explicitly set parseCdl to false because backends cannot handle it
393
+ // Explicitly delete all toCsn options:
394
+ delete options.toCsn;
395
+
396
+ const file = csn.$location && csn.$location.file &&
397
+ csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
398
+
399
+ const sources = Object.create(null);
400
+ const model = { sources, options };
401
+ model.$messageFunctions = createMessageFunctions( options, 'compile', model );
402
+ // TODO: or use module which invokes the recompilation?
403
+
404
+ sources[file] = parseCsn.augment( csn, file, options, model.$messageFunctions );
405
+ moduleLayers.setLayers( sources );
406
+ const compiled = compileDoX( model ); // calls throwWithError()
407
+ if (options.messages) // does not help with exception in compileDoX()
408
+ deduplicateMessages(options.messages); // TODO: do better
409
+ return compiled;
410
+ }
411
+
362
412
  /**
363
413
  * On the given model (AST like CSN) run the definer, resolver as well as semantic checks.
364
414
  * Creates an augmented CSN (XSN) and returns it.
@@ -368,24 +418,31 @@ function compileSourcesX( sourcesDict, options = {} ) {
368
418
  */
369
419
  function compileDoX( model ) {
370
420
  const { options } = model;
421
+ const { throwWithError } = model.$messageFunctions;
371
422
  if (!options.testMode)
372
423
  model.meta = {}; // provide initial central meta object
373
- if (options.parseOnly)
374
- return handleMessages( model, options );
375
-
424
+ if (options.parseOnly) {
425
+ throwWithError();
426
+ return model;
427
+ }
428
+ model.$functions = {};
429
+ model.$volatileFunctions = {};
430
+ fns( model ); // attach (mostly) paths functions
376
431
  define( model );
377
432
  // do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
378
- if (options.parseCdl)
379
- return handleMessages( model, options );
380
-
433
+ // TODO: do not use this function for parseCdl anyway…
434
+ if (options.parseCdl) {
435
+ throwWithError();
436
+ return model;
437
+ }
381
438
  resolve( model );
382
439
  assertConsistency( model );
383
- handleMessages( model, options ); // stop compilation with errors
440
+ throwWithError();
384
441
  if (options.lintMode)
385
442
  return model;
386
443
 
387
444
  check(model);
388
- handleMessages( model, options );
445
+ throwWithError();
389
446
  return propagator.propagate( model );
390
447
  }
391
448
 
@@ -433,5 +490,6 @@ module.exports = {
433
490
  compileX,
434
491
  compileSyncX,
435
492
  compileSourcesX,
493
+ recompileX,
436
494
  InvocationError, // TODO: make it no error if same file name is provided twice
437
495
  };
@@ -8,7 +8,7 @@ const {
8
8
  setProp,
9
9
  isDeprecatedEnabled,
10
10
  } = require( '../base/model');
11
- const { linkToOrigin, withAssociation } = require('./shared');
11
+ const { linkToOrigin, withAssociation } = require('./utils');
12
12
  // const { refString } = require( '../base/messages')
13
13
 
14
14
  function propagate( model ) {
@@ -179,6 +179,8 @@ function propagate( model ) {
179
179
  // accessed at their type being a main artifact
180
180
  function expensive( prop, target, source ) {
181
181
  // console.log(prop,source.name,'->',target.kind,target.name);
182
+ if (source.kind === 'builtin')
183
+ return;
182
184
  if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
183
185
  // foreignKeys must always be copied with target to avoid any confusion
184
186
  // whether we have to generated implicit keys
@@ -211,7 +213,8 @@ function propagate( model ) {
211
213
  }
212
214
 
213
215
  function onlyViaParent( prop, target, source ) {
214
- if (target.$inferred === 'proxy') // assocs and enums do not have 'include'
216
+ if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
217
+ // assocs and enums do not have 'include'
215
218
  always( prop, target, source );
216
219
  }
217
220