@sap/cds-compiler 6.6.0 → 6.7.1
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 +34 -1
- package/bin/cdsc.js +2 -0
- package/bin/cdsse.js +1 -1
- package/lib/base/message-registry.js +6 -7
- package/lib/base/model.js +0 -72
- package/lib/checks/elements.js +1 -1
- package/lib/checks/featureFlags.js +2 -2
- package/lib/compiler/assert-consistency.js +3 -4
- package/lib/compiler/base.js +8 -0
- package/lib/compiler/builtins.js +8 -9
- package/lib/compiler/checks.js +27 -6
- package/lib/compiler/cycle-detector.js +4 -4
- package/lib/compiler/define.js +65 -83
- package/lib/compiler/extend.js +357 -325
- package/lib/compiler/finalize-parse-cdl.js +3 -4
- package/lib/compiler/generate.js +205 -203
- package/lib/compiler/kick-start.js +34 -49
- package/lib/compiler/populate.js +95 -28
- package/lib/compiler/propagator.js +3 -5
- package/lib/compiler/resolve.js +17 -13
- package/lib/compiler/shared.js +47 -19
- package/lib/compiler/tweak-assocs.js +2 -4
- package/lib/compiler/utils.js +84 -31
- package/lib/gen/BaseParser.js +924 -1055
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +5 -2
- package/lib/json/from-csn.js +25 -16
- package/lib/main.d.ts +13 -0
- package/lib/model/revealInternalProperties.js +18 -0
- package/lib/parsers/AstBuildingParser.js +22 -5
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/utils/sql.js +2 -2
- package/lib/render/utils/standardDatabaseFunctions.js +2 -2
- package/lib/transform/db/constraints.js +3 -4
- package/lib/transform/db/killAnnotations.js +1 -1
- package/lib/transform/db/processSqlServices.js +10 -11
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/forOdata.js +7 -124
- package/lib/transform/odata/fioriTreeViews.js +173 -0
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +7 -4
- package/package.json +1 -1
- package/share/messages/message-explanations.json +0 -2
- package/share/messages/type-unexpected-foreign-keys.md +0 -52
- package/share/messages/type-unexpected-on-condition.md +0 -52
package/lib/compiler/shared.js
CHANGED
|
@@ -44,7 +44,7 @@ function fns( model ) {
|
|
|
44
44
|
const referenceSemantics = {
|
|
45
45
|
// global: ------------------------------------------------------------------
|
|
46
46
|
using: { // only used to produce error message
|
|
47
|
-
isMainRef: '
|
|
47
|
+
isMainRef: 'no-leaf-gap',
|
|
48
48
|
lexical: null,
|
|
49
49
|
dynamic: modelDefinitions,
|
|
50
50
|
notFound: undefinedDefinition,
|
|
@@ -58,14 +58,14 @@ function fns( model ) {
|
|
|
58
58
|
},
|
|
59
59
|
// only used for the main annotate/extend statements, not inner ones:
|
|
60
60
|
annotate: {
|
|
61
|
-
isMainRef: '
|
|
61
|
+
isMainRef: 'no-leaf-gap',
|
|
62
62
|
lexical: userBlock,
|
|
63
63
|
dynamic: modelDefinitions,
|
|
64
64
|
notFound: undefinedForAnnotate,
|
|
65
65
|
accept: extendableArtifact,
|
|
66
66
|
},
|
|
67
67
|
'annotate-sec': {
|
|
68
|
-
isMainRef: '
|
|
68
|
+
isMainRef: 'no-leaf-gap',
|
|
69
69
|
lexical: userBlock,
|
|
70
70
|
dynamic: modelDefinitions,
|
|
71
71
|
notFound: undefinedDefinition,
|
|
@@ -660,7 +660,6 @@ function fns( model ) {
|
|
|
660
660
|
if (!root)
|
|
661
661
|
return setArtifactLink( ref, root );
|
|
662
662
|
|
|
663
|
-
// how many path items are for artifacts (rest: elements)
|
|
664
663
|
let art = getPathItem( ref, semantics, user );
|
|
665
664
|
if (!art)
|
|
666
665
|
return setArtifactLink( ref, art );
|
|
@@ -838,16 +837,19 @@ function fns( model ) {
|
|
|
838
837
|
const dynamicDict = semantics.dynamic( ruser, user._user && user._artifact );
|
|
839
838
|
if (!dynamicDict) // avoid consequential errors
|
|
840
839
|
return setArtifactLink( head, null );
|
|
840
|
+
|
|
841
841
|
const isVar = (semantics.dollar && head.id.charAt( 0 ) === '$');
|
|
842
842
|
const dict = (isVar) ? model.$magicVariables.elements : dynamicDict;
|
|
843
|
-
const r = dict[head.id]
|
|
843
|
+
const r = dict[head.id] || // or a ref like "MyEntity.texts" in CSN
|
|
844
|
+
isMainRef && Functions.generateOnDemand?.( head.id );
|
|
844
845
|
if (r)
|
|
845
846
|
return setArtifactLink( head, r );
|
|
846
847
|
|
|
847
848
|
if (!semantics.dollar) {
|
|
848
849
|
valid.push( dynamicDict );
|
|
850
|
+
const noGen = !definedViaCdl( ruser );
|
|
849
851
|
if (isMainRef) // eslint-disable-next-line no-return-assign
|
|
850
|
-
valid.forEach( ( d, idx ) => (valid[idx] = removeGapArtifact( d )) );
|
|
852
|
+
valid.forEach( ( d, idx ) => (valid[idx] = removeGapArtifact( d, noGen )) );
|
|
851
853
|
}
|
|
852
854
|
else {
|
|
853
855
|
const filterFn = semantics.variableFilter || removeRestrictedVariables;
|
|
@@ -871,6 +873,7 @@ function fns( model ) {
|
|
|
871
873
|
function getPathItem( ref, semantics, user ) {
|
|
872
874
|
// let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
|
|
873
875
|
const { path } = ref;
|
|
876
|
+
// how many path items are for artifacts (the rest are for elements):
|
|
874
877
|
let artItemsCount = 0;
|
|
875
878
|
const { isMainRef } = semantics;
|
|
876
879
|
if (isMainRef) {
|
|
@@ -894,7 +897,10 @@ function fns( model ) {
|
|
|
894
897
|
const envFn = (artItemsCount >= 0) ? artifactsEnv : elementsEnv;
|
|
895
898
|
// TOOD: call envFn with location of last item (for dependency error)
|
|
896
899
|
const env = envFn( art, path[index - 1].location, user );
|
|
897
|
-
|
|
900
|
+
let found = env && env[item.id]; // not env?.[…] - keep a `0`
|
|
901
|
+
if (!found && found !== 0 && artItemsCount >= 0)
|
|
902
|
+
found = Functions.generateOnDemand?.( `${ prev.name.id }.${ item.id }` );
|
|
903
|
+
|
|
898
904
|
// Reject `$self.$_column_1`: TODO: necessary to do here again?
|
|
899
905
|
art = setArtifactLink( item, (found?.name?.$inferred === '$internal') ? undefined : found );
|
|
900
906
|
|
|
@@ -904,24 +910,33 @@ function fns( model ) {
|
|
|
904
910
|
const notFound = (artItemsCount >= 0) ? semantics.notFound : undefinedItemElement;
|
|
905
911
|
// TODO: streamline function arguments (probably: user, path, semantics, prev )
|
|
906
912
|
// false returned by semantics.navigation: no further error:
|
|
907
|
-
if (env !== false)
|
|
908
|
-
|
|
913
|
+
if (env !== false) {
|
|
914
|
+
const exp = (artItemsCount >= 0)
|
|
915
|
+
? removeGapArtifact( env, !definedViaCdl( user ) )
|
|
916
|
+
: env;
|
|
917
|
+
notFound( user, item, [ exp ], null, prev, path, semantics );
|
|
918
|
+
}
|
|
909
919
|
return null;
|
|
910
920
|
}
|
|
911
921
|
// need to do that here, because we also need to disallow Service.AutoExposed:elem
|
|
912
922
|
// TODO: but Service.AutoExposed.NotAuto should be fine
|
|
913
923
|
if (isMainRef && isMainRef !== 'all' && artItemsCount === 0) {
|
|
914
924
|
if (art.kind === 'namespace') {
|
|
925
|
+
if (Functions.generateOnDemand?.( art ) ||
|
|
926
|
+
isMainRef === 'no-leaf-gap' && art._subArtifacts)
|
|
927
|
+
continue; // art is generated entity assigned to gap object
|
|
915
928
|
if (env !== false) {
|
|
916
|
-
semantics.notFound( user, item, [ removeGapArtifact( env ) ],
|
|
929
|
+
semantics.notFound( user, item, [ removeGapArtifact( env, !definedViaCdl( user ) ) ],
|
|
917
930
|
null, prev, path, semantics );
|
|
918
931
|
}
|
|
919
932
|
return null;
|
|
920
933
|
}
|
|
921
|
-
else if (art.$inferred === 'autoexposed' && !user.$inferred
|
|
934
|
+
else if (art.$inferred === 'autoexposed' && !user.$inferred &&
|
|
935
|
+
isMainRef !== 'no-leaf-gap') {
|
|
922
936
|
// Depending on the processing sequence, the following could be a
|
|
923
937
|
// simple 'ref-undefined-art'/'ref-undefined-def' - TODO: which we
|
|
924
938
|
// could "change" to this message at the end of compile():
|
|
939
|
+
// HINT: this does not appear anymore
|
|
925
940
|
error( 'ref-unexpected-autoexposed', [ item.location, user ], { art },
|
|
926
941
|
'An auto-exposed entity can\'t be referred to - expose entity $(ART) explicitly' );
|
|
927
942
|
return null; // continuation semantics: like “not found”
|
|
@@ -983,9 +998,8 @@ function fns( model ) {
|
|
|
983
998
|
|
|
984
999
|
switch (art.kind) {
|
|
985
1000
|
case 'using': {
|
|
986
|
-
const def = model.definitions[art.extern.id]
|
|
987
|
-
|
|
988
|
-
return def;
|
|
1001
|
+
const def = model.definitions[art.extern.id] ||
|
|
1002
|
+
Functions.createGapArtifact( art.extern.id, art.extern.location );
|
|
989
1003
|
if (def.$duplicates)
|
|
990
1004
|
return false;
|
|
991
1005
|
art = setArtifactLink( head, def ); // we do not want to see the using
|
|
@@ -994,15 +1008,21 @@ function fns( model ) {
|
|
|
994
1008
|
}
|
|
995
1009
|
/* FALLTHROUGH */
|
|
996
1010
|
case 'namespace': {
|
|
997
|
-
if (semantics.isMainRef === 'all' ||
|
|
1011
|
+
if (semantics.isMainRef === 'all' ||
|
|
1012
|
+
semantics.isMainRef === 'no-leaf-gap' && art._subArtifacts ||
|
|
1013
|
+
path.length !== 1 && ref.scope !== 1 ||
|
|
1014
|
+
// "MyEntity.texts" in CSN or via `using` which is (at the moment)
|
|
1015
|
+
// just a gap artifact
|
|
1016
|
+
Functions.generateOnDemand?.( art ))
|
|
998
1017
|
return art;
|
|
1018
|
+
|
|
999
1019
|
const valid = [];
|
|
1000
1020
|
const lexical = userBlock( user );
|
|
1001
1021
|
if (lexical) {
|
|
1002
1022
|
for (let env = lexical; env; env = env._block)
|
|
1003
1023
|
valid.push( removeGapArtifact( env.artifacts || Object.create( null ) ) );
|
|
1004
1024
|
}
|
|
1005
|
-
valid.push( removeGapArtifact( model.definitions ) );
|
|
1025
|
+
valid.push( removeGapArtifact( model.definitions, !definedViaCdl( user ) ) );
|
|
1006
1026
|
semantics.notFound?.( user._user || user, head, valid, model.definitions,
|
|
1007
1027
|
null, path, semantics );
|
|
1008
1028
|
|
|
@@ -1488,7 +1508,7 @@ function fns( model ) {
|
|
|
1488
1508
|
}
|
|
1489
1509
|
else {
|
|
1490
1510
|
const target = art._effectiveType?.target;
|
|
1491
|
-
if (target
|
|
1511
|
+
if (resolvePath( target, 'target', art )) {
|
|
1492
1512
|
signalNotFound( 'ref-undefined-element', [ item.location, user ], valid,
|
|
1493
1513
|
{ '#': 'target', art: target, id: item.id }, semantics );
|
|
1494
1514
|
}
|
|
@@ -1622,6 +1642,9 @@ function fns( model ) {
|
|
|
1622
1642
|
}
|
|
1623
1643
|
|
|
1624
1644
|
function acceptStructOrBare( art, user, ref ) { // for includes[]
|
|
1645
|
+
// eslint-disable-next-line @stylistic/object-curly-newline
|
|
1646
|
+
if (user.kind in { context: 1, service: 1, action: 1, function: 1 })
|
|
1647
|
+
return false;
|
|
1625
1648
|
// It had been checked before that `includes` is already forbidden for
|
|
1626
1649
|
// non-entity/aspect/type/event.
|
|
1627
1650
|
//
|
|
@@ -1650,6 +1673,9 @@ function fns( model ) {
|
|
|
1650
1673
|
// We might allow includes with elements in the future, they'd probably
|
|
1651
1674
|
// count as specified elements with lower priority, i.e. annos, types, key
|
|
1652
1675
|
// etc on columns beat those inherited from the include.
|
|
1676
|
+
// TODO v7 forbid `extend Proj with ZeroElemAspect` even if ZeroElemAspect has
|
|
1677
|
+
// 0 elements. Making the element-length check work requires that effectiveType()
|
|
1678
|
+
// has been called on the include already before.
|
|
1653
1679
|
if (art.kind === 'aspect' &&
|
|
1654
1680
|
(!art.elements || base.query && !Object.keys( art.elements ).length))
|
|
1655
1681
|
return art;
|
|
@@ -2181,12 +2207,14 @@ function removeDollarNames( dict ) {
|
|
|
2181
2207
|
return r;
|
|
2182
2208
|
}
|
|
2183
2209
|
|
|
2184
|
-
function removeGapArtifact( dict ) {
|
|
2210
|
+
function removeGapArtifact( dict, alsoRemoveInferred ) {
|
|
2185
2211
|
const r = Object.create( null );
|
|
2186
2212
|
for (const name in dict) {
|
|
2187
2213
|
const art = dict[name];
|
|
2188
|
-
// TODO: for gaps with sub artifacts,
|
|
2214
|
+
// TODO: for gaps with sub artifacts, use `${name}.` as name
|
|
2189
2215
|
// TODO: clarify with LSP
|
|
2216
|
+
if (alsoRemoveInferred && art.$inferred)
|
|
2217
|
+
continue;
|
|
2190
2218
|
if (art.kind !== 'namespace' || art._subArtifacts)
|
|
2191
2219
|
r[name] = dict[name];
|
|
2192
2220
|
}
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const {
|
|
6
|
-
forEachGeneric,
|
|
7
|
-
forEachInOrder,
|
|
8
|
-
} = require('../base/model');
|
|
9
5
|
const { dictLocation, weakLocation, weakRefLocation } = require('../base/location');
|
|
10
6
|
|
|
11
7
|
const {
|
|
@@ -19,6 +15,8 @@ const {
|
|
|
19
15
|
traverseQueryExtra,
|
|
20
16
|
setExpandStatus,
|
|
21
17
|
getUnderlyingBuiltinType,
|
|
18
|
+
forEachGeneric,
|
|
19
|
+
forEachInOrder,
|
|
22
20
|
} = require('./utils');
|
|
23
21
|
const { Location } = require('../base/location');
|
|
24
22
|
const { CompilerAssertion } = require('../base/error');
|
package/lib/compiler/utils.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const { dictAdd,
|
|
13
|
+
const { dictAdd, dictFirst } = require('../base/dictionaries');
|
|
14
14
|
const { Location, weakLocation } = require('../base/location');
|
|
15
15
|
const { XsnName, XsnArtifact } = require('./xsn-model');
|
|
16
16
|
|
|
@@ -145,10 +145,11 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
|
|
|
145
145
|
* @return {XSN.Artifact}
|
|
146
146
|
*/
|
|
147
147
|
function initItemsLinks( obj, block ) {
|
|
148
|
+
const parent = obj._parent;
|
|
148
149
|
let { items } = obj;
|
|
149
150
|
while (items) {
|
|
150
151
|
setLink( items, '_outer', obj );
|
|
151
|
-
setLink( items, '_parent',
|
|
152
|
+
setLink( items, '_parent', parent );
|
|
152
153
|
setLink( items, '_block', block );
|
|
153
154
|
obj = items;
|
|
154
155
|
items = obj.items;
|
|
@@ -162,7 +163,7 @@ function initItemsLinks( obj, block ) {
|
|
|
162
163
|
* is most often the property `elem.name.id`.
|
|
163
164
|
*
|
|
164
165
|
* If argument `prop` is provided, add `elem` to the dictionary of that name,
|
|
165
|
-
* e.g. `elements`.
|
|
166
|
+
* e.g. `elements`. TODO: remove `prop`.
|
|
166
167
|
*/
|
|
167
168
|
function setMemberParent( elem, name, parent, prop ) {
|
|
168
169
|
if (prop) { // extension or structure include
|
|
@@ -286,21 +287,6 @@ function dependsOnSilent( user, art ) {
|
|
|
286
287
|
user._deps.push( { art } );
|
|
287
288
|
}
|
|
288
289
|
|
|
289
|
-
function storeExtension( elem, name, prop, parent ) {
|
|
290
|
-
if (prop === 'enum')
|
|
291
|
-
prop = 'elements';
|
|
292
|
-
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
293
|
-
if (!parent[kind])
|
|
294
|
-
setLink( parent, kind, {} );
|
|
295
|
-
// if (name === '' && prop === 'params') {
|
|
296
|
-
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
297
|
-
// return;
|
|
298
|
-
// }
|
|
299
|
-
if (!parent[kind][prop])
|
|
300
|
-
parent[kind][prop] = Object.create( null );
|
|
301
|
-
pushToDict( parent[kind][prop], name, elem );
|
|
302
|
-
}
|
|
303
|
-
|
|
304
290
|
/** @type {(a: any, b: any) => boolean} */
|
|
305
291
|
const testFunctionPlaceholder = () => true;
|
|
306
292
|
|
|
@@ -348,11 +334,11 @@ function augmentPath( location, ...args ) {
|
|
|
348
334
|
return { path: args.map( id => ({ id, location }) ), location };
|
|
349
335
|
}
|
|
350
336
|
|
|
351
|
-
function copyExpr( expr, location ) {
|
|
337
|
+
function copyExpr( expr, location, noLinks = false ) {
|
|
352
338
|
if (!expr || typeof expr !== 'object')
|
|
353
339
|
return expr;
|
|
354
340
|
else if (Array.isArray( expr ))
|
|
355
|
-
return expr.map( e => copyExpr( e, location ) );
|
|
341
|
+
return expr.map( e => copyExpr( e, location, noLinks ) );
|
|
356
342
|
|
|
357
343
|
const proto = Object.getPrototypeOf( expr );
|
|
358
344
|
if (proto && proto !== Object.prototype && proto !== XsnName.prototype &&
|
|
@@ -363,16 +349,21 @@ function copyExpr( expr, location ) {
|
|
|
363
349
|
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
364
350
|
const pd = Object.getOwnPropertyDescriptor( expr, prop );
|
|
365
351
|
if (!proto)
|
|
366
|
-
r[prop] = copyExpr( pd.value, location );
|
|
367
|
-
|
|
368
|
-
else if (!pd.enumerable || prop.charAt(0) === '$')
|
|
369
|
-
Object.defineProperty( r, prop, pd );
|
|
352
|
+
r[prop] = copyExpr( pd.value, location, noLinks );
|
|
370
353
|
|
|
371
354
|
else if (prop === 'location')
|
|
372
355
|
r[prop] = location || pd.value;
|
|
373
356
|
|
|
374
|
-
else
|
|
375
|
-
r
|
|
357
|
+
else if (prop.charAt(0) === '$')
|
|
358
|
+
Object.defineProperty( r, prop, pd );
|
|
359
|
+
|
|
360
|
+
else if (pd.enumerable)
|
|
361
|
+
r[prop] = copyExpr( pd.value, location, noLinks );
|
|
362
|
+
|
|
363
|
+
else if (!noLinks)
|
|
364
|
+
Object.defineProperty( r, prop, pd );
|
|
365
|
+
// TODO: probably consider noLinks just for element references (_artifact of
|
|
366
|
+
// first path item has _main) - test calc element with `cast`
|
|
376
367
|
}
|
|
377
368
|
return r;
|
|
378
369
|
}
|
|
@@ -660,22 +651,37 @@ function isDirectComposition( art ) {
|
|
|
660
651
|
return path?.length === 1 && path[0].id === 'cds.Composition';
|
|
661
652
|
}
|
|
662
653
|
|
|
663
|
-
function targetCantBeAspect( elem, calledForTargetAspectProp ) {
|
|
654
|
+
function targetCantBeAspect( elem, calledForTargetAspectProp, definitions ) { // see #11658
|
|
664
655
|
// Remark: we do not check `on` and `keys` here - the error should complain
|
|
665
656
|
// at the `on`/`keys`, not the aspect
|
|
666
657
|
if (!isDirectComposition( elem ) || elem.targetAspect && !calledForTargetAspectProp)
|
|
667
658
|
return (elem.type && !elem.type.$inferred) ? 'std' : 'redirected';
|
|
659
|
+
// TODO: better msg if due to calledForTargetAspectProp
|
|
668
660
|
if (!elem._main)
|
|
669
661
|
return elem.kind; // type or annotation
|
|
670
662
|
// TODO: extra for "in many"?
|
|
671
663
|
let art = elem;
|
|
672
664
|
while (art.kind === 'element')
|
|
673
665
|
art = art._parent;
|
|
666
|
+
// Remark: if we include entity/aspect, we might still have a targetAspects in a
|
|
667
|
+
// type/event - see Error[type-managed-composition]
|
|
668
|
+
// With a future feature “managed compositions in sub elements“, we might allow
|
|
669
|
+
// moving an aspect to targetAspect independently from the main artifact
|
|
670
|
+
if (definitions && art.kind === 'extend')
|
|
671
|
+
art = definitions[art.name.id];
|
|
674
672
|
if (![ 'entity', 'aspect', 'event' ].includes( art.kind ))
|
|
675
673
|
return (art.kind !== 'mixin') ? art.kind : 'select';
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
674
|
+
if ((art.query || art.kind === 'event') && !(calledForTargetAspectProp && elem.target))
|
|
675
|
+
return art.kind;
|
|
676
|
+
if (elem._parent.kind === 'element')
|
|
677
|
+
return 'sub';
|
|
678
|
+
if (!calledForTargetAspectProp) {
|
|
679
|
+
if (elem.on)
|
|
680
|
+
return 'on';
|
|
681
|
+
if (elem.foreignKeys)
|
|
682
|
+
return 'keys';
|
|
683
|
+
}
|
|
684
|
+
return null;
|
|
679
685
|
}
|
|
680
686
|
|
|
681
687
|
function userQuery( user ) {
|
|
@@ -781,6 +787,50 @@ function compositionTextVariant( art, composition, association = 'std' ) {
|
|
|
781
787
|
: association;
|
|
782
788
|
}
|
|
783
789
|
|
|
790
|
+
// Looping: ---------------------------------------------------------------------
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Apply function `callback` to all artifacts in dictionary
|
|
794
|
+
* `model.definitions`. See function `forEachGeneric` for details.
|
|
795
|
+
* TODO: should we skip "namespaces" already here?
|
|
796
|
+
*/
|
|
797
|
+
function forEachDefinition( model, callback ) {
|
|
798
|
+
forEachGeneric( model, 'definitions', callback );
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Apply function `callback` to all members of object `construct` (main artifact
|
|
803
|
+
* or parent member). Members are considered those in dictionaries `elements`,
|
|
804
|
+
* `enum`, `actions` and `params` of `obj`.
|
|
805
|
+
*
|
|
806
|
+
* @param {XSN.Artifact} construct
|
|
807
|
+
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
808
|
+
* @param {XSN.Artifact} [target]
|
|
809
|
+
*/
|
|
810
|
+
function forEachMember( construct, callback ) {
|
|
811
|
+
forEachGeneric( construct, 'elements', callback );
|
|
812
|
+
forEachGeneric( construct, 'enum', callback );
|
|
813
|
+
forEachGeneric( construct, 'foreignKeys', callback );
|
|
814
|
+
forEachGeneric( construct, 'actions', callback );
|
|
815
|
+
forEachGeneric( construct, 'params', callback );
|
|
816
|
+
if (construct.returns)
|
|
817
|
+
callback( construct.returns, '', 'params' );
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// Apply function `callback` to all objects in dictionary `parent[prop]`,
|
|
821
|
+
// including all duplicates (found under the same name). Function `callback` is
|
|
822
|
+
// called with the following arguments: the object, the name and `prop`.
|
|
823
|
+
function forEachGeneric( parent, prop, callback ) {
|
|
824
|
+
const dict = parent[prop];
|
|
825
|
+
for (const name in dict) {
|
|
826
|
+
const obj = dict[name];
|
|
827
|
+
const { $duplicates } = obj;
|
|
828
|
+
callback( obj, name, prop );
|
|
829
|
+
if (Array.isArray( $duplicates )) // redefinitions
|
|
830
|
+
$duplicates.forEach( o => callback( o, name, prop ) );
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
784
834
|
module.exports = {
|
|
785
835
|
pushLink,
|
|
786
836
|
annotationVal,
|
|
@@ -801,7 +851,6 @@ module.exports = {
|
|
|
801
851
|
initDollarSelf,
|
|
802
852
|
initDollarParameters,
|
|
803
853
|
initBoundSelfParam,
|
|
804
|
-
storeExtension,
|
|
805
854
|
withAssociation,
|
|
806
855
|
pathName,
|
|
807
856
|
augmentPath,
|
|
@@ -827,4 +876,8 @@ module.exports = {
|
|
|
827
876
|
definedViaCdl,
|
|
828
877
|
artifactRefLocation,
|
|
829
878
|
compositionTextVariant,
|
|
879
|
+
forEachDefinition,
|
|
880
|
+
forEachMember,
|
|
881
|
+
forEachGeneric,
|
|
882
|
+
forEachInOrder: forEachGeneric,
|
|
830
883
|
};
|