@sap/cds-compiler 4.0.2 → 4.2.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- 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 +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- 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} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- 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
|
@@ -67,6 +67,9 @@
|
|
|
67
67
|
'use strict';
|
|
68
68
|
|
|
69
69
|
const { locationString, hasErrors } = require('../base/messages');
|
|
70
|
+
const {
|
|
71
|
+
XsnSource, XsnName, XsnArtifact, CsnLocation,
|
|
72
|
+
} = require('./classes');
|
|
70
73
|
|
|
71
74
|
|
|
72
75
|
// Properties that can appear where a type can have type arguments.
|
|
@@ -77,14 +80,15 @@ const typeProperties = [
|
|
|
77
80
|
|
|
78
81
|
class InternalConsistencyError extends Error {
|
|
79
82
|
constructor(msg) {
|
|
80
|
-
super(`cds-compiler XSN consistency: ${ msg }`);
|
|
83
|
+
super( `cds-compiler XSN consistency: ${ msg }` );
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
function assertConsistency( model, stage ) {
|
|
85
88
|
const stageParser = typeof stage === 'object';
|
|
86
89
|
const options = stageParser && stage || model.options || { testMode: true };
|
|
87
|
-
if (!options.testMode || options.
|
|
90
|
+
if (!options.testMode || options.testMode === '$noAssertConsistency' ||
|
|
91
|
+
options.parseOnly && !stageParser)
|
|
88
92
|
return;
|
|
89
93
|
|
|
90
94
|
const schema = {
|
|
@@ -105,6 +109,7 @@ function assertConsistency( model, stage ) {
|
|
|
105
109
|
'$blocks',
|
|
106
110
|
'$messageFunctions',
|
|
107
111
|
'$functions',
|
|
112
|
+
'$assert',
|
|
108
113
|
'_sortedSources',
|
|
109
114
|
],
|
|
110
115
|
},
|
|
@@ -122,12 +127,14 @@ function assertConsistency( model, stage ) {
|
|
|
122
127
|
'$withLocalized',
|
|
123
128
|
'$sources',
|
|
124
129
|
],
|
|
130
|
+
instanceOf: XsnSource,
|
|
125
131
|
},
|
|
126
132
|
location: {
|
|
127
133
|
// every thing with a $location in CSN must have a XSN location even
|
|
128
134
|
// with syntax errors (currently even internal artifacts like $using):
|
|
129
135
|
isRequired: parent => noSyntaxErrors() || parent && parent.kind,
|
|
130
136
|
kind: true,
|
|
137
|
+
instanceOf: CsnLocation,
|
|
131
138
|
requires: [ 'file' ], // line is optional in top-level location
|
|
132
139
|
optional: [ 'line', 'col', 'endLine', 'endCol', '$notFound' ],
|
|
133
140
|
schema: {
|
|
@@ -138,8 +145,8 @@ function assertConsistency( model, stage ) {
|
|
|
138
145
|
$notFound: { test: isBoolean },
|
|
139
146
|
},
|
|
140
147
|
},
|
|
141
|
-
sources: { test: isDictionary( isObject ) },
|
|
142
|
-
_sortedSources: { test: isArray( isObject ) },
|
|
148
|
+
sources: { test: isDictionary( isObject ), instanceOf: XsnSource },
|
|
149
|
+
_sortedSources: { test: isArray( isObject ), instanceOf: XsnSource },
|
|
143
150
|
file: { test: isString },
|
|
144
151
|
dirname: { test: isString }, // TODO: really necessary?
|
|
145
152
|
realname: { test: isString }, // TODO: really necessary?
|
|
@@ -158,11 +165,13 @@ function assertConsistency( model, stage ) {
|
|
|
158
165
|
test: isDictionary( definition ),
|
|
159
166
|
requires: [ 'kind', 'location', 'name' ],
|
|
160
167
|
optional: thoseWithKind,
|
|
168
|
+
instanceOf: XsnArtifact,
|
|
161
169
|
},
|
|
162
170
|
vocabularies: {
|
|
163
171
|
test: isDictionary( definition ),
|
|
164
172
|
requires: [ 'kind', 'name' ],
|
|
165
173
|
optional: thoseWithKind,
|
|
174
|
+
instanceOf: XsnArtifact,
|
|
166
175
|
},
|
|
167
176
|
extensions: {
|
|
168
177
|
kind: [ 'context' ], // syntax error (as opposed to HANA CDS), but still there
|
|
@@ -174,7 +183,7 @@ function assertConsistency( model, stage ) {
|
|
|
174
183
|
i18n: {
|
|
175
184
|
test: isDictionary( ( val, parent, prop, spec, lang ) => {
|
|
176
185
|
const textValueIsString = (v, p, textProp, s, textKey) => {
|
|
177
|
-
isString(v.val, p, textKey, s);
|
|
186
|
+
isString( v.val, p, textKey, s );
|
|
178
187
|
};
|
|
179
188
|
const innerDict = isDictionary( textValueIsString );
|
|
180
189
|
return innerDict( val, parent, lang, spec );
|
|
@@ -197,7 +206,7 @@ function assertConsistency( model, stage ) {
|
|
|
197
206
|
],
|
|
198
207
|
schema: {
|
|
199
208
|
kind: { test: isString, enum: [ 'builtin' ] },
|
|
200
|
-
name: { test: isObject, requires: [ 'id', 'element' ] },
|
|
209
|
+
name: { test: isObject, instanceOf: XsnName, requires: [ 'id', 'element' ] },
|
|
201
210
|
$autoElement: { test: isString },
|
|
202
211
|
$uncheckedElements: { test: isBoolean },
|
|
203
212
|
$requireElementAccess: { test: isBoolean },
|
|
@@ -240,7 +249,7 @@ function assertConsistency( model, stage ) {
|
|
|
240
249
|
typeProps$: { kind: true, enumerable: false, test: TODO },
|
|
241
250
|
actions: { kind: true, inherits: 'definitions' },
|
|
242
251
|
enum: { kind: true, inherits: 'definitions' },
|
|
243
|
-
foreignKeys: { kind: true, inherits: 'definitions' },
|
|
252
|
+
foreignKeys: { kind: true, inherits: 'definitions', instanceOf: 'ignore' },
|
|
244
253
|
$keysNavigation: { kind: true, test: TODO },
|
|
245
254
|
params: { kind: true, inherits: 'definitions' },
|
|
246
255
|
_extendType: { kind: true, test: TODO },
|
|
@@ -261,7 +270,8 @@ function assertConsistency( model, stage ) {
|
|
|
261
270
|
requires: [ 'op', 'location', 'from' ],
|
|
262
271
|
optional: [
|
|
263
272
|
'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
|
|
264
|
-
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '
|
|
273
|
+
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '$limit',
|
|
274
|
+
'_origin', '_block',
|
|
265
275
|
'_projections', '_parent', '_main', '_effectiveType', '$effectiveSeqNo', '$expand',
|
|
266
276
|
'$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
|
|
267
277
|
],
|
|
@@ -275,7 +285,7 @@ function assertConsistency( model, stage ) {
|
|
|
275
285
|
requires: [ 'op', 'location', 'args', 'join' ],
|
|
276
286
|
optional: [
|
|
277
287
|
'on', '$parens', 'cardinality',
|
|
278
|
-
'kind', 'name', '_block', '_parent', '_main',
|
|
288
|
+
'kind', 'name', '_block', '_parent', '_main', '_user',
|
|
279
289
|
'$tableAliases', '_combined', '_joinParent', '$joinArgsIndex',
|
|
280
290
|
'_leadingQuery', '_$next', '_deps',
|
|
281
291
|
],
|
|
@@ -306,6 +316,7 @@ function assertConsistency( model, stage ) {
|
|
|
306
316
|
columns: {
|
|
307
317
|
kind: [ 'extend', '$column' ],
|
|
308
318
|
test: isArray( column ),
|
|
319
|
+
instanceOf: XsnArtifact,
|
|
309
320
|
optional: thoseWithKind,
|
|
310
321
|
enum: [ '*' ],
|
|
311
322
|
requires: [ 'location' ],
|
|
@@ -325,6 +336,7 @@ function assertConsistency( model, stage ) {
|
|
|
325
336
|
nulls: { test: locationVal( isString ), enum: [ 'first', 'last' ] },
|
|
326
337
|
$orderBy: { inherits: 'orderBy' },
|
|
327
338
|
groupBy: { inherits: 'value', test: isArray( expression ) },
|
|
339
|
+
$limit: { test: TODO },
|
|
328
340
|
limit: { requires: [ 'rows' ], optional: [ 'offset', 'location' ] },
|
|
329
341
|
rows: { inherits: 'value' },
|
|
330
342
|
offset: { inherits: 'value' },
|
|
@@ -383,6 +395,7 @@ function assertConsistency( model, stage ) {
|
|
|
383
395
|
'select', '$join', 'mixin',
|
|
384
396
|
'source', 'namespace', 'using',
|
|
385
397
|
'$tableAlias', '$navElement',
|
|
398
|
+
'builtin', // magic variables
|
|
386
399
|
],
|
|
387
400
|
},
|
|
388
401
|
// locations of parentheses pairs around expression:
|
|
@@ -496,11 +509,12 @@ function assertConsistency( model, stage ) {
|
|
|
496
509
|
],
|
|
497
510
|
// TODO: name requires if not in parser?
|
|
498
511
|
},
|
|
499
|
-
$priority: { test: isOneOf([ undefined, false, 'extend', 'annotate' ]) },
|
|
512
|
+
$priority: { test: isOneOf( [ undefined, false, 'extend', 'annotate' ] ) },
|
|
500
513
|
$annotations: { parser: true, kind: true, test: TODO }, // deprecated, still there for cds-lsp
|
|
501
514
|
name: {
|
|
502
515
|
isRequired: stageParser && (() => false), // not required in parser
|
|
503
516
|
kind: true,
|
|
517
|
+
instanceOf: 'ignore', // TODO: XsnName,
|
|
504
518
|
schema: {
|
|
505
519
|
select: { test: TODO },
|
|
506
520
|
}, // TODO: rename query prop in name
|
|
@@ -527,6 +541,7 @@ function assertConsistency( model, stage ) {
|
|
|
527
541
|
kind: [ 'action', 'function' ],
|
|
528
542
|
requires: [ 'kind', 'location' ],
|
|
529
543
|
optional: thoseWithKind,
|
|
544
|
+
instanceOf: XsnArtifact,
|
|
530
545
|
},
|
|
531
546
|
items: {
|
|
532
547
|
kind: true,
|
|
@@ -592,7 +607,8 @@ function assertConsistency( model, stage ) {
|
|
|
592
607
|
// query specific
|
|
593
608
|
'where', 'columns', 'mixin', 'quantifier', 'offset',
|
|
594
609
|
'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
|
|
595
|
-
'limit', '
|
|
610
|
+
'$limit', 'limit', '_status', '_origin',
|
|
611
|
+
'_effectiveType', '$effectiveSeqNo',
|
|
596
612
|
],
|
|
597
613
|
},
|
|
598
614
|
_leadingQuery: { kind: true, test: TODO },
|
|
@@ -634,7 +650,7 @@ function assertConsistency( model, stage ) {
|
|
|
634
650
|
$inferred: {
|
|
635
651
|
parser: true,
|
|
636
652
|
kind: true,
|
|
637
|
-
test: isOneOf([
|
|
653
|
+
test: isOneOf( [
|
|
638
654
|
'', // constructed “super annotate” statement, redirected user-provided target
|
|
639
655
|
// Uppercase values are used in logic, lowercase value are "just for us", i.e.
|
|
640
656
|
// debugging or to add properties such as $generated in Universal CSN.
|
|
@@ -666,7 +682,7 @@ function assertConsistency( model, stage ) {
|
|
|
666
682
|
'rewrite', // on-conditions or FKeys are rewritten
|
|
667
683
|
'parent-origin', // annotation/property copied from parent that does not come through
|
|
668
684
|
// $origin and is not a direct annotation
|
|
669
|
-
]),
|
|
685
|
+
] ),
|
|
670
686
|
},
|
|
671
687
|
|
|
672
688
|
// Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
|
|
@@ -675,7 +691,7 @@ function assertConsistency( model, stage ) {
|
|
|
675
691
|
$expand: {
|
|
676
692
|
kind: true,
|
|
677
693
|
// See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
|
|
678
|
-
test: isOneOf([ 'origin', 'annotate', 'target' ]),
|
|
694
|
+
test: isOneOf( [ 'origin', 'annotate', 'target' ] ),
|
|
679
695
|
},
|
|
680
696
|
$inCycle: { kind: true, test: isBoolean },
|
|
681
697
|
|
|
@@ -683,9 +699,10 @@ function assertConsistency( model, stage ) {
|
|
|
683
699
|
$extra: { parser: true, test: TODO }, // for unexpected properties in CSN
|
|
684
700
|
$withLocalized: { test: isBoolean },
|
|
685
701
|
$sources: { parser: true, test: isArray( isString ) },
|
|
686
|
-
$expected: { parser: true, test: isOneOf([ 'approved-exists', 'exists' ]) },
|
|
702
|
+
$expected: { parser: true, test: isOneOf( [ 'approved-exists', 'exists' ] ) },
|
|
687
703
|
$messageFunctions: { test: TODO },
|
|
688
704
|
$functions: { test: TODO },
|
|
705
|
+
$assert: { test: TODO }, // currently just for missing Error[ref-cycle]
|
|
689
706
|
};
|
|
690
707
|
let _noSyntaxErrors = null;
|
|
691
708
|
assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
|
|
@@ -714,8 +731,10 @@ function assertConsistency( model, stage ) {
|
|
|
714
731
|
: {}.propertyIsEnumerable.call( parent, prop ) !== enumerable)
|
|
715
732
|
throw new InternalConsistencyError( `Unexpected enumerability ${ !enumerable }${ at( [ node, parent ], prop ) }` );
|
|
716
733
|
}
|
|
717
|
-
(
|
|
718
|
-
|
|
734
|
+
if (node !== undefined) { // ignore if undefined
|
|
735
|
+
(spec.test || standard)( node, parent, prop, spec,
|
|
736
|
+
typeof noPropertyTest === 'string' && noPropertyTest );
|
|
737
|
+
}
|
|
719
738
|
}
|
|
720
739
|
|
|
721
740
|
function definition( node, parent, prop, spec, name ) {
|
|
@@ -768,7 +787,7 @@ function assertConsistency( model, stage ) {
|
|
|
768
787
|
const requires = spec.requires || [];
|
|
769
788
|
// Do not test 'requires' with parse errors:
|
|
770
789
|
for (const p of requires) {
|
|
771
|
-
if (!names.includes(p)) {
|
|
790
|
+
if (!names.includes( p )) {
|
|
772
791
|
const req = spec.schema && spec.schema[p] && spec.schema[p].isRequired;
|
|
773
792
|
if ((req || schema[p] && schema[p].isRequired || noSyntaxErrors)( node ))
|
|
774
793
|
throw new InternalConsistencyError( `Required property '${ p }' missing in object${ at( [ node, parent ], prop, name ) }` );
|
|
@@ -776,13 +795,14 @@ function assertConsistency( model, stage ) {
|
|
|
776
795
|
}
|
|
777
796
|
const optional = spec.optional || [];
|
|
778
797
|
for (const n of names) {
|
|
779
|
-
const opt = Array.isArray(optional)
|
|
798
|
+
const opt = Array.isArray( optional )
|
|
780
799
|
? optional.includes( n ) || optional.includes( n.charAt(0) )
|
|
781
800
|
: optional( n, spec );
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
801
|
+
if (node[n] !== undefined) {
|
|
802
|
+
if (!(opt || requires.includes( n ) || n === '$extra'))
|
|
803
|
+
throw new InternalConsistencyError( `Property '${ n }' is not expected${ at( [ node[n], node, parent ], prop, name ) }` );
|
|
804
|
+
assertProp( node[n], node, n, spec.schema && spec.schema[n] );
|
|
805
|
+
}
|
|
786
806
|
}
|
|
787
807
|
}
|
|
788
808
|
|
|
@@ -838,7 +858,7 @@ function assertConsistency( model, stage ) {
|
|
|
838
858
|
// TODO CSN parser?: { val: <token>, literal: 'token' } for keywords
|
|
839
859
|
if (typeof node === 'string')
|
|
840
860
|
return;
|
|
841
|
-
while (Array.isArray(node)) {
|
|
861
|
+
while (Array.isArray( node )) {
|
|
842
862
|
// TODO: also check getOwnPropertyNames(node)
|
|
843
863
|
if (node.length !== 1) {
|
|
844
864
|
node.forEach( n => expression( n, parent, prop, spec ) );
|
|
@@ -850,7 +870,7 @@ function assertConsistency( model, stage ) {
|
|
|
850
870
|
return;
|
|
851
871
|
isObject( node, parent, prop, spec, idx );
|
|
852
872
|
|
|
853
|
-
const s = spec[expressionSpec(node)] || {};
|
|
873
|
+
const s = spec[expressionSpec( node )] || {};
|
|
854
874
|
const sub = Object.assign( {}, s.inherits && schema[s.inherits], s );
|
|
855
875
|
if (spec.requires && sub.requires)
|
|
856
876
|
sub.requires = [ ...sub.requires, ...spec.requires ];
|
|
@@ -873,9 +893,9 @@ function assertConsistency( model, stage ) {
|
|
|
873
893
|
}
|
|
874
894
|
|
|
875
895
|
function args( node, parent, prop, spec ) {
|
|
876
|
-
if (Array.isArray(node)) {
|
|
896
|
+
if (Array.isArray( node )) {
|
|
877
897
|
if (parent.op && parent.op.val === 'xpr') // remove keywords for `xpr` expressions
|
|
878
|
-
node = node.filter( a => typeof a !== 'string');
|
|
898
|
+
node = node.filter( a => typeof a !== 'string' );
|
|
879
899
|
node.forEach( (item, idx) => expression( item, parent, prop, spec, idx ) );
|
|
880
900
|
}
|
|
881
901
|
else if (node && typeof node === 'object' && !Object.getPrototypeOf( node )) {
|
|
@@ -910,7 +930,7 @@ function assertConsistency( model, stage ) {
|
|
|
910
930
|
|
|
911
931
|
function isArray( func = standard ) {
|
|
912
932
|
return function vector( node, parent, prop, spec ) {
|
|
913
|
-
if (!Array.isArray(node))
|
|
933
|
+
if (!Array.isArray( node ))
|
|
914
934
|
throw new InternalConsistencyError( `Expected array${ at( [ null, parent ], prop ) }` );
|
|
915
935
|
node.forEach( (item, n) => func( item, parent, prop, spec, n ) );
|
|
916
936
|
};
|
|
@@ -921,7 +941,9 @@ function assertConsistency( model, stage ) {
|
|
|
921
941
|
const valSchema = { val: Object.assign( {}, spec, { test: func } ) };
|
|
922
942
|
const requires = [ 'val', 'location' ];
|
|
923
943
|
const optional = [ 'literal', '$inferred', '$priority', '_pathHead' ];
|
|
924
|
-
standard( node, parent, prop, {
|
|
944
|
+
standard( node, parent, prop, {
|
|
945
|
+
schema: valSchema, requires, optional, instanceOf: spec.instanceOf,
|
|
946
|
+
}, name );
|
|
925
947
|
};
|
|
926
948
|
}
|
|
927
949
|
|
|
@@ -941,33 +963,42 @@ function assertConsistency( model, stage ) {
|
|
|
941
963
|
|
|
942
964
|
function isOneOf( values ) {
|
|
943
965
|
return function isOneOfInner( node, parent, prop ) {
|
|
944
|
-
if (!values.includes(node))
|
|
945
|
-
throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify(values) }${ at( [ node, parent ], prop ) }` );
|
|
966
|
+
if (!values.includes( node ))
|
|
967
|
+
throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify( values ) }${ at( [ node, parent ], prop ) }` );
|
|
946
968
|
};
|
|
947
969
|
}
|
|
948
970
|
|
|
949
971
|
function isString( node, parent, prop, spec ) {
|
|
950
972
|
if (typeof node !== 'string')
|
|
951
|
-
throw new InternalConsistencyError( `Expected string${ at( [ node, parent ], prop ) }` );
|
|
973
|
+
throw new InternalConsistencyError( `Expected string but found ${ typeof node }${ at( [ node, parent ], prop ) }` );
|
|
952
974
|
// TODO: also check getOwnPropertyNames(node)
|
|
953
975
|
if (spec.enum && !spec.enum.includes( node ))
|
|
954
976
|
throw new InternalConsistencyError( `Unexpected value '${ node }'${ at( [ node, parent ], prop ) }` );
|
|
955
977
|
}
|
|
956
978
|
|
|
957
979
|
function isVal( node, parent, prop, spec ) {
|
|
958
|
-
if (Array.isArray(node))
|
|
980
|
+
if (Array.isArray( node ))
|
|
959
981
|
node.forEach( (item, n) => standard( item, parent, prop, spec, n ) );
|
|
960
982
|
else if (node !== null && ![ 'string', 'number', 'boolean' ].includes( typeof node ))
|
|
961
983
|
throw new InternalConsistencyError( `Expected array or simple value${ at( [ null, parent ], prop ) }` );
|
|
962
984
|
}
|
|
963
985
|
|
|
964
986
|
function isObject( node, parent, prop, spec, name ) {
|
|
965
|
-
if (!node || typeof node !== 'object'
|
|
966
|
-
throw new InternalConsistencyError( `Expected
|
|
987
|
+
if (!node || typeof node !== 'object' )
|
|
988
|
+
throw new InternalConsistencyError( `Expected object${ at( [ null, parent ], prop, name ) }` );
|
|
989
|
+
const found = Object.getPrototypeOf( node )?.constructor?.name || 'null';
|
|
990
|
+
if (!spec.instanceOf && Object.getPrototypeOf( node ) !== Object.prototype)
|
|
991
|
+
throw new InternalConsistencyError( `Expected standard object but found ${ found }${ at( [ null, parent ], prop, name ) }` );
|
|
992
|
+
// TODO
|
|
993
|
+
// else if (spec.instanceOf && spec.instanceOf !== 'ignore' &&
|
|
994
|
+
// Object.getPrototypeOf( node ) !== spec.instanceOf.prototype)
|
|
995
|
+
// eslint-disable-next-line max-len
|
|
996
|
+
// throw new InternalConsistencyError( `Expected object of class ${ spec.instanceOf.name } but found ${ found }${ at( [ null, parent ], prop, name ) }` );
|
|
967
997
|
}
|
|
968
998
|
|
|
999
|
+
|
|
969
1000
|
function inDefinitions( art, parent, prop, spec, name ) {
|
|
970
|
-
if (Array.isArray(art)) // do not check with redefinitions
|
|
1001
|
+
if (Array.isArray( art )) // do not check with redefinitions
|
|
971
1002
|
return;
|
|
972
1003
|
isObject( art, parent, prop, spec, name );
|
|
973
1004
|
if (stageParser) {
|
|
@@ -979,7 +1010,7 @@ function assertConsistency( model, stage ) {
|
|
|
979
1010
|
!(model.vocabularies && model.vocabularies[art.name.absolute])) {
|
|
980
1011
|
// TODO: sign ignored artifacts with $inferred = 'IGNORED'
|
|
981
1012
|
if (parent.kind === 'source' ||
|
|
982
|
-
art.name.absolute && art.name.absolute.startsWith('localized.'))
|
|
1013
|
+
art.name.absolute && art.name.absolute.startsWith( 'localized.' ))
|
|
983
1014
|
standard( art, parent, prop, spec, name );
|
|
984
1015
|
else
|
|
985
1016
|
throw new InternalConsistencyError( `Expected definition${ at( [ art, parent ], prop, name ) }` );
|
|
@@ -988,11 +1019,11 @@ function assertConsistency( model, stage ) {
|
|
|
988
1019
|
|
|
989
1020
|
function isScope( node, parent, prop ) {
|
|
990
1021
|
// artifact refs in CDL have scope:0 in XSN
|
|
991
|
-
if (Number.isInteger(node))
|
|
1022
|
+
if (Number.isInteger( node ))
|
|
992
1023
|
return;
|
|
993
1024
|
const validValues = [ 'typeOf', 'global', 'param' ];
|
|
994
|
-
if (!validValues.includes(node))
|
|
995
|
-
throw new InternalConsistencyError( `Property '${ prop }' must be either "${ validValues.join('", "') }" or a number but was "${ node }"` );
|
|
1025
|
+
if (!validValues.includes( node ))
|
|
1026
|
+
throw new InternalConsistencyError( `Property '${ prop }' must be either "${ validValues.join( '", "' ) }" or a number but was "${ node }"` );
|
|
996
1027
|
}
|
|
997
1028
|
|
|
998
1029
|
function TODO() { /* no-op */ }
|
package/lib/compiler/base.js
CHANGED
|
@@ -29,7 +29,11 @@ const kindProperties = {
|
|
|
29
29
|
$inline: { normalized: 'element' }, // column with inline property
|
|
30
30
|
event: { elements: true, include: true },
|
|
31
31
|
type: { elements: propExists, enum: propExists, include: true },
|
|
32
|
-
aspect: {
|
|
32
|
+
aspect: {
|
|
33
|
+
elements: propExists,
|
|
34
|
+
actions: ( _p, parent ) => propExists( 'elements', parent ),
|
|
35
|
+
include: true,
|
|
36
|
+
},
|
|
33
37
|
annotation: { elements: propExists, enum: propExists },
|
|
34
38
|
enum: { normalized: 'element', dict: 'enum' },
|
|
35
39
|
element: { elements: propExists, enum: propExists, dict: 'elements' },
|
|
@@ -72,6 +76,7 @@ function propExists( prop, parent ) {
|
|
|
72
76
|
* have a "sparse" name structure.
|
|
73
77
|
*
|
|
74
78
|
* @param {XSN.Artifact} art
|
|
79
|
+
* @returns {XSN.Name}
|
|
75
80
|
*/
|
|
76
81
|
function getArtifactName( art ) {
|
|
77
82
|
if (!art.name || art.name.absolute) // no name or “old style”
|
|
@@ -109,7 +114,7 @@ function getMemberNameProp( elem, kind ) {
|
|
|
109
114
|
obj = obj.items;
|
|
110
115
|
if (obj.elements || obj.enum)
|
|
111
116
|
return 'element';
|
|
112
|
-
throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
|
|
117
|
+
throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join( '+' ) }` );
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
module.exports = {
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -235,7 +235,7 @@ const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]\d+)?[ \t]*$/i;
|
|
|
235
235
|
const quotedLiteralPatterns = {
|
|
236
236
|
x: {
|
|
237
237
|
test_variant: 'uneven-hex',
|
|
238
|
-
test_fn: (str => Number.isInteger(str.length / 2)),
|
|
238
|
+
test_fn: (str => Number.isInteger( str.length / 2 )),
|
|
239
239
|
unexpected_variant: 'invalid-hex',
|
|
240
240
|
unexpected_char: /[^0-9a-f]/i,
|
|
241
241
|
json_type: 'string',
|
|
@@ -293,12 +293,12 @@ const quotedLiteralPatterns = {
|
|
|
293
293
|
*/
|
|
294
294
|
function checkDate( year, month, day ) {
|
|
295
295
|
// Negative years are allowed
|
|
296
|
-
year = Math.abs(Number.parseInt(year, 10));
|
|
297
|
-
month = Number.parseInt(month, 10);
|
|
298
|
-
day = Number.parseInt(day, 10);
|
|
296
|
+
year = Math.abs( Number.parseInt( year, 10 ) );
|
|
297
|
+
month = Number.parseInt( month, 10 );
|
|
298
|
+
day = Number.parseInt( day, 10 );
|
|
299
299
|
// If any is NaN, the condition will be false.
|
|
300
300
|
// Year 0 does not exist, but ISO 8601 allows it and defines it as 1 BC.
|
|
301
|
-
return !Number.isNaN(year) && month > 0 && month < 13 && day > 0 && day < 32;
|
|
301
|
+
return !Number.isNaN( year ) && month > 0 && month < 13 && day > 0 && day < 32;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
/**
|
|
@@ -308,9 +308,9 @@ function checkDate( year, month, day ) {
|
|
|
308
308
|
* @returns {boolean} True if the date is valid.
|
|
309
309
|
*/
|
|
310
310
|
function checkTime( hour, minutes, seconds ) {
|
|
311
|
-
hour = Number.parseInt(hour, 10);
|
|
312
|
-
minutes = Number.parseInt(minutes, 10);
|
|
313
|
-
seconds = seconds ? Number.parseInt(seconds, 10) : 0;
|
|
311
|
+
hour = Number.parseInt( hour, 10 );
|
|
312
|
+
minutes = Number.parseInt( minutes, 10 );
|
|
313
|
+
seconds = seconds ? Number.parseInt( seconds, 10 ) : 0;
|
|
314
314
|
if (hour === 24) // allow 24:00:00 (ISO 8601 version earlier than 2019)
|
|
315
315
|
return minutes === 0 && seconds === 0;
|
|
316
316
|
// If any is NaN, the condition will be false.
|
|
@@ -332,47 +332,47 @@ const typeCategories = {
|
|
|
332
332
|
geo: [],
|
|
333
333
|
};
|
|
334
334
|
// Fill type categories with `cds.*` types
|
|
335
|
-
Object.keys(core).forEach((type) => {
|
|
335
|
+
Object.keys( core ).forEach( (type) => {
|
|
336
336
|
if (core[type].category)
|
|
337
|
-
typeCategories[core[type].category].push(`cds.${ type }`);
|
|
338
|
-
});
|
|
337
|
+
typeCategories[core[type].category].push( `cds.${ type }` );
|
|
338
|
+
} );
|
|
339
339
|
// Fill type categories with `cds.hana.*` types
|
|
340
|
-
Object.keys(coreHana).forEach((type) => {
|
|
340
|
+
Object.keys( coreHana ).forEach( (type) => {
|
|
341
341
|
if (coreHana[type].category)
|
|
342
|
-
typeCategories[coreHana[type].category].push(`cds.hana.${ type }`);
|
|
343
|
-
});
|
|
342
|
+
typeCategories[coreHana[type].category].push( `cds.hana.${ type }` );
|
|
343
|
+
} );
|
|
344
344
|
|
|
345
345
|
/** @param {string} typeName */
|
|
346
346
|
function isIntegerTypeName( typeName ) {
|
|
347
|
-
return typeCategories.integer.includes(typeName);
|
|
347
|
+
return typeCategories.integer.includes( typeName );
|
|
348
348
|
}
|
|
349
349
|
/** @param {string} typeName */
|
|
350
350
|
function isDecimalTypeName( typeName ) {
|
|
351
|
-
return typeCategories.decimal.includes(typeName);
|
|
351
|
+
return typeCategories.decimal.includes( typeName );
|
|
352
352
|
}
|
|
353
353
|
/** @param {string} typeName */
|
|
354
354
|
function isNumericTypeName( typeName ) {
|
|
355
|
-
return isIntegerTypeName(typeName) || isDecimalTypeName(typeName);
|
|
355
|
+
return isIntegerTypeName( typeName ) || isDecimalTypeName( typeName );
|
|
356
356
|
}
|
|
357
357
|
/** @param {string} typeName */
|
|
358
358
|
function isStringTypeName( typeName ) {
|
|
359
|
-
return typeCategories.string.includes(typeName);
|
|
359
|
+
return typeCategories.string.includes( typeName );
|
|
360
360
|
}
|
|
361
361
|
/** @param {string} typeName */
|
|
362
362
|
function isDateOrTimeTypeName( typeName ) {
|
|
363
|
-
return typeCategories.dateTime.includes(typeName);
|
|
363
|
+
return typeCategories.dateTime.includes( typeName );
|
|
364
364
|
}
|
|
365
365
|
/** @param {string} typeName */
|
|
366
366
|
function isBooleanTypeName( typeName ) {
|
|
367
|
-
return typeCategories.boolean.includes(typeName);
|
|
367
|
+
return typeCategories.boolean.includes( typeName );
|
|
368
368
|
}
|
|
369
369
|
/** @param {string} typeName */
|
|
370
370
|
function isBinaryTypeName( typeName ) {
|
|
371
|
-
return typeCategories.binary.includes(typeName);
|
|
371
|
+
return typeCategories.binary.includes( typeName );
|
|
372
372
|
}
|
|
373
373
|
/** @param {string} typeName */
|
|
374
374
|
function isGeoTypeName( typeName ) {
|
|
375
|
-
return typeCategories.geo.includes(typeName);
|
|
375
|
+
return typeCategories.geo.includes( typeName );
|
|
376
376
|
}
|
|
377
377
|
/**
|
|
378
378
|
* Whether the given type name is a relation, i.e. an association or composition.
|
|
@@ -380,7 +380,7 @@ function isGeoTypeName( typeName ) {
|
|
|
380
380
|
* @param {string} typeName
|
|
381
381
|
*/
|
|
382
382
|
function isRelationTypeName( typeName ) {
|
|
383
|
-
return typeCategories.relation.includes(typeName);
|
|
383
|
+
return typeCategories.relation.includes( typeName );
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
/**
|
|
@@ -390,10 +390,10 @@ function isRelationTypeName( typeName ) {
|
|
|
390
390
|
* @returns {boolean}
|
|
391
391
|
*/
|
|
392
392
|
function isInReservedNamespace( absolute ) {
|
|
393
|
-
return absolute.startsWith( 'cds.') &&
|
|
394
|
-
!absolute.match(/^cds\.foundation(\.|$)/) &&
|
|
395
|
-
!absolute.match(/^cds\.outbox(\.|$)/) && // Requested by Node runtime
|
|
396
|
-
!absolute.match(/^cds\.xt(\.|$)/); // Requested by Mtx
|
|
393
|
+
return absolute === 'cds' || absolute.startsWith( 'cds.' ) &&
|
|
394
|
+
!absolute.match( /^cds\.foundation(\.|$)/ ) &&
|
|
395
|
+
!absolute.match( /^cds\.outbox(\.|$)/ ) && // Requested by Node runtime
|
|
396
|
+
!absolute.match( /^cds\.xt(\.|$)/ ); // Requested by Mtx
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
/**
|
|
@@ -408,7 +408,7 @@ function isInReservedNamespace( absolute ) {
|
|
|
408
408
|
* @returns {boolean}
|
|
409
409
|
*/
|
|
410
410
|
function isBuiltinType( type ) {
|
|
411
|
-
return typeof type === 'string' && isInReservedNamespace(type);
|
|
411
|
+
return typeof type === 'string' && isInReservedNamespace( type );
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
/**
|
|
@@ -446,7 +446,7 @@ function initBuiltins( model ) {
|
|
|
446
446
|
builtin,
|
|
447
447
|
location: builtinLocation(),
|
|
448
448
|
};
|
|
449
|
-
setProp( art, '_subArtifacts', Object.create(null) );
|
|
449
|
+
setProp( art, '_subArtifacts', Object.create( null ) );
|
|
450
450
|
return art;
|
|
451
451
|
}
|
|
452
452
|
|
|
@@ -460,7 +460,7 @@ function initBuiltins( model ) {
|
|
|
460
460
|
* @returns {object} Artifacts dictionary with the builtin artifacts without prefixes.
|
|
461
461
|
*/
|
|
462
462
|
function env( builtins, prefix, parent ) {
|
|
463
|
-
const artifacts = Object.create(null);
|
|
463
|
+
const artifacts = Object.create( null );
|
|
464
464
|
for (const name of Object.keys( builtins )) {
|
|
465
465
|
const absolute = prefix + name;
|
|
466
466
|
// TODO: reconsider whether to set a type to itself - looks wrong
|
|
@@ -481,11 +481,14 @@ function initBuiltins( model ) {
|
|
|
481
481
|
}
|
|
482
482
|
|
|
483
483
|
function setMagicVariables( builtins ) {
|
|
484
|
-
const artifacts = Object.create(null);
|
|
484
|
+
const artifacts = Object.create( null );
|
|
485
485
|
for (const name in builtins) {
|
|
486
486
|
const magic = builtins[name];
|
|
487
487
|
// TODO: rename to $builtinFunction
|
|
488
|
-
const art = {
|
|
488
|
+
const art = {
|
|
489
|
+
kind: 'builtin',
|
|
490
|
+
name: { id: name, absolute: '', element: name },
|
|
491
|
+
};
|
|
489
492
|
artifacts[name] = art;
|
|
490
493
|
|
|
491
494
|
if (magic.$autoElement)
|
|
@@ -507,18 +510,14 @@ function initBuiltins( model ) {
|
|
|
507
510
|
if (!elements)
|
|
508
511
|
return;
|
|
509
512
|
|
|
510
|
-
const names = Object.keys(elements);
|
|
513
|
+
const names = Object.keys( elements );
|
|
511
514
|
if (names.length > 0 && !art.elements)
|
|
512
|
-
art.elements = Object.create(null);
|
|
515
|
+
art.elements = Object.create( null );
|
|
513
516
|
|
|
514
517
|
for (const n of names) {
|
|
515
518
|
const magic = {
|
|
516
|
-
kind: 'builtin',
|
|
517
|
-
name: {
|
|
518
|
-
id: n,
|
|
519
|
-
absolute: art.name.absolute,
|
|
520
|
-
element: art.name.element ? `${ art.name.element }.${ n }` : n,
|
|
521
|
-
},
|
|
519
|
+
kind: 'builtin', // TODO: '$variable'
|
|
520
|
+
name: { id: n, absolute: '', element: `${ art.name.element }.${ n }` },
|
|
522
521
|
};
|
|
523
522
|
// Propagate this property so that it is available for sub-elements.
|
|
524
523
|
if (art.$uncheckedElements)
|
|
@@ -526,7 +525,7 @@ function initBuiltins( model ) {
|
|
|
526
525
|
setProp( magic, '_parent', art );
|
|
527
526
|
// setProp( magic, '_effectiveType', magic );
|
|
528
527
|
if (elements[n] && typeof elements[n] === 'object')
|
|
529
|
-
createMagicElements(magic, elements[n]);
|
|
528
|
+
createMagicElements( magic, elements[n] );
|
|
530
529
|
|
|
531
530
|
art.elements[n] = magic;
|
|
532
531
|
}
|