@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
package/lib/compiler/utils.js
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
// Please do not add functions “for completeness”, this is not an API file for
|
|
7
7
|
// others but only by the core compiler.
|
|
8
8
|
|
|
9
|
-
// TODO:
|
|
9
|
+
// TODO: split this file into utils/….js, add some functions from lib/base/model.js
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
|
|
14
14
|
const { kindProperties } = require('./base');
|
|
15
|
+
const { XsnName, XsnArtifact, CsnLocation } = require('./classes');
|
|
16
|
+
|
|
17
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
15
18
|
|
|
16
19
|
// for links, i.e., properties starting with an underscore '_':
|
|
17
20
|
|
|
@@ -106,17 +109,23 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
106
109
|
return elem;
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
function proxyCopyMembers( art, dictProp, originDict, location, kind ) {
|
|
112
|
+
function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDeprecated ) {
|
|
110
113
|
art[dictProp] = Object.create( null );
|
|
114
|
+
// TODO: set $inferred ? for dict?
|
|
111
115
|
for (const name in originDict) {
|
|
112
116
|
const origin = originDict[name];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (origin !== undefined) {
|
|
118
|
+
const member = linkToOrigin( origin, name, art, dictProp,
|
|
119
|
+
location || origin.location, true );
|
|
120
|
+
member.$inferred = 'expanded';
|
|
121
|
+
// TODO throughout the compiler: do not set art.‹prop›.$inferred if art.$inferred
|
|
122
|
+
if (kind)
|
|
123
|
+
member.kind = kind;
|
|
124
|
+
else if (origin.key && !tmpDeprecated) // TODO(v5/v6): remove tmpDeprecated
|
|
125
|
+
member.key = Object.assign( { $inferred: 'expanded' }, origin.key );
|
|
126
|
+
if (kind && origin.masked) // TODO: remove!
|
|
127
|
+
member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
|
|
128
|
+
}
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
|
|
@@ -132,8 +141,8 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
132
141
|
if (prop) { // extension or structure include
|
|
133
142
|
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
134
143
|
const p = parent.items || parent.targetAspect || parent;
|
|
135
|
-
if (
|
|
136
|
-
p[prop] = Object.create(null);
|
|
144
|
+
if (p[prop] === undefined)
|
|
145
|
+
p[prop] = Object.create( null );
|
|
137
146
|
dictAdd( p[prop], name, elem );
|
|
138
147
|
}
|
|
139
148
|
if (parent._outer && parent._outer.items) // TODO: remove for items, too
|
|
@@ -155,7 +164,7 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
155
164
|
|
|
156
165
|
else
|
|
157
166
|
delete elem.name[kind];
|
|
158
|
-
});
|
|
167
|
+
} );
|
|
159
168
|
// try { throw new CompilerAssertion('Foo') } catch (e) { elem.name.stack = e; };
|
|
160
169
|
}
|
|
161
170
|
|
|
@@ -166,10 +175,12 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
166
175
|
* @param {XSN.Artifact} art
|
|
167
176
|
* @param {XSN.Location} location
|
|
168
177
|
*/
|
|
169
|
-
function dependsOn( user, art, location ) {
|
|
178
|
+
function dependsOn( user, art, location, semanticLoc = undefined ) {
|
|
179
|
+
while (user._outer && !user.kind)
|
|
180
|
+
user = user._outer;
|
|
170
181
|
if (!user._deps)
|
|
171
182
|
setLink( user, '_deps', [] );
|
|
172
|
-
user._deps.push( { art, location } );
|
|
183
|
+
user._deps.push( { art, location, semanticLoc } );
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
/**
|
|
@@ -180,6 +191,8 @@ function dependsOn( user, art, location ) {
|
|
|
180
191
|
* @param {XSN.Artifact} art
|
|
181
192
|
*/
|
|
182
193
|
function dependsOnSilent( user, art ) {
|
|
194
|
+
while (user._outer && !user.kind)
|
|
195
|
+
user = user._outer;
|
|
183
196
|
if (!user._deps)
|
|
184
197
|
setLink( user, '_deps', [] );
|
|
185
198
|
user._deps.push( { art } );
|
|
@@ -197,7 +210,7 @@ function storeExtension( elem, name, prop, parent, block ) {
|
|
|
197
210
|
// return;
|
|
198
211
|
// }
|
|
199
212
|
if (!parent[kind][prop])
|
|
200
|
-
parent[kind][prop] = Object.create(null);
|
|
213
|
+
parent[kind][prop] = Object.create( null );
|
|
201
214
|
pushToDict( parent[kind][prop], name, elem );
|
|
202
215
|
}
|
|
203
216
|
|
|
@@ -223,7 +236,7 @@ function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = fa
|
|
|
223
236
|
* @param {XSN.Path} path
|
|
224
237
|
*/
|
|
225
238
|
function pathName( path ) {
|
|
226
|
-
return (path && !path.broken) ? path.map( id => id.id ).join('.') : '';
|
|
239
|
+
return (path && !path.broken) ? path.map( id => id.id ).join( '.' ) : '';
|
|
227
240
|
}
|
|
228
241
|
|
|
229
242
|
/**
|
|
@@ -235,7 +248,7 @@ function pathName( path ) {
|
|
|
235
248
|
* @returns {XSN.Path}
|
|
236
249
|
*/
|
|
237
250
|
function splitIntoPath( location, name ) {
|
|
238
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
251
|
+
return name.split( '.' ).map( id => ({ id, location }) );
|
|
239
252
|
}
|
|
240
253
|
|
|
241
254
|
/**
|
|
@@ -249,11 +262,13 @@ function augmentPath( location, ...args ) {
|
|
|
249
262
|
function copyExpr( expr, location, skipUnderscored, rewritePath ) {
|
|
250
263
|
if (!expr || typeof expr !== 'object')
|
|
251
264
|
return expr;
|
|
252
|
-
else if (Array.isArray(expr))
|
|
265
|
+
else if (Array.isArray( expr ))
|
|
253
266
|
return expr.map( e => copyExpr( e, location, skipUnderscored, rewritePath ) );
|
|
254
267
|
|
|
255
268
|
const proto = Object.getPrototypeOf( expr );
|
|
256
|
-
if (proto && proto !== Object.prototype
|
|
269
|
+
if (proto && proto !== Object.prototype && proto !== XsnName.prototype &&
|
|
270
|
+
// do not copy object from special classes outside the compiler domain&&
|
|
271
|
+
proto !== XsnArtifact.prototype && proto !== CsnLocation.prototype)
|
|
257
272
|
return expr;
|
|
258
273
|
const r = Object.create( proto );
|
|
259
274
|
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
@@ -284,7 +299,7 @@ function testExpr( expr, pathTest, queryTest, user ) {
|
|
|
284
299
|
if (!expr || typeof expr === 'string') { // parse error or keywords in {xpr:...}
|
|
285
300
|
return false;
|
|
286
301
|
}
|
|
287
|
-
else if (Array.isArray(expr)) {
|
|
302
|
+
else if (Array.isArray( expr )) {
|
|
288
303
|
return expr.some( e => testExpr( e, pathTest, queryTest, user ) );
|
|
289
304
|
}
|
|
290
305
|
else if (expr.path) {
|
|
@@ -295,11 +310,11 @@ function testExpr( expr, pathTest, queryTest, user ) {
|
|
|
295
310
|
}
|
|
296
311
|
else if (expr.op && expr.args) {
|
|
297
312
|
// unnamed args => array
|
|
298
|
-
if (Array.isArray(expr.args))
|
|
313
|
+
if (Array.isArray( expr.args ))
|
|
299
314
|
return expr.args.some( e => testExpr( e, pathTest, queryTest, user ) );
|
|
300
315
|
// named args => dictionary
|
|
301
|
-
for (const namedArg of Object.keys(expr.args)) {
|
|
302
|
-
if (testExpr(expr.args[namedArg], pathTest, queryTest, user))
|
|
316
|
+
for (const namedArg of Object.keys( expr.args )) {
|
|
317
|
+
if (testExpr( expr.args[namedArg], pathTest, queryTest, user ))
|
|
303
318
|
return true;
|
|
304
319
|
}
|
|
305
320
|
}
|
|
@@ -314,6 +329,129 @@ function targetMaxNotOne( assoc, item ) {
|
|
|
314
329
|
return cardinality && cardinality.targetMax && cardinality.targetMax.val !== 1;
|
|
315
330
|
}
|
|
316
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Call function `callback(art)` for each user-defined main artifact and member
|
|
334
|
+
* `art` of the model reachable from the dictionary `model[prop]`. User-defined
|
|
335
|
+
* artifacts are those with no or a falsy `art.$inferred` value, i.e. this
|
|
336
|
+
* function is useful for checks.
|
|
337
|
+
*
|
|
338
|
+
* The callback function is not called on the following artifacts:
|
|
339
|
+
* - `enum` symbol definitions (use forEachUserDict() yourself if needed)
|
|
340
|
+
* - the anonymous aspect in the `target`/`targetAspect` property (but the
|
|
341
|
+
* callback function is called on its elements).
|
|
342
|
+
* - table aliases
|
|
343
|
+
*
|
|
344
|
+
* The callback function is also called on duplicates. For example, if there are
|
|
345
|
+
* two entities named `E`, the callback function is called on both.
|
|
346
|
+
* It is also called on columns with `inline`.
|
|
347
|
+
*
|
|
348
|
+
* See also function forEachDefinition(), currently in lib/base/model.js.
|
|
349
|
+
*/
|
|
350
|
+
function forEachUserArtifact( model, prop, callback ) { // not enums
|
|
351
|
+
forEachUserDict( model, prop, function main( art ) {
|
|
352
|
+
callback( art );
|
|
353
|
+
forEachUserDict( art, 'params', function param( par ) {
|
|
354
|
+
callback( par );
|
|
355
|
+
forEachUserElementAndFKey( par, callback );
|
|
356
|
+
} );
|
|
357
|
+
if (art.$queries) {
|
|
358
|
+
for (const query of art.$queries) {
|
|
359
|
+
callback( query );
|
|
360
|
+
forEachUserDict( query, 'mixin', callback );
|
|
361
|
+
forEachUserElementAndFKey( query, callback );
|
|
362
|
+
if (query.$inlines) // e.g. not with `entity V as projection on V;`
|
|
363
|
+
query.$inlines.forEach( callback );
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else if (art.returns) {
|
|
367
|
+
callback( art.returns );
|
|
368
|
+
forEachUserElementAndFKey( art.returns, callback );
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
forEachUserElementAndFKey( art, callback );
|
|
372
|
+
}
|
|
373
|
+
forEachUserArtifact( art, 'actions', callback );
|
|
374
|
+
} );
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Call function `callback(art)` for each user-defined element and foreign key
|
|
379
|
+
* reachable from artifact `art`. Do not (again) call the callback function on
|
|
380
|
+
* `art` itself, even if it is an element.
|
|
381
|
+
*
|
|
382
|
+
* Consider that we have (nested) `array of`/`many` types, but do not call the
|
|
383
|
+
* callback function on the array item itself (only on elements inside).
|
|
384
|
+
*/
|
|
385
|
+
function forEachUserElementAndFKey( art, callback ) {
|
|
386
|
+
while (art.items)
|
|
387
|
+
art = art.items;
|
|
388
|
+
if (art.target) {
|
|
389
|
+
forEachUserDict( art, 'foreignKeys', callback );
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (art.targetAspect)
|
|
393
|
+
art = art.targetAspect;
|
|
394
|
+
forEachUserDict( art, 'elements', function element( elem ) {
|
|
395
|
+
callback( elem );
|
|
396
|
+
forEachUserElementAndFKey( elem, callback );
|
|
397
|
+
} );
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function forEachUserDict( art, prop, callback ) {
|
|
401
|
+
const dict = art[prop];
|
|
402
|
+
if (!dict || dict[$inferred])
|
|
403
|
+
return;
|
|
404
|
+
for (const name in dict) {
|
|
405
|
+
const obj = dict[name];
|
|
406
|
+
if (obj.$inferred)
|
|
407
|
+
continue;
|
|
408
|
+
callback( obj, name, prop );
|
|
409
|
+
if (Array.isArray( obj.$duplicates )) // redefinitions
|
|
410
|
+
obj.$duplicates.forEach( o => callback( o, name, prop ) );
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Call `callback( expr, exprCtx, query )` on all direct expressions `expr` of
|
|
416
|
+
* `query`, where `exprCtx` is the expression context used as key for the
|
|
417
|
+
* `referenceSemantics` in shared.js.
|
|
418
|
+
*
|
|
419
|
+
* Indirect expressions are not called, these are:
|
|
420
|
+
* - the `from` reference (expression of the table alias)
|
|
421
|
+
* - the ON-condition of mixins (expression of the mixin)
|
|
422
|
+
* - the expressions in columns (expression of the column/element)
|
|
423
|
+
*/
|
|
424
|
+
function forEachQueryExpr( query, callback ) { // see resolveQuery()
|
|
425
|
+
forEachJoinOn( query, query.from, callback );
|
|
426
|
+
// TODO: run over $inlines ?
|
|
427
|
+
if (query.where)
|
|
428
|
+
callback( query.where, 'where', query );
|
|
429
|
+
if (query.groupBy)
|
|
430
|
+
forEachExprArray( query, query.groupBy, 'groupBy', 'groupBy', callback );
|
|
431
|
+
if (query.having)
|
|
432
|
+
callback( query.having, 'having', query );
|
|
433
|
+
if (query.$orderBy)
|
|
434
|
+
forEachExprArray( query, query.$orderBy, 'orderBy-set-ref', 'orderBy-set-expr', callback );
|
|
435
|
+
if (query.orderBy)
|
|
436
|
+
forEachExprArray( query, query.orderBy, 'orderBy-ref', 'orderBy-expr', callback );
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function forEachJoinOn( query, from, callback ) {
|
|
440
|
+
if (!from?.join)
|
|
441
|
+
return; // TODO: run over from.path here?
|
|
442
|
+
for (const tab of from.args)
|
|
443
|
+
forEachJoinOn( query, tab, callback );
|
|
444
|
+
if (from.on)
|
|
445
|
+
callback( from.on, 'join-on', query );
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function forEachExprArray( query, array, refContext, exprContext, callback ) {
|
|
449
|
+
for (const expr of array ) {
|
|
450
|
+
if (expr)
|
|
451
|
+
callback( expr, (expr.path ? refContext : exprContext), query );
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
317
455
|
// Query tree post-order traversal - called for everything which contributes to the query
|
|
318
456
|
// i.e. is necessary to calculate the elements of the query
|
|
319
457
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
@@ -442,28 +580,6 @@ function isDirectComposition( art ) {
|
|
|
442
580
|
return type && type[0] && type[0].id === 'cds.Composition';
|
|
443
581
|
}
|
|
444
582
|
|
|
445
|
-
function traverseExpr( expr, exprCtx, user, callback ) {
|
|
446
|
-
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
447
|
-
return;
|
|
448
|
-
|
|
449
|
-
if (expr.path) {
|
|
450
|
-
callback( expr, exprCtx, user );
|
|
451
|
-
// TODO: move arguments and filter traversal to here
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
else if (expr.type || expr.query) {
|
|
455
|
-
callback( expr, exprCtx, user );
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (expr.args) {
|
|
459
|
-
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
460
|
-
// TODO: re-think $expected
|
|
461
|
-
args.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
462
|
-
}
|
|
463
|
-
if (expr.suffix) // fn( … ) OVER …
|
|
464
|
-
expr.suffix.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
465
|
-
}
|
|
466
|
-
|
|
467
583
|
function userQuery( user ) {
|
|
468
584
|
// TODO: we need _query links set by the definer
|
|
469
585
|
while (user._main) {
|
|
@@ -474,6 +590,63 @@ function userQuery( user ) {
|
|
|
474
590
|
return null;
|
|
475
591
|
}
|
|
476
592
|
|
|
593
|
+
function pathStartsWithSelf( ref ) {
|
|
594
|
+
const head = ref && !ref.scope && ref.path?.[0];
|
|
595
|
+
if (head?._navigation?.kind === '$self')
|
|
596
|
+
return true;
|
|
597
|
+
if (head?._artifact?.kind === 'builtin') // CDS variable
|
|
598
|
+
return false;
|
|
599
|
+
return undefined;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function columnRefStartsWithSelf( col ) {
|
|
603
|
+
for (; col; col = col._pathHead) {
|
|
604
|
+
const ref = col.value;
|
|
605
|
+
const head = ref && !ref.scope && ref.path?.[0];
|
|
606
|
+
if (head?._navigation?.kind === '$self')
|
|
607
|
+
return true;
|
|
608
|
+
if (head?._artifact?.kind === 'builtin') // CDS variable
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Remark: this function is based on an early check that no target element is
|
|
615
|
+
// covered more than once by a foreign key: then…
|
|
616
|
+
// we only need to check that all foreign key references are primary keys and
|
|
617
|
+
// that the number of foreign and primary keys are the same.
|
|
618
|
+
function isAssocToPrimaryKeys( assoc ) {
|
|
619
|
+
let fkeys = 0;
|
|
620
|
+
const { foreignKeys } = assoc;
|
|
621
|
+
if (!foreignKeys)
|
|
622
|
+
return undefined;
|
|
623
|
+
for (const name in foreignKeys) {
|
|
624
|
+
const fk = foreignKeys[name];
|
|
625
|
+
const elem = fk.targetElement._artifact;
|
|
626
|
+
if (!elem || fk.$duplicates)
|
|
627
|
+
return undefined;
|
|
628
|
+
if (!elem.key?.val)
|
|
629
|
+
return false;
|
|
630
|
+
++fkeys;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const elements = assoc.target._artifact?.elements;
|
|
634
|
+
if (!elements)
|
|
635
|
+
return undefined;
|
|
636
|
+
for (const name in elements) {
|
|
637
|
+
if (elements[name].key?.val)
|
|
638
|
+
--fkeys;
|
|
639
|
+
}
|
|
640
|
+
return fkeys === 0;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// only if _effectiveType has been computed:
|
|
644
|
+
function getUnderlyingBuiltinType( art ) {
|
|
645
|
+
while (art?._effectiveType && !art.builtin)
|
|
646
|
+
art = art._origin || art.type?._artifact;
|
|
647
|
+
return art;
|
|
648
|
+
}
|
|
649
|
+
|
|
477
650
|
function definedViaCdl( art ) {
|
|
478
651
|
// return !!art._block?.artifacts;
|
|
479
652
|
// TODO: the above code would work when _block links are correctly set on
|
|
@@ -483,6 +656,21 @@ function definedViaCdl( art ) {
|
|
|
483
656
|
return $frontend !== 'json' && $frontend !== '$internal';
|
|
484
657
|
}
|
|
485
658
|
|
|
659
|
+
// For error messages: ----------------------------------------------------------
|
|
660
|
+
|
|
661
|
+
// (To be) used for the location in error messages
|
|
662
|
+
function artifactRefLocation( ref ) {
|
|
663
|
+
return (ref._artifact?._main)
|
|
664
|
+
? ref.path[ref.path.length - 1].location
|
|
665
|
+
: ref.location;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function compositionTextVariant( art, composition, association = 'std' ) {
|
|
669
|
+
return (getUnderlyingBuiltinType( art )?.name.absolute === 'cds.Composition')
|
|
670
|
+
? composition
|
|
671
|
+
: association;
|
|
672
|
+
}
|
|
673
|
+
|
|
486
674
|
module.exports = {
|
|
487
675
|
pushLink,
|
|
488
676
|
annotationVal,
|
|
@@ -505,13 +693,20 @@ module.exports = {
|
|
|
505
693
|
copyExpr,
|
|
506
694
|
testExpr,
|
|
507
695
|
targetMaxNotOne,
|
|
696
|
+
forEachUserArtifact,
|
|
697
|
+
forEachQueryExpr,
|
|
508
698
|
traverseQueryPost,
|
|
509
699
|
traverseQueryExtra,
|
|
510
700
|
viewFromPrimary,
|
|
511
701
|
setExpandStatus,
|
|
512
702
|
setExpandStatusAnnotate,
|
|
513
703
|
isDirectComposition,
|
|
514
|
-
traverseExpr,
|
|
515
704
|
userQuery,
|
|
705
|
+
pathStartsWithSelf,
|
|
706
|
+
columnRefStartsWithSelf,
|
|
707
|
+
isAssocToPrimaryKeys,
|
|
708
|
+
getUnderlyingBuiltinType,
|
|
516
709
|
definedViaCdl,
|
|
710
|
+
artifactRefLocation,
|
|
711
|
+
compositionTextVariant,
|
|
517
712
|
};
|
package/lib/edm/.eslintrc.json
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
+
"root": true,
|
|
3
|
+
//"plugins": ["sonarjs", "jsdoc"],
|
|
4
|
+
//"extends": ["plugin:jsdoc/recommended", "../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
|
|
5
|
+
"plugins": ["sonarjs"],
|
|
6
|
+
"extends": ["../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
|
|
2
7
|
"rules": {
|
|
3
|
-
"
|
|
8
|
+
"prefer-const": "error",
|
|
9
|
+
"quotes": ["error", "single", "avoid-escape"],
|
|
10
|
+
"prefer-template": "error",
|
|
11
|
+
"no-trailing-spaces": "error",
|
|
12
|
+
"template-curly-spacing":["error", "never"],
|
|
13
|
+
"complexity": ["warn", 50],
|
|
14
|
+
"max-len": "off",
|
|
15
|
+
// there seem to be false positives
|
|
16
|
+
"jsdoc/require-returns-check": "off",
|
|
17
|
+
// Don't enforce stupid descriptions
|
|
18
|
+
"jsdoc/require-param-description": "off",
|
|
19
|
+
"jsdoc/require-returns-description": "off",
|
|
20
|
+
// Sometimes if-else's are more specific
|
|
21
|
+
"sonarjs/prefer-single-boolean-return": "off",
|
|
22
|
+
// Very whiny and nitpicky
|
|
23
|
+
"sonarjs/cognitive-complexity": "off",
|
|
24
|
+
"sonarjs/no-duplicate-string": "off",
|
|
25
|
+
// Does not recognize TS types
|
|
26
|
+
"jsdoc/no-undefined-types": "off",
|
|
27
|
+
"jsdoc/tag-lines": "off",
|
|
28
|
+
"no-nested-ternary": "off",
|
|
29
|
+
"sonarjs/no-nested-template-literals": "off"
|
|
30
|
+
},
|
|
31
|
+
"parserOptions": {
|
|
32
|
+
"ecmaVersion": 2022,
|
|
33
|
+
"sourceType": "script"
|
|
34
|
+
},
|
|
35
|
+
"env": {
|
|
36
|
+
"es2022": true,
|
|
37
|
+
"node": true
|
|
38
|
+
},
|
|
39
|
+
"settings": {
|
|
40
|
+
"jsdoc": {
|
|
41
|
+
"mode": "typescript"
|
|
42
|
+
}
|
|
4
43
|
}
|
|
5
44
|
}
|