@sap/cds-compiler 4.0.2 → 4.1.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.
- package/CHANGELOG.md +100 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +31 -11
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +5 -4
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Base classes used as prototypes for XSN definitions, elements, etc.
|
|
2
|
+
// The goal is to have named classes that can be seen in performance analyses, e.g.
|
|
3
|
+
// by using the [DeOpt Explorer][1].
|
|
4
|
+
// All classes should also be constructible using `{ __proto__: Class, …}`, i.e.
|
|
5
|
+
// their constructors must not do anything besides assigning properties.
|
|
6
|
+
//
|
|
7
|
+
// Refer to these resources:
|
|
8
|
+
// - <https://mathiasbynens.be/notes/shapes-ics>
|
|
9
|
+
// - <https://v8.dev/blog/fast-properties>
|
|
10
|
+
//
|
|
11
|
+
// Before adding new properties, evaluate whether it has any performance
|
|
12
|
+
// impact. Too many properties that are rarely used could reduce performance,
|
|
13
|
+
// but too few could lead to inconsistent object shapes for commonly
|
|
14
|
+
// used properties.
|
|
15
|
+
//
|
|
16
|
+
// Use [DeOpt Explorer][1] to see the different object Maps by v8.
|
|
17
|
+
//
|
|
18
|
+
// [1]: https://devblogs.microsoft.com/typescript/introducing-deopt-explorer/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
class XsnSource {
|
|
23
|
+
kind = 'source';
|
|
24
|
+
location;
|
|
25
|
+
usings = [];
|
|
26
|
+
dependencies = [];
|
|
27
|
+
artifacts = Object.create(null);
|
|
28
|
+
vocabularies = Object.create(null);
|
|
29
|
+
extensions = [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class XsnArtifact {
|
|
33
|
+
location;
|
|
34
|
+
name;
|
|
35
|
+
kind;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class XsnName {
|
|
39
|
+
location;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class CsnLocation {
|
|
43
|
+
file;
|
|
44
|
+
line;
|
|
45
|
+
col;
|
|
46
|
+
endLine;
|
|
47
|
+
endCol;
|
|
48
|
+
constructor(file, line, col, endLine, endCol) {
|
|
49
|
+
this.file = file;
|
|
50
|
+
this.line = line;
|
|
51
|
+
this.col = col;
|
|
52
|
+
this.endLine = endLine;
|
|
53
|
+
this.endCol = endCol;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
XsnSource,
|
|
59
|
+
XsnArtifact,
|
|
60
|
+
XsnName,
|
|
61
|
+
CsnLocation,
|
|
62
|
+
};
|
|
@@ -64,14 +64,14 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
64
64
|
// console.log('PUSH: ', v.kind,v.name)
|
|
65
65
|
}
|
|
66
66
|
if (!v._deps) // builtins, otherwise forgotten (TODO: assert in --test-mode)
|
|
67
|
-
setProp(v, '_deps', []);
|
|
67
|
+
setProp( v, '_deps', [] );
|
|
68
68
|
// assert( v._scc.onStack );
|
|
69
69
|
|
|
70
70
|
// Now consider successors of v (called w):
|
|
71
71
|
while (v._scc.depIndex < v._deps.length) {
|
|
72
72
|
const w = v._deps[v._scc.depIndex++].art;
|
|
73
73
|
if (!w._scc) { // node has not yet been visited
|
|
74
|
-
setProp(w, '_sccCaller', v);
|
|
74
|
+
setProp( w, '_sccCaller', v );
|
|
75
75
|
// console.log('CALL: ', v._scc.depIndex )
|
|
76
76
|
return w; // recursive call with w in recursive algorithm
|
|
77
77
|
}
|
|
@@ -112,7 +112,7 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
112
112
|
function defaultCb( w, v, r ) {
|
|
113
113
|
for (const dep of w._deps) {
|
|
114
114
|
if (dep.art._scc.lowlink === w._scc.lowlink) // in same SCC
|
|
115
|
-
reportCycle( w, dep.art, dep.location );
|
|
115
|
+
reportCycle( w, dep.art, dep.location, dep.semanticLoc );
|
|
116
116
|
}
|
|
117
117
|
return r;
|
|
118
118
|
}
|
package/lib/compiler/define.js
CHANGED
|
@@ -142,6 +142,7 @@ const { compareLayer } = require('./moduleLayers');
|
|
|
142
142
|
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
143
143
|
|
|
144
144
|
const $location = Symbol.for('cds.$location');
|
|
145
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
145
146
|
|
|
146
147
|
/**
|
|
147
148
|
* Export function of this file. Transform argument `sources` = dictionary of
|
|
@@ -172,6 +173,7 @@ function define( model ) {
|
|
|
172
173
|
initArtifact,
|
|
173
174
|
initMembers,
|
|
174
175
|
checkDefinitions, // TODO: remove
|
|
176
|
+
initSelectItems,
|
|
175
177
|
} );
|
|
176
178
|
|
|
177
179
|
let boundSelfParamType = true; // special `$self` for binding param must still be initialised
|
|
@@ -252,7 +254,7 @@ function define( model ) {
|
|
|
252
254
|
}
|
|
253
255
|
else if (src.definitions) { // CSN input
|
|
254
256
|
prefix = '';
|
|
255
|
-
dictForEach( shuffleDict( src.definitions ),
|
|
257
|
+
dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ));
|
|
256
258
|
}
|
|
257
259
|
if (src.vocabularies) {
|
|
258
260
|
if (!model.vocabularies)
|
|
@@ -264,7 +266,11 @@ function define( model ) {
|
|
|
264
266
|
}
|
|
265
267
|
}
|
|
266
268
|
|
|
267
|
-
function addDefinition( art, block ) {
|
|
269
|
+
function addDefinition( art, block, prefix ) {
|
|
270
|
+
if (!art.name.absolute) {
|
|
271
|
+
// TODO: art.name.absolute = art.name.id || …
|
|
272
|
+
art.name.absolute = (!art.name.path) ? art.name.id : prefix + pathName(art.name.path);
|
|
273
|
+
}
|
|
268
274
|
const { absolute } = art.name;
|
|
269
275
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
270
276
|
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
@@ -373,8 +379,7 @@ function define( model ) {
|
|
|
373
379
|
function addArtifact( art, block, prefix ) {
|
|
374
380
|
if (art.kind === 'using')
|
|
375
381
|
return;
|
|
376
|
-
art
|
|
377
|
-
addDefinition( art, block );
|
|
382
|
+
addDefinition( art, block, prefix );
|
|
378
383
|
if (art.artifacts) {
|
|
379
384
|
const p = `${ art.name.absolute }.`;
|
|
380
385
|
// path prefixes (usings) must be added before extensions in artifacts:
|
|
@@ -444,8 +449,10 @@ function define( model ) {
|
|
|
444
449
|
function addVocabulary( vocab, block, prefix ) {
|
|
445
450
|
setLink( vocab, '_block', block );
|
|
446
451
|
const { name } = vocab;
|
|
447
|
-
if (!name.absolute)
|
|
448
|
-
name.absolute =
|
|
452
|
+
if (!name.absolute) {
|
|
453
|
+
// TODO: art.name.absolute = vocab.name.id || …
|
|
454
|
+
vocab.name.absolute = (!vocab.name.path) ? vocab.name.id : prefix + pathName(vocab.name.path);
|
|
455
|
+
}
|
|
449
456
|
dictAdd( model.vocabularies, name.absolute, vocab );
|
|
450
457
|
}
|
|
451
458
|
|
|
@@ -574,7 +581,7 @@ function define( model ) {
|
|
|
574
581
|
if (!art.query)
|
|
575
582
|
return;
|
|
576
583
|
art.$queries = [];
|
|
577
|
-
setLink( art, '_from', [] ); // for sequence of resolve steps
|
|
584
|
+
setLink( art, '_from', [] ); // for sequence of resolve steps - TODO: remove
|
|
578
585
|
if (!setLink( art, '_leadingQuery', initQueryExpression( art.query, art ) ) )
|
|
579
586
|
return; // null or undefined in case of parse error
|
|
580
587
|
// if (art._leadingQuery !== art.$queries [0]) throw Error('FOO');
|
|
@@ -880,9 +887,9 @@ function define( model ) {
|
|
|
880
887
|
}
|
|
881
888
|
}
|
|
882
889
|
|
|
883
|
-
function initSelectItems( parent, columns, user ) {
|
|
884
|
-
|
|
885
|
-
|
|
890
|
+
function initSelectItems( parent, columns, user, inExtension = false ) {
|
|
891
|
+
let wildcard = !!inExtension; // no `extend … with columns { * }`
|
|
892
|
+
// TODO: forbid expand/inline in ref-where, outside queries (CSN), ...
|
|
886
893
|
let hasItems = false;
|
|
887
894
|
for (const col of columns || parent.expand || parent.inline || []) {
|
|
888
895
|
if (!col) // parse error
|
|
@@ -898,6 +905,11 @@ function define( model ) {
|
|
|
898
905
|
if (!wildcard) {
|
|
899
906
|
wildcard = col;
|
|
900
907
|
}
|
|
908
|
+
else if (wildcard === true) { // in `extend … with columns {…}`
|
|
909
|
+
error( 'ext-unexpected-wildcard', [ col.location, parent ], { code: '*' },
|
|
910
|
+
'Unexpected $(CODE) (wildcard) in an extension' );
|
|
911
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
912
|
+
}
|
|
901
913
|
else {
|
|
902
914
|
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
903
915
|
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
@@ -917,7 +929,8 @@ function define( model ) {
|
|
|
917
929
|
}
|
|
918
930
|
// Either expression (value), expand or new association (target && type)
|
|
919
931
|
else if (col.value || col.expand || (col.target && col.type)) {
|
|
920
|
-
|
|
932
|
+
if (!col._block)
|
|
933
|
+
setLink( col, '_block', parent._block );
|
|
921
934
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
922
935
|
if (col.doc) {
|
|
923
936
|
warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
|
|
@@ -932,23 +945,18 @@ function define( model ) {
|
|
|
932
945
|
}
|
|
933
946
|
}
|
|
934
947
|
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
935
|
-
if (columns)
|
|
948
|
+
if (columns && !inExtension) // not (yet) in `extend … with columns {…}`
|
|
936
949
|
initExprForQuery( col.value, parent );
|
|
937
|
-
initSelectItems( col, null, user );
|
|
950
|
+
initSelectItems( col, null, user ); // TODO: use col as user (i.e. remove param)
|
|
938
951
|
}
|
|
939
952
|
}
|
|
940
953
|
|
|
941
|
-
if (hasItems && !wildcard && parent.excludingDict) {
|
|
942
|
-
// TODO:
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
if (block.$frontend === 'cdl') {
|
|
948
|
-
warning('query-ignoring-exclude', [ parent.excludingDict[$location], user ],
|
|
949
|
-
{ prop: '*' },
|
|
950
|
-
'Excluding elements without wildcard $(PROP) has no effect');
|
|
951
|
-
}
|
|
954
|
+
if (hasItems && !wildcard && parent.excludingDict && !options.$recompile) {
|
|
955
|
+
// TODO: the SQL backend should probably delete `excluding` when expanding `*`
|
|
956
|
+
// TODO: use `parent` for semantic location, use `-excluding`
|
|
957
|
+
warning( 'query-ignoring-exclude', [ parent.excludingDict[$location], user ],
|
|
958
|
+
{ prop: '*' },
|
|
959
|
+
'Excluding elements without wildcard $(PROP) has no effect');
|
|
952
960
|
}
|
|
953
961
|
}
|
|
954
962
|
|
|
@@ -1016,13 +1024,13 @@ function define( model ) {
|
|
|
1016
1024
|
const { targetAspect } = obj;
|
|
1017
1025
|
if (targetAspect) {
|
|
1018
1026
|
if (obj.foreignKeys) {
|
|
1019
|
-
error( 'unexpected-keys
|
|
1027
|
+
error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], construct ],
|
|
1020
1028
|
{},
|
|
1021
1029
|
'A managed aspect composition can\'t have a foreign keys specification' );
|
|
1022
1030
|
delete obj.foreignKeys; // continuation semantics: not specified
|
|
1023
1031
|
}
|
|
1024
1032
|
if (obj.on && !obj.target) {
|
|
1025
|
-
error( 'unexpected-on-
|
|
1033
|
+
error( 'type-unexpected-on-condition', [ obj.on.location, construct ],
|
|
1026
1034
|
{},
|
|
1027
1035
|
'A managed aspect composition can\'t have a specified ON-condition' );
|
|
1028
1036
|
delete obj.on; // continuation semantics: not specified
|
|
@@ -1056,30 +1064,33 @@ function define( model ) {
|
|
|
1056
1064
|
|
|
1057
1065
|
function initElementsAsEnum() {
|
|
1058
1066
|
// in extensions, extended enums are represented as elements
|
|
1059
|
-
let
|
|
1067
|
+
let hasElement = false;
|
|
1060
1068
|
for (const n in obj.elements) {
|
|
1061
1069
|
const e = obj.elements[n];
|
|
1062
|
-
if (e.kind === '
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1070
|
+
if (e.kind === 'extend')
|
|
1071
|
+
continue;
|
|
1072
|
+
const noVal = e.value?.val === undefined && e.value?.sym === undefined;
|
|
1073
|
+
// TODO: forbid #symbol as enum value
|
|
1074
|
+
if (e.$syntax === 'element' || // `extend … with elements` or `extend with { element … }`
|
|
1075
|
+
noVal && e.$syntax !== 'enum' || // no value in CDL input
|
|
1076
|
+
e.virtual || e.key || e.masked || e.type || e.elements || e.items || e.stored) {
|
|
1077
|
+
// We do not want to complain separately about all element properties:
|
|
1078
|
+
error( 'ext-unexpected-element', [ e.location, construct ],
|
|
1079
|
+
{ name: e.name.id, code: 'extend … with enum' },
|
|
1080
|
+
// eslint-disable-next-line max-len
|
|
1081
|
+
'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
|
|
1082
|
+
// Don't emit 'ext-expecting-enum' if this error is emitted.
|
|
1083
|
+
return;
|
|
1076
1084
|
}
|
|
1085
|
+
e.kind = 'enum';
|
|
1086
|
+
if (noVal || e.$syntax !== 'enum')
|
|
1087
|
+
hasElement = true; // warning with CDL input or `name: {}` in CSN input
|
|
1077
1088
|
}
|
|
1078
|
-
if (
|
|
1079
|
-
//
|
|
1080
|
-
//
|
|
1081
|
-
// and
|
|
1082
|
-
warning( 'ext-expecting-enum', [
|
|
1089
|
+
if (hasElement) {
|
|
1090
|
+
// This message is similar to the one above. In v5/6, we could probably
|
|
1091
|
+
// turn this warning into an error, remove `$syntax: 'element' (also in
|
|
1092
|
+
// language.g4), and use the above `ext-unexpected-element` only for CSN input.
|
|
1093
|
+
warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
|
|
1083
1094
|
{ code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
|
|
1084
1095
|
}
|
|
1085
1096
|
forEachGeneric( { enum: obj.elements }, 'enum', init );
|
|
@@ -1231,7 +1242,7 @@ function define( model ) {
|
|
|
1231
1242
|
if (!feature) {
|
|
1232
1243
|
// Note: This error can't be triggered at the moment. But as we likely want to
|
|
1233
1244
|
// allow extensions with params in the future, we keep the code.
|
|
1234
|
-
error( 'unexpected-params', [ location, construct ], {},
|
|
1245
|
+
error( 'def-unexpected-params', [ location, construct ], {},
|
|
1235
1246
|
'Parameters only exist for entities, actions or functions' );
|
|
1236
1247
|
}
|
|
1237
1248
|
else {
|
|
@@ -1252,7 +1263,7 @@ function define( model ) {
|
|
|
1252
1263
|
}
|
|
1253
1264
|
}
|
|
1254
1265
|
else if (prop === 'elements') {
|
|
1255
|
-
error( 'unexpected-elements', [ location, construct ], {},
|
|
1266
|
+
error( 'def-unexpected-elements', [ location, construct ], {},
|
|
1256
1267
|
'Elements only exist in entities, types or typed constructs' );
|
|
1257
1268
|
}
|
|
1258
1269
|
else if (prop === 'columns') {
|
|
@@ -1270,14 +1281,16 @@ function define( model ) {
|
|
|
1270
1281
|
function targetIsTargetAspect( elem ) {
|
|
1271
1282
|
const { target } = elem;
|
|
1272
1283
|
if (target.elements) {
|
|
1273
|
-
// TODO: error if CSN has both target.elements and targetAspect.elements
|
|
1284
|
+
// TODO: error if CSN has both target.elements and targetAspect.elements
|
|
1285
|
+
// -> delete target
|
|
1274
1286
|
return true;
|
|
1275
1287
|
}
|
|
1276
1288
|
if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
|
|
1277
1289
|
return false;
|
|
1278
1290
|
const name = resolveUncheckedPath( target, 'target', elem );
|
|
1279
1291
|
const aspect = name && model.definitions[name];
|
|
1280
|
-
return
|
|
1292
|
+
return (aspect?.kind === 'aspect' || aspect?.kind === 'type') && // type is sloppy
|
|
1293
|
+
aspect.elements && !aspect.elements[$inferred];
|
|
1281
1294
|
}
|
|
1282
1295
|
}
|
|
1283
1296
|
|
package/lib/compiler/extend.js
CHANGED
|
@@ -23,10 +23,12 @@ const {
|
|
|
23
23
|
} = require('./utils');
|
|
24
24
|
const layers = require('./moduleLayers');
|
|
25
25
|
const { CompilerAssertion } = require('../base/error');
|
|
26
|
+
const { CsnLocation } = require('./classes');
|
|
26
27
|
|
|
27
28
|
const $location = Symbol.for('cds.$location');
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// attach stupid location - TODO: remove in v5
|
|
31
|
+
const genLocation = new CsnLocation( '' );
|
|
30
32
|
|
|
31
33
|
// Array.prototype.spread = 42; // prototype-polluted JS classes
|
|
32
34
|
|
|
@@ -41,6 +43,7 @@ function extend( model ) {
|
|
|
41
43
|
resolveTypeArgumentsUnchecked,
|
|
42
44
|
attachAndEmitValidNames,
|
|
43
45
|
initMembers,
|
|
46
|
+
initSelectItems,
|
|
44
47
|
} = model.$functions;
|
|
45
48
|
|
|
46
49
|
Object.assign( model.$functions, {
|
|
@@ -383,12 +386,16 @@ function extend( model ) {
|
|
|
383
386
|
const { query } = art;
|
|
384
387
|
for (const col of ext.columns)
|
|
385
388
|
col.$extended = true;
|
|
386
|
-
|
|
389
|
+
|
|
390
|
+
if (!query?.from?.path) {
|
|
387
391
|
error( 'extend-columns', [ ext.columns[$location], ext ], { art } );
|
|
388
|
-
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!query.columns)
|
|
389
395
|
query.columns = [ { location: query.from.location, val: '*' }, ...ext.columns ];
|
|
390
396
|
else
|
|
391
397
|
query.columns.push( ...ext.columns );
|
|
398
|
+
initSelectItems( query, ext.columns, query, true );
|
|
392
399
|
}
|
|
393
400
|
else if ([ 'length', 'precision', 'scale', 'srid' ].includes( prop )) {
|
|
394
401
|
const typeExts = art.$typeExts || (art.$typeExts = {});
|
|
@@ -719,6 +726,11 @@ function extend( model ) {
|
|
|
719
726
|
warning( 'anno-unexpected-params', [ location, ext._parent ], {},
|
|
720
727
|
'Parameters only exist for actions or functions' );
|
|
721
728
|
break;
|
|
729
|
+
case 'actions':
|
|
730
|
+
// TODO: check if artifact can have actions, similar to `anno-unexpected-actions`
|
|
731
|
+
notFound( 'anno-undefined-action', ext.name.location, ext,
|
|
732
|
+
{ '#': 'action', art: parent, name } );
|
|
733
|
+
break;
|
|
722
734
|
default:
|
|
723
735
|
if (model.options.testMode)
|
|
724
736
|
throw new CompilerAssertion(`Missing case for prop: ${ prop }`);
|
|
@@ -887,8 +899,15 @@ function extend( model ) {
|
|
|
887
899
|
model._entities.push( art ); // add structure with includes in dep order
|
|
888
900
|
art.$entity = ++model.$entity;
|
|
889
901
|
}
|
|
890
|
-
if (
|
|
891
|
-
|
|
902
|
+
if (art.includes) {
|
|
903
|
+
if (!noIncludes) {
|
|
904
|
+
applyIncludes( art, art );
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
for (const ref of art.includes)
|
|
908
|
+
resolvePath( ref, 'include', art );
|
|
909
|
+
}
|
|
910
|
+
}
|
|
892
911
|
// checkExtensionsKind( extensions, art );
|
|
893
912
|
extendMembers( extensions, art, noIncludes === 'gen' );
|
|
894
913
|
if (!noIncludes && art.includes) {
|
|
@@ -1055,7 +1074,6 @@ function extend( model ) {
|
|
|
1055
1074
|
function canApplyIncludes( art, target, justResolveCyclic ) {
|
|
1056
1075
|
if (!art.includes)
|
|
1057
1076
|
return true;
|
|
1058
|
-
const isView = !!target.query;
|
|
1059
1077
|
for (const ref of art.includes) {
|
|
1060
1078
|
const name = resolveUncheckedPath( ref, 'include', art );
|
|
1061
1079
|
// console.log('CAI:',justResolveCyclic, name, ref.path, Object.keys(extensionsDict))
|
|
@@ -1071,7 +1089,6 @@ function extend( model ) {
|
|
|
1071
1089
|
else if (ref._artifact) {
|
|
1072
1090
|
delete ref._artifact;
|
|
1073
1091
|
}
|
|
1074
|
-
resolvePath( ref, isView ? 'viewInclude' : 'include', art );
|
|
1075
1092
|
}
|
|
1076
1093
|
return true;
|
|
1077
1094
|
}
|
|
@@ -1096,20 +1113,18 @@ function extend( model ) {
|
|
|
1096
1113
|
return;
|
|
1097
1114
|
}
|
|
1098
1115
|
|
|
1099
|
-
if (!art.query)
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
if (template)
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
art._ancestors.push( template );
|
|
1109
|
-
}
|
|
1116
|
+
if (!art._ancestors && !art.query)
|
|
1117
|
+
setLink( art, '_ancestors', [] ); // recursive array of includes
|
|
1118
|
+
for (const ref of ext.includes) {
|
|
1119
|
+
const template = resolvePath( ref, 'include', art );
|
|
1120
|
+
// !template -> non-includable, e.g. scalar type, or cyclic
|
|
1121
|
+
if (template && !art.query) {
|
|
1122
|
+
if (template._ancestors)
|
|
1123
|
+
art._ancestors.push( ...template._ancestors );
|
|
1124
|
+
art._ancestors.push( template );
|
|
1110
1125
|
}
|
|
1111
1126
|
}
|
|
1112
|
-
if (!art.query)
|
|
1127
|
+
if (!art.query && art.elements) // do not set art.elements and art.enums with query entity!
|
|
1113
1128
|
includeMembers( ext, art, 'elements' );
|
|
1114
1129
|
includeMembers( ext, art, 'actions' );
|
|
1115
1130
|
}
|
|
@@ -1128,12 +1143,15 @@ function extend( model ) {
|
|
|
1128
1143
|
// Warning 'Overwrites definition from include "I" (at elem def)
|
|
1129
1144
|
const parent = ext === art && art;
|
|
1130
1145
|
const members = ext[prop];
|
|
1131
|
-
|
|
1146
|
+
if (members || art[prop])
|
|
1147
|
+
ext[prop] = Object.create(null);
|
|
1132
1148
|
let hasNewElement = false;
|
|
1133
1149
|
|
|
1134
1150
|
for (const ref of ext.includes) {
|
|
1135
1151
|
const template = ref._artifact; // already resolved
|
|
1136
1152
|
if (template) { // be robust
|
|
1153
|
+
if (template[prop] && !ext[prop])
|
|
1154
|
+
ext[prop] = Object.create(null);
|
|
1137
1155
|
// eslint-disable-next-line no-loop-func
|
|
1138
1156
|
forEachInOrder( template, prop, ( origin, name ) => {
|
|
1139
1157
|
if (members && members[name]) {
|
|
@@ -101,7 +101,8 @@ function finalizeParseCdl( model ) {
|
|
|
101
101
|
// Recursively go through all XSN properties. There are a few that need to be
|
|
102
102
|
// handled specifically. Refer to the code below this loop for details.
|
|
103
103
|
for (const prop in artifact) {
|
|
104
|
-
if (
|
|
104
|
+
if (artifact[prop] === undefined || parseCdlSpeciallyHandledXsnProps.includes(prop) ||
|
|
105
|
+
parseCdlIgnoredXsnProps.includes(prop))
|
|
105
106
|
continue;
|
|
106
107
|
|
|
107
108
|
if (artifact[prop] && Object.getPrototypeOf(artifact[prop]) === null)
|
package/lib/compiler/generate.js
CHANGED
|
@@ -214,12 +214,6 @@ function generate( model ) {
|
|
|
214
214
|
else if (isLocalized) {
|
|
215
215
|
textElems.push( elem );
|
|
216
216
|
}
|
|
217
|
-
|
|
218
|
-
if (isKey && isLocalized) { // key with localized is wrong - ignore localized
|
|
219
|
-
const errpos = elem.localized || elem.type || elem.name;
|
|
220
|
-
warning( 'def-ignoring-localized-key', [ errpos.location, elem ], { keyword: 'localized' },
|
|
221
|
-
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
222
|
-
}
|
|
223
217
|
}
|
|
224
218
|
if (textElems.length <= keys)
|
|
225
219
|
return false;
|
|
@@ -386,7 +380,6 @@ function generate( model ) {
|
|
|
386
380
|
|
|
387
381
|
if (!fioriEnabled) {
|
|
388
382
|
// To be compatible, we switch off draft without @fiori.draft.enabled
|
|
389
|
-
// TODO (next major version): remove?
|
|
390
383
|
setAnnotation( art, '@odata.draft.enabled', art.location, false );
|
|
391
384
|
}
|
|
392
385
|
else {
|
|
@@ -446,7 +439,6 @@ function generate( model ) {
|
|
|
446
439
|
if (!fioriEnabled) {
|
|
447
440
|
locale.key = { val: true, location };
|
|
448
441
|
// To be compatible, we switch off draft without @fiori.draft.enabled
|
|
449
|
-
// TODO (next major version): remove?
|
|
450
442
|
setAnnotation( art, '@odata.draft.enabled', art.location, false );
|
|
451
443
|
}
|
|
452
444
|
else {
|
package/lib/compiler/index.js
CHANGED
|
@@ -38,6 +38,7 @@ const { cdsFs } = require('../utils/file');
|
|
|
38
38
|
|
|
39
39
|
const fs = require('fs');
|
|
40
40
|
const path = require('path');
|
|
41
|
+
const { CsnLocation, XsnSource } = require('./classes');
|
|
41
42
|
|
|
42
43
|
const extensionParsers = {
|
|
43
44
|
csn: parseCsn.parse,
|
|
@@ -91,7 +92,8 @@ function parseX( source, filename, options = {}, messageFunctions = null ) {
|
|
|
91
92
|
if (parser)
|
|
92
93
|
return parser( source, filename, options, messageFunctions );
|
|
93
94
|
|
|
94
|
-
const model =
|
|
95
|
+
const model = new XsnSource();
|
|
96
|
+
model.location = new CsnLocation( filename );
|
|
95
97
|
messageFunctions.error( 'file-unknown-ext', emptyWeakLocation(filename),
|
|
96
98
|
{ file: ext, '#': !ext && 'none' }, {
|
|
97
99
|
std: 'Unknown file extension $(FILE)',
|
|
@@ -171,12 +173,12 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
171
173
|
return []; // no further dependency processing
|
|
172
174
|
// no parallel readAndParse with same resolved filename should read the file,
|
|
173
175
|
// also ensure deterministic sequence in sources:
|
|
174
|
-
sources[filename] = { location:
|
|
176
|
+
sources[filename] = { location: new CsnLocation( rel ) };
|
|
175
177
|
|
|
176
178
|
const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
|
|
177
179
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
178
180
|
sources[filename] = ast;
|
|
179
|
-
ast.location =
|
|
181
|
+
ast.location = new CsnLocation( rel );
|
|
180
182
|
ast.dirname = path.dirname( filename );
|
|
181
183
|
assertConsistency( ast, options );
|
|
182
184
|
|
|
@@ -290,7 +292,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
290
292
|
}
|
|
291
293
|
// no parallel readAndParse with same resolved filename should read the file,
|
|
292
294
|
// also ensure deterministic sequence in a.sources:
|
|
293
|
-
a.sources[filename] = { location:
|
|
295
|
+
a.sources[filename] = { location: new CsnLocation( rel ) };
|
|
294
296
|
|
|
295
297
|
cdsFs( fileCache, options.traceFs ).readFileSync( filename, 'utf8', (err, source) => {
|
|
296
298
|
if (err) {
|
|
@@ -300,7 +302,7 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
300
302
|
try {
|
|
301
303
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
302
304
|
a.sources[filename] = ast;
|
|
303
|
-
ast.location =
|
|
305
|
+
ast.location = new CsnLocation( rel );
|
|
304
306
|
ast.dirname = path.dirname( filename );
|
|
305
307
|
assertConsistency( ast, options );
|
|
306
308
|
cb( null, ast );
|
|
@@ -373,7 +375,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
373
375
|
if (typeof source === 'string') {
|
|
374
376
|
const ast = parseX( source, filename, options, model.$messageFunctions );
|
|
375
377
|
sources[filename] = ast;
|
|
376
|
-
ast.location =
|
|
378
|
+
ast.location = new CsnLocation( filename );
|
|
377
379
|
assertConsistency( ast, options );
|
|
378
380
|
}
|
|
379
381
|
else if (options.$xsnObjects) { // source is a XSN object with option $xsnObjects
|
|
@@ -382,7 +384,7 @@ function compileSourcesX( sourcesDict, options = {} ) {
|
|
|
382
384
|
else { // source is a CSN object
|
|
383
385
|
const ast = parseCsn.augment( source, filename, options, model.$messageFunctions );
|
|
384
386
|
sources[filename] = ast;
|
|
385
|
-
ast.location =
|
|
387
|
+
ast.location = new CsnLocation( filename );
|
|
386
388
|
assertConsistency( ast, options );
|
|
387
389
|
}
|
|
388
390
|
|
|
@@ -125,6 +125,7 @@ function kickStart( model ) {
|
|
|
125
125
|
if (elem.target && type && type[0] && type[0].id === 'cds.Composition') {
|
|
126
126
|
// A target aspect would have already moved to property `targetAspect` in
|
|
127
127
|
// define.js (hm... more something for kick-start.js...)
|
|
128
|
+
// TODO: for safety, just use resolveUncheckedPath()
|
|
128
129
|
const target = resolvePath( elem.target, 'target', elem );
|
|
129
130
|
if (target)
|
|
130
131
|
model.$compositionTargets[target.name.absolute] = true;
|
|
@@ -134,6 +135,7 @@ function kickStart( model ) {
|
|
|
134
135
|
|
|
135
136
|
// Resolve the using declarations in `using`. Issue
|
|
136
137
|
// error message if the referenced artifact does not exist.
|
|
138
|
+
// TODO: think of moving this to resolve.js
|
|
137
139
|
function resolveUsings( src, topLevel ) {
|
|
138
140
|
if (!src.usings)
|
|
139
141
|
return;
|