@sap/cds-compiler 4.2.2 → 4.3.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 +32 -0
- package/bin/cdsc.js +8 -0
- package/bin/cdshi.js +3 -3
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +19 -0
- package/lib/base/location.js +16 -0
- package/lib/base/message-registry.js +47 -16
- package/lib/base/messages.js +49 -38
- package/lib/base/model.js +2 -1
- package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
- package/lib/checks/existsMustEndInAssoc.js +27 -0
- package/lib/checks/onConditions.js +47 -1
- package/lib/checks/validator.js +10 -1
- package/lib/compiler/assert-consistency.js +23 -15
- package/lib/compiler/base.js +31 -14
- package/lib/compiler/builtins.js +21 -20
- package/lib/compiler/checks.js +36 -49
- package/lib/compiler/define.js +71 -91
- package/lib/compiler/extend.js +27 -25
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +67 -87
- package/lib/compiler/kick-start.js +7 -5
- package/lib/compiler/populate.js +32 -30
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +29 -25
- package/lib/compiler/shared.js +57 -31
- package/lib/compiler/tweak-assocs.js +203 -22
- package/lib/compiler/utils.js +0 -18
- package/lib/gen/Dictionary.json +10 -4
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/languageParser.js +3 -3
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +63 -28
- package/lib/json/to-csn.js +23 -13
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/errorStrategy.js +5 -1
- package/lib/language/genericAntlrParser.js +67 -61
- package/lib/main.d.ts +26 -1
- package/lib/main.js +2 -1
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +28 -0
- package/lib/model/revealInternalProperties.js +3 -9
- package/lib/optionProcessor.js +17 -1
- package/lib/render/toCdl.js +1 -1
- package/lib/transform/db/associations.js +3 -4
- package/lib/transform/db/backlinks.js +293 -0
- package/lib/transform/db/expansion.js +18 -7
- package/lib/transform/db/flattening.js +3 -2
- package/lib/transform/db/rewriteCalculatedElements.js +1 -67
- package/lib/transform/db/transformExists.js +3 -58
- package/lib/transform/db/views.js +8 -14
- package/lib/transform/effective/.eslintrc.json +4 -0
- package/lib/transform/effective/associations.js +101 -0
- package/lib/transform/effective/main.js +88 -0
- package/lib/transform/effective/misc.js +61 -0
- package/lib/transform/effective/queries.js +42 -0
- package/lib/transform/effective/types.js +121 -0
- package/lib/transform/forRelationalDB.js +12 -235
- package/lib/transform/localized.js +22 -3
- package/lib/transform/parseExpr.js +7 -3
- package/lib/transform/transformUtils.js +5 -22
- package/lib/transform/translateAssocsToJoins.js +42 -38
- package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
- package/package.json +1 -2
- package/lib/language/language.g4 +0 -3260
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { setProp } = require('../../base/model');
|
|
4
|
+
const flattening = require('../db/flattening');
|
|
5
|
+
const {
|
|
6
|
+
applyTransformations, forEachDefinition, forEachMemberRecursively, implicitAs, cloneCsnNonDict,
|
|
7
|
+
} = require('../../model/csnUtils');
|
|
8
|
+
const associations = require('../db/associations');
|
|
9
|
+
const backlinks = require('../db/backlinks');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Turn managed associations into unmanaged ones by
|
|
14
|
+
* - unfolding the .keys
|
|
15
|
+
* - creating the foreign keys in .elements
|
|
16
|
+
* - adding a corresponding on-condition
|
|
17
|
+
* @param {CSN.Model} csn Input CSN - will not be transformed
|
|
18
|
+
* @param {CSN.Options} options
|
|
19
|
+
* @param {object} csnUtils
|
|
20
|
+
* @param {object} messageFunctions
|
|
21
|
+
* @param {Function} messageFunctions.error
|
|
22
|
+
* @param {Function} messageFunctions.warning
|
|
23
|
+
* @todo Remove .keys afterwards
|
|
24
|
+
* @todo Add created foreign keys into .columns in case of a query?
|
|
25
|
+
* @returns {CSN.Model}
|
|
26
|
+
*/
|
|
27
|
+
function turnAssociationsIntoUnmanaged( csn, options, csnUtils, { error, warning } ) {
|
|
28
|
+
// TODO: Do we really need this?
|
|
29
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
30
|
+
setProp(artifact, '$path', [ 'definitions', artifactName ]);
|
|
31
|
+
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
32
|
+
setProp(member, '$path', path);
|
|
33
|
+
}, [ 'definitions', artifactName ]);
|
|
34
|
+
});
|
|
35
|
+
// Flatten out the fks and create the corresponding elements
|
|
36
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, error, warning, '_', true, csnUtils, { allowArtifact: a => a.kind === 'entity' });
|
|
37
|
+
|
|
38
|
+
// Add the foreign keys also to the columns if the association itself was explicitly selected
|
|
39
|
+
// TODO: Extend the expansion to also expand managed to their foreign
|
|
40
|
+
// TODO: Remember where in .elements we had associations and do it then?
|
|
41
|
+
applyTransformations(csn, {
|
|
42
|
+
columns: (parent, prop, columns) => {
|
|
43
|
+
const newColumns = [];
|
|
44
|
+
for (const col of columns) {
|
|
45
|
+
newColumns.push(col);
|
|
46
|
+
const element = csnUtils.getElement(col);
|
|
47
|
+
if (element && element.keys)
|
|
48
|
+
element.keys.forEach(fk => addForeignKeyToColumns(fk, newColumns, col, options));
|
|
49
|
+
}
|
|
50
|
+
parent.columns = newColumns;
|
|
51
|
+
},
|
|
52
|
+
}, [], { allowArtifact: artifact => artifact.kind === 'entity' });
|
|
53
|
+
|
|
54
|
+
forEachDefinition(csn, associations.getFKAccessFinalizer(csn, csnUtils, '_'));
|
|
55
|
+
// Calculate the on-conditions from the .keys -
|
|
56
|
+
associations.attachOnConditions(csn, csnUtils, '_');
|
|
57
|
+
|
|
58
|
+
return csn;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* FKs need to be added to the .columns
|
|
63
|
+
* @todo stolen from lib/transform/db/views.js
|
|
64
|
+
* @todo Can we maybe do this during expansion already?
|
|
65
|
+
* @param {object} foreignKey
|
|
66
|
+
* @param {object[]} columns
|
|
67
|
+
* @param {CSN.Column} associationColumn
|
|
68
|
+
* @param {CSN.Options} options
|
|
69
|
+
*/
|
|
70
|
+
function addForeignKeyToColumns( foreignKey, columns, associationColumn, options ) {
|
|
71
|
+
const ref = cloneCsnNonDict(associationColumn.ref, options);
|
|
72
|
+
ref[ref.length - 1] = [ implicitAs(ref) ].concat(foreignKey.as || foreignKey.ref).join('_');
|
|
73
|
+
const result = {
|
|
74
|
+
ref,
|
|
75
|
+
};
|
|
76
|
+
if (associationColumn.as) {
|
|
77
|
+
const columnName = `${associationColumn.as}_${foreignKey.as || implicitAs(foreignKey.ref)}`;
|
|
78
|
+
result.as = columnName;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (associationColumn.key)
|
|
82
|
+
result.key = true;
|
|
83
|
+
|
|
84
|
+
columns.push(result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Translate $self backlinks to "normal" on-conditions
|
|
89
|
+
* @param {CSN.Model} csn Input CSN - will not be transformed
|
|
90
|
+
* @param {CSN.Options} options
|
|
91
|
+
* @param {object} csnUtils
|
|
92
|
+
* @param {object} messageFunctions
|
|
93
|
+
*/
|
|
94
|
+
function transformBacklinks( csn, options, csnUtils, messageFunctions ) {
|
|
95
|
+
forEachDefinition(csn, backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, '_', true));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
managedToUnmanaged: turnAssociationsIntoUnmanaged,
|
|
100
|
+
transformBacklinks,
|
|
101
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isBetaEnabled } = require('../../base/model');
|
|
4
|
+
const { CompilerAssertion } = require('../../base/error');
|
|
5
|
+
const { makeMessageFunction } = require('../../base/messages');
|
|
6
|
+
const { cloneCsnNonDict, getUtils, isAspect } = require('../../model/csnUtils');
|
|
7
|
+
const transformUtils = require('../transformUtils');
|
|
8
|
+
const flattening = require('../db/flattening');
|
|
9
|
+
const types = require('./types');
|
|
10
|
+
// const { addLocalizationViews } = require('../../transform/localized');
|
|
11
|
+
const validate = require('../../checks/validator');
|
|
12
|
+
const expansion = require('../db/expansion');
|
|
13
|
+
const queries = require('./queries');
|
|
14
|
+
const associations = require('./associations');
|
|
15
|
+
const generateDrafts = require('../draft/db');
|
|
16
|
+
const handleExists = require('../db/transformExists');
|
|
17
|
+
const misc = require('./misc');
|
|
18
|
+
const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities } = require('../db/rewriteCalculatedElements');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This is just a PoC for now!
|
|
22
|
+
*
|
|
23
|
+
* Transform the given CSN into a so called effective CSN, by
|
|
24
|
+
* - dissolving structured types
|
|
25
|
+
* - turning managed into unmanaged associations
|
|
26
|
+
* @private
|
|
27
|
+
* @param {CSN.Model} model Input CSN - will not be transformed
|
|
28
|
+
* @param {CSN.Options} options
|
|
29
|
+
* @returns {CSN.Model}
|
|
30
|
+
*/
|
|
31
|
+
function effectiveCsn( model, options ) {
|
|
32
|
+
if (!isBetaEnabled(options, 'effectiveCsn'))
|
|
33
|
+
throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
|
|
34
|
+
|
|
35
|
+
const csn = cloneCsnNonDict(model, options);
|
|
36
|
+
|
|
37
|
+
const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, '_');
|
|
38
|
+
queries.projectionToSELECTAndAddColumns(csn);
|
|
39
|
+
|
|
40
|
+
let csnUtils = getUtils(csn, 'init-all');
|
|
41
|
+
const messageFunctions = makeMessageFunction(csn, options, 'for.effective');
|
|
42
|
+
|
|
43
|
+
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
44
|
+
const cleanup = validate.forRelationalDB(csn, {
|
|
45
|
+
...messageFunctions, csnUtils, ...csnUtils, csn, options, isAspect,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
rewriteCalculatedElementsInViews(csn, options, csnUtils, '_', messageFunctions);
|
|
49
|
+
|
|
50
|
+
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
51
|
+
handleExists(csn, options, messageFunctions.error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
|
|
52
|
+
|
|
53
|
+
// Check if structured elements and managed associations are compared in an expression
|
|
54
|
+
// and expand these structured elements. This tuple expansion allows all other
|
|
55
|
+
// subsequent procession steps to see plain paths in expressions.
|
|
56
|
+
// If errors are detected, throwWithAnyError() will return from further processing
|
|
57
|
+
expandStructsInExpression(csn, { drillRef: true });
|
|
58
|
+
|
|
59
|
+
messageFunctions.throwWithAnyError();
|
|
60
|
+
|
|
61
|
+
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
|
|
62
|
+
|
|
63
|
+
// Expand a structured thing in: keys, columns, order by, group by
|
|
64
|
+
expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils);
|
|
65
|
+
|
|
66
|
+
csnUtils = getUtils(csn, 'init-all');
|
|
67
|
+
|
|
68
|
+
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
69
|
+
cleanup();
|
|
70
|
+
|
|
71
|
+
flattening.flattenAllStructStepsInRefs(csn, options, new WeakMap(), '_');
|
|
72
|
+
flattening.flattenElements(csn, options, '_', messageFunctions.error);
|
|
73
|
+
|
|
74
|
+
resolveTypesInActionsAfterFlattening();
|
|
75
|
+
|
|
76
|
+
processCalculatedElementsInEntities(csn);
|
|
77
|
+
associations.managedToUnmanaged(csn, options, csnUtils, messageFunctions);
|
|
78
|
+
associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
|
|
79
|
+
generateDrafts(csn, options, '_', messageFunctions);
|
|
80
|
+
misc.attachPersistenceName(csn, options, csnUtils);
|
|
81
|
+
misc.removeDefinitionsAndProperties(csn);
|
|
82
|
+
|
|
83
|
+
messageFunctions.throwWithError();
|
|
84
|
+
|
|
85
|
+
return csn;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { effectiveCsn };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
forEachDefinition, forEachMemberRecursively, getArtifactDatabaseNameOf, getElementDatabaseNameOf, forEachMember,
|
|
5
|
+
} = require('../../model/csnUtils');
|
|
6
|
+
/**
|
|
7
|
+
* Attach @cds.persistence.name to all artifacts and "things".
|
|
8
|
+
* We could also do it more selectively like we do in forRelationalDb, but: Why? Space maybe?
|
|
9
|
+
* @param {CSN.Model} csn
|
|
10
|
+
* @param {CSN.Options} options
|
|
11
|
+
* @param {object} csnUtils
|
|
12
|
+
*/
|
|
13
|
+
function attachPersistenceName( csn, options, csnUtils ) {
|
|
14
|
+
const { addStringAnnotationTo } = csnUtils;
|
|
15
|
+
|
|
16
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
17
|
+
if (artifact.kind === 'entity') {
|
|
18
|
+
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
19
|
+
|
|
20
|
+
forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const artifactPropertiesToRemove = [ 'includes' ];
|
|
26
|
+
const memberPropertiesToRemove = [ 'localized', 'enum', 'keys' ];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Remove definitions from the CSN:
|
|
30
|
+
* - types
|
|
31
|
+
* - aspects
|
|
32
|
+
*
|
|
33
|
+
* Remove properties from artifacts:
|
|
34
|
+
* - includes
|
|
35
|
+
* - localized
|
|
36
|
+
* @param {CSN.Model} csn
|
|
37
|
+
* @todo Callback-like architecture and merge with persistence name?
|
|
38
|
+
*/
|
|
39
|
+
function removeDefinitionsAndProperties( csn ) {
|
|
40
|
+
forEachDefinition(csn, (artifact, artifactName) => {
|
|
41
|
+
if (artifact.kind === 'aspect' || artifact.kind === 'type') {
|
|
42
|
+
delete csn.definitions[artifactName];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
if (artifact['@cds.persistence.skip'] === 'if-unused')
|
|
46
|
+
artifact['@cds.persistence.skip'] = false;
|
|
47
|
+
for (const prop of artifactPropertiesToRemove)
|
|
48
|
+
delete artifact[prop];
|
|
49
|
+
|
|
50
|
+
forEachMember(artifact, (member) => {
|
|
51
|
+
for (const prop of memberPropertiesToRemove)
|
|
52
|
+
delete member[prop];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
attachPersistenceName,
|
|
60
|
+
removeDefinitionsAndProperties,
|
|
61
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { forEachDefinition, applyTransformationsOnNonDictionary } = require('../../model/csnUtils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* - Make .projections look like simple SELECTS
|
|
7
|
+
* - ensure we always have a .columns by adding a .columns = ['*'] if none is present
|
|
8
|
+
* @param {CSN.Model} csn
|
|
9
|
+
* @returns {Function[]} Callbacks to re-add the .projection
|
|
10
|
+
*/
|
|
11
|
+
function projectionToSELECTAndAddColumns( csn ) {
|
|
12
|
+
const redoProjections = [];
|
|
13
|
+
forEachDefinition(csn, (artifact) => {
|
|
14
|
+
if (artifact.kind === 'entity') {
|
|
15
|
+
if (artifact.projection) {
|
|
16
|
+
if (!artifact.projection.columns)
|
|
17
|
+
artifact.projection.columns = [ '*' ];
|
|
18
|
+
artifact.query = { SELECT: artifact.projection };
|
|
19
|
+
delete artifact.projection;
|
|
20
|
+
redoProjections.push(() => {
|
|
21
|
+
if (artifact.query) {
|
|
22
|
+
artifact.projection = artifact.query.SELECT;
|
|
23
|
+
delete artifact.query;
|
|
24
|
+
if (artifact.$syntax === 'projection')
|
|
25
|
+
delete artifact.$syntax;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
else if (artifact.query) {
|
|
30
|
+
applyTransformationsOnNonDictionary(artifact, 'query', {
|
|
31
|
+
SELECT: (parent, prop, SELECT) => {
|
|
32
|
+
SELECT.columns ??= [ '*' ];
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return redoProjections;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { projectionToSELECTAndAddColumns };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
cloneCsnNonDict, applyTransformations, applyTransformationsOnNonDictionary, cloneCsnDictionary, applyTransformationsOnDictionary,
|
|
5
|
+
} = require('../../model/csnUtils');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolve all references to structured types in entities to the underlying elements.
|
|
9
|
+
* Resolve all simple type refs to their cds builtin type.
|
|
10
|
+
*
|
|
11
|
+
* When setting the elements, we deeply clone them from the type to avoid accidental changes
|
|
12
|
+
* since they are linked by a reference.
|
|
13
|
+
* @todo What about annotations on the type?
|
|
14
|
+
* @param {CSN.Model} csn will be transformed
|
|
15
|
+
* @param {object} csnUtils
|
|
16
|
+
* @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
|
|
17
|
+
* so we can call this callback after flattening is done - then we can safely resolve their types.
|
|
18
|
+
*/
|
|
19
|
+
function resolveTypes( csn, csnUtils ) {
|
|
20
|
+
const { getFinalTypeInfo } = csnUtils;
|
|
21
|
+
const later = [];
|
|
22
|
+
applyTransformations(csn, {
|
|
23
|
+
type: (parent, prop, type, path) => {
|
|
24
|
+
const artifact = csn.definitions[path[1]];
|
|
25
|
+
// TODO: What about events?
|
|
26
|
+
if (!(artifact.kind === 'action' || artifact.kind === 'function'))
|
|
27
|
+
resolveType(parent);
|
|
28
|
+
},
|
|
29
|
+
}, [ (definitions, artifactName, artifact) => {
|
|
30
|
+
// In a non-flat model, replacing types with some $self inside causes issues for actions (bound or unbound)
|
|
31
|
+
// we remember them and replace them after flattening.
|
|
32
|
+
if (artifact.kind === 'action' || artifact.kind === 'function')
|
|
33
|
+
later.push({ [artifactName]: artifact });
|
|
34
|
+
else if (artifact.actions)
|
|
35
|
+
later.push(artifact.actions);
|
|
36
|
+
} ], { skipDict: { actions: true } });
|
|
37
|
+
|
|
38
|
+
return function resolveTypesInActions() {
|
|
39
|
+
later.forEach(action => applyTransformationsOnDictionary(action, { type: parent => resolveType(parent) }));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a type to its
|
|
44
|
+
* - elements
|
|
45
|
+
* - items
|
|
46
|
+
* - basic builtin
|
|
47
|
+
*
|
|
48
|
+
* Drill down into .elements and .items
|
|
49
|
+
*
|
|
50
|
+
* @param {object} parent Object with a .type propertie
|
|
51
|
+
*/
|
|
52
|
+
function resolveType( parent ) {
|
|
53
|
+
// TODO: I assume there can be cases with a type ref but still having .elements already? Subelement anno?
|
|
54
|
+
const final = getFinalTypeInfo(parent.type);
|
|
55
|
+
if (final?.elements) {
|
|
56
|
+
// We do full clones so users don't get unexpected linkage later
|
|
57
|
+
if (!parent.elements)
|
|
58
|
+
parent.elements = cloneCsnDictionary(final.elements);
|
|
59
|
+
delete parent.type;
|
|
60
|
+
}
|
|
61
|
+
else if (final && final.items) {
|
|
62
|
+
if (!parent.items)
|
|
63
|
+
parent.items = cloneCsnNonDict(final.items);
|
|
64
|
+
delete parent.type;
|
|
65
|
+
}
|
|
66
|
+
else if (final?.type) {
|
|
67
|
+
parent.type = final.type;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Drill down - there might be other type references
|
|
71
|
+
const stack = [ ];
|
|
72
|
+
if (parent.elements || parent.items)
|
|
73
|
+
stack.push(parent);
|
|
74
|
+
while (stack.length > 0) {
|
|
75
|
+
const obj = stack.pop();
|
|
76
|
+
if (obj.elements) {
|
|
77
|
+
applyTransformationsOnDictionary(obj.elements, {
|
|
78
|
+
type: getTypeTransformer(stack),
|
|
79
|
+
}, { skipDict: { actions: true } });
|
|
80
|
+
}
|
|
81
|
+
else if (obj.items) {
|
|
82
|
+
applyTransformationsOnNonDictionary(obj, 'items', {
|
|
83
|
+
type: getTypeTransformer(stack),
|
|
84
|
+
}, { skipDict: { actions: true } });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Function to transform a type to its most basic thing
|
|
91
|
+
* @param {object[]} stack
|
|
92
|
+
* @returns {Function}
|
|
93
|
+
*/
|
|
94
|
+
function getTypeTransformer( stack ) {
|
|
95
|
+
return function typeTransformer( _parent, _prop, _type ) {
|
|
96
|
+
const finalSub = getFinalTypeInfo(_type);
|
|
97
|
+
|
|
98
|
+
if (finalSub?.elements) {
|
|
99
|
+
// We do full clones so users don't get unexpected linkage later
|
|
100
|
+
if (!_parent.elements)
|
|
101
|
+
_parent.elements = cloneCsnDictionary(finalSub.elements);
|
|
102
|
+
delete _parent.type;
|
|
103
|
+
stack.push( _parent );
|
|
104
|
+
}
|
|
105
|
+
else if (finalSub?.items) {
|
|
106
|
+
if (!_parent.items)
|
|
107
|
+
_parent.items = cloneCsnNonDict(finalSub.items);
|
|
108
|
+
delete _parent.type;
|
|
109
|
+
stack.push( _parent );
|
|
110
|
+
}
|
|
111
|
+
else if (finalSub?.type) {
|
|
112
|
+
_parent.type = finalSub.type;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
resolve: resolveTypes,
|
|
121
|
+
};
|