@sap/cds-compiler 2.13.8 → 2.15.6
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 +128 -4
- package/bin/cdsc.js +112 -37
- package/lib/api/main.js +63 -22
- package/lib/api/options.js +2 -3
- package/lib/api/validate.js +6 -6
- package/lib/base/message-registry.js +100 -17
- package/lib/base/messages.js +85 -64
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/validator.js +2 -4
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +11 -0
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +59 -11
- package/lib/compiler/extend.js +20 -3
- package/lib/compiler/finalize-parse-cdl.js +26 -20
- package/lib/compiler/index.js +75 -26
- package/lib/compiler/populate.js +36 -17
- package/lib/compiler/propagator.js +4 -1
- package/lib/compiler/resolve.js +104 -16
- package/lib/compiler/shared.js +61 -27
- package/lib/compiler/tweak-assocs.js +7 -1
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +216 -98
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +499 -423
- package/lib/edm/edmUtils.js +22 -22
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageParser.js +4636 -4368
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +0 -2
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +47 -2
- package/lib/language/language.g4 +59 -27
- package/lib/main.d.ts +19 -1
- package/lib/main.js +6 -0
- package/lib/model/csnRefs.js +33 -6
- package/lib/model/csnUtils.js +193 -75
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +2 -2
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +62 -26
- package/lib/render/toCdl.js +844 -679
- package/lib/render/toHdbcds.js +189 -243
- package/lib/render/toSql.js +180 -198
- package/lib/render/utils/common.js +131 -15
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/constraints.js +3 -1
- package/lib/transform/db/expansion.js +15 -10
- package/lib/transform/db/flattening.js +94 -64
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +6 -3
- package/lib/transform/forHanaNew.js +43 -26
- package/lib/transform/forOdataNew.js +43 -42
- package/lib/transform/localized.js +12 -7
- package/lib/transform/odata/toFinalBaseType.js +8 -6
- package/lib/transform/odata/typesExposure.js +145 -197
- package/lib/transform/transformUtilsNew.js +9 -12
- package/lib/transform/translateAssocsToJoins.js +5 -1
- package/lib/transform/universalCsn/coreComputed.js +5 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
- package/lib/utils/moduleResolve.js +13 -6
- package/package.json +1 -1
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
package/lib/model/csnUtils.js
CHANGED
|
@@ -447,13 +447,13 @@ function getUtils(model, universalReady) {
|
|
|
447
447
|
* The transformer functions are called with the following signature:
|
|
448
448
|
* transformer(value, node, resultNode, key)
|
|
449
449
|
*
|
|
450
|
-
* @param {any}
|
|
450
|
+
* @param {any} rootNode Node to transform
|
|
451
451
|
* @param {any} transformers Object defining transformer functions
|
|
452
452
|
* @returns {object}
|
|
453
453
|
*/
|
|
454
|
-
function cloneWithTransformations(
|
|
454
|
+
function cloneWithTransformations(rootNode, transformers) {
|
|
455
455
|
|
|
456
|
-
return transformNode(
|
|
456
|
+
return transformNode(rootNode);
|
|
457
457
|
|
|
458
458
|
// This general transformation function will be applied to each node recursively
|
|
459
459
|
function transformNode(node) {
|
|
@@ -588,7 +588,7 @@ function getUtils(model, universalReady) {
|
|
|
588
588
|
* @param {object} csn Top-level CSN. You can pass non-dictionary values.
|
|
589
589
|
* @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
|
|
590
590
|
*/
|
|
591
|
-
function
|
|
591
|
+
function cloneCsnNonDict(csn, options) {
|
|
592
592
|
return sortCsn(csn, options);
|
|
593
593
|
}
|
|
594
594
|
|
|
@@ -596,7 +596,7 @@ function cloneCsn(csn, options) {
|
|
|
596
596
|
* Deeply clone the given CSN dictionary and return it.
|
|
597
597
|
* Note that annotations are only copied shallowly.
|
|
598
598
|
* This function does _not_ sort the given dictionary.
|
|
599
|
-
* See
|
|
599
|
+
* See cloneCsnNonDict() if you want sorted definitions.
|
|
600
600
|
*
|
|
601
601
|
* @param {object} csn
|
|
602
602
|
* @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
|
|
@@ -632,8 +632,10 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
|
|
|
632
632
|
* @param {CSN.Path} [path]
|
|
633
633
|
* @param {boolean} [ignoreIgnore]
|
|
634
634
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
635
|
+
* @param constructCallback
|
|
635
636
|
*/
|
|
636
|
-
function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}
|
|
637
|
+
function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
638
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
637
639
|
// Allow processing _ignored elements if requested
|
|
638
640
|
if (ignoreIgnore && construct._ignore) {
|
|
639
641
|
return;
|
|
@@ -641,8 +643,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
641
643
|
|
|
642
644
|
// `items` itself is a structure that can contain "elements", and more.
|
|
643
645
|
if (construct.items) {
|
|
644
|
-
|
|
645
|
-
forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions );
|
|
646
|
+
forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions, constructCallback );
|
|
646
647
|
}
|
|
647
648
|
|
|
648
649
|
// Unlike XSN, we don't make "returns" a "params" in the callback.
|
|
@@ -650,12 +651,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
650
651
|
// `elements` of the return type (if structured).
|
|
651
652
|
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
652
653
|
if (construct.returns && !iterateOptions.elementsOnly) {
|
|
653
|
-
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
|
|
654
|
+
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions, constructCallback );
|
|
654
655
|
}
|
|
655
656
|
|
|
656
657
|
path = [...path]; // Copy
|
|
657
658
|
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
|
|
658
|
-
propsWithMembers.forEach((prop) =>
|
|
659
|
+
propsWithMembers.forEach((prop) => {
|
|
660
|
+
forEachGeneric( construct, prop, callback, path, iterateOptions );
|
|
661
|
+
if (construct[prop]) {
|
|
662
|
+
if (Array.isArray(constructCallback))
|
|
663
|
+
constructCallback.forEach(cb => cb(construct, prop, path));
|
|
664
|
+
else
|
|
665
|
+
constructCallback(construct, prop, path);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Call `forEachMember` and then apply `forEachMember` on queries.
|
|
672
|
+
*
|
|
673
|
+
* @param {CSN.Artifact} construct
|
|
674
|
+
* @param {genericCallback|genericCallback[]} callback
|
|
675
|
+
* @param {CSN.Path} [path]
|
|
676
|
+
* @param {boolean} [ignoreIgnore]
|
|
677
|
+
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
678
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
679
|
+
*/
|
|
680
|
+
function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
681
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
682
|
+
forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
683
|
+
if (construct.query) {
|
|
684
|
+
forAllQueries(construct.query, (q, p) => {
|
|
685
|
+
const s = q.SELECT;
|
|
686
|
+
if(s) {
|
|
687
|
+
forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
|
|
688
|
+
}
|
|
689
|
+
}, [ ...path, 'query' ]);
|
|
690
|
+
}
|
|
659
691
|
}
|
|
660
692
|
|
|
661
693
|
/**
|
|
@@ -667,16 +699,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
667
699
|
* @param {CSN.Path} [path]
|
|
668
700
|
* @param {boolean} [ignoreIgnore]
|
|
669
701
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
702
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
670
703
|
*/
|
|
671
|
-
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}
|
|
672
|
-
|
|
704
|
+
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
705
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
706
|
+
forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
|
|
673
707
|
if(Array.isArray(callback))
|
|
674
|
-
callback.forEach(cb => cb( member, memberName, prop, subpath,
|
|
708
|
+
callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
|
|
675
709
|
else
|
|
676
|
-
callback( member, memberName, prop, subpath,
|
|
710
|
+
callback( member, memberName, prop, subpath, parent );
|
|
677
711
|
// Descend into nested members, too
|
|
678
|
-
forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions);
|
|
679
|
-
}, path, ignoreIgnore, iterateOptions);
|
|
712
|
+
forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions, constructCallback);
|
|
713
|
+
}, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Apply function `callback(member, memberName)` to each member in `construct`,
|
|
718
|
+
* recursively (i.e. also for sub-elements of elements).
|
|
719
|
+
* Recursively iterate over elements of `construct` query.
|
|
720
|
+
*
|
|
721
|
+
* @param {CSN.Artifact} construct
|
|
722
|
+
* @param {genericCallback|genericCallback[]} callback
|
|
723
|
+
* @param {CSN.Path} [path]
|
|
724
|
+
* @param {boolean} [ignoreIgnore]
|
|
725
|
+
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
726
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
727
|
+
*/
|
|
728
|
+
function forEachMemberRecursivelyWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
729
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
730
|
+
forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
731
|
+
if(construct.query) {
|
|
732
|
+
forAllQueries(construct.query, (q, p) => {
|
|
733
|
+
const s = q.SELECT;
|
|
734
|
+
if(s) {
|
|
735
|
+
forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
|
|
736
|
+
}
|
|
737
|
+
}, [ ...path, 'query' ]);
|
|
738
|
+
}
|
|
680
739
|
}
|
|
681
740
|
|
|
682
741
|
/**
|
|
@@ -685,14 +744,14 @@ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=tr
|
|
|
685
744
|
* the following arguments: the object, the name, and -if it is a duplicate-
|
|
686
745
|
* the array index and the array containing all duplicates.
|
|
687
746
|
*
|
|
688
|
-
* @param {object}
|
|
747
|
+
* @param {object} construct
|
|
689
748
|
* @param {string} prop
|
|
690
749
|
* @param {genericCallback|genericCallback[]} callback
|
|
691
750
|
* @param {CSN.Path} path
|
|
692
751
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
693
752
|
*/
|
|
694
|
-
function forEachGeneric(
|
|
695
|
-
const dict =
|
|
753
|
+
function forEachGeneric( construct, prop, callback, path = [], iterateOptions = {}) {
|
|
754
|
+
const dict = construct[prop];
|
|
696
755
|
for (const name in dict) {
|
|
697
756
|
if (!Object.prototype.hasOwnProperty.call(dict, name))
|
|
698
757
|
continue;
|
|
@@ -701,26 +760,26 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
|
|
|
701
760
|
|| (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
|
|
702
761
|
&& iterateOptions.skipArtifact(dictObj, name)))
|
|
703
762
|
continue;
|
|
704
|
-
|
|
763
|
+
executeCallbacks( dictObj, name );
|
|
705
764
|
}
|
|
706
|
-
function
|
|
765
|
+
function executeCallbacks(o, name ) {
|
|
707
766
|
if (Array.isArray(callback))
|
|
708
|
-
callback.forEach(cb => cb( o, name, prop, path.concat([prop, name])));
|
|
767
|
+
callback.forEach(cb => cb( o, name, prop, path.concat([prop, name]), construct ));
|
|
709
768
|
else
|
|
710
|
-
callback( o, name, prop, path.concat([prop, name]))
|
|
769
|
+
callback( o, name, prop, path.concat([prop, name]), construct )
|
|
711
770
|
}
|
|
712
771
|
}
|
|
713
772
|
|
|
714
773
|
// Like Object.assign() but copies also non enumerable properties
|
|
715
774
|
function assignAll(target, ...sources) {
|
|
716
775
|
sources.forEach(source => {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return
|
|
776
|
+
const descriptors = Object.getOwnPropertyNames(source).reduce((propertyDescriptors, current) => {
|
|
777
|
+
propertyDescriptors[current] = Object.getOwnPropertyDescriptor(source, current);
|
|
778
|
+
return propertyDescriptors;
|
|
720
779
|
}, {});
|
|
721
780
|
// by default, Object.assign copies enumerable Symbols too
|
|
722
781
|
Object.getOwnPropertySymbols(source).forEach(sym => {
|
|
723
|
-
|
|
782
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, sym);
|
|
724
783
|
if (descriptor.enumerable) {
|
|
725
784
|
descriptors[sym] = descriptor;
|
|
726
785
|
}
|
|
@@ -731,68 +790,68 @@ function assignAll(target, ...sources) {
|
|
|
731
790
|
}
|
|
732
791
|
|
|
733
792
|
/**
|
|
734
|
-
* @param {CSN.Query}
|
|
735
|
-
* @param {queryCallback|queryCallback[]}
|
|
793
|
+
* @param {CSN.Query} mainQuery
|
|
794
|
+
* @param {queryCallback|queryCallback[]} queryCallback
|
|
736
795
|
* @param {CSN.Path} path
|
|
737
796
|
*/
|
|
738
|
-
function forAllQueries(
|
|
739
|
-
return traverseQuery(
|
|
740
|
-
function traverseQuery(
|
|
741
|
-
if (
|
|
797
|
+
function forAllQueries(mainQuery, queryCallback, path = []){
|
|
798
|
+
return traverseQuery(mainQuery, queryCallback, path);
|
|
799
|
+
function traverseQuery( query, callback, queryPath ) {
|
|
800
|
+
if (query.SELECT) {
|
|
742
801
|
// The projection is turned into a normalized query - there
|
|
743
802
|
// is no real SELECT, it is fake
|
|
744
803
|
if(!(path.length === 3 && path[2] === 'projection'))
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
804
|
+
queryPath.push('SELECT');
|
|
805
|
+
executeCallbacks();
|
|
806
|
+
query = query.SELECT;
|
|
748
807
|
}
|
|
749
|
-
else if (
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
808
|
+
else if (query.SET) {
|
|
809
|
+
queryPath.push('SET');
|
|
810
|
+
executeCallbacks();
|
|
811
|
+
query = query.SET;
|
|
753
812
|
}
|
|
754
813
|
|
|
755
|
-
if (
|
|
756
|
-
traverseFrom(
|
|
814
|
+
if (query.from)
|
|
815
|
+
traverseFrom( query.from, callback, queryPath.concat(['from']) );
|
|
757
816
|
|
|
758
817
|
for (const prop of ['args', 'xpr', 'columns', 'where', 'having']) {
|
|
759
818
|
// all properties which could have sub queries (directly or indirectly)
|
|
760
|
-
const expr =
|
|
819
|
+
const expr = query[prop];
|
|
761
820
|
if (expr && typeof expr === 'object') {
|
|
762
821
|
if(Array.isArray(expr)){
|
|
763
822
|
for(let i = 0; i < expr.length; i++){
|
|
764
|
-
traverseQuery(expr[i], callback,
|
|
823
|
+
traverseQuery(expr[i], callback, queryPath.concat([prop, i]));
|
|
765
824
|
}
|
|
766
825
|
} else {
|
|
767
826
|
for(const argName of Object.keys( expr )){
|
|
768
|
-
traverseQuery(expr[argName], callback,
|
|
827
|
+
traverseQuery(expr[argName], callback, queryPath.concat([prop, argName]))
|
|
769
828
|
}
|
|
770
829
|
}
|
|
771
830
|
}
|
|
772
831
|
}
|
|
773
|
-
function
|
|
832
|
+
function executeCallbacks() {
|
|
774
833
|
if(Array.isArray(callback))
|
|
775
|
-
callback.forEach(cb => cb(
|
|
834
|
+
callback.forEach(cb => cb( query, queryPath ));
|
|
776
835
|
else
|
|
777
|
-
callback(
|
|
836
|
+
callback( query, queryPath );
|
|
778
837
|
}
|
|
779
838
|
}
|
|
780
839
|
|
|
781
840
|
/**
|
|
782
841
|
* @param {CSN.QueryFrom} from
|
|
783
842
|
* @param {Function} callback
|
|
784
|
-
* @param {CSN.Path}
|
|
843
|
+
* @param {CSN.Path} csnPath
|
|
785
844
|
*/
|
|
786
|
-
function traverseFrom( from, callback,
|
|
845
|
+
function traverseFrom( from, callback, csnPath = [] ) {
|
|
787
846
|
if (from.ref) // ignore
|
|
788
847
|
return;
|
|
789
848
|
else if (from.args){ // join
|
|
790
849
|
for(let i = 0; i < from.args.length; i++){
|
|
791
|
-
traverseFrom(from.args[i], callback,
|
|
850
|
+
traverseFrom(from.args[i], callback, csnPath.concat(['args', i]));
|
|
792
851
|
}
|
|
793
852
|
}
|
|
794
853
|
else
|
|
795
|
-
traverseQuery( from, callback,
|
|
854
|
+
traverseQuery( from, callback, csnPath ); // sub query in FROM
|
|
796
855
|
}
|
|
797
856
|
}
|
|
798
857
|
|
|
@@ -967,7 +1026,7 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
967
1026
|
|
|
968
1027
|
return result;
|
|
969
1028
|
} else if(art && art.kind === 'service') {
|
|
970
|
-
// inside services, we
|
|
1029
|
+
// inside services, we immediately turn . into _
|
|
971
1030
|
const prefix = parts.slice(0, i).join('.');
|
|
972
1031
|
const suffix = parts.slice(i).join('_');
|
|
973
1032
|
const result = [];
|
|
@@ -1139,17 +1198,17 @@ function mergeOptions(...optionsObjects) {
|
|
|
1139
1198
|
|
|
1140
1199
|
// Recursively used for scalars, too
|
|
1141
1200
|
function mergeTwo(left, right, name) {
|
|
1142
|
-
let
|
|
1201
|
+
let intermediateResult;
|
|
1143
1202
|
// Copy left as far as required
|
|
1144
1203
|
if (Array.isArray(left)) {
|
|
1145
1204
|
// Shallow-copy left array
|
|
1146
|
-
|
|
1205
|
+
intermediateResult = left.slice();
|
|
1147
1206
|
} else if (isObject(left)) {
|
|
1148
1207
|
// Deep-copy left object (unless empty)
|
|
1149
|
-
|
|
1208
|
+
intermediateResult = Object.keys(left).length ? mergeTwo({}, left, name) : {};
|
|
1150
1209
|
} else {
|
|
1151
1210
|
// Just use left scalar
|
|
1152
|
-
|
|
1211
|
+
intermediateResult = left;
|
|
1153
1212
|
}
|
|
1154
1213
|
// Check against improper overwriting
|
|
1155
1214
|
if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
|
|
@@ -1162,21 +1221,21 @@ function mergeOptions(...optionsObjects) {
|
|
|
1162
1221
|
// Copy or overwrite properties from right to left
|
|
1163
1222
|
if (Array.isArray(right)) {
|
|
1164
1223
|
// Shallow-copy right array
|
|
1165
|
-
|
|
1224
|
+
intermediateResult = right.slice();
|
|
1166
1225
|
} else if (isObject(right)) {
|
|
1167
1226
|
// Object overwrites undefined, scalars and arrays
|
|
1168
|
-
if (
|
|
1169
|
-
|
|
1227
|
+
if (intermediateResult === undefined || isScalar(intermediateResult) || Array.isArray(intermediateResult)) {
|
|
1228
|
+
intermediateResult = {};
|
|
1170
1229
|
}
|
|
1171
1230
|
// Deep-copy right object into result
|
|
1172
1231
|
for (let key of Object.keys(right)) {
|
|
1173
|
-
|
|
1232
|
+
intermediateResult[key] = mergeTwo(intermediateResult[key], right[key], `${name}.${key}`);
|
|
1174
1233
|
}
|
|
1175
1234
|
} else {
|
|
1176
1235
|
// Right scalar wins (unless undefined)
|
|
1177
|
-
|
|
1236
|
+
intermediateResult = (right !== undefined) ? right : intermediateResult;
|
|
1178
1237
|
}
|
|
1179
|
-
return
|
|
1238
|
+
return intermediateResult;
|
|
1180
1239
|
}
|
|
1181
1240
|
|
|
1182
1241
|
// Return true if 'o' is a non-null object or array
|
|
@@ -1264,19 +1323,73 @@ function getParentNamesOf(name) {
|
|
|
1264
1323
|
}
|
|
1265
1324
|
|
|
1266
1325
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1326
|
+
/**
|
|
1327
|
+
* Copy all annotations from 'fromNode' to 'toNode'.
|
|
1328
|
+
*
|
|
1329
|
+
* Overwrite existing ones only if 'overwrite' is true.
|
|
1330
|
+
*
|
|
1331
|
+
* @param {object} fromNode
|
|
1332
|
+
* @param {object} toNode
|
|
1333
|
+
* @param {boolean} [overwrite]
|
|
1334
|
+
*/
|
|
1335
|
+
function copyAnnotations(fromNode, toNode, overwrite = false) {
|
|
1269
1336
|
// Ignore if no toNode (in case of errors)
|
|
1270
|
-
if (!toNode)
|
|
1337
|
+
if (!toNode)
|
|
1271
1338
|
return;
|
|
1339
|
+
|
|
1340
|
+
const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1341
|
+
|
|
1342
|
+
for (const anno of annotations) {
|
|
1343
|
+
if (toNode[anno] === undefined || overwrite) {
|
|
1344
|
+
toNode[anno] = fromNode[anno];
|
|
1345
|
+
}
|
|
1272
1346
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* Same as `copyAnnotations()` but also copies the
|
|
1352
|
+
* annotation-like property `doc`.
|
|
1353
|
+
*
|
|
1354
|
+
* Overwrite existing ones only if 'overwrite' is true.
|
|
1355
|
+
*
|
|
1356
|
+
* @param {object} fromNode
|
|
1357
|
+
* @param {object} toNode
|
|
1358
|
+
* @param {boolean} [overwrite]
|
|
1359
|
+
*/
|
|
1360
|
+
function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
1361
|
+
// Ignore if no toNode (in case of errors)
|
|
1362
|
+
if (!toNode)
|
|
1363
|
+
return;
|
|
1364
|
+
|
|
1365
|
+
const annotations = Object.keys(fromNode)
|
|
1366
|
+
.filter(key => key.startsWith('@') || key === 'doc');
|
|
1367
|
+
|
|
1368
|
+
for (const anno of annotations) {
|
|
1369
|
+
if (toNode[anno] === undefined || overwrite) {
|
|
1370
|
+
toNode[anno] = fromNode[anno];
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Applies annotations from `csn.extensions` to definitions, i.e. top-level artifacts.
|
|
1377
|
+
* Does _not_ apply element/param/action/... annotations.
|
|
1378
|
+
* `config.filter` can be used to only copy annotations for those definitions,
|
|
1379
|
+
* for which the filter returns true.
|
|
1380
|
+
*
|
|
1381
|
+
* @param {CSN.Model} csn
|
|
1382
|
+
* @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
|
|
1383
|
+
*/
|
|
1384
|
+
function applyDefinitionAnnotationsFromExtensions(csn, config) {
|
|
1385
|
+
if (!csn.extensions)
|
|
1386
|
+
return;
|
|
1387
|
+
|
|
1388
|
+
const filter = config.filter || ((_name) => true);
|
|
1389
|
+
for (const ext of csn.extensions) {
|
|
1390
|
+
const name = ext.annotate || ext.extend;
|
|
1391
|
+
if (name && csn.definitions[name] && filter(name)) {
|
|
1392
|
+
copyAnnotationsAndDoc(ext, csn.definitions[name], config.overwrite);
|
|
1280
1393
|
}
|
|
1281
1394
|
}
|
|
1282
1395
|
}
|
|
@@ -1442,7 +1555,7 @@ function getVariableReplacement(ref, options) {
|
|
|
1442
1555
|
* @returns {boolean} returns equality
|
|
1443
1556
|
*
|
|
1444
1557
|
* noExtendedProps remove '$', '_' and '@' properties from
|
|
1445
|
-
* the
|
|
1558
|
+
* the comparison. This eliminates false negatives such as
|
|
1446
1559
|
* mismatching $locations or @odata.foreignKey4.
|
|
1447
1560
|
*/
|
|
1448
1561
|
function isDeepEqual(obj, other, noExtendedProps) {
|
|
@@ -1472,14 +1585,18 @@ function isDeepEqual(obj, other, noExtendedProps) {
|
|
|
1472
1585
|
|
|
1473
1586
|
module.exports = {
|
|
1474
1587
|
getUtils,
|
|
1475
|
-
cloneCsn,
|
|
1588
|
+
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
1589
|
+
cloneCsnNonDict,
|
|
1476
1590
|
cloneCsnDictionary,
|
|
1477
1591
|
isBuiltinType,
|
|
1478
1592
|
assignAll,
|
|
1593
|
+
applyDefinitionAnnotationsFromExtensions,
|
|
1479
1594
|
forEachGeneric,
|
|
1480
1595
|
forEachDefinition,
|
|
1481
1596
|
forEachMember,
|
|
1597
|
+
forEachMemberWithQuery,
|
|
1482
1598
|
forEachMemberRecursively,
|
|
1599
|
+
forEachMemberRecursivelyWithQuery,
|
|
1483
1600
|
forAllQueries,
|
|
1484
1601
|
hasAnnotationValue,
|
|
1485
1602
|
isEdmPropertyRendered,
|
|
@@ -1500,6 +1617,7 @@ module.exports = {
|
|
|
1500
1617
|
getParentNameOf,
|
|
1501
1618
|
getLastPartOf,
|
|
1502
1619
|
copyAnnotations,
|
|
1620
|
+
copyAnnotationsAndDoc,
|
|
1503
1621
|
isAspect,
|
|
1504
1622
|
forEachPath,
|
|
1505
1623
|
hasValidSkipOrExists,
|
package/lib/model/enrichCsn.js
CHANGED
|
@@ -36,7 +36,7 @@ const kindsRepresentedAsLinks = {
|
|
|
36
36
|
// represent table alias in from / join-args property as link:
|
|
37
37
|
$tableAlias: tableAliasAsLink,
|
|
38
38
|
// represent "navigation elemens" in _combined as links:
|
|
39
|
-
$navElement: (art, parent) => art._parent && parent !== art._parent.elements,
|
|
39
|
+
$navElement: (art, parent) => art._parent && parent !== art._parent.elements && art._parent.kind !== 'aspect',
|
|
40
40
|
// represent mixin in $tableAliases as link:
|
|
41
41
|
mixin: tableAliasAsLink,
|
|
42
42
|
// represent $projection as link, as it is just another search name for $self:
|
|
@@ -129,7 +129,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
129
129
|
|
|
130
130
|
path = path.split('/');
|
|
131
131
|
if (path.length === 1) {
|
|
132
|
-
return reveal( xsn.definitions[path] );
|
|
132
|
+
return reveal( xsn.definitions[path] || xsn.vocabularies && xsn.vocabularies[path] );
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// with the code below, we might miss the right transformer function
|
|
@@ -45,19 +45,19 @@ function validateCsnVersions(beforeModel, afterModel, options) {
|
|
|
45
45
|
let afterVersionParts = afterVersion && afterVersion.split('.');
|
|
46
46
|
|
|
47
47
|
if (!beforeVersionParts || beforeVersionParts.length < 2) {
|
|
48
|
-
const { error,
|
|
48
|
+
const { error, throwWithAnyError } = makeMessageFunction(beforeModel, options, 'modelCompare');
|
|
49
49
|
error(null, null, `Invalid CSN version: ${beforeVersion}`);
|
|
50
|
-
|
|
50
|
+
throwWithAnyError();
|
|
51
51
|
}
|
|
52
52
|
if (!afterVersionParts || afterVersionParts.length < 2) {
|
|
53
|
-
const { error,
|
|
53
|
+
const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
|
|
54
54
|
error(null, null, `Invalid CSN version: ${afterVersion}`);
|
|
55
|
-
|
|
55
|
+
throwWithAnyError();
|
|
56
56
|
}
|
|
57
57
|
if (beforeVersionParts[0] > afterVersionParts[0] && !(options && options.allowCsnDowngrade)) {
|
|
58
|
-
const { error,
|
|
58
|
+
const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
|
|
59
59
|
error(null, null, `Incompatible CSN versions: ${afterVersion} is a major downgrade from ${beforeVersion}. Is @sap/cds-compiler version ${require('../../package.json').version} outdated?`);
|
|
60
|
-
|
|
60
|
+
throwWithAnyError();
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|