@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/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
3
|
<!-- markdownlint-disable MD024 -->
|
|
4
4
|
<!-- markdownlint-disable MD004 -->
|
|
@@ -13,6 +13,39 @@ we might not list every change in its behavior here.
|
|
|
13
13
|
Productive code should never require a `beta` flag to be set, and
|
|
14
14
|
might use a deprecated flag only for a limited period of time.
|
|
15
15
|
|
|
16
|
+
## Version 6.7.1 - 2026-01-28
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- compiler: Properly accept aspects as composition targets in an `extend`
|
|
21
|
+
(fixes regression introduced with v6.7.0)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Version 6.7.0 - 2026-01-23
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- to.hdi: Support .hdbprojectionview for Data Product Production
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- compiler: Change internal processing sequence (extensions and entity generation) for
|
|
33
|
+
potentially upcoming compiler features; messages for erroneous models might differ slightly
|
|
34
|
+
|
|
35
|
+
- for.odata/to.edm(x): Enhancements for Fiori Tree Views: support managed associations with explicit foreign keys,
|
|
36
|
+
raise messages when the `@hierarchy` annotation cannot be applied.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- to.sql: portable hana functions `*_between` with dates
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Version 6.6.2 - 2026-01-16
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- for.effective: Don't resolve backlinks in aspects
|
|
48
|
+
|
|
16
49
|
## Version 6.6.0 - 2025-12-12
|
|
17
50
|
|
|
18
51
|
### Added
|
package/bin/cdsc.js
CHANGED
package/bin/cdsse.js
CHANGED
|
@@ -126,7 +126,7 @@ function find( err, buf ) {
|
|
|
126
126
|
const vn = messageAt( messages, 'validNames', off.col ) || Object.create(null);
|
|
127
127
|
let art = vn[buf.substring( off.prefix, off.cursor )];
|
|
128
128
|
while (art?._origin && art.$inferred && art._effectiveType ||
|
|
129
|
-
autoNavigateKinds[art
|
|
129
|
+
autoNavigateKinds[art?.kind]?.( art ))
|
|
130
130
|
art = art._origin || art.extern._artifact;
|
|
131
131
|
// TODO: set _origin in using proxies
|
|
132
132
|
// TODO: why no _effectiveType for $navElement ?
|
|
@@ -207,6 +207,7 @@ const centralMessages = {
|
|
|
207
207
|
'syntax-unexpected-many-one': { severity: 'Error' },
|
|
208
208
|
'syntax-deprecated-ref-virtual': { severity: 'Error' },
|
|
209
209
|
'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
|
|
210
|
+
'syntax-unexpected-with-returns': { severity: 'Warning' }, // errorFor: [ 'v8' ] }, or v7?
|
|
210
211
|
'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
|
|
211
212
|
'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'deprecated' },
|
|
212
213
|
'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
|
|
@@ -914,9 +915,6 @@ const centralMessageTexts = {
|
|
|
914
915
|
min: 'Expecting argument $(PROP) for type $(TYPE) to be greater than or equal to $(NUMBER)',
|
|
915
916
|
'incorrect-type': 'Expected $(NAMES) for argument $(PROP), but found $(CODE)',
|
|
916
917
|
},
|
|
917
|
-
'type-unexpected-foreign-keys': 'A managed aspect composition can\'t have a foreign keys specification. Use composition-of-entity or remove foreign keys',
|
|
918
|
-
'type-unexpected-on-condition': 'A managed aspect composition can\'t have a specified ON-condition. Use composition-of-entity or remove the ON-condition',
|
|
919
|
-
|
|
920
918
|
'type-invalid-items': {
|
|
921
919
|
std: 'Unexpected $(PROP)', // unused
|
|
922
920
|
nested: 'Unexpected $(PROP) inside $(PROP)',
|
|
@@ -1060,10 +1058,9 @@ const centralMessageTexts = {
|
|
|
1060
1058
|
|
|
1061
1059
|
'def-invalid-texts-aspect': {
|
|
1062
1060
|
std: '$(ART) is not valid', // unused
|
|
1063
|
-
'no-aspect': '$(ART) must be an aspect',
|
|
1064
|
-
key: '$(ART) must be a key',
|
|
1065
|
-
|
|
1066
|
-
missing: '$(ART) must have an element $(NAME)',
|
|
1061
|
+
'no-aspect': '$(ART) must be an aspect with elements',
|
|
1062
|
+
key: 'Element $(NAME) of $(ART) must be a key',
|
|
1063
|
+
missing: '$(ART) must have a direct element $(NAME)',
|
|
1067
1064
|
},
|
|
1068
1065
|
'def-invalid-element-type': {
|
|
1069
1066
|
std: 'Element $(ELEMREF) of $(ART) must be of type $(TYPE)',
|
|
@@ -1114,6 +1111,8 @@ const centralMessageTexts = {
|
|
|
1114
1111
|
'ref-invalid-target': {
|
|
1115
1112
|
std: 'Expecting an entity as target',
|
|
1116
1113
|
composition: 'Expecting an entity or aspect as composition target',
|
|
1114
|
+
on: 'Expecting an entity as composition target when followed by an ON-condition',
|
|
1115
|
+
keys: 'Expecting an entity as composition target when followed by foreign keys',
|
|
1117
1116
|
bare: 'Expecting the target aspect to have elements',
|
|
1118
1117
|
aspect: 'Expecting an aspect in property $(PROP)', // `targetAspect` in CSN input
|
|
1119
1118
|
redirected: 'Expecting an entity as target; a target aspect can\'t be specified for redirections',
|
package/lib/base/model.js
CHANGED
|
@@ -30,7 +30,6 @@ const availableBetaFlags = {
|
|
|
30
30
|
v7preview: true,
|
|
31
31
|
rewriteAnnotationExpressionsViaType: true,
|
|
32
32
|
sqlServiceDummies: true,
|
|
33
|
-
projectionViews: true,
|
|
34
33
|
// disabled by --beta-mode
|
|
35
34
|
nestedServices: false,
|
|
36
35
|
};
|
|
@@ -119,72 +118,6 @@ function checkRemovedDeprecatedFlags( options, { error } ) {
|
|
|
119
118
|
});
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
/**
|
|
123
|
-
* Apply function `callback` to all artifacts in dictionary
|
|
124
|
-
* `model.definitions`. See function `forEachGeneric` for details.
|
|
125
|
-
* TODO: should we skip "namespaces" already here?
|
|
126
|
-
*/
|
|
127
|
-
function forEachDefinition( model, callback ) {
|
|
128
|
-
forEachGeneric( model, 'definitions', callback );
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Apply function `callback` to all members of object `obj` (main artifact or
|
|
133
|
-
* parent member). Members are considered those in dictionaries `elements`,
|
|
134
|
-
* `enum`, `actions` and `params` of `obj`, `elements` and `enums` are also
|
|
135
|
-
* searched inside property `items` (array of). See function `forEachGeneric`
|
|
136
|
-
* for details.
|
|
137
|
-
*
|
|
138
|
-
* @param {XSN.Artifact} construct
|
|
139
|
-
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
140
|
-
* @param {XSN.Artifact} [target]
|
|
141
|
-
*/
|
|
142
|
-
function forEachMember( construct, callback, target ) {
|
|
143
|
-
let obj = construct;
|
|
144
|
-
while (obj.items)
|
|
145
|
-
obj = obj.items;
|
|
146
|
-
forEachGeneric( target || obj, 'elements', callback );
|
|
147
|
-
forEachGeneric( obj, 'enum', callback );
|
|
148
|
-
forEachGeneric( obj, 'foreignKeys', callback );
|
|
149
|
-
forEachGeneric( construct, 'actions', callback );
|
|
150
|
-
forEachGeneric( construct, 'params', callback );
|
|
151
|
-
if (construct.returns)
|
|
152
|
-
callback( construct.returns, '', 'params' );
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Same as forEachMember, but inside each member, calls itself recursively, i.e.
|
|
157
|
-
* sub members are traversed as well.
|
|
158
|
-
*
|
|
159
|
-
* @param {XSN.Artifact} construct
|
|
160
|
-
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
161
|
-
* @param {XSN.Artifact} [target]
|
|
162
|
-
*/
|
|
163
|
-
function forEachMemberRecursively( construct, callback, target ) {
|
|
164
|
-
forEachMember( construct, ( member, memberName, prop ) => {
|
|
165
|
-
callback( member, memberName, prop );
|
|
166
|
-
// Descend into nested members, too
|
|
167
|
-
forEachMemberRecursively( member, callback );
|
|
168
|
-
}, target);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Apply function `callback` to all objects in dictionary `dict`, including all
|
|
172
|
-
// duplicates (found under the same name). Function `callback` is called with
|
|
173
|
-
// the following arguments: the object, the name, and - if it is a duplicate -
|
|
174
|
-
// the array index and the array containing all duplicates.
|
|
175
|
-
function forEachGeneric( obj, prop, callback ) {
|
|
176
|
-
const dict = obj[prop];
|
|
177
|
-
for (const name in dict) {
|
|
178
|
-
obj = dict[name];
|
|
179
|
-
const { $duplicates } = obj;
|
|
180
|
-
callback( obj, name, prop );
|
|
181
|
-
if (Array.isArray($duplicates)) // redefinitions
|
|
182
|
-
$duplicates.forEach( o => callback( o, name, prop ) );
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const forEachInOrder = forEachGeneric;
|
|
187
|
-
|
|
188
121
|
/**
|
|
189
122
|
* Like `obj.prop = value`, but not contained in JSON / CSN
|
|
190
123
|
* It's important to set enumerable explicitly to false (although 'false' is the default),
|
|
@@ -208,10 +141,5 @@ module.exports = {
|
|
|
208
141
|
availableBetaFlags,
|
|
209
142
|
isDeprecatedEnabled,
|
|
210
143
|
checkRemovedDeprecatedFlags,
|
|
211
|
-
forEachDefinition,
|
|
212
|
-
forEachMember,
|
|
213
|
-
forEachMemberRecursively,
|
|
214
|
-
forEachGeneric,
|
|
215
|
-
forEachInOrder,
|
|
216
144
|
setProp,
|
|
217
145
|
};
|
package/lib/checks/elements.js
CHANGED
|
@@ -129,7 +129,7 @@ function checkManagedAssoc( member ) {
|
|
|
129
129
|
// Special case for "--with-mocks" of the cds cli: For testing databases such as H2 and SQLite, we allow
|
|
130
130
|
// associations with neither on-condition nor foreign keys if the CSN is mocked.
|
|
131
131
|
// See #13916 for details.
|
|
132
|
-
const allowForMocked = this.csn._mocked && (this.options.sqlDialect
|
|
132
|
+
const allowForMocked = this.csn._mocked && (this.options.sqlDialect in { sqlite: 1, h2: 1, plain: 1 });
|
|
133
133
|
|
|
134
134
|
const isPersisted = !hasPersistenceSkipAnnotation(this.artifact) && !this.artifact['@cds.persistence.exists'];
|
|
135
135
|
if (isPersisted && !allowForMocked && !member.keys && (targetMax === '*' || Number(targetMax) > 1) && this.options.transformation === 'sql') {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { setProp } = require('../base/model');
|
|
4
4
|
const { featureFlags } = require('../transform/featureFlags');
|
|
5
|
-
const { isSqlService, isDummyService,
|
|
5
|
+
const { isSqlService, isDummyService, isDataProductProductionService } = require('../transform/db/processSqlServices');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
*
|
|
@@ -33,7 +33,7 @@ module.exports = {
|
|
|
33
33
|
if (isDummyService(artifact, this.options))
|
|
34
34
|
setFeatureFlag( '$dummyService' ).call(this);
|
|
35
35
|
|
|
36
|
-
if (
|
|
36
|
+
if (isDataProductProductionService(artifact, this.options))
|
|
37
37
|
setFeatureFlag( '$dataProductService' ).call(this);
|
|
38
38
|
},
|
|
39
39
|
};
|
|
@@ -104,7 +104,7 @@ function assertConsistency( model, stage ) {
|
|
|
104
104
|
'$internal',
|
|
105
105
|
'$compositionTargets',
|
|
106
106
|
'$collectedExtensions',
|
|
107
|
-
'_entities',
|
|
107
|
+
'_entities',
|
|
108
108
|
'$blocks',
|
|
109
109
|
'$messageFunctions',
|
|
110
110
|
'$functions',
|
|
@@ -407,9 +407,8 @@ function assertConsistency( model, stage ) {
|
|
|
407
407
|
func: { test: TODO },
|
|
408
408
|
suffix: { test: TODO },
|
|
409
409
|
kind: {
|
|
410
|
-
isRequired:
|
|
410
|
+
isRequired: () => true, // required even with parse or other errors
|
|
411
411
|
kind: true,
|
|
412
|
-
// required to be set by Core Compiler even with parse errors
|
|
413
412
|
test: isString,
|
|
414
413
|
enum: [
|
|
415
414
|
'context', 'service', 'entity', 'type', 'aspect', 'annotation',
|
|
@@ -675,7 +674,6 @@ function assertConsistency( model, stage ) {
|
|
|
675
674
|
_status: { kind: true, test: TODO }, // TODO: $status
|
|
676
675
|
_projections: { kind: true, test: TODO },
|
|
677
676
|
_complexProjections: { kind: true, test: TODO }, // for projected paths with filters
|
|
678
|
-
$entity: { kind: true, test: TODO },
|
|
679
677
|
_entities: { test: TODO },
|
|
680
678
|
$compositionTargets: { test: isDictionary( isBoolean ) },
|
|
681
679
|
$collectedExtensions: { test: TODO },
|
|
@@ -684,6 +682,7 @@ function assertConsistency( model, stage ) {
|
|
|
684
682
|
// for implicit redirection - direct and indirect query sources of simple
|
|
685
683
|
// projections/views without @(cds.redirection.target: false):
|
|
686
684
|
_ancestors: { kind: [ 'type', 'entity' ], test: isArray( TODO ) },
|
|
685
|
+
_entityIncludes: { kind: [ 'entity' ], test: isArray( TODO ) },
|
|
687
686
|
// for implicit redirection - maps service name to simple projections/views
|
|
688
687
|
// in that service which have the current artifact in _ancestors
|
|
689
688
|
// (it can contain the artifact itself with no/failed autoexposure):
|
package/lib/compiler/base.js
CHANGED
|
@@ -134,8 +134,16 @@ function getMemberNameProp( elem, kind ) {
|
|
|
134
134
|
throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join( '+' ) }` );
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Return true if an element's `value` is also a valid (recommended) enum value.
|
|
139
|
+
*/
|
|
140
|
+
function isValidEnumValue( value ) {
|
|
141
|
+
return !value || !value.stored && typeof value.val in { string: 1, number: 1 };
|
|
142
|
+
}
|
|
143
|
+
|
|
137
144
|
module.exports = {
|
|
138
145
|
dictKinds,
|
|
139
146
|
kindProperties,
|
|
140
147
|
getArtifactName,
|
|
148
|
+
isValidEnumValue,
|
|
141
149
|
};
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const { builtinLocation } = require('../base/location');
|
|
11
|
-
const { setLink
|
|
11
|
+
const { setLink } = require('./utils');
|
|
12
12
|
const { isBetaEnabled } = require('../base/model');
|
|
13
13
|
|
|
14
14
|
// TODO: make type parameters a dict
|
|
@@ -395,7 +395,7 @@ function initBuiltins( model ) {
|
|
|
395
395
|
builtin,
|
|
396
396
|
location: builtinLocation(),
|
|
397
397
|
};
|
|
398
|
-
|
|
398
|
+
setLink( art, '_subArtifacts', Object.create( null ) );
|
|
399
399
|
return art;
|
|
400
400
|
}
|
|
401
401
|
|
|
@@ -418,9 +418,8 @@ function initBuiltins( model ) {
|
|
|
418
418
|
};
|
|
419
419
|
if (parent)
|
|
420
420
|
parent._subArtifacts[name] = art;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
setProp( art, '_deps', [] );
|
|
421
|
+
setLink( art, '_origin', '' );
|
|
422
|
+
setLink( art, '_deps', [] );
|
|
424
423
|
Object.assign( art, builtins[name] );
|
|
425
424
|
if (!art.internal)
|
|
426
425
|
artifacts[name] = art;
|
|
@@ -443,7 +442,7 @@ function initBuiltins( model ) {
|
|
|
443
442
|
name: { id },
|
|
444
443
|
};
|
|
445
444
|
elements[id] = art;
|
|
446
|
-
|
|
445
|
+
setLink( art, '_parent', model.$magicVariables );
|
|
447
446
|
|
|
448
447
|
if (magic.$autoElement)
|
|
449
448
|
art.$autoElement = magic.$autoElement;
|
|
@@ -459,7 +458,7 @@ function initBuiltins( model ) {
|
|
|
459
458
|
createMagicElements( art, magic.elements );
|
|
460
459
|
if (options.variableReplacements?.[id])
|
|
461
460
|
createMagicElements( art, options.variableReplacements[id] );
|
|
462
|
-
//
|
|
461
|
+
// setLink( art, '_effectiveType', art );
|
|
463
462
|
}
|
|
464
463
|
}
|
|
465
464
|
|
|
@@ -481,8 +480,8 @@ function initBuiltins( model ) {
|
|
|
481
480
|
magic.$uncheckedElements = art.$uncheckedElements;
|
|
482
481
|
if (art.$restricted)
|
|
483
482
|
magic.$restricted = art.$restricted;
|
|
484
|
-
|
|
485
|
-
//
|
|
483
|
+
setLink( magic, '_parent', art );
|
|
484
|
+
// setLink( magic, '_effectiveType', magic );
|
|
486
485
|
if (elements[id] && typeof elements[id] === 'object')
|
|
487
486
|
createMagicElements( magic, elements[id] );
|
|
488
487
|
|
package/lib/compiler/checks.js
CHANGED
|
@@ -9,16 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
13
|
+
const { propagationRules, acceptsExprValues } = require('../base/builtins');
|
|
14
|
+
const { typeParameters } = require('./builtins');
|
|
12
15
|
const {
|
|
13
16
|
forEachGeneric,
|
|
14
17
|
forEachDefinition,
|
|
15
18
|
forEachMember,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} = require('../base/model');
|
|
19
|
-
const { typeParameters } = require('./builtins');
|
|
20
|
-
const { propagationRules, acceptsExprValues } = require('../base/builtins');
|
|
21
|
-
const { annotationVal } = require('./utils');
|
|
19
|
+
annotationVal,
|
|
20
|
+
} = require('./utils');
|
|
22
21
|
|
|
23
22
|
const $location = Symbol.for( 'cds.$location' );
|
|
24
23
|
|
|
@@ -50,6 +49,8 @@ function check( model ) {
|
|
|
50
49
|
checkGenericConstruct( def );
|
|
51
50
|
if (def.includes && def.elements)
|
|
52
51
|
checkElementIncludeOverride( def );
|
|
52
|
+
while (def.items)
|
|
53
|
+
def = def.items;
|
|
53
54
|
forEachMember( def, member => checkMember( member ) );
|
|
54
55
|
if (def.$queries)
|
|
55
56
|
def.$queries.forEach( checkQuery );
|
|
@@ -107,6 +108,8 @@ function check( model ) {
|
|
|
107
108
|
if (member.kind === 'element')
|
|
108
109
|
checkElement( member, parentProps );
|
|
109
110
|
|
|
111
|
+
while (member.items)
|
|
112
|
+
member = member.items;
|
|
110
113
|
forEachMember( member, m => checkMember( m, parentProps ) );
|
|
111
114
|
}
|
|
112
115
|
|
|
@@ -1419,4 +1422,22 @@ function isComplexView( art ) {
|
|
|
1419
1422
|
return (!art.query.from?._artifact || art.query.from._artifact.kind === 'element');
|
|
1420
1423
|
}
|
|
1421
1424
|
|
|
1425
|
+
/**
|
|
1426
|
+
* Same as forEachMember, but inside each member, calls itself recursively, i.e.
|
|
1427
|
+
* sub members are traversed as well.
|
|
1428
|
+
*
|
|
1429
|
+
* @param {XSN.Artifact} construct
|
|
1430
|
+
* @param {(member: XSN.Artifact, memberName: string, prop: string) => void} callback
|
|
1431
|
+
* @param {XSN.Artifact} [target]
|
|
1432
|
+
*/
|
|
1433
|
+
function forEachMemberRecursively( construct, callback ) {
|
|
1434
|
+
while (construct.items)
|
|
1435
|
+
construct = construct.items;
|
|
1436
|
+
forEachMember( construct, ( member, memberName, prop ) => {
|
|
1437
|
+
callback( member, memberName, prop );
|
|
1438
|
+
// Descend into nested members, too
|
|
1439
|
+
forEachMemberRecursively( member, callback );
|
|
1440
|
+
} );
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1422
1443
|
module.exports = check;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
'use strict';
|
|
18
18
|
|
|
19
|
-
const { setLink
|
|
19
|
+
const { setLink } = require('./utils'); // check enum/non-enum
|
|
20
20
|
|
|
21
21
|
// Detect cyclic dependencies between all nodes reachable from `definitions`.
|
|
22
22
|
// If such a dependency is found, call `reportCycle` with arguments `dep.art`
|
|
@@ -51,7 +51,7 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
51
51
|
// console.log('CALL: ', v.kind,v.name)
|
|
52
52
|
++index;
|
|
53
53
|
if (!v._scc) {
|
|
54
|
-
|
|
54
|
+
setLink( v, '_scc', {
|
|
55
55
|
index,
|
|
56
56
|
lowlink: index,
|
|
57
57
|
onStack: true,
|
|
@@ -61,14 +61,14 @@ function detectCycles( definitions, reportCycle, cbScc ) {
|
|
|
61
61
|
// console.log('PUSH: ', v.kind,v.name)
|
|
62
62
|
}
|
|
63
63
|
if (!v._deps) // builtins, otherwise forgotten (TODO: assert in --test-mode)
|
|
64
|
-
|
|
64
|
+
setLink( v, '_deps', [] );
|
|
65
65
|
// assert( v._scc.onStack );
|
|
66
66
|
|
|
67
67
|
// Now consider successors of v (called w):
|
|
68
68
|
while (v._scc.depIndex < v._deps.length) {
|
|
69
69
|
const w = v._deps[v._scc.depIndex++].art;
|
|
70
70
|
if (!w._scc) { // node has not yet been visited
|
|
71
|
-
|
|
71
|
+
setLink( w, '_sccCaller', v );
|
|
72
72
|
// console.log('CALL: ', v._scc.depIndex )
|
|
73
73
|
return w; // recursive call with w in recursive algorithm
|
|
74
74
|
}
|