@sap/cds-compiler 4.9.2 → 5.0.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 +74 -0
- package/bin/cds_remove_invalid_whitespace.js +2 -1
- package/bin/cdsc.js +15 -11
- package/bin/cdshi.js +1 -0
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +7 -19
- package/lib/api/options.js +5 -11
- package/lib/api/trace.js +0 -1
- package/lib/base/builtins.js +1 -0
- package/lib/base/location.js +4 -1
- package/lib/base/message-registry.js +29 -29
- package/lib/base/messages.js +22 -26
- package/lib/base/model.js +0 -2
- package/lib/base/node-helpers.js +0 -1
- package/lib/checks/enricher.js +1 -5
- package/lib/checks/structuredAnnoExpressions.js +30 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +4 -1
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +18 -2
- package/lib/compiler/checks.js +2 -5
- package/lib/compiler/define.js +7 -7
- package/lib/compiler/extend.js +68 -33
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/index.js +23 -6
- package/lib/compiler/lsp-api.js +501 -2
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +1 -4
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +112 -31
- package/lib/compiler/tweak-assocs.js +2 -16
- package/lib/compiler/utils.js +2 -1
- package/lib/compiler/xsn-model.js +4 -0
- package/lib/edm/annotations/genericTranslation.js +95 -42
- package/lib/edm/csn2edm.js +16 -4
- package/lib/edm/edm.js +2 -3
- package/lib/edm/edmAnnoPreprocessor.js +1 -2
- package/lib/edm/edmPreprocessor.js +1 -7
- package/lib/gen/Dictionary.json +29 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4995 -4817
- package/lib/json/csnVersion.js +1 -1
- package/lib/json/from-csn.js +4 -7
- package/lib/json/to-csn.js +23 -12
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/errorStrategy.js +0 -1
- package/lib/language/genericAntlrParser.js +35 -12
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/language/textUtils.js +1 -0
- package/lib/main.d.ts +28 -9
- package/lib/main.js +7 -4
- package/lib/model/csnRefs.js +20 -4
- package/lib/model/csnUtils.js +0 -2
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/modelCompare/compare.js +1 -1
- package/lib/optionProcessor.js +28 -9
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +36 -7
- package/lib/render/toSql.js +1 -0
- package/lib/render/utils/common.js +12 -9
- package/lib/render/utils/stringEscapes.js +1 -0
- package/lib/transform/db/applyTransformations.js +13 -8
- package/lib/transform/db/associations.js +62 -54
- package/lib/transform/db/constraints.js +23 -25
- package/lib/transform/db/expansion.js +1 -6
- package/lib/transform/db/flattening.js +89 -111
- package/lib/transform/db/temporal.js +3 -4
- package/lib/transform/db/views.js +0 -1
- package/lib/transform/draft/odata.js +51 -3
- package/lib/transform/effective/annotations.js +3 -2
- package/lib/transform/effective/flattening.js +135 -0
- package/lib/transform/effective/main.js +6 -6
- package/lib/transform/effective/types.js +13 -9
- package/lib/transform/forOdata.js +0 -2
- package/lib/transform/forRelationalDB.js +0 -19
- package/lib/transform/localized.js +7 -8
- package/lib/transform/odata/flattening.js +39 -31
- package/lib/transform/odata/typesExposure.js +5 -17
- package/lib/transform/transformUtils.js +1 -1
- package/lib/transform/translateAssocsToJoins.js +21 -3
- package/lib/utils/file.js +13 -7
- package/lib/utils/moduleResolve.js +59 -8
- package/lib/utils/term.js +3 -2
- package/package.json +7 -3
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/type-unexpected-foreign-keys.md +52 -0
- package/share/messages/type-unexpected-on-condition.md +52 -0
|
@@ -18,18 +18,15 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
18
18
|
* @param {CSN.Model} csn will be transformed
|
|
19
19
|
* @param {object} csnUtils
|
|
20
20
|
* @param {CSN.Options} options
|
|
21
|
-
* @returns {Function} Callback to resolve
|
|
21
|
+
* @returns {Function} Callback to resolve types in action returns later - as for them, $self would lead to unresolvable constructs at this point
|
|
22
22
|
* so we can call this callback after flattening is done - then we can safely resolve their types.
|
|
23
23
|
*/
|
|
24
24
|
function resolveTypes( csn, csnUtils, options ) {
|
|
25
25
|
const { getFinalTypeInfo } = csnUtils;
|
|
26
26
|
const later = [];
|
|
27
27
|
applyTransformations(csn, {
|
|
28
|
-
type: (parent
|
|
29
|
-
|
|
30
|
-
// TODO: What about events?
|
|
31
|
-
if (!(artifact.kind === 'action' || artifact.kind === 'function'))
|
|
32
|
-
resolveType(parent);
|
|
28
|
+
type: (parent) => {
|
|
29
|
+
resolveType(parent);
|
|
33
30
|
},
|
|
34
31
|
}, [ (definitions, artifactName, artifact) => {
|
|
35
32
|
// In a non-flat model, replacing types with some $self inside causes issues for actions (bound or unbound)
|
|
@@ -38,12 +35,20 @@ function resolveTypes( csn, csnUtils, options ) {
|
|
|
38
35
|
later.push({ [artifactName]: artifact });
|
|
39
36
|
else if (artifact.actions)
|
|
40
37
|
later.push(artifact.actions);
|
|
41
|
-
} ], {
|
|
38
|
+
} ], { skipStandard: { returns: true }, processAnnotations: true });
|
|
42
39
|
|
|
40
|
+
// TODO: Directly push the .returns into the later so we have a more minimal looping
|
|
43
41
|
return function resolveTypesInActions() {
|
|
44
|
-
later.forEach(
|
|
42
|
+
later.forEach((a) => {
|
|
43
|
+
applyTransformationsOnDictionary(a, {
|
|
44
|
+
type: (parent) => {
|
|
45
|
+
resolveType(parent);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
});
|
|
45
49
|
};
|
|
46
50
|
|
|
51
|
+
|
|
47
52
|
/**
|
|
48
53
|
* Resolve a type to its
|
|
49
54
|
* - elements
|
|
@@ -123,7 +128,6 @@ function resolveTypes( csn, csnUtils, options ) {
|
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
|
|
127
131
|
module.exports = {
|
|
128
132
|
resolve: resolveTypes,
|
|
129
133
|
};
|
|
@@ -167,8 +167,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
167
167
|
{ skipArtifact: isExternalServiceMember }
|
|
168
168
|
);
|
|
169
169
|
|
|
170
|
-
flattening.linkForeignKeyAnnotationExtensionsToAssociation(csn, options);
|
|
171
|
-
|
|
172
170
|
// All type refs must be resolved, including external APIs.
|
|
173
171
|
// OData has no 'type of' so 'real' imported OData APIs marked @cds.external are safe.
|
|
174
172
|
// If in the future 'other' APIs that might support type refs are imported, these refs must be
|
|
@@ -169,8 +169,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
169
169
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
170
170
|
handleExists(csn, options, error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
|
|
171
171
|
|
|
172
|
-
doA2J && flattening.linkForeignKeyAnnotationExtensionsToAssociation(csn, options);
|
|
173
|
-
|
|
174
172
|
// Check if structured elements and managed associations are compared in an expression
|
|
175
173
|
// and expand these structured elements. This tuple expansion allows all other
|
|
176
174
|
// subsequent procession steps (especially a2j) to see plain paths in expressions.
|
|
@@ -702,23 +700,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
702
700
|
newCsn.definitions[artName].technicalConfig = art.technicalConfig;
|
|
703
701
|
|
|
704
702
|
});
|
|
705
|
-
// restore $fkExtensions and $structRef for foreign key annotations
|
|
706
|
-
if (isBetaEnabled(options, 'annotateForeignKeys')) {
|
|
707
|
-
forEachDefinition(csn, (oldDef, artName) => {
|
|
708
|
-
const newDef = newCsn.definitions[artName];
|
|
709
|
-
if(oldDef?.elements) {
|
|
710
|
-
Object.entries(oldDef.elements).forEach(([eltName, oldElt]) => {
|
|
711
|
-
const newElt = newDef.elements[eltName];
|
|
712
|
-
if(oldElt.$fkExtensions)
|
|
713
|
-
setProp(newElt, '$fkExtensions', oldElt.$fkExtensions);
|
|
714
|
-
oldElt.keys?.forEach((fk, i) => {
|
|
715
|
-
if(fk.$structRef && newElt.keys?.[i])
|
|
716
|
-
setProp(newElt.keys[i], '$structRef', fk.$structRef);
|
|
717
|
-
})
|
|
718
|
-
})
|
|
719
|
-
}
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
703
|
csn = newCsn;
|
|
723
704
|
}
|
|
724
705
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
|
-
const { setProp
|
|
4
|
+
const { setProp } = require('../base/model');
|
|
5
5
|
const { forEachKey } = require('../utils/objectUtils');
|
|
6
6
|
const { cleanSymbols } = require('../base/cleanSymbols.js');
|
|
7
7
|
const {
|
|
@@ -91,6 +91,7 @@ const annoPersistenceSkip = '@cds.persistence.skip';
|
|
|
91
91
|
* Deprecated version of localizedLanguageFallback. Do not use.
|
|
92
92
|
*
|
|
93
93
|
* @param {boolean} [options.fewerLocalizedViews]
|
|
94
|
+
* Default: true
|
|
94
95
|
*
|
|
95
96
|
* @param {object} config
|
|
96
97
|
* Configuration for creating convenience views. Non-user visible options.
|
|
@@ -114,7 +115,8 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
114
115
|
const { useJoins, acceptLocalizedView, ignoreUnknownExtensions } = config;
|
|
115
116
|
const noCoalesce = (options.localizedLanguageFallback === 'none' ||
|
|
116
117
|
options.localizedWithoutCoalesce);
|
|
117
|
-
|
|
118
|
+
// default is true, hence only check for explicitly disabled option
|
|
119
|
+
const ignoreAssocToLocalized = options.fewerLocalizedViews !== false;
|
|
118
120
|
|
|
119
121
|
createDirectConvenienceViews(); // 1
|
|
120
122
|
createTransitiveConvenienceViews(); // 2 + 3
|
|
@@ -176,7 +178,7 @@ function _addLocalizationViews(csn, options, config) {
|
|
|
176
178
|
else
|
|
177
179
|
view = createLocalizedViewForEntity(art, artName, textElements);
|
|
178
180
|
|
|
179
|
-
copyPersistenceAnnotations(view, art
|
|
181
|
+
copyPersistenceAnnotations(view, art);
|
|
180
182
|
csn.definitions[viewName] = view;
|
|
181
183
|
}
|
|
182
184
|
|
|
@@ -752,18 +754,15 @@ function copyLocation(target, source) {
|
|
|
752
754
|
*
|
|
753
755
|
* @param {CSN.Artifact} target
|
|
754
756
|
* @param {CSN.Artifact} source
|
|
755
|
-
* @param {CSN.Options} options
|
|
756
757
|
*/
|
|
757
|
-
function copyPersistenceAnnotations(target, source
|
|
758
|
-
const copyExists = !isBetaEnabled(options, 'v5preview') &&
|
|
759
|
-
!isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
|
|
758
|
+
function copyPersistenceAnnotations(target, source) {
|
|
760
759
|
forEachKey(source, anno => {
|
|
761
760
|
// Note:
|
|
762
761
|
// v3/v4: Because `.exists` is copied to the convenience view, it could
|
|
763
762
|
// lead to some localization views referencing non-existing ones.
|
|
764
763
|
// But that is the contract: User says that it already exists!
|
|
765
764
|
// v2/>=v5, `.exists` is never copied.
|
|
766
|
-
if (anno === annoPersistenceSkip
|
|
765
|
+
if (anno === annoPersistenceSkip)
|
|
767
766
|
target[anno] = source[anno];
|
|
768
767
|
});
|
|
769
768
|
}
|
|
@@ -8,8 +8,7 @@ const transformUtils = require('../transformUtils');
|
|
|
8
8
|
const { setProp } = require('../../base/model');
|
|
9
9
|
const { applyTransformationsOnDictionary,
|
|
10
10
|
applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');
|
|
11
|
-
const {
|
|
12
|
-
handleManagedAssociationsAndCreateForeignKeys } = require('../db/flattening');
|
|
11
|
+
const { handleManagedAssociationsAndCreateForeignKeys } = require('../db/flattening');
|
|
13
12
|
const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
14
13
|
const { forEach } = require('../../utils/objectUtils');
|
|
15
14
|
|
|
@@ -293,17 +292,27 @@ function allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternal
|
|
|
293
292
|
|
|
294
293
|
const refCheck = {
|
|
295
294
|
ref: (elemref, prop, xpr, path) => {
|
|
296
|
-
const { art, scope } = inspectRef(path);
|
|
295
|
+
const { links, art, scope } = inspectRef(path);
|
|
297
296
|
if (scope !== '$magic' && art) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
297
|
+
// try to find rightmost 'items', terminate if association comes first.
|
|
298
|
+
let i = links.length-1;
|
|
299
|
+
const getProp = (propName) => links[i].art?.[propName];
|
|
300
|
+
|
|
301
|
+
let hasItems = false;
|
|
302
|
+
for(; i >= 0 && !getProp('target') && !hasItems; i--) {
|
|
303
|
+
hasItems = !!getProp('items')
|
|
304
|
+
}
|
|
305
|
+
if(!hasItems) {
|
|
306
|
+
const ft = csnUtils.getFinalTypeInfo(art.type);
|
|
307
|
+
if (!isBuiltinType(ft?.items?.type || ft?.type) && refCheck.anno !== 'value') {
|
|
308
|
+
error('odata-anno-xpr-ref', path,
|
|
309
|
+
{
|
|
310
|
+
anno: refCheck.anno,
|
|
311
|
+
elemref,
|
|
312
|
+
name: refCheck.eltLocationStr,
|
|
313
|
+
'#': 'flatten_builtin'
|
|
314
|
+
});
|
|
315
|
+
}
|
|
307
316
|
}
|
|
308
317
|
}
|
|
309
318
|
}
|
|
@@ -421,24 +430,24 @@ function flattenAllStructStepsInRefs( csn, refFlattener, adaptRefs, inspectRef,
|
|
|
421
430
|
|
|
422
431
|
const refCheck = {
|
|
423
432
|
ref: (elemref, prop, xpr, path) => {
|
|
424
|
-
const { links, art } = (
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
433
|
+
const { links, art, scope } = inspectRef(path);
|
|
434
|
+
|
|
435
|
+
if (scope !== '$magic' && art) {
|
|
436
|
+
let i = links.length-2;
|
|
437
|
+
const getProp = (propName) =>
|
|
438
|
+
(links[i].art?.[propName] ||
|
|
439
|
+
effectiveType(links[i].art)[propName]);
|
|
440
|
+
|
|
441
|
+
let target = undefined;
|
|
442
|
+
for(; i >= 0 && !getProp('items') && !target; i--) {
|
|
443
|
+
target = getProp('target');
|
|
444
|
+
}
|
|
445
|
+
const ft = csnUtils.getFinalTypeInfo(art.type);
|
|
446
|
+
if (target && csn.definitions[target].$flatelements
|
|
447
|
+
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
|
|
448
|
+
error('odata-anno-xpr-ref', path,
|
|
449
|
+
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
|
|
450
|
+
}
|
|
442
451
|
}
|
|
443
452
|
}
|
|
444
453
|
}
|
|
@@ -548,7 +557,6 @@ function getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, optio
|
|
|
548
557
|
module.exports = {
|
|
549
558
|
allInOneFlattening,
|
|
550
559
|
flattenAllStructStepsInRefs,
|
|
551
|
-
linkForeignKeyAnnotationExtensionsToAssociation,
|
|
552
560
|
handleManagedAssociationsAndCreateForeignKeys,
|
|
553
561
|
getStructRefFlatteningTransformer
|
|
554
562
|
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const { getNamespace, copyAnnotations,
|
|
11
|
+
const { getNamespace, copyAnnotations,
|
|
12
12
|
forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');
|
|
13
13
|
const { isBuiltinType } = require('../../base/builtins');
|
|
14
14
|
const { CompilerAssertion } = require('../../base/error');
|
|
@@ -229,22 +229,10 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
229
229
|
}
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (!isAnonymous) {
|
|
237
|
-
copyAnnotations(typeDef, newType);
|
|
238
|
-
}
|
|
239
|
-
} else {
|
|
240
|
-
// expression annos and their sub annotations are not propagated to type
|
|
241
|
-
let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
|
|
242
|
-
if (pn[0] === '@')
|
|
243
|
-
acc[findAnnotationExpression(typeDef, pn) ? 0 : 1].push(pn);
|
|
244
|
-
return acc;
|
|
245
|
-
}, [ [], [] ]);
|
|
246
|
-
nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
|
|
247
|
-
copyAnnotations(typeDef, newType, false, {}, nxprANames);
|
|
232
|
+
// Annotations are propagated only from user defined structured
|
|
233
|
+
// types that need to be added to a service
|
|
234
|
+
if (!isAnonymous) {
|
|
235
|
+
copyAnnotations(typeDef, newType);
|
|
248
236
|
}
|
|
249
237
|
|
|
250
238
|
// if the origin type had items, add items to exposed type
|
|
@@ -266,7 +266,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
266
266
|
} else {
|
|
267
267
|
// Primitive child - clone it and restore its cross references
|
|
268
268
|
const flatElemName = elemName + pathDelimiter + childName;
|
|
269
|
-
const flatElem = cloneCsnNonDict(childElem,
|
|
269
|
+
const flatElem = cloneCsnNonDict(childElem, options);
|
|
270
270
|
// Don't take over notNull from leaf elements
|
|
271
271
|
delete flatElem.notNull;
|
|
272
272
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
@@ -1110,8 +1110,27 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1110
1110
|
}
|
|
1111
1111
|
else {
|
|
1112
1112
|
const [ tableAlias, path ] = getTableAliasAndPathSteps(pathNode);
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1113
|
+
const rewrittenPath = [];
|
|
1114
|
+
const leafArtifact = path.at(-1)._artifact;
|
|
1115
|
+
// Walk from left to right and search for first assoc. If assocs in filters become join relevant in the future,
|
|
1116
|
+
// i.e. not only fk-access, we need to revisit this
|
|
1117
|
+
for(let i = 0; i < path.length; i++) {
|
|
1118
|
+
const pathStep = path[i];
|
|
1119
|
+
if(pathStep._artifact?.foreignKeys) {
|
|
1120
|
+
const possibleNonAliasedFkName = path.slice(i).map(ps => ps.id).join(pathDelimiter);
|
|
1121
|
+
if(!pathStep._artifact.$flatSrcFKs)
|
|
1122
|
+
setProp(pathStep._artifact, '$flatSrcFKs', flattenElement(pathStep._artifact, true, pathStep._artifact.name.id, pathStep._artifact.name.id));
|
|
1123
|
+
const fk = pathStep._artifact.$flatSrcFKs.find(f => f._artifact === leafArtifact && f.acc.startsWith(possibleNonAliasedFkName));
|
|
1124
|
+
if(fk) {
|
|
1125
|
+
rewrittenPath.push(fk);
|
|
1126
|
+
i = path.length;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
rewrittenPath.push(pathStep);
|
|
1132
|
+
}
|
|
1133
|
+
replaceNodeContent(pathNode, constructPathNode([ tableAlias, { id: rewrittenPath.map(ps => ps.id).join(pathDelimiter), _artifact: pathNode._artifact } ]));
|
|
1115
1134
|
}
|
|
1116
1135
|
}
|
|
1117
1136
|
} ]
|
|
@@ -1691,7 +1710,6 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1691
1710
|
const path = pathDict.path;
|
|
1692
1711
|
const s = pathAsStr(path, '"');
|
|
1693
1712
|
const me = env.lead && (env.lead.name.id || env.lead.op);
|
|
1694
|
-
// eslint-disable-next-line no-console
|
|
1695
1713
|
console.log(me + ': ' + env.location + ': ' + s + ' alias: ' + alias);
|
|
1696
1714
|
}
|
|
1697
1715
|
|
package/lib/utils/file.js
CHANGED
|
@@ -49,22 +49,28 @@ function cdsFs( fileCache, enableTrace ) {
|
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
/** @type {function(string, string)} */
|
|
52
|
-
readFileAsync: util.promisify(readFile),
|
|
52
|
+
readFileAsync: util.promisify( readFile ),
|
|
53
53
|
readFile,
|
|
54
54
|
readFileSync,
|
|
55
|
-
isFileAsync: util.promisify(isFile),
|
|
56
55
|
isFile,
|
|
57
56
|
isFileSync,
|
|
58
|
-
|
|
59
|
-
realpath,
|
|
57
|
+
realpath: fs.realpath,
|
|
58
|
+
realpathNative: fs.realpath.native,
|
|
60
59
|
realpathSync,
|
|
60
|
+
realpathSyncNative,
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
function realpath( path, cb ) {
|
|
64
|
-
return fs.realpath.native(path, cb);
|
|
65
|
-
}
|
|
66
63
|
|
|
67
64
|
function realpathSync( path, cb ) {
|
|
65
|
+
try {
|
|
66
|
+
cb(null, fs.realpathSync(path));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
cb(err, null);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function realpathSyncNative( path, cb ) {
|
|
68
74
|
try {
|
|
69
75
|
cb(null, fs.realpathSync.native(path));
|
|
70
76
|
}
|
|
@@ -70,7 +70,7 @@ function makeModuleResolver( options, fileCache, messageFunctions ) {
|
|
|
70
70
|
extensions,
|
|
71
71
|
isFile: _fs.isFile,
|
|
72
72
|
readFile: _fs.readFile,
|
|
73
|
-
realpath: _fs.
|
|
73
|
+
realpath: _fs.realpathNative,
|
|
74
74
|
lookupDirs: _getLookupDirectories( options, messageFunctions ),
|
|
75
75
|
};
|
|
76
76
|
|
|
@@ -90,7 +90,20 @@ function makeModuleResolver( options, fileCache, messageFunctions ) {
|
|
|
90
90
|
const body = fileCache[res];
|
|
91
91
|
if (body === undefined || body === true) { // use fs if no or just temp entry
|
|
92
92
|
dep.absname = res;
|
|
93
|
-
_fs.realpath( res,
|
|
93
|
+
_fs.realpath( res, function realPathResult(realpathErr, modulePath) {
|
|
94
|
+
if (realpathErr) {
|
|
95
|
+
cb(realpathErr, modulePath);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
_fs.realpathNative( res, function nativeRealPathResult(nativeErr, nativePath) {
|
|
99
|
+
if (!nativeErr)
|
|
100
|
+
checkFileCase( dep, modulePath, nativePath, messageFunctions );
|
|
101
|
+
// Pass the _native_ path to ensure that we use the actual
|
|
102
|
+
// file's path (include case-differences)
|
|
103
|
+
cb(realpathErr, nativePath);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} );
|
|
94
107
|
}
|
|
95
108
|
else if (body && typeof body === 'object' && body.realname) {
|
|
96
109
|
// dep.absname = body.realname;
|
|
@@ -138,7 +151,7 @@ function makeModuleResolverSync( options, fileCache, messageFunctions ) {
|
|
|
138
151
|
extensions,
|
|
139
152
|
isFile: _fs.isFileSync,
|
|
140
153
|
readFile: _fs.readFileSync,
|
|
141
|
-
realpath: _fs.
|
|
154
|
+
realpath: _fs.realpathSyncNative,
|
|
142
155
|
lookupDirs: _getLookupDirectories( options, messageFunctions ),
|
|
143
156
|
};
|
|
144
157
|
|
|
@@ -166,11 +179,23 @@ function makeModuleResolverSync( options, fileCache, messageFunctions ) {
|
|
|
166
179
|
const body = result ? fileCache[result] : undefined;
|
|
167
180
|
if (body === undefined || body === true) { // use fs if no or just temp entry
|
|
168
181
|
dep.absname = result;
|
|
169
|
-
_fs.realpathSync( result, (
|
|
170
|
-
if (
|
|
171
|
-
error =
|
|
172
|
-
|
|
173
|
-
|
|
182
|
+
_fs.realpathSync( result, function realPathResult(realpathErr, modulePath) {
|
|
183
|
+
if (realpathErr) {
|
|
184
|
+
error = realpathErr;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
_fs.realpathSyncNative( result, function nativeRealPathResult(nativeErr, nativePath) {
|
|
188
|
+
if (nativeErr) {
|
|
189
|
+
error = nativeErr;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
checkFileCase(dep, modulePath, nativePath, messageFunctions);
|
|
193
|
+
// Use the _native_ path to ensure that we use the actual
|
|
194
|
+
// file's path (include case-differences)
|
|
195
|
+
result = nativePath;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
174
199
|
});
|
|
175
200
|
}
|
|
176
201
|
else if (body && typeof body === 'object' && body.realname) {
|
|
@@ -533,6 +558,32 @@ function packageCdsMain( pkg ) {
|
|
|
533
558
|
return null;
|
|
534
559
|
}
|
|
535
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Check if the given paths for case-differences. If there are case differences
|
|
563
|
+
* emit a warning. This can happen on systems with case-insensitive file
|
|
564
|
+
* systems. As that is a hard-to-debug issue, we help the user by emitting
|
|
565
|
+
* a corresponding warning.
|
|
566
|
+
*
|
|
567
|
+
* @param {object} dep
|
|
568
|
+
* @param {string} realpath
|
|
569
|
+
* @param {string} nativeRealpath
|
|
570
|
+
* @param {Function} warning
|
|
571
|
+
*/
|
|
572
|
+
function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
|
|
573
|
+
if (realpath === nativeRealpath)
|
|
574
|
+
return;
|
|
575
|
+
if (realpath.toLowerCase() !== nativeRealpath.toLowerCase()) {
|
|
576
|
+
// safe-guard: in case realpath() resolved symlinks more deeply or sockets/pipes were used,
|
|
577
|
+
// which realpath.native() handles differently, don't report a possible false positive.
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
for (const using of dep.usingFroms) {
|
|
581
|
+
warning('file-unexpected-case-mismatch', [ using.location, using ], {},
|
|
582
|
+
// eslint-disable-next-line max-len
|
|
583
|
+
'The imported filename differs on the filesystem; ensure that capitalization matches the actual file\'s name');
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
536
587
|
/**
|
|
537
588
|
* @typedef {object} ResolveConfig
|
|
538
589
|
* @property {string[]} lookupDirs
|
package/lib/utils/term.js
CHANGED
|
@@ -57,8 +57,9 @@ function term( useColor = 'auto' ) {
|
|
|
57
57
|
// > that, when present (regardless of its value), prevents the addition
|
|
58
58
|
// > of ANSI color.
|
|
59
59
|
// Note: To be able to disable colors in tests, we check the environment
|
|
60
|
-
//
|
|
61
|
-
hasColor =
|
|
60
|
+
// variables again.
|
|
61
|
+
hasColor = (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0') ||
|
|
62
|
+
(hasColorShell && process.env.NO_COLOR === undefined);
|
|
62
63
|
break;
|
|
63
64
|
}
|
|
64
65
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.6",
|
|
4
4
|
"description": "CDS (Core Data Services) compiler and backends",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -24,11 +24,15 @@
|
|
|
24
24
|
"test3": "node scripts/verifyGrammarChecksum.js && mocha --reporter-option maxDiffSize=0 test3/",
|
|
25
25
|
"deployHanaSql": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.hana-sql.js",
|
|
26
26
|
"deployHdiHdbcds": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.hdi.hdbcds.js",
|
|
27
|
+
"deployHdi": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.hdi.hdbcds.js",
|
|
28
|
+
"deployHdbcds": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.hdi.hdbcds.js",
|
|
27
29
|
"deployGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.git-diffs.js",
|
|
30
|
+
"deployHdbcdsGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.git-diffs.js",
|
|
31
|
+
"deployHdiGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.git-diffs.js",
|
|
28
32
|
"gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
|
|
29
33
|
"coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
|
|
30
34
|
"coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
|
|
31
|
-
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/
|
|
35
|
+
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
|
|
32
36
|
"tslint": "tsc --pretty -p .",
|
|
33
37
|
"updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
|
|
34
38
|
"updateTocs": "node scripts/update-toc.js",
|
|
@@ -60,6 +64,6 @@
|
|
|
60
64
|
"LICENSE"
|
|
61
65
|
],
|
|
62
66
|
"engines": {
|
|
63
|
-
"node": ">=
|
|
67
|
+
"node": ">=18"
|
|
64
68
|
}
|
|
65
69
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# type-unexpected-foreign-keys
|
|
2
|
+
|
|
3
|
+
Foreign keys were specified in a composition-of-aspect.
|
|
4
|
+
|
|
5
|
+
Compositions of aspects are managed by the compiler.
|
|
6
|
+
Specifying a foreign key list is not supported.
|
|
7
|
+
If you need to specify foreign keys, use a composition
|
|
8
|
+
of an entity instead.
|
|
9
|
+
|
|
10
|
+
The message's severity is `Error`.
|
|
11
|
+
|
|
12
|
+
## Example
|
|
13
|
+
|
|
14
|
+
Erroneous code example:
|
|
15
|
+
|
|
16
|
+
```cds
|
|
17
|
+
aspect Item {
|
|
18
|
+
key ID : UUID;
|
|
19
|
+
field : String;
|
|
20
|
+
};
|
|
21
|
+
entity Model {
|
|
22
|
+
key ID : UUID;
|
|
23
|
+
Item : Composition of Item { ID }; // ❌
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`Item` is an aspect. Because an explicit list of foreign keys is specified,
|
|
28
|
+
the compiler rejects this CDS snippet. With an explicit foreign key list,
|
|
29
|
+
only entities can be used, but not aspects.
|
|
30
|
+
|
|
31
|
+
## How to Fix
|
|
32
|
+
|
|
33
|
+
Either remove the explicit list of foreign keys and let the compiler handle
|
|
34
|
+
the composition, or use a composition of entity instead.
|
|
35
|
+
|
|
36
|
+
```cds
|
|
37
|
+
aspect Item {
|
|
38
|
+
key ID : UUID;
|
|
39
|
+
field : String;
|
|
40
|
+
};
|
|
41
|
+
entity Model {
|
|
42
|
+
key ID : UUID;
|
|
43
|
+
Item : Composition of Model.Item { ID }; // ok
|
|
44
|
+
};
|
|
45
|
+
entity Model.Item : Item { };
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The snippet uses a user-defined entity, that includes the aspects.
|
|
49
|
+
|
|
50
|
+
## Related Messages
|
|
51
|
+
|
|
52
|
+
- `type-unexpected-on-condition`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# type-unexpected-on-condition
|
|
2
|
+
|
|
3
|
+
An ON-condition was specified in a composition-of-aspect.
|
|
4
|
+
|
|
5
|
+
Compositions of aspects are managed by the compiler.
|
|
6
|
+
Specifying an ON-condition is not supported.
|
|
7
|
+
If you need to specify an ON-condition, use a composition
|
|
8
|
+
of an entity instead.
|
|
9
|
+
|
|
10
|
+
The message's severity is `Error`.
|
|
11
|
+
|
|
12
|
+
## Example
|
|
13
|
+
|
|
14
|
+
Erroneous code example:
|
|
15
|
+
|
|
16
|
+
```cds
|
|
17
|
+
aspect Item {
|
|
18
|
+
key ID : UUID;
|
|
19
|
+
field : String;
|
|
20
|
+
};
|
|
21
|
+
entity Model {
|
|
22
|
+
key ID : UUID;
|
|
23
|
+
Item : Composition of Item on Item.ID = ID; // ❌
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`Item` is an aspect. Because an ON-condition is specified, the compiler
|
|
28
|
+
rejects this CDS snippet. With an ON-condition, only entities can be used,
|
|
29
|
+
but not aspects.
|
|
30
|
+
|
|
31
|
+
## How to Fix
|
|
32
|
+
|
|
33
|
+
Either remove the ON-condition and let the compiler handle
|
|
34
|
+
the composition, or use a composition of entity instead.
|
|
35
|
+
|
|
36
|
+
```cds
|
|
37
|
+
aspect Item {
|
|
38
|
+
key ID : UUID;
|
|
39
|
+
field : String;
|
|
40
|
+
};
|
|
41
|
+
entity Model {
|
|
42
|
+
key ID : UUID;
|
|
43
|
+
Item : Composition of Model.Item on Item.ID = ID; // ok
|
|
44
|
+
};
|
|
45
|
+
entity Model.Item : Item { };
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The snippet uses a user-defined entity, that includes the aspects.
|
|
49
|
+
|
|
50
|
+
## Related Messages
|
|
51
|
+
|
|
52
|
+
- `type-unexpected-foreign-keys`
|