@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/compiler/definer.js
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
// Sub Phase 2 (initXYZ)
|
|
89
89
|
// - set _parent, _main (later: _service?) links, and _block links of members
|
|
90
90
|
// - add _subArtifacts dictionary and "namespace artifacts" for name resolution
|
|
91
|
-
// - duplicate checks
|
|
91
|
+
// - duplicate checks
|
|
92
92
|
// - structure checks ?
|
|
93
93
|
// - annotation assignments
|
|
94
94
|
// - POST: resolvePath() can be called for artifact references (if complete model)
|
|
@@ -101,9 +101,11 @@
|
|
|
101
101
|
// to avoid consequential or repeated errors.
|
|
102
102
|
// - But: The same artifact is added to multiple dictionaries.
|
|
103
103
|
// - Solution part 1: $duplicates as property of the artifact or member
|
|
104
|
-
// for
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
|
|
105
|
+
// dictionary of the whole model, `$tableAliases` dictionary of queries.
|
|
106
|
+
// - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
|
|
107
|
+
// dictionary, `_combined` dictionary for query search, `$tableAliases`
|
|
108
|
+
// of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
|
|
107
109
|
|
|
108
110
|
'use strict';
|
|
109
111
|
|
|
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
|
|
|
111
113
|
const {
|
|
112
114
|
isDeprecatedEnabled, isBetaEnabled,
|
|
113
115
|
setProp, forEachGeneric, forEachInOrder,
|
|
114
|
-
|
|
116
|
+
forEachDefinition,
|
|
115
117
|
forEachMemberRecursivelyWithQuery,
|
|
116
118
|
} = require('../base/model');
|
|
117
119
|
const {
|
|
@@ -120,20 +122,21 @@ const {
|
|
|
120
122
|
const {
|
|
121
123
|
dictLocation,
|
|
122
124
|
} = require('../base/location');
|
|
125
|
+
const { kindProperties, dictKinds } = require('./base');
|
|
123
126
|
const {
|
|
124
|
-
annotationVal,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
dictKinds,
|
|
128
|
-
kindProperties,
|
|
129
|
-
fns,
|
|
127
|
+
annotationVal,
|
|
128
|
+
annotationIsFalse,
|
|
129
|
+
annotateWith,
|
|
130
130
|
linkToOrigin,
|
|
131
131
|
setMemberParent,
|
|
132
132
|
storeExtension,
|
|
133
133
|
dependsOnSilent,
|
|
134
|
-
|
|
134
|
+
pathName,
|
|
135
|
+
augmentPath,
|
|
136
|
+
splitIntoPath,
|
|
137
|
+
} = require('./utils');
|
|
135
138
|
const { compareLayer, layer } = require('./moduleLayers');
|
|
136
|
-
const { initBuiltins } = require('./builtins');
|
|
139
|
+
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
137
140
|
const setLink = setProp;
|
|
138
141
|
|
|
139
142
|
/**
|
|
@@ -148,7 +151,7 @@ const setLink = setProp;
|
|
|
148
151
|
*
|
|
149
152
|
* @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
|
|
150
153
|
*/
|
|
151
|
-
function
|
|
154
|
+
function define( model ) {
|
|
152
155
|
const { options } = model;
|
|
153
156
|
// Get simplified "resolve" functionality and the message function:
|
|
154
157
|
const {
|
|
@@ -160,24 +163,29 @@ function getDefinerFunctions( model ) {
|
|
|
160
163
|
resolveTypeArguments,
|
|
161
164
|
defineAnnotations,
|
|
162
165
|
attachAndEmitValidNames,
|
|
163
|
-
} =
|
|
164
|
-
|
|
165
|
-
let addTextsLanguageAssoc = false;
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
define,
|
|
166
|
+
} = model.$functions;
|
|
167
|
+
Object.assign( model.$functions, {
|
|
169
168
|
initArtifact,
|
|
170
169
|
lateExtensions,
|
|
171
170
|
projectionAncestor,
|
|
172
171
|
hasTruthyProp,
|
|
172
|
+
} );
|
|
173
|
+
// During the definer, we can only resolve artifact references, i.e,
|
|
174
|
+
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
175
|
+
model.$volatileFunctions.environment = function artifactsEnv( art ) {
|
|
176
|
+
return art._subArtifacts || Object.create(null);
|
|
173
177
|
};
|
|
174
178
|
|
|
179
|
+
const extensionsDict = Object.create(null);
|
|
180
|
+
let addTextsLanguageAssoc = false;
|
|
181
|
+
return doDefine();
|
|
182
|
+
|
|
175
183
|
/**
|
|
176
184
|
* Main function of the definer.
|
|
177
185
|
*
|
|
178
186
|
* @returns {XSN.Model}
|
|
179
187
|
*/
|
|
180
|
-
function
|
|
188
|
+
function doDefine() {
|
|
181
189
|
if (options.deprecated &&
|
|
182
190
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
183
191
|
warning( 'api-deprecated-option', {},
|
|
@@ -206,8 +214,6 @@ function getDefinerFunctions( model ) {
|
|
|
206
214
|
|
|
207
215
|
if (options.parseCdl) {
|
|
208
216
|
initExtensionsWithoutApplying();
|
|
209
|
-
// Check for redefinitions
|
|
210
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
211
217
|
// If no extensions shall be applied then we can skip further
|
|
212
218
|
// artifact processing and return the model with an `extensions` property.
|
|
213
219
|
return model;
|
|
@@ -215,7 +221,6 @@ function getDefinerFunctions( model ) {
|
|
|
215
221
|
|
|
216
222
|
applyExtensions();
|
|
217
223
|
|
|
218
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
219
224
|
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
220
225
|
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
221
226
|
model.definitions['sap.common.Languages'];
|
|
@@ -238,13 +243,13 @@ function getDefinerFunctions( model ) {
|
|
|
238
243
|
* @param {XSN.AST} src
|
|
239
244
|
*/
|
|
240
245
|
function addSource( src ) {
|
|
241
|
-
// handle sub model from
|
|
246
|
+
// handle sub model from parser
|
|
242
247
|
if (!src.kind)
|
|
243
248
|
src.kind = 'source';
|
|
244
249
|
|
|
245
250
|
let namespace = src.namespace && src.namespace.path;
|
|
246
251
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
247
|
-
if (
|
|
252
|
+
if (isInReservedNamespace(prefix)) {
|
|
248
253
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
249
254
|
// TODO: use $(NAME)
|
|
250
255
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -280,8 +285,7 @@ function getDefinerFunctions( model ) {
|
|
|
280
285
|
function addDefinition( art, block ) {
|
|
281
286
|
const { absolute } = art.name;
|
|
282
287
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
283
|
-
if (absolute === 'cds' ||
|
|
284
|
-
absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
|
|
288
|
+
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
285
289
|
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
286
290
|
// TODO: use $(NAME)
|
|
287
291
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -305,7 +309,7 @@ function getDefinerFunctions( model ) {
|
|
|
305
309
|
}
|
|
306
310
|
else {
|
|
307
311
|
setLink( art, '_block', block );
|
|
308
|
-
// dictAdd might set $duplicates to true
|
|
312
|
+
// dictAdd might set $duplicates to true
|
|
309
313
|
dictAdd( model.definitions, absolute, art );
|
|
310
314
|
return true;
|
|
311
315
|
}
|
|
@@ -366,7 +370,7 @@ function getDefinerFunctions( model ) {
|
|
|
366
370
|
// TODO: check name: no "."
|
|
367
371
|
if (path[0].id === 'localized' || path[0].id.startsWith( 'localized.' )) {
|
|
368
372
|
decl.$inferred = 'LOCALIZED-IGNORED';
|
|
369
|
-
warning( 'using-localized-view', [
|
|
373
|
+
warning( 'using-localized-view', [ decl.location, decl ], {},
|
|
370
374
|
'Localization views can\'t be referred to - ignored USING' );
|
|
371
375
|
// actually not ignored anymore
|
|
372
376
|
}
|
|
@@ -444,6 +448,23 @@ function getDefinerFunctions( model ) {
|
|
|
444
448
|
|
|
445
449
|
// Phase 2 ("init") --------------------------------------------------------
|
|
446
450
|
|
|
451
|
+
function checkRedefinition( art ) {
|
|
452
|
+
if (!art.$duplicates)
|
|
453
|
+
return;
|
|
454
|
+
if (art._main) {
|
|
455
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
456
|
+
name: art.name.id,
|
|
457
|
+
'#': kindProperties[art.kind].normalized || art.kind,
|
|
458
|
+
} );
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
462
|
+
name: art.name.absolute,
|
|
463
|
+
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
464
|
+
} );
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
447
468
|
function initNamespaceAndUsing( src ) {
|
|
448
469
|
if (src.namespace) {
|
|
449
470
|
const decl = src.namespace;
|
|
@@ -488,6 +509,7 @@ function getDefinerFunctions( model ) {
|
|
|
488
509
|
if (!reInit)
|
|
489
510
|
initParentLink( art, model.definitions );
|
|
490
511
|
const block = art._block;
|
|
512
|
+
checkRedefinition( art );
|
|
491
513
|
defineAnnotations( art, art, block );
|
|
492
514
|
initMembers( art, art, block );
|
|
493
515
|
initDollarSelf( art ); // $self
|
|
@@ -513,6 +535,7 @@ function getDefinerFunctions( model ) {
|
|
|
513
535
|
|
|
514
536
|
function initVocabulary( art ) {
|
|
515
537
|
initParentLink( art, model.vocabularies );
|
|
538
|
+
checkRedefinition( art );
|
|
516
539
|
const block = art._block;
|
|
517
540
|
defineAnnotations( art, art, block );
|
|
518
541
|
initMembers( art, art, block );
|
|
@@ -554,20 +577,6 @@ function getDefinerFunctions( model ) {
|
|
|
554
577
|
|
|
555
578
|
// From here til EOF, reexamine code ---------------------------------------
|
|
556
579
|
|
|
557
|
-
// currently called from preProcessArtifact(), do be called in "init"
|
|
558
|
-
function checkRedefinitions( obj, name, prop ) {
|
|
559
|
-
forEachMember( obj, checkRedefinitions, obj.targetAspect );
|
|
560
|
-
if (!obj.$duplicates)
|
|
561
|
-
return;
|
|
562
|
-
if (obj.name.location.file === '<built-in>') {
|
|
563
|
-
// builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
|
|
564
|
-
// The error shall only be printed for the user-defined conflicting artifact.
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
error( 'duplicate-definition', [ obj.name.location, obj ],
|
|
568
|
-
{ name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
|
|
569
|
-
}
|
|
570
|
-
|
|
571
580
|
function initDollarSelf( art ) {
|
|
572
581
|
const selfname = '$self';
|
|
573
582
|
// TODO: use setMemberParent() ?
|
|
@@ -774,12 +783,13 @@ function getDefinerFunctions( model ) {
|
|
|
774
783
|
setMemberParent( table, table.name.id, query );
|
|
775
784
|
setProp( table, '_block', query._block );
|
|
776
785
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
777
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': '
|
|
786
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
778
787
|
} );
|
|
779
788
|
// also add to JOIN nodes for name restrictions:
|
|
780
789
|
for (const p of joinParents) {
|
|
781
|
-
//
|
|
782
|
-
|
|
790
|
+
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
791
|
+
// already used for duplicate aliases of queries:
|
|
792
|
+
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
783
793
|
}
|
|
784
794
|
if (table.name.id[0] === '$') {
|
|
785
795
|
warning( 'syntax-dollar-ident', [ table.name.location, table ], {
|
|
@@ -793,8 +803,8 @@ function getDefinerFunctions( model ) {
|
|
|
793
803
|
|
|
794
804
|
// art is:
|
|
795
805
|
// - entity for top-level queries (including UNION args)
|
|
796
|
-
// - $tableAlias for sub query in FROM
|
|
797
|
-
// - $query for real sub query (in columns, WHERE, ...)
|
|
806
|
+
// - $tableAlias for sub query in FROM - TODO: what about UNION there?
|
|
807
|
+
// - $query for real sub query (in columns, WHERE, ...), again: what about UNION there?
|
|
798
808
|
function initQueryExpression( query, art ) {
|
|
799
809
|
if (!query) // parse error
|
|
800
810
|
return query;
|
|
@@ -846,7 +856,7 @@ function getDefinerFunctions( model ) {
|
|
|
846
856
|
query.kind = 'select';
|
|
847
857
|
query.name = { location: query.location };
|
|
848
858
|
setMemberParent( query, main.$queries.length + 1, main );
|
|
849
|
-
// console.log(
|
|
859
|
+
// console.log(art.kind,art.name,query.name,query._$next.name)
|
|
850
860
|
// if (query.name.query === 1 && query.name.absolute === 'S') throw Error();
|
|
851
861
|
main.$queries.push( query );
|
|
852
862
|
setProp( query, '_parent', art ); // _parent should point to alias/main/query
|
|
@@ -866,7 +876,7 @@ function getDefinerFunctions( model ) {
|
|
|
866
876
|
// assignments on the mixin... (also for future mixin definitions
|
|
867
877
|
// with generated values)
|
|
868
878
|
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
869
|
-
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '
|
|
879
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
870
880
|
} );
|
|
871
881
|
if (mixin.name.id[0] === '$') {
|
|
872
882
|
warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
|
|
@@ -993,6 +1003,7 @@ function getDefinerFunctions( model ) {
|
|
|
993
1003
|
setProp( elem, '_block', bl );
|
|
994
1004
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
995
1005
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1006
|
+
checkRedefinition( elem );
|
|
996
1007
|
defineAnnotations( elem, elem, bl );
|
|
997
1008
|
initMembers( elem, elem, bl, initExtensions );
|
|
998
1009
|
|
|
@@ -1141,7 +1152,8 @@ function getDefinerFunctions( model ) {
|
|
|
1141
1152
|
break; // only direct projection for auto-exposed
|
|
1142
1153
|
}
|
|
1143
1154
|
let ancestors = art && (!autoexposed && art._ancestors || []);
|
|
1144
|
-
|
|
1155
|
+
chain.reverse();
|
|
1156
|
+
for (const a of chain) {
|
|
1145
1157
|
ancestors = (ancestors ? [ ...ancestors, art ] : []);
|
|
1146
1158
|
setProp( a, '_ancestors', ancestors );
|
|
1147
1159
|
art = a;
|
|
@@ -1263,6 +1275,9 @@ function getDefinerFunctions( model ) {
|
|
|
1263
1275
|
}
|
|
1264
1276
|
}
|
|
1265
1277
|
|
|
1278
|
+
/**
|
|
1279
|
+
* @returns {boolean|0} `true`, if allowed, `false` if forbidden, `0` if circular containment.
|
|
1280
|
+
*/
|
|
1266
1281
|
function allowAspectComposition( target, elem, keys, entityName ) {
|
|
1267
1282
|
if (!target.elements || Object.values( target.elements ).some( e => e.$duplicates ))
|
|
1268
1283
|
return false; // no elements or with redefinitions
|
|
@@ -1330,7 +1345,7 @@ function getDefinerFunctions( model ) {
|
|
|
1330
1345
|
name: { path: splitIntoPath( location, entityName ), absolute: entityName, location },
|
|
1331
1346
|
location,
|
|
1332
1347
|
elements,
|
|
1333
|
-
$inferred: '
|
|
1348
|
+
$inferred: 'composition-entity',
|
|
1334
1349
|
};
|
|
1335
1350
|
if (target.name) { // named target aspect
|
|
1336
1351
|
setLink( art, '_origin', target );
|
|
@@ -1509,8 +1524,15 @@ function getDefinerFunctions( model ) {
|
|
|
1509
1524
|
forEachMemberRecursivelyWithQuery(art, checkArtifact);
|
|
1510
1525
|
}
|
|
1511
1526
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1527
|
+
/**
|
|
1528
|
+
* Function for parse.cdl.
|
|
1529
|
+
* This parse.cdl function resolves types inside the artifact ("resolveTypeUnchecked").
|
|
1530
|
+
*
|
|
1531
|
+
* @todo This function needs to be properly reworked because at the moment we simply
|
|
1532
|
+
* added checks as issues arose (e.g. for "artifact.value").
|
|
1533
|
+
*
|
|
1534
|
+
* @param {XSN.Artifact} artifact
|
|
1535
|
+
* */
|
|
1514
1536
|
function checkArtifact( artifact ) {
|
|
1515
1537
|
// columns are initialized (and made to elements) in the resolver - do init here
|
|
1516
1538
|
for (const col of artifact.columns || []) {
|
|
@@ -1521,11 +1543,21 @@ function getDefinerFunctions( model ) {
|
|
|
1521
1543
|
recursivelyResolveExpressionCastTypes(col.value, artifact);
|
|
1522
1544
|
}
|
|
1523
1545
|
|
|
1546
|
+
// Possible inside expand/inline
|
|
1547
|
+
if (artifact.value)
|
|
1548
|
+
recursivelyResolveExpressionCastTypes(artifact.value, artifact);
|
|
1549
|
+
|
|
1524
1550
|
resolveTypeUnchecked(artifact, artifact);
|
|
1525
1551
|
|
|
1526
1552
|
if (artifact.items)
|
|
1527
1553
|
resolveTypeUnchecked(artifact.items, artifact);
|
|
1528
1554
|
|
|
1555
|
+
if (Array.isArray(artifact.expand))
|
|
1556
|
+
artifact.expand.forEach(art => checkArtifact(art));
|
|
1557
|
+
|
|
1558
|
+
if (Array.isArray(artifact.inline))
|
|
1559
|
+
artifact.inline.forEach(art => checkArtifact(art));
|
|
1560
|
+
|
|
1529
1561
|
for (const include of (artifact.includes || []))
|
|
1530
1562
|
resolveUncheckedPath(include, 'include', artifact);
|
|
1531
1563
|
|
|
@@ -1958,24 +1990,6 @@ function getDefinerFunctions( model ) {
|
|
|
1958
1990
|
}
|
|
1959
1991
|
}
|
|
1960
1992
|
|
|
1961
|
-
// TODO: move to "init" phase
|
|
1962
|
-
/**
|
|
1963
|
-
* Check whether redefinitions of the given artifact name exist and
|
|
1964
|
-
* adapt to `targetAspect`.
|
|
1965
|
-
*
|
|
1966
|
-
* @param {string} name
|
|
1967
|
-
*/
|
|
1968
|
-
function preProcessArtifact( name ) {
|
|
1969
|
-
const art = model.definitions[name];
|
|
1970
|
-
if (Array.isArray(art.$duplicates)) {
|
|
1971
|
-
// A definition name containing a `.` is not invalid (TODO: starting or
|
|
1972
|
-
// ending with a dot is invalid and could be checked here)
|
|
1973
|
-
for (const a of art.$duplicates)
|
|
1974
|
-
checkRedefinitions( a, name, 'definitions' );
|
|
1975
|
-
}
|
|
1976
|
-
checkRedefinitions( art, name, 'definitions' );
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
1993
|
/**
|
|
1980
1994
|
* Process "composition of" artifacts.
|
|
1981
1995
|
*
|
|
@@ -2111,7 +2125,7 @@ function getDefinerFunctions( model ) {
|
|
|
2111
2125
|
name: { path: splitIntoPath( location, absolute ), absolute, location },
|
|
2112
2126
|
location: base.location,
|
|
2113
2127
|
elements,
|
|
2114
|
-
$inferred: 'localized',
|
|
2128
|
+
$inferred: 'localized-entity',
|
|
2115
2129
|
};
|
|
2116
2130
|
const locale = {
|
|
2117
2131
|
name: { location, id: 'locale' },
|
|
@@ -2212,7 +2226,7 @@ function getDefinerFunctions( model ) {
|
|
|
2212
2226
|
name: { location, id: 'texts' },
|
|
2213
2227
|
kind: 'element',
|
|
2214
2228
|
location,
|
|
2215
|
-
$inferred: 'localized
|
|
2229
|
+
$inferred: 'localized',
|
|
2216
2230
|
type: augmentPath( location, 'cds.Composition' ),
|
|
2217
2231
|
cardinality: { targetMax: { literal: 'string', val: '*', location }, location },
|
|
2218
2232
|
target: augmentPath( location, textsName ),
|
|
@@ -2227,7 +2241,7 @@ function getDefinerFunctions( model ) {
|
|
|
2227
2241
|
name: { location, id: 'localized' },
|
|
2228
2242
|
kind: 'element',
|
|
2229
2243
|
location,
|
|
2230
|
-
$inferred: 'localized
|
|
2244
|
+
$inferred: 'localized',
|
|
2231
2245
|
type: augmentPath( location, 'cds.Association' ),
|
|
2232
2246
|
target: augmentPath( location, textsName ),
|
|
2233
2247
|
on: augmentEqual( location, 'localized', keys ),
|
|
@@ -2316,36 +2330,6 @@ function mergeI18nBlocks( model ) {
|
|
|
2316
2330
|
}
|
|
2317
2331
|
}
|
|
2318
2332
|
|
|
2319
|
-
/**
|
|
2320
|
-
* Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
|
|
2321
|
-
* locations).
|
|
2322
|
-
*
|
|
2323
|
-
* @param {XSN.Path} path
|
|
2324
|
-
*/
|
|
2325
|
-
function pathName(path) {
|
|
2326
|
-
return path.map( id => id.id ).join('.');
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
/**
|
|
2330
|
-
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
2331
|
-
* Each segment will have the given location assigned.
|
|
2332
|
-
*
|
|
2333
|
-
* @param {CSN.Location} location
|
|
2334
|
-
* @param {string} name
|
|
2335
|
-
* @returns {XSN.Path}
|
|
2336
|
-
*/
|
|
2337
|
-
function splitIntoPath( location, name ) {
|
|
2338
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
/**
|
|
2342
|
-
* @param {CSN.Location} location
|
|
2343
|
-
* @param {...any} args
|
|
2344
|
-
*/
|
|
2345
|
-
function augmentPath( location, ...args ) {
|
|
2346
|
-
return { path: args.map( id => ({ id, location }) ), location };
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
2333
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
2350
2334
|
const args = relations.map( eq );
|
|
2351
2335
|
return (args.length === 1)
|
|
@@ -2374,9 +2358,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
|
2374
2358
|
// these function could be used to a future lib/compiler/utils.js, but DO NOT
|
|
2375
2359
|
// SHARE with utility functions for CSN processors
|
|
2376
2360
|
|
|
2377
|
-
module.exports = {
|
|
2378
|
-
define: model => getDefinerFunctions( model ).define(),
|
|
2379
|
-
getDefinerFunctions,
|
|
2380
|
-
augmentPath,
|
|
2381
|
-
splitIntoPath,
|
|
2382
|
-
};
|
|
2361
|
+
module.exports = { define };
|
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,12 +20,12 @@ 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');
|
|
14
27
|
const check = require('./checks');
|
|
15
28
|
|
|
16
|
-
|
|
17
29
|
const { emptyWeakLocation } = require('../base/location');
|
|
18
30
|
const { createMessageFunctions, deduplicateMessages } = require('../base/messages');
|
|
19
31
|
const { promiseAllDoNotRejectImmediately } = require('../base/node-helpers');
|
|
@@ -52,7 +64,7 @@ class ArgumentError extends Error {
|
|
|
52
64
|
* @param {object} options Compile options
|
|
53
65
|
* @param {object} messageFunctions If not provided, parse errors will not lead to an exception
|
|
54
66
|
*/
|
|
55
|
-
function parseX( source, filename, options = {}, messageFunctions ) {
|
|
67
|
+
function parseX( source, filename, options = {}, messageFunctions = null ) {
|
|
56
68
|
if (!messageFunctions)
|
|
57
69
|
messageFunctions = createMessageFunctions( options, 'parse' );
|
|
58
70
|
const ext = path.extname( filename ).toLowerCase();
|
|
@@ -110,7 +122,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
110
122
|
// fileCache = Object.assign( Object.create(null), fileCache );
|
|
111
123
|
dir = path.resolve(dir);
|
|
112
124
|
const a = processFilenames( filenames, dir );
|
|
113
|
-
a.fileContentDict = Object.create(null);
|
|
114
125
|
|
|
115
126
|
const model = { sources: a.sources, options };
|
|
116
127
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -132,37 +143,25 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
132
143
|
});
|
|
133
144
|
|
|
134
145
|
// Read file `filename` and parse its content, return messages
|
|
135
|
-
function readAndParse( filename ) {
|
|
146
|
+
async function readAndParse( filename ) {
|
|
147
|
+
const { sources } = a;
|
|
136
148
|
if ( filename === false ) // module which has not been found
|
|
137
149
|
return [];
|
|
138
|
-
const rel =
|
|
150
|
+
const rel = sources[filename] || path.relative( dir, filename );
|
|
139
151
|
if (typeof rel === 'object') // already parsed
|
|
140
152
|
return []; // no further dependency processing
|
|
141
153
|
// no parallel readAndParse with same resolved filename should read the file,
|
|
142
|
-
// also ensure deterministic sequence in
|
|
143
|
-
|
|
154
|
+
// also ensure deterministic sequence in sources:
|
|
155
|
+
sources[filename] = { location: { file: rel } };
|
|
144
156
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
154
|
-
a.sources[filename] = ast;
|
|
155
|
-
ast.location = { file: rel };
|
|
156
|
-
ast.dirname = path.dirname( filename );
|
|
157
|
-
assertConsistency( ast, options );
|
|
158
|
-
fulfill( ast );
|
|
159
|
-
}
|
|
160
|
-
catch (e) {
|
|
161
|
-
reject( e );
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
});
|
|
157
|
+
const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
|
|
158
|
+
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
159
|
+
sources[filename] = ast;
|
|
160
|
+
ast.location = { file: rel };
|
|
161
|
+
ast.dirname = path.dirname( filename );
|
|
162
|
+
assertConsistency( ast, options );
|
|
163
|
+
|
|
164
|
+
return ast;
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
// Combine the parse results (if there are not file IO errors)
|
|
@@ -220,7 +219,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
220
219
|
// absolute file names - they start with `/` or `\` or similar
|
|
221
220
|
dir = path.resolve(dir);
|
|
222
221
|
const a = processFilenames( filenames, dir );
|
|
223
|
-
a.fileContentDict = Object.create(null);
|
|
224
222
|
|
|
225
223
|
const model = { sources: a.sources, options };
|
|
226
224
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -281,7 +279,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
281
279
|
}
|
|
282
280
|
else {
|
|
283
281
|
try {
|
|
284
|
-
a.fileContentDict[filename] = source;
|
|
285
282
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
286
283
|
a.sources[filename] = ast;
|
|
287
284
|
ast.location = { file: rel };
|
|
@@ -416,6 +413,9 @@ function compileDoX( model ) {
|
|
|
416
413
|
throwWithError();
|
|
417
414
|
return model;
|
|
418
415
|
}
|
|
416
|
+
model.$functions = {};
|
|
417
|
+
model.$volatileFunctions = {};
|
|
418
|
+
fns( model ); // attach (mostly) paths functions
|
|
419
419
|
define( model );
|
|
420
420
|
// do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
|
|
421
421
|
// TODO: do not use this function for parseCdl anyway…
|
|
@@ -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 ) {
|
|
@@ -19,6 +19,8 @@ function propagate( model ) {
|
|
|
19
19
|
'@cds.persistence.calcview': never,
|
|
20
20
|
'@cds.persistence.udf': never,
|
|
21
21
|
'@cds.persistence.skip': notWithPersistenceTable,
|
|
22
|
+
'@sql.prepend': never,
|
|
23
|
+
'@sql.append': never,
|
|
22
24
|
'@Analytics.hidden': never,
|
|
23
25
|
'@Analytics.visible': never,
|
|
24
26
|
'@cds.autoexpose': onlyViaArtifact,
|
|
@@ -64,8 +66,7 @@ function propagate( model ) {
|
|
|
64
66
|
if (!art)
|
|
65
67
|
return;
|
|
66
68
|
if (!checkAndSetStatus( art )) {
|
|
67
|
-
|
|
68
|
-
runMembers( art );
|
|
69
|
+
runMembers( art );
|
|
69
70
|
return;
|
|
70
71
|
}
|
|
71
72
|
// console.log('RUN:', art.name, art.elements ? Object.keys(art.elements) : 0)
|
|
@@ -179,6 +180,8 @@ function propagate( model ) {
|
|
|
179
180
|
// accessed at their type being a main artifact
|
|
180
181
|
function expensive( prop, target, source ) {
|
|
181
182
|
// console.log(prop,source.name,'->',target.kind,target.name);
|
|
183
|
+
if (source.kind === 'builtin')
|
|
184
|
+
return;
|
|
182
185
|
if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
|
|
183
186
|
// foreignKeys must always be copied with target to avoid any confusion
|
|
184
187
|
// whether we have to generated implicit keys
|
|
@@ -211,7 +214,8 @@ function propagate( model ) {
|
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
function onlyViaParent( prop, target, source ) {
|
|
214
|
-
if (target.$inferred === 'proxy'
|
|
217
|
+
if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
|
|
218
|
+
// assocs and enums do not have 'include'
|
|
215
219
|
always( prop, target, source );
|
|
216
220
|
}
|
|
217
221
|
|