@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.
- package/CHANGELOG.md +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/compiler/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
|
|
440
|
+
throwWithError();
|
|
384
441
|
if (options.lintMode)
|
|
385
442
|
return model;
|
|
386
443
|
|
|
387
444
|
check(model);
|
|
388
|
-
|
|
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('./
|
|
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'
|
|
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
|
|