Socket
Socket
Sign inDemoInstall

@sap/cds-compiler

Package Overview
Dependencies
Maintainers
1
Versions
105
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sap/cds-compiler - npm Package Compare versions

Comparing version 4.9.4 to 5.0.6

lib/checks/structuredAnnoExpressions.js

3

bin/cds_remove_invalid_whitespace.js

@@ -67,3 +67,4 @@ #!/usr/bin/env node

// Users should use the compiler to get all messages.
const errors = options.messages.filter(msg => (msg.severity === 'Error'));
const errors = options.messages
.filter(msg => (msg.severity === 'Error' && msg.messageId !== 'syntax-invalid-space'));
if (errors.length > 0) {

@@ -70,0 +71,0 @@ errors.forEach((msg) => {

@@ -148,2 +148,3 @@ #!/usr/bin/env node

cmdLine.command = 'toCsn';
cmdLine.options.toCsn = cmdLine.options.parseCdl;
cmdLine.options.parseCdl = true;

@@ -173,9 +174,17 @@ cmdLine.args.files = [ cmdLine.args.file ];

const to = cmdLine.options.toSql ? 'toSql' : 'toHana';
// remap string values for `assertIntegrity` option to boolean
if (cmdLine.options[to] && cmdLine.options[to].assertIntegrity &&
(cmdLine.options[to].assertIntegrity === 'true' ||
cmdLine.options[to].assertIntegrity === 'false')
)
cmdLine.options[to].assertIntegrity = cmdLine.options[to].assertIntegrity === 'true';
if (cmdLine.options[to]) {
// remap string values in options to boolean
if (cmdLine.options[to].assertIntegrity &&
(cmdLine.options[to].assertIntegrity === 'true' ||
cmdLine.options[to].assertIntegrity === 'false')
)
cmdLine.options[to].assertIntegrity = cmdLine.options[to].assertIntegrity === 'true';
if (cmdLine.options[to].withHanaAssociations)
cmdLine.options[to].withHanaAssociations = cmdLine.options[to].withHanaAssociations !== 'false';
if (cmdLine.options[to].betterSqliteSessionVariables)
cmdLine.options[to].betterSqliteSessionVariables = cmdLine.options[to].betterSqliteSessionVariables === 'true';
}
// Enable all beta-flags if betaMode is set to true

@@ -451,7 +460,2 @@ if (cmdLine.options.betaMode)

function toSql( model ) {
if (options.withoutHanaAssociations) {
options.withHanaAssociations = false;
delete options.withoutHanaAssociations;
}
const csn = options.directBackend ? model : compactModel(model, options);

@@ -458,0 +462,0 @@ if (options.src === 'hdi') {

@@ -37,2 +37,3 @@ #!/usr/bin/env node

Event: 'Y',
KeyImplicit: 'r', // handle as normal ref
// Remark: do not use `x`/`X` (hex literal `x'1e3d'`)

@@ -39,0 +40,0 @@ };

@@ -11,2 +11,9 @@ # ChangeLog of Beta Features for cdx compiler and backends

## Version 5.0.0 - 2024-05-29
## Removed `v5preview`
It is now enabled by default.
## Version 4.9.0 - 2024-04-25

@@ -13,0 +20,0 @@

@@ -715,5 +715,5 @@ /** @module API */

// no "isBetaEnabled", because this warning must also appear with "deprecated" flags
if (internalOptions.betaMode || internalOptions.beta?.v5preview)
messageFunctions.warning('api-deprecated-v5', null, null);
// Since v5, the HDBCDS backend is considered deprecated.
// TODO(v6): Make this a configurable error
messageFunctions.warning('api-deprecated-v5', null, null);

@@ -745,3 +745,2 @@ if (options.tenantDiscriminator) {

const internalOptions = prepareOptions.to.edm(
// eslint-disable-next-line comma-dangle
options.service ? options : Object.assign({ service: undefined }, options)

@@ -815,3 +814,2 @@ );

const internalOptions = prepareOptions.to.edmx(
// eslint-disable-next-line comma-dangle
options.service ? options : Object.assign({ service: undefined }, options)

@@ -886,5 +884,4 @@ );

function odata2( csn, options, messageFunctions ) {
// If not provided at all, set service to undefined to trigger validation
// If not provided at all, set service to 'undefined' to trigger validation
const internalOptions = prepareOptions.to.odata(
// eslint-disable-next-line comma-dangle
options.service ? options : Object.assign({ service: undefined }, options)

@@ -1081,11 +1078,2 @@ );

for_seal: publishCsnProcessor(forSeal, 'for.seal'),
/* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
preparedCsnToEdmx(csn, service, options) {
preparedCsnToEdmx(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edmx' ));
},
/* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
preparedCsnToEdm(csn, service, options) {
preparedCsnToEdm(csn, service, options, messages.makeMessageFunction( csn, options, 'to.edm' ));
},
};

@@ -1274,3 +1262,3 @@

return new Proxy(((...args) => {
if (!module) // eslint-disable-next-line global-require
if (!module)
module = require(moduleName);

@@ -1283,3 +1271,3 @@

get(target, name) {
if (!module) // eslint-disable-next-line global-require
if (!module)
module = require(moduleName);

@@ -1286,0 +1274,0 @@

@@ -78,3 +78,3 @@ 'use strict';

'tenantDiscriminator', // not published yet
'localizedWithoutCoalesce', // deprecated version of 'localizedLanguageFallback', TODO(v5): Remove option
'localizedWithoutCoalesce', // deprecated version of 'localizedLanguageFallback', TODO(v6): Remove option
];

@@ -149,11 +149,3 @@

options.severities['odata-spec-violation-array'] = 'Warning';
options.severities['odata-spec-violation-assoc'] = 'Warning';
options.severities['odata-spec-violation-namespace'] = 'Warning';
options.severities['odata-spec-violation-param'] = 'Warning';
options.severities['odata-spec-violation-returns'] = 'Warning';
options.severities['odata-spec-violation-type-unknown'] = 'Warning';
options.severities['odata-spec-violation-no-key'] = 'Warning';
options.severities['odata-spec-violation-key-type'] = 'Warning';
options.severities['odata-spec-violation-property-name'] = 'Warning';
}

@@ -221,3 +213,3 @@ }

effective: (options) => {
const hardOptions = {};
const hardOptions = { addCdsPersistenceName: false };
const defaultOptions = {

@@ -232,3 +224,3 @@ sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, remapOdataAnnotations: false, keepLocalized: false,

const hardOptions = {
sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, keepLocalized: false,
sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, keepLocalized: false, addCdsPersistenceName: true,
};

@@ -235,0 +227,0 @@ const defaultOptions = { remapOdataAnnotations: true };

@@ -30,3 +30,2 @@

// Local require: Only load on-demand, not when tracing is disabled.
// eslint-disable-next-line global-require
const { version } = require('../../package.json');

@@ -33,0 +32,0 @@ // eslint-disable-next-line no-console

@@ -67,2 +67,3 @@ 'use strict';

'$session',
'$draft',
];

@@ -69,0 +70,0 @@

@@ -14,3 +14,3 @@ 'use strict';

endCol;
constructor(file, line, col, endLine, endCol) {
constructor( file, line, col, endLine, endCol ) {
this.file = file;

@@ -22,2 +22,5 @@ this.line = line;

}
toString() {
return locationString( this );
}
}

@@ -24,0 +27,0 @@

@@ -34,7 +34,5 @@ // module- and csn/XSN-independent definitions

optionalActionFunctionParameters: true, // not supported by runtime, yet.
annotateForeignKeys: true,
effectiveCsn: true,
tenantVariable: true,
calcAssoc: true,
v5preview: true,
temporalRawProjection: true,

@@ -41,0 +39,0 @@ // disabled by --beta-mode

@@ -31,5 +31,4 @@ // Wrappers around core JavaScript / node functions

module.exports = {
promiseAllDoNotRejectImmediately,
};

@@ -52,3 +52,2 @@ // This is very similar to lib/model/enrichCsn - but the goal and the execution differ a bit:

// eslint-disable-next-line jsdoc/require-jsdoc
function standard( parent, prop, node ) {

@@ -73,3 +72,3 @@ if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ))

}
// eslint-disable-next-line jsdoc/require-jsdoc
function dictionary( node, prop, dict ) {

@@ -120,3 +119,2 @@ setProp(node, '$path', [ ...csnPath ]);

// eslint-disable-next-line jsdoc/require-jsdoc
function columns( parent, prop, node ) {

@@ -136,3 +134,2 @@ // Establish the link relationships

// eslint-disable-next-line jsdoc/require-jsdoc
function simpleRef( node, prop, ref ) {

@@ -165,3 +162,2 @@ setProp(node, '$path', [ ...csnPath ]);

// eslint-disable-next-line jsdoc/require-jsdoc
function pathRef( node, prop, path ) {

@@ -168,0 +164,0 @@ const {

@@ -18,2 +18,3 @@ 'use strict';

const checkForHanaTypes = require('./checkForTypes');
const { checkAnnotationExpression } = require('./structuredAnnoExpressions');
const checkForParams = require('./parameters');

@@ -214,2 +215,9 @@ // forOdata

forEachMember(artifact, checkUsedTypesForAnonymousAspectComposition.bind(that));
},
(artifact, artifactName) => {
if (that.options.transformation === 'effective') {
forEachMemberRecursively(artifact, checkAnnotationExpression.bind(that), [ 'definitions', artifactName ], false, {
skipArtifact: a => a.returns || (a.params && !a.query),
});
}
}

@@ -216,0 +224,0 @@ ),

@@ -211,2 +211,3 @@ // Consistency checker on model (XSN = augmented CSN)

'$calcDepElement', '$filtered', '$enclosed', '_parent',
'deprecated', '$onlyInExprCtx',
],

@@ -219,2 +220,4 @@ schema: {

$requireElementAccess: { test: isBoolean },
deprecated: { test: isBoolean },
$onlyInExprCtx: { test: TODO },
// missing location for normal "elements"

@@ -708,3 +711,3 @@ elements: { test: TODO },

'localized-origin', // `.texts` entity
'nav', // only used for MASKED, TODO(v5): Remove
'nav', // only used for MASKED, TODO(v6): Remove
'none', // only used in ensureColumnName(): Used in object representing empty alias

@@ -711,0 +714,0 @@ 'query', // inferred query properties, e.g. `key`

@@ -51,3 +51,3 @@ // Base Definitions for the Core Compiler

}, // no extend params, only annotate
key: { normalized: 'element' },
key: { normalized: 'element', dict: 'elements' }, // dict for annotate
param: { elements: () => false, enum: () => false, dict: 'params' },

@@ -54,0 +54,0 @@ source: { artifacts: true }, // TODO -> $source

@@ -181,3 +181,3 @@ // The builtin artifacts of CDS

},
$at: {
$at: { // $at is considered deprecated since cds-compiler v5
elements: {

@@ -188,4 +188,5 @@ from: {}, to: {},

$requireElementAccess: true,
deprecated: true, // $at is deprecated; use $valid
},
$valid: { // dito
$valid: {
elements: {

@@ -205,2 +206,11 @@ from: {}, to: {},

},
$draft: {
elements: {
IsActiveEntity: {},
},
// Require that elements are accessed, i.e. no $draft, only $draft.<element>.
$requireElementAccess: true,
// See reference semantics in shared.js
$onlyInExprCtx: [ 'annotation', 'annoRewrite' ],
},
};

@@ -443,2 +453,6 @@

art.$requireElementAccess = magic.$requireElementAccess;
if (magic.deprecated)
art.deprecated = magic.deprecated;
if (magic.$onlyInExprCtx)
art.$onlyInExprCtx = magic.$onlyInExprCtx;

@@ -468,2 +482,4 @@ createMagicElements( art, magic.elements );

magic.$uncheckedElements = art.$uncheckedElements;
if (art.$onlyInExprCtx)
magic.$onlyInExprCtx = art.$onlyInExprCtx;
setProp( magic, '_parent', art );

@@ -470,0 +486,0 @@ // setProp( magic, '_effectiveType', magic );

@@ -57,5 +57,2 @@ // Checks on XSN performed during compile() that are useful for the user

// because when we introduced them, it was not fully specified what they are.
// TODO(v5):
// - Make this a (configurable) error; move to acceptTypeOrElement
// - require type/elements to be set in parser
if (def.kind === 'event' && !def._effectiveType?.elements && !def._effectiveType?.projection)

@@ -866,4 +863,4 @@ message( 'def-expected-structured', [ (def.type || def.name).location, def ] );

return (isAssociationOperand( xpr.args[0] ) && isDollarSelfOrProjectionOperand( xpr.args[2] ) ||
// eslint-disable-next-line max-len
isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
// eslint-disable-next-line max-len
isAssociationOperand( xpr.args[2] ) && isDollarSelfOrProjectionOperand( xpr.args[0] ));
}

@@ -870,0 +867,0 @@

@@ -367,3 +367,3 @@ // Compiler phase 1 = "define": transform dictionary of AST-like XSNs into XSN

// create using for own namespace:
// TODO: should we really do that in v5? See also initNamespaceAndUsing().
// TODO: should we really do that (in v6)? See also initNamespaceAndUsing().
const last = namespace.path[namespace.path.length - 1];

@@ -511,5 +511,6 @@ const { id } = last;

if (!art.$duplicates || !art.name.id ||
art.$errorReported === 'syntax-duplicate-extend' ||
art.$errorReported === 'syntax-duplicate-annotate')
art.$errorReported === 'syntax-duplicate-extend')
return;
if (art.kind === 'annotate' || art.kind === 'extend')
return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
if (art._main) {

@@ -642,3 +643,2 @@ error( 'duplicate-definition', [ art.name.location, art ], {

function initDollarParameters( art ) {
// TODO: remove $parameters in v5?
// TODO: use setMemberParent() ?

@@ -945,3 +945,3 @@ const parameters = {

if (col.doc) {
message( 'syntax-ignoring-anno', [ col.doc.location, col ],
message( 'syntax-unexpected-anno', [ col.doc.location, col ],
{ '#': 'doc', code: '.{ ‹inline› }' } );

@@ -953,3 +953,3 @@ }

if (firstAnno) {
message( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
message( 'syntax-unexpected-anno', [ col[firstAnno].name.location, col ],
{ code: '.{ ‹inline› }' } );

@@ -1091,3 +1091,3 @@ }

if (hasElement) {
// This message is similar to the one above. In v5/6, we could probably
// This message is similar to the one above. In v6, we could probably
// turn this warning into an error, remove `$syntax: 'element' (also in

@@ -1094,0 +1094,0 @@ // language.g4), and use the above `ext-unexpected-element` only for CSN input.

@@ -11,4 +11,4 @@ // Extend

forEachMember,
forEachGeneric,
isDeprecatedEnabled,
isBetaEnabled,
} = require('../base/model');

@@ -34,3 +34,3 @@ const { dictAdd, pushToDict } = require('../base/dictionaries');

// attach stupid location - TODO: remove in v5
// attach stupid location - TODO: remove in v6
const genLocation = new Location( '' );

@@ -64,2 +64,3 @@

resolveTypeArgumentsUnchecked,
resolveDefinitionName,
attachAndEmitValidNames,

@@ -78,3 +79,2 @@ initMembers,

const includesNonShadowedFirst = isDeprecatedEnabled( model.options, 'includesNonShadowedFirst' );
const isV5preview = isBetaEnabled( model.options, 'v5preview' );

@@ -168,2 +168,3 @@ sortModelSources();

if (art.returns) {
ensureArtifactNotProcessed( art.returns );
pushToDict( art.returns, '_extensions', ...extensionsMap.elements || [] );

@@ -179,2 +180,3 @@ pushToDict( art.returns, '_extensions', ...extensionsMap.enum || [] );

if (sub) {
ensureArtifactNotProcessed( sub );
pushToDict( sub, '_extensions', ...extensionsMap.elements || [] );

@@ -184,4 +186,6 @@ pushToDict( sub, '_extensions', ...extensionsMap.enum || [] );

else {
moveDictExtensions( art, extensionsMap,
(art.enum && art.kind !== 'annotate' ? 'enum' : 'elements'), 'elements' );
let elementsProp = 'elements';
if (art.kind !== 'annotate')
elementsProp = art.enum && 'enum' || art.foreignKeys && 'foreignKeys' || 'elements';
moveDictExtensions( art, extensionsMap, elementsProp, 'elements' );
moveDictExtensions( art, extensionsMap, 'enum' );

@@ -192,2 +196,18 @@ }

/**
* Applying extensions is handled in extendArtifactAfter(). And only afterward,
* an effective sequence number is set. Meaning that if a sub-artifact already
* has a sequence number, then extensions would be lost.
*/
function ensureArtifactNotProcessed( art ) {
if (!model.options.testMode)
return;
if (art.$effectiveSeqNo !== 0 && art.$effectiveSeqNo !== undefined) {
// if the artifact already has a sequence number, then
// extendArtifactAfter() was already called -> annotations would be lost.
throw new CompilerAssertion('artifact already processed; extensions would be lost');
}
}
/**
* Create super annotate statements for remaining extensions

@@ -207,11 +227,21 @@ */

// TODO: delete again - if not, what about extensions in contexts/services?
// Check test.lsp-api.js! Links in extensions are needed.
function setArtifactLinkForExtensions( source ) {
if (!source.extensions)
return;
for (const ext of source.extensions ) {
for (const ext of source.extensions) {
if (!ext.name?.id)
continue;
const { name } = ext;
if (name?.id && name._artifact === undefined) {
const { path } = name;
if (name._artifact === undefined) {
const refCtx = (name.id.startsWith( 'localized.' )) ? '_extensions' : ext.kind;
resolvePath( name, refCtx, ext ); // for LSP
}
else if (model.options.lspMode && path?.[0]._artifact === undefined) {
// we don't use resolvePath(…,'extend'), as that would add a dependency
resolveDefinitionName( ext );
setArtifactLink( path[path.length - 1], name._artifact );
}
}

@@ -236,3 +266,3 @@ }

};
// TODO(v5): Discuss: make this an error?
// TODO(v6): Discuss: make this an error?
warning( 'ext-invalid-kind', [ loc, ext ], msgArgs, {

@@ -406,2 +436,5 @@ std: 'Artifact $(ART) is not of kind $(KIND), use $(CODE) instead',

if (art.kind === 'annotate' && art.$inferred === '')
return; // internal super-annotate for unknown artifacts
if (!query?.from?.path) {

@@ -504,3 +537,3 @@ const variant = (query?.from || query)?.op?.val || 'std';

return true;
// TODO v5: delete the speciao UP TO comparison?
// TODO v6: delete the special UP TO comparison?
const upToVal = upToSpec.val;

@@ -555,3 +588,3 @@ const prevVal = previousItem.val;

//
// TODO v5: do not allow `extend … with (precision: …)` alone if original def also has `scale`
// TODO v6: do not allow `extend … with (precision: …)` alone if original def also has `scale`
function applyTypeExtensions( art, ext, prop, scaleDiff ) {

@@ -633,13 +666,13 @@ // console.log('ATE:',art?.[prop],ext?.[prop],scaleDiff)

for (const ext of extensions) {
const extDict = ext[extProp];
let dictCheck = (art.kind !== 'annotate'); // no check in super annotate statement
for (const name in extDict) {
const elemExt = extDict[name];
forEachGeneric(ext, extProp, (elemExt, name) => {
if (elemExt.kind !== 'annotate' && elemExt.kind !== 'extend') // TODO: specified elems
continue; // definitions inside extend, already handled
return; // definitions inside extend, already handled
dictCheck = dictCheck && checkRemainingMemberExtensions( art, elemExt, artProp, name );
const elem = artDict[name] || annotateFor( art, extProp, name );
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null ) );
pushToDict( elem, '_extensions', elemExt );
}
ensureArtifactNotProcessed( elem );
if (elem.$duplicates !== true)
pushToDict( elem, '_extensions', elemExt );
});
}

@@ -795,2 +828,6 @@ }

break;
case 'foreignKeys':
notFound( 'ext-undefined-key', ext.name.location, ext,
{ name }, parent.foreignKeys );
break;
case 'params':

@@ -809,3 +846,4 @@ notFound( 'ext-undefined-param', ext.name.location, ext,

default:
// assert
if (model.options.testMode)
throw new CompilerAssertion(`Missing case for prop: ${ prop }`);
}

@@ -851,4 +889,5 @@ }

function checkRemainingMainExtensions( art, ext, localized ) {
const isExtend = ext.kind === 'extend';
if (localized) {
if (isV5preview && ext.kind === 'extend') {
if (isExtend) {
// In v5, reject any `extend` on localized.

@@ -867,3 +906,4 @@ error( 'ref-undefined-art', [ ext.location || ext.name.location, ext ],

}
else if (art?.kind === 'namespace') {
else if (isExtend && art?.kind === 'namespace') {
// `annotate` on namespaces already handled before
const hasAnnotations = Object.keys(ext).find(a => a.charAt(0) === '@');

@@ -874,12 +914,10 @@ const firstAnno = ext[hasAnnotations];

// Non-artifact extensions are reported in resolvePath() already (for v5).
if ((hasAnnotations || !ext.artifacts) ) {
if (isV5preview) {
error( 'ref-undefined-art', [ (firstAnno?.name || ext.name).location, ext ], {
'#': 'namespace', art: ext,
} );
}
else {
info( 'anno-namespace', [ (firstAnno?.name || ext.name).location, ext ], {},
'Namespaces can\'t be annotated nor extended' );
}
// Because "namespaces" are the same as "unknown" artifacts in CSN, we don't report
// an error for `annotate`s.
// FIXME: The compiler generates empty `annotate` statements for
// `extend ns with definitions {…}`. That's why we check the frontend.
if (hasAnnotations || (!ext.artifacts && ext._block.$frontend !== 'json')) {
error( 'ref-undefined-art', [ (firstAnno?.name || ext.name).location, ext ], {
'#': 'namespace', art: ext,
} );
}

@@ -1123,3 +1161,2 @@ }

std: 'Unknown $(ART) - nothing to extend',
// eslint-disable-next-line max-len
element: 'Artifact $(ART) has no element or enum $(MEMBER) - nothing to extend',

@@ -1255,3 +1292,3 @@ action: 'Artifact $(ART) has no action $(MEMBER) - nothing to extend',

elem.$inferred = 'include';
if (origin.masked) // TODO(v5): remove 'masked'
if (origin.masked) // TODO(v6): remove 'masked'
elem.masked = Object.assign( { $inferred: 'include' }, origin.masked );

@@ -1293,4 +1330,2 @@ if (origin.key)

* same member. Covers `entity G : E, G {};` but not `entity G : E {}; extend G with F;`.
*
* TODO(v5): Make this a hard error; see checkRedefinition(); maybe combine both;
*/

@@ -1297,0 +1332,0 @@ function checkRedefinitionThroughIncludes( parent, prop ) {

@@ -659,3 +659,3 @@ // Generate: localized data and managed compositions

// Only issue warning for direct usages, not for projections, includes, etc.
// TODO: Make it configurable error; v5: error
// TODO: Make it configurable error; v6: error
// TODO: move to resolve.js where we test the targetAspect,

@@ -662,0 +662,0 @@ warning( 'type-expecting-composition', [ elem.type.location, elem ],

@@ -86,7 +86,3 @@ // Main XSN-based compiler functions

const ext = path.extname( filename ).slice(1).toLowerCase();
// eslint-disable-next-line no-nested-ternary
const parser = options.fallbackParser === 'auto!'
? (source?.startsWith( '{' ) ? parseCsn.parse : parseLanguage)
: (extensionParsers[ext] || extensionParsers[options.fallbackParser] ||
source.startsWith( '{' ) && parseCsn.parse);
const parser = parserForFile( source, ext, options );
if (parser)

@@ -105,2 +101,23 @@ return parser( source, filename, options, messageFunctions );

/**
* Get the correct parser for the given source / file extension.
* Respects the set fallback parser.
*
* @param {string} source
* @param {string} ext
* @param {object} options
*/
function parserForFile( source, ext, options ) {
// 'auto!' ignores the file's extension
if (options.fallbackParser === 'auto!')
return (source?.startsWith( '{' ) ? parseCsn.parse : parseLanguage);
if (options.fallbackParser === 'csn!')
return parseCsn.parse;
return extensionParsers[ext] ||
extensionParsers[options.fallbackParser] ||
(source.startsWith( '{' ) && parseCsn.parse);
}
// Main function: Compile the sources from the files given by the array of

@@ -535,3 +552,3 @@ // `filenames`. As usual with the `fs` library, relative file names are

}
catch (e) {
catch {
// Ignore the not-found (ENOENT) error

@@ -538,0 +555,0 @@ }

'use strict';
// Placeholder for future LSP API.
// API for `@sap/cds-lsp`.
//
// THIS FILE IS CONSIDERED INTERNAL!
// We do not guarantee stability for any project besides the CAP LSP server.
//
// This files includes an iterator over "semantic tokens" in an XSN model.
// "Semantic tokens" are identifiers, but also the "return" parameter.
// See `internalDoc/lsp/IdentifierCrawling.md` for details.
module.exports = {};
const { CompilerAssertion } = require('../base/error');
const $inferred = Symbol.for( 'cds.$inferred' );
// TODO: Remove hints; they should not be necessary in the best case
const HINTS = {
USING_ALIAS: 'using-alias',
DEFINITION_NAME: 'definition',
NAMESPACE_STATEMENT: 'namespace-statement',
};
// eslint-disable-next-line no-unused-vars
class LspSemanticTokenEvent {
event; // 'reference' | 'definition',
semanticToken;
node;
hint; // TODO: Remove
}
/**
* All actions to report semantic tokens in a model.
*/
const artifactActions = {
__proto__: null,
// e.g. sources or services
artifacts: dictOf( artifactTokens ),
extensions: arrayOf( extensionTokens ),
namespace: namespaceTokens,
// e.g. via CSN input
vocabularies: dictOf( artifactTokens ),
definitions: dictOf( artifactTokens ),
extern: artifactTokens,
name: definitionNameTokens,
path: pathReferenceTokens,
type: artifactTokens,
target: artifactTokens,
targetAspect: artifactTokens,
targetElement: artifactTokens,
returns: returnsTokens,
items: artifactTokens,
elements: elementsTokens,
enum: dictOf( artifactTokens ),
foreignKeys: dictOf( artifactTokens ),
actions: dictOf( artifactTokens ),
params: dictOf( artifactTokens ),
mixin: dictOf( artifactTokens ),
excludingDict: dictOf( nameAsReference ),
// Don't crawl `$tableAliases`, as they are set multiple times in queries
// via different `$tableAliases`.
// $tableAliases: null,
// NOT $queries, as that doesn't cover UNIONs (e.g. `orderBy` vs `$orderBy`)
query: artifactTokens,
from: artifactTokens,
includes: arrayOf( artifactTokens ),
columns: arrayOf( artifactTokens ),
expand: arrayOf( artifactTokens ),
inline: arrayOf( artifactTokens ),
args: argsTokens,
on: artifactTokens,
default: artifactTokens,
value: artifactTokens,
sym: enumSymToken,
where: artifactTokens,
groupBy: artifactTokens,
orderBy: artifactTokens,
having: artifactTokens,
suffix: artifactTokens,
limit: artifactTokens,
rows: artifactTokens,
offset: artifactTokens,
'@': annotationTokens,
};
/** Returns a generator that applies the given function on all entries and yields the result. */
function dictOf( func ) {
return function* dictionary( dict ) {
for (const [ item ] of iterateGeneric({ dict }, 'dict'))
yield* func( item );
};
}
/** Returns a generator that applies the given function on all entries and yields the result. */
function arrayOf( func ) {
return function* array( arr ) {
if (!Array.isArray(arr))
return;
for (const item of arr)
yield* func( item );
};
}
/** Generator equivalent of iterateGeneric of forEachGeneric() */
function* iterateGeneric( obj, prop ) {
const dict = obj[prop];
if (!dict)
return;
for (const name in dict) {
obj = dict[name];
if (Array.isArray( obj )) {
for (const item of obj)
yield [ item, name, prop ]; // parser or source duplicates (e.g. USING vs definition)
}
else {
yield [ obj, name, prop ];
if (Array.isArray( obj.$duplicates )) { // redefinitions
for (const dup of obj.$duplicates)
yield [ dup, name, prop ];
}
}
}
}
/**
* A generator that yields all semantic tokens in an XSN model.
* Semantic tokens include identifiers (references/definitions) and the "returns" parameter.
*
* @param {XSN.Model} xsn
* @param {CSN.Options} options
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* traverseSemanticTokens( xsn, options ) {
if (!xsn)
throw new CompilerAssertion('Expected valid XSN model for traverseSemanticTokens(…)');
if (!options)
throw new CompilerAssertion('Expected valid options for traverseSemanticTokens(…)');
if (xsn.sources)
yield* dictOf( artifactTokens )( xsn.sources );
}
/**
* Report semantic tokens in artifacts, including definitions, elements, params, etc.
*
* @param {XSN.Artifact} art
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* artifactTokens( art ) {
if (!art || art.builtin || art.$inferred)
return null;
if (Array.isArray( art )) {
for (const entry of art)
yield* artifactTokens( entry );
return null;
}
for (const prop in art) {
if (artifactActions[prop])
yield* artifactActions[prop](art[prop], art);
else if (prop.charAt(0) === '@')
yield* artifactActions['@'](art[prop], art);
}
return null;
}
/**
* For an extension, yield all semantic tokens.
* We don't use `artifactTokens` for it, because extensions are a special case:
* - they have a name, but actually refer to some other artifact.
* - their artifacts such as elements may overlap with existing definitions, because
* extensions are applied; if they were applied, `_parent` does not point to the
* extension, which means we can't use it to skip them in `artifactTokens`.
* - we only need to handle `annotate` and `extend` kinds specifically:
* if an extension was not applied, pass it to `artifactTokens`;
* if an extension was applied, we only need to report its name (i.e. reference)
* and traverse over all artifacts
*
* @param {XSN.Extension} ext
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* extensionTokens( ext ) {
if (ext.kind !== 'extend' && ext.kind !== 'annotate')
return null;
const wasApplied = ext.name._artifact && !ext.name._artifact.$inferred;
if (!wasApplied) {
yield* artifactTokens( ext );
return null;
}
yield* nameAsReference( ext );
// We need to traverse all dictionaries that could themselves contain
// extensions. Enum extensions or columns don't need to be traversed,
// for example, because there can't be inner extensions.
yield* dictOf( extensionTokens )( ext.params );
yield* dictOf( extensionTokens )( ext.actions );
yield* dictOf( extensionTokens )( ext.elements );
if (ext.returns)
yield* extensionTokens( ext.returns );
// Artifact extensions are always definitions, and can't have nested `extend`s,
// hence no need to traverse them with `extensionTokens`.
yield* dictOf( artifactTokens )( ext.artifacts );
return null;
}
/**
* Report all semantic tokens in an annotation assignment.
*
* @param {XSN.Artifact} anno
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* annotationTokens( anno ) {
// TODO: Also report annotation names
if (anno.kind === '$annotation')
yield* annotationValueTokens( anno );
}
function* argsTokens( args, art ) {
if (Array.isArray(args)) {
// e.g. unnamed function arguments
yield* arrayOf( artifactTokens )( args );
}
else {
// e.g. named arguments
for (const [ param ] of iterateGeneric( art, 'args' )) {
yield* nameAsReference( param );
yield* artifactTokens( param );
}
}
}
function* enumSymToken( sym, expr ) {
yield {
event: 'reference',
semanticToken: expr.sym,
node: expr,
hint: undefined,
};
}
/**
* A namespace is always considered a reference and not a definition.
*
* @param {XSN.Artifact} def
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* namespaceTokens( def ) {
if (!def.name)
return null;
for (let i = 0; i < def.name.path.length; ++i) {
yield {
event: 'reference',
semanticToken: def.name.path[i],
node: def,
hint: (i === def.name.path.length - 1) ? HINTS.NAMESPACE_STATEMENT : null,
};
}
return null;
}
/**
* An annotation value may contain expressions which we need to report.
*
* @param {object} anno
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* annotationValueTokens( anno ) {
if (Array.isArray(anno)) {
for (const entry of anno)
yield* annotationValueTokens( entry );
}
else if (anno.$tokenTexts) {
yield* artifactTokens( anno );
}
else if (Array.isArray(anno.val)) {
yield* annotationValueTokens( anno.val );
}
else if (anno.struct) {
for (const [ struct ] of iterateGeneric( anno, 'struct' ))
yield* annotationValueTokens( struct );
}
}
/**
* A `returns` structure may contain sub-elements. But we report the `returns`
* token as well, as it is considered a token with semantic value.
*
* @param {XSN.Artifact} art
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* returnsTokens( art ) {
if (art.kind === 'param') {
// report the `returns` parameter
yield {
event: 'definition',
semanticToken: art.name,
node: art,
hint: undefined,
};
yield* artifactTokens( art );
}
}
/**
* Report elements if they should be traversed. They are not always traversed
* to avoid duplication due to `expand` and `columns` also being traversed.
*
* @param {Record<string, XSN.Artifact>} elements
* @param {XSN.Artifact} art
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* elementsTokens( elements, art ) {
if (shouldTraverseElements( art ))
yield* dictOf( artifactTokens )( elements );
}
/**
* Report all references in `ref`.
*
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* pathReferenceTokens( path, ref, user = ref, hint = null ) {
if (!path)
return null;
// don't report cds.Association/cds.Composition
// TODO: Or report the `Association` keyword, similar to `returns`?
if (path.length === 1 && ref._artifact?.category === 'relation')
return null;
yield* artifactTokens( path );
// parser prepends a fake `type of` segment, which we need to skip
const root = ref.scope === 'typeOf' ? 1 : 0;
for (let i = root; i < path.length; ++i) {
if (!path[i].$inferred) { // e.g. `id` when expanded from `$user`
yield {
event: 'reference',
semanticToken: path[i],
node: user,
hint,
};
}
}
return null;
}
/**
* Some XSN nodes such as entries in `excludingDict` or named arguments are references
* but don't have a `path` property, only a `name` property. Report such names
* as references.
*
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* nameAsReference( ref, hint = null ) {
if (!ref.name || ref.name.$inferred)
return null;
if (ref.name.path) {
yield* pathReferenceTokens( ref.name.path, ref.name, ref, hint );
}
else {
yield {
event: 'reference',
semanticToken: ref.name,
node: ref,
hint,
};
}
return null;
}
/**
* Traverse the name of a definition, and report N-1 path steps as references
* and of course the definition itself.
*
* @returns {Generator<LspSemanticTokenEvent>}
*/
function* definitionNameTokens( name, art ) {
if (!art.kind)
return null; // e.g. parameter references
if (art.kind === '$annotation')
return null; // annotation name, e.g. in `@anno: (elem)`
if ((name.$inferred && name.$inferred !== 'as') ||
art.kind === 'select' || art.kind === '$join') {
// Internal names such as numbers for SELECTs or the `$internal` names must
// not be reported.
return null;
}
if (art.kind === 'extend' || art.kind === 'annotate') {
yield* nameAsReference( art );
return null;
}
// Report references in a name (N-1 path steps).
for (let i = 0; i < name.path?.length - 1; ++i) {
yield {
event: 'reference',
semanticToken: name.path[i],
node: art,
hint: HINTS.DEFINITION_NAME,
};
}
const hint = art.kind === 'using' ? HINTS.USING_ALIAS : null;
if (name.path) {
// Only take the last path step; all others are considered references.
const implicitName = name.path[name.path.length - 1];
yield {
event: 'definition',
semanticToken: implicitName,
node: art,
hint,
};
}
else if (name.id) {
// Not all names have a path; some (e.g. parameters) only have an ID.
yield {
event: 'definition',
semanticToken: name,
node: art,
hint,
};
}
return null;
}
/**
* Returns true if `elements` of the given `art` should be traversed.
* Elements are _not_ traversed, e.g. for `expand`, to avoid duplicates.
*
* @returns {boolean}
*/
function shouldTraverseElements( art ) {
return (
// $expand: 'origin' -> normal expansion
// $expand: 'annotate' -> additional annotation (needs to traverse annotation expressions)
art.$expand !== 'origin' && !art.elements[$inferred] && (
// sub-elements are always traversed except for `expand`, which is handled on its own.
art.kind === 'element' && !art.expand ||
// all non-query elements are traversed; because `_main` on bound actions may point
// to a query, we handle parameters explicitly.
art.kind === 'param' || !(art._main || art).$queries
)
);
}
/**
* Given a LspSemanticTokenEvent, returns a generator that yields the referenced
* object and its origin's until the deepest entry is found.
*
* @param obj
* @returns {Generator<*, void, *>}
*/
function* getSemanticTokenOrigin( obj ) {
let ref = obj.semanticToken;
if (obj.event === 'definition') {
ref = obj.node;
}
else {
if (!ref?._artifact)
return; // unknown -> abort
// take first artifact for duplicates (best effort)
ref = Array.isArray(ref._artifact) ? ref._artifact[0] : ref._artifact;
yield ref;
}
if (!ref._effectiveType)
return; // abort for unresolved references and cyclic ones
while (ref._origin) {
yield ref._origin;
ref = ref._origin;
if (!ref || typeof ref === 'string')
break;
}
}
module.exports = {
traverseSemanticTokens,
getSemanticTokenOrigin,
};

@@ -1072,6 +1072,6 @@ // Populate views with elements, elements with association targets, ...

// HINT: consider bin/cdsv2m.js when changing the following message text
// No grouped and sub messages yet (TODO v5): mention at all target places with all assocs
// No grouped and sub messages yet (TODO v6): mention at all target places with all assocs
const withAnno = annotationVal( exposed[0]['@cds.redirection.target'] );
for (const proj of exposed) {
// TODO: def-ambiguous-target (just v5, as the current is infamous and used in options),
// TODO: def-ambiguous-target (just v6, as the current is infamous and used in options),
message( 'redirected-implicitly-ambiguous',

@@ -1078,0 +1078,0 @@ [ weakLocation( proj.name.location ), proj ],

@@ -16,3 +16,2 @@ // Propagate properties in XSN

isDeprecatedEnabled,
isBetaEnabled,
} = require( '../base/model');

@@ -60,3 +59,2 @@ const {

returns,
$filtered: annotation, // TODO(v5): Remove
$enclosed: annotation,

@@ -80,4 +78,3 @@ };

forEachDefinition( model, run );
if (isBetaEnabled( model.options, 'v5preview' ))
forEachGeneric( model, 'vocabularies', run );
forEachGeneric( model, 'vocabularies', run );

@@ -84,0 +81,0 @@ // TODO: move 'virtual' handling/checks to resolver if

@@ -8,3 +8,2 @@ // Tweak associations: rewrite keys and on conditions

forEachInOrder,
isBetaEnabled,
} = require('../base/model');

@@ -49,4 +48,2 @@ const { dictLocation, weakLocation, weakRefLocation } = require('../base/location');

const isV5preview = isBetaEnabled( model.options, 'v5preview' );
// Phase 5: rewrite associations

@@ -116,5 +113,3 @@ model._entities.forEach( rewriteArtifact );

std: 'Target $(TARGET) of association is outside any service', // not used
// eslint-disable-next-line max-len
define: 'Target $(TARGET) of explicitly defined association is outside any service',
// eslint-disable-next-line max-len
select: 'Target $(TARGET) of explicitly selected association is outside any service',

@@ -135,3 +130,3 @@ } );

function rewriteAssociationCheck( element ) {
const elem = element.items || element; // TODO v5: nested items
const elem = element.items || element; // TODO v6: nested items
if (elem.elements)

@@ -243,3 +238,3 @@ forEachGeneric( elem, 'elements', rewriteAssociationCheck );

function rewriteAssociation( element ) {
let elem = element.items || element; // TODO v5: nested items
let elem = element.items || element; // TODO v6: nested items
if (elem.elements)

@@ -464,11 +459,2 @@ forEachGeneric( elem, 'elements', rewriteAssociation );

if (!isV5preview) { // TODO(v5): Remove, only use $enclosed
elem.$filtered = {
val: true,
literal: 'boolean',
location,
$inferred: '$generated',
};
}
const isComp = (getUnderlyingBuiltinType( assoc )?.name?.id === 'cds.Composition');

@@ -475,0 +461,0 @@ if (isComp) {

@@ -128,3 +128,4 @@ // Simple compiler utility functions

member.kind = kind;
else if (origin.key && !tmpDeprecated) // TODO(v5/v6): remove tmpDeprecated
else if (origin.key && !tmpDeprecated)
// TODO(v6): remove tmpDeprecated once `noKeyPropagationWithExpansions` is removed
member.key = Object.assign( { $inferred: 'expanded' }, origin.key );

@@ -131,0 +132,0 @@ if (kind && origin.masked) // TODO: remove!

@@ -30,2 +30,6 @@ // Base classes used as prototypes for XSN definitions, elements, etc.

extensions = [];
$frontend;
constructor( frontend ) {
this.$frontend = frontend;
}
}

@@ -32,0 +36,0 @@

@@ -735,7 +735,9 @@ 'use strict';

setProp(actionCsn, '$bindingParam', firstParam);
// preserve the original type (as it is the key to reqDefs.defintions)
// for annotation path resolution (eg. for $draft.IsActiveEntity)
if (bpType) {
if (firstParam.items?.type)
firstParam.items.type = bpType;
setProp(firstParam.items, '_edmType', bpType);
if (firstParam.type)
firstParam.type = bpType;
setProp(firstParam, '_edmType', bpType);
}

@@ -779,7 +781,16 @@ if (!edmUtils.isODataSimpleIdentifier(bpName))

if (!actionCsn.$bindingParam) {
const bpDef = {
name: bpName,
viaAnno: true,
};
// Binding Parameter: 'in' at first position in sequence, this is decisive!
if (actionCsn['@cds.odata.bindingparameter.collection'])
if (actionCsn['@cds.odata.bindingparameter.collection']) {
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType, Collection: true/* , Nullable: false */ } ));
else
bpDef.items = { type: bpType };
}
else {
actionNode.append(new Edm.Parameter(v, { Name: bpName, Type: bpType } ));
bpDef.type = bpType;
}
setProp(actionCsn, '$bindingParam', bpDef);
}

@@ -896,2 +907,3 @@ }

// omit annotation rendering later on
setProp(actionCsn, '$bindingParam', parameterCsn);
delete actionCsn.params[parameterName];

@@ -898,0 +910,0 @@ }

@@ -628,3 +628,4 @@ 'use strict';

else {
this._edmAttributes[typeName] = typecsn.type;
// it's either _edmType or type (_edmType only used for explicit binding param)
this._edmAttributes[typeName] = typecsn._edmType || typecsn.type;
}

@@ -901,3 +902,2 @@ }

if (this.v2) {
// eslint-disable-next-line sonarjs/no-redundant-boolean
if (csn['@odata.etag'] || csn['@cds.etag'])

@@ -1042,3 +1042,2 @@ this._edmAttributes.ConcurrencyMode = 'Fixed';

*/
// eslint-disable-next-line sonarjs/no-redundant-boolean
if (csn['@odata.contained'] || csn.containsTarget)

@@ -1045,0 +1044,0 @@ this._edmAttributes.ContainsTarget = true;

@@ -62,3 +62,2 @@ 'use strict';

if (options.isV4() && (element['@odata.etag'] || element['@cds.etag'])) {
// eslint-disable-next-line sonarjs/no-redundant-boolean
// don't put element name into collection as per advice from Ralf Handl, as

@@ -247,3 +246,3 @@ // no runtime is interested in the property itself, it is sufficient to mark

'@sap.searchable': addToSetAttr,
'@sap.pagable': addToSetAttr,
'@sap.pageable': addToSetAttr,
'@sap.topable': addToSetAttr,

@@ -250,0 +249,0 @@ '@sap.countable': addToSetAttr,

@@ -35,3 +35,3 @@ 'use strict';

// the new transformer works only with new CSN
// eslint-disable-next-line global-require
const { makeMessageFunction } = require('../base/messages');

@@ -38,0 +38,0 @@ const { error, throwWithAnyError } = makeMessageFunction(csn, options);

@@ -96,4 +96,3 @@ // Transform XSN (augmented CSN) into CSN

target,
$filtered: value, // assoc+filter v4
$enclosed: value, // comp+filter v5
$enclosed: value, // comp+filter since v5
foreignKeys,

@@ -124,3 +123,3 @@ enum: enumDict,

default: expression,
// targetElement: ignore, // special display of foreign key, renameTo: select
targetElement, // special display of foreign key
value: enumValueOrCalc, // do not list for select items as elements

@@ -349,3 +348,3 @@ query,

if ($sources) {
setHidden( csn, '$sources', $sources );
setHidden( csn, '$sources', normalize$sources( $sources ) );
return undefined;

@@ -355,3 +354,4 @@ }

names = model._sortedSources.map( s => s.realname );
setHidden( csn, '$sources', (!strictMode) ? names : names.map( relativeName ) );
names = (!strictMode) ? names : normalize$sources( names.map( relativeName ) );
setHidden( csn, '$sources', names );
return undefined;

@@ -363,2 +363,7 @@

}
function normalize$sources( src ) {
return strictMode
? src.map( name => locationString( name, true ) )
: src;
}
}

@@ -674,3 +679,3 @@

function definition( art, _csn, _node, prop ) {
function definition( art, csn, _node, prop ) {
if (!art || typeof art !== 'object' || art.builtin)

@@ -681,9 +686,9 @@ return undefined; // TODO: complain with strict

return undefined;
if (art.kind === 'key') { // foreignkey
const key = addExplicitAs( { ref: art.targetElement.path.map( pathItem ) },
art.name, neqPath( art.targetElement ) );
if (!art.$inferred)
if (art.kind === 'key') { // foreign key
const key = standard( art );
if (!art.$inferred) // override location; otherwise only alias would be used
addLocation( art.targetElement.location, key );
return extra( key, art );
}
const c = standard( art );

@@ -956,3 +961,3 @@ // The XSN of actions in extensions do not contain a returns yet - TODO?

// Return parameter is in XSN - kind: 'param', name.id: ''
// eslint-disable-next-line no-nested-ternary, max-len
// eslint-disable-next-line no-nested-ternary
r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );

@@ -1153,6 +1158,12 @@ parent = parent._parent;

function targetElement( val, csn, node ) {
const key = addExplicitAs( { ref: val.path.map( pathItem ) },
node.name, neqPath( val ) );
Object.assign(csn, key);
}
function enumValueOrCalc( v, csn, node ) {
if (v.$inferred && (universalCsn || gensrcFlavor))
return undefined;
// Enums values in CSN are without outer `value: { … }`:
// Enum values in CSN are without outer `value: { … }`:
if (node.kind === 'enum') {

@@ -1159,0 +1170,0 @@ Object.assign( csn, expression( v ) );

@@ -23,3 +23,2 @@ // @ts-nocheck : Issues with Tokens on `this`, e.g. `this.DOT`.

// method which is called by generated parser with --trace-parser[-amg]:
// eslint-disable-next-line class-methods-use-this
syntaxError( recognizer, offendingSymbol, line, column, msg, e ) {

@@ -54,3 +53,3 @@ if (!(e instanceof CompileMessage)) // not already reported

const n = super.LT(k + 1);
// TODO v5: rewrite token in grammar via `this.setLocalToken`
// TODO: rewrite token in grammar via `this.setLocalToken`
if (n?.type === this.Identifier) {

@@ -89,2 +88,3 @@ const o = super.LT(k + 2);

ts.NEW = Parser.NEW;
ts.RETURNS = Parser.RETURNS;
ts.Identifier = Parser.Identifier;

@@ -91,0 +91,0 @@ ts.PAREN = tokenTypeOf( recognizer, "'('" );

@@ -32,3 +32,2 @@ // Error strategy with special handling for (non-reserved) keywords

const antlr4 = require('antlr4');
// eslint-disable-next-line camelcase
const Antlr4LL1Analyzer = require('antlr4/src/antlr4/LL1Analyzer');

@@ -35,0 +34,0 @@ const { DefaultErrorStrategy } = require('antlr4/src/antlr4/error/ErrorStrategy');

@@ -450,2 +450,3 @@ // Generic ANTLR parser class with AST-building functions

this.addAnnotation( def, prop, dup[prop] );
delete dup[prop]; // we want to keep $duplicates, but not have duplicate props
}

@@ -459,2 +460,3 @@ else if (prop === 'doc') {

def.doc = dup.doc;
delete dup[prop]; // we want to keep $duplicates for LSP, but not have duplicate props
}

@@ -465,2 +467,3 @@ else if (extensionDicts[prop]) {

def[prop] = dup[prop]; // continuation semantics: last wins
delete dup[prop]; // we want to keep $duplicates for LSP, but not have duplicate props
}

@@ -475,3 +478,5 @@ }

}
def.$duplicates = null;
// We keep duplicate statements for LSP, as it needs to traverse all identifiers;
// annotations were removed above to avoid traversing annotations twice.
}

@@ -716,10 +721,22 @@ }

// Classify token (identifier category) for implicit names,
// to be used in the empty alternative to AS <explicitName>.
function classifyImplicitName( category, ref, tokpos = 1 ) {
if (!ref || ref.path && this.getCurrentToken().text !== '.') {
const implicit = this._input.LT( tokpos - 1 || -1 );
if (implicit.isIdentifier)
/**
* Classify token (identifier category) for implicit names. To be used in the
* empty alternative to AS <explicitName>. If `ref` is given, uses the last
* path segment's `tokenIndex`. The return value can be used to reset the
* token's category, e.g. for inline select items.
*
* @param {string} category
* @param [ref]
*/
function classifyImplicitName( category, ref ) {
if (!ref || ref.path) {
const tokenIndex = ref?.path[ref.path.length - 1]?.location.tokenIndex;
const implicit = (tokenIndex === undefined) ? this._input.LT(-1) : this._input.get(tokenIndex);
if (implicit.isIdentifier) {
const previous = implicit.isIdentifier;
implicit.isIdentifier = category;
return { token: implicit, previous };
}
}
return null;
}

@@ -950,4 +967,3 @@

nextToken.type < this.constructor.Identifier && /^[a-z]+$/i.test( nextToken.text ))) {
// TODO: Make it an error in v5
this.warning('syntax-expecting-space', nextToken, {},
this.message('syntax-expecting-space', nextToken, {},
'Expecting a space between a number and a keyword/identifier');

@@ -1120,3 +1136,4 @@ }

function reportUnexpectedSpace( prefix = this._input.LT(-1),
location = this.tokenLocation( this._input.LT(1) ) ) {
location = this.tokenLocation( this._input.LT(1) ),
isError = false ) {
const prefixLoc = this.tokenLocation( prefix );

@@ -1132,4 +1149,10 @@ if (prefixLoc.endLine !== location.line ||

};
this.warning( 'syntax-unexpected-space', wsLocation, { op: prefix.text },
'Delete the whitespace after $(OP)' );
if (isError) {
this.message( 'syntax-invalid-space', wsLocation, { op: prefix.text },
'Delete the whitespace after $(OP)' );
}
else {
this.warning( 'syntax-unexpected-space', wsLocation, { op: prefix.text },
'Delete the whitespace after $(OP)' );
}
}

@@ -1136,0 +1159,0 @@ return prefixLoc;

@@ -259,3 +259,3 @@ 'use strict';

}
catch (e) {
catch {
// RangeError is thrown if number isn't a valid code point

@@ -488,3 +488,3 @@ reportInvalidCodePoint();

*/
_makeCode(code) { // eslint-disable-line class-methods-use-this
_makeCode(code) {
// For characters that may be rendered as newline,

@@ -505,2 +505,3 @@ // see <https://www.unicode.org/reports/tr14/tr14-32.html>.

// U+23CE: ⏎
// eslint-disable-next-line no-control-regex
const allNewLineCharacters = /[\u{000A}\u{000B}\u{000C}\u{000D}\u{0085}\u{2028}\u{2029}]/ug;

@@ -507,0 +508,0 @@ return code.replace(allNewLineCharacters, '\u{23CE}');

'use strict';
/** Whitespace characters without line-breaks. */
// eslint-disable-next-line no-control-regex
const whitespaceRegEx = /[\t\u{000B}\u{000C} \u{00A0}\u{FEFF}\p{Zs}]/u;

@@ -5,0 +6,0 @@ const cdlNewLineRegEx = /\r\n?|\n|\u2028|\u2029/u;

@@ -71,14 +71,18 @@ // Official cds-compiler API.

* - universal : In development (BETA)
*
* @default 'client'
*/
csnFlavor?: string | 'client' | 'gensrc' | 'universal'
/**
* If set, backends will not create localized convenience views for those views,
* that only have an association to a localized entity/view. Views will only get
* a convenience view, if they themselves contain localized elements (i.e. either
* If set to false, backends will create localized convenience views for those views,
* that only have an association to a localized entity/view. If set to true, views will
* only get a convenience view, if they themselves contain localized elements (i.e. either
* have simple projection on localized elements and CDL-casts to a localized element).
*
* The OData backend will not set `$localized: true` markers for such cases.
* If true, the OData backend will not set `$localized: true` markers for such cases.
*
* Does not work for backends to.hdi(), to.hdbcds() or to.sql() with `sqlDialect: 'hana'`,
* since in all those dialects, associations still exist in generated artifacts.
*
* @default true
*/

@@ -702,6 +706,2 @@ fewerLocalizedViews?: boolean

*
* @param config.idInBrackets
* If true, the message ID (if there is one and noMessageId is falsey) will be put in brackets.
* This will be the default in cds-compiler v5.
*
* @param config.noHome

@@ -719,3 +719,2 @@ * If true, will _not_ show message's semantic location.

noMessageId?: boolean
idInBrackets?: boolean
noHome?: boolean

@@ -1385,2 +1384,22 @@ module?: string

function getArtifactName(artifact: object): object;
type LspSemanticTokenEvent = {
event: 'reference' | 'definition',
semanticToken: object,
hint?: string
node?: object
}
/**
* Traverse the given XSN model and yield all _semantic tokens_ that are required by
* the LSP. These semantic tokens mostly include _identifiers_, that is, references
* or definitions. They also include the `returns` structure, as it is an annotation
* target as well.
*/
function traverseSemanticTokens(xsn: object, options: CompileOptions): Generator<LspSemanticTokenEvent>;
/**
* Given an XSN reference object, e.g. the `semanticToken` value of a `traverseSemanticTokens`
* event, return a generator that yields the reference's target and their origins until the
* base definition is reached.
*/
function getSemanticTokenOrigin(obj: LspSemanticTokenEvent): Generator<object>;
}

@@ -1387,0 +1406,0 @@

@@ -32,2 +32,3 @@ // Main entry point for the CDS Compiler

const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
const lsp = lazyload('./compiler/lsp-api');

@@ -98,3 +99,3 @@ // The compiler version (taken from package.json)

configurable: false,
enumerable: false
enumerable: true
});

@@ -188,3 +189,5 @@ return messages.CompilationError;

compile: (...args) => compiler.compileX(...args),
getArtifactName: (...args) => base.getArtifactName(...args),
getArtifactName: (art) => base.getArtifactName(art),
traverseSemanticTokens: (xsn, options) => lsp.traverseSemanticTokens(xsn, options),
getSemanticTokenOrigin: (obj) => lsp.getSemanticTokenOrigin(obj),
},

@@ -207,3 +210,3 @@

return new Proxy(((...args) => {
if (!module) // eslint-disable-next-line global-require
if (!module)
module = require(moduleName);

@@ -216,3 +219,3 @@

get(target, name) {
if (!module) // eslint-disable-next-line global-require
if (!module)
module = require(moduleName);

@@ -219,0 +222,0 @@

@@ -514,2 +514,4 @@ // CSN functionality for resolving references

setCache( art, '_parent', parent );
if (art.keys)
setCache(art, '_keys', getKeysDict( art ));
if (kind === 'target') {

@@ -654,5 +656,8 @@ // Prevent re-initialization of anonymous aspect with initDefinition():

if (!query) { // outside queries - TODO: items?
let art = parent.elements[head];
// Ref to up_ in anonymous aspect
if (!art && head === 'up_') {
let art = parent.elements?.[head];
if (parent.keys) {
const keysDict = getCache( parent, '_keys' );
art = keysDict[head];
} // Ref to up_ in anonymous aspect
else if (!art && head === 'up_') {
const up = getCache( parent, '_parent' );

@@ -995,2 +1000,13 @@ const target = up && typeof up.target === 'string' && csn.definitions[up.target];

/**
* Foreign keys are stored in an array; for easier name resolution, create
* a dictionary of them.
*/
function getKeysDict( art ) {
const dict = Object.create(null);
for (const key of art.keys)
dict[key.as || implicitAs( key.ref )] = key;
return dict;
}
/**
* Return value of a query SELECT for the query node, or the main artifact,

@@ -1186,3 +1202,3 @@ * i.e. a value with an `elements` property.

if (refCtx === 'annotation' && typeof obj === 'object') {
// we do not know yet whether the annotation value is a expression or not →
// we do not know yet whether the annotation value is an expression or not →
// loop over outer array and records (structure values):

@@ -1189,0 +1205,0 @@ if (Array.isArray( obj ) || !isAnnotationExpression( obj )) {

@@ -709,3 +709,2 @@ 'use strict';

*/
// eslint-disable-next-line no-unused-vars
function getArtifactDatabaseNameOf( artifactName, sqlMapping, csn, sqlDialect = 'plain' ) {

@@ -830,3 +829,2 @@ if (csn && typeof csn === 'object' && csn.definitions) {

*/
// eslint-disable-next-line no-unused-vars
function getElementDatabaseNameOf( elemName, sqlMapping, sqlDialect = 'plain' ) {

@@ -833,0 +831,0 @@ isValidMappingDialectCombi(sqlDialect, sqlMapping);

@@ -388,3 +388,3 @@ // Make internal properties of the XSN / augmented CSN visible

function logXsnModel( model, name ) {
// eslint-disable-next-line no-console, global-require
// eslint-disable-next-line no-console
console.log( require('util').inspect( revealInternalProperties( model, name ), false, null ) );

@@ -391,0 +391,0 @@ }

@@ -70,3 +70,3 @@ 'use strict';

const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
// eslint-disable-next-line global-require
const { version } = require('../../package.json');

@@ -73,0 +73,0 @@ error(null, null, { value: afterVersion, othervalue: beforeVersion, version },

@@ -0,1 +1,9 @@

// Compiler options
// Remarks:
// - The specification is client-tool centric (bin/cdsc.js):
// an option named `fooBar` is “produced” by `.option(' --foo-bar')`.
// - Also list the option in the `help` text, used with `cdsc -h`.
// - Specify valid values for non-boolean options in lib/api/validate.js.
'use strict';

@@ -37,3 +45,3 @@

.option(' --direct-backend')
.option(' --fallback-parser <type>', { valid: ['cdl', 'csn', 'csn!'] })
.option(' --fallback-parser <type>', { valid: [ 'auto!', 'cdl', 'csn', 'csn!' ] })
.option(' --shuffle <seed>') // 0 | 1..4294967296

@@ -121,5 +129,6 @@ .option(' --test-mode')

parser as a fallback. Valid values are:
cdl : Use CDL parser
csn : Use CSN parser
csn! : Use CSN parser even with extension cds, cdl, hdbcds and hdbdd
cdl : Use CDL parser
csn : Use CSN parser
csn! : Use CSN parser even with extension cds, cdl, hdbcds and hdbdd
auto! : Ignore file extension; use CSN parser if file content starts with '{'
--direct-backend Do not compile the given CSN but directly pass it to the backend.

@@ -166,2 +175,4 @@ Can only be used with certain new CSN based backends. Combination with

Can be overwritten by '--color'
FORCE_COLOR If set, compiler messages (/output) will be colored. Overrides NO_COLOR.
Can be overwritten by '--color'
CDSC_TRACE_TIME If set, additional timing information is printed to stderr.

@@ -324,5 +335,5 @@ CDSC_TRACE_API If set, additional API calling information is printed to stderr.

.option(' --generated-by-comment')
.option(' --better-sqlite-session-variables')
.option(' --better-sqlite-session-variables <bool>')
.option(' --fewer-localized-views')
.option(' --without-hana-associations')
.option(' --with-hana-associations <bool>', { valid: [ 'true', 'false' ] })
.help(`

@@ -379,7 +390,13 @@ Usage: cdsc toSql [options] <files...>

--generated-by-comment Enable rendering of the initial SQL comment for HDI-based artifacts
--better-sqlite-session-variables Enable better-sqlite compatible rendering of $user. Only
active if sqlDialect is \`sqlite\`
--better-sqlite-session-variables <bool>
Enable better-sqlite compatible rendering of $user. Only
active if sqlDialect is \`sqlite\`:
true : (default) Render better-sqlite session_context(…)
false : Render session variables as string literals, used e.g. with sqlite3 driver
--fewer-localized-views If set, the backends will not create localized convenience views for
those views, that only have an association to a localized entity/view.
--without-hana-associations If set, the backend will not render a "WITH ASSOCIATIONS" for sqlDialect 'hana'
--with-hana-associations <bool>
Enable and disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
true : (default) Render "WITH ASSOCIATIONS"
false : Do not render "WITH ASSOCIATIONS"
`);

@@ -490,2 +507,3 @@

.positionalArgument('<file>')
.option(' --with-locations')
.help(`

@@ -498,2 +516,3 @@ Usage: cdsc parseCdl [options] <file>

Options
--with-locations Add $location to CSN artifacts.
-h, --help Show this help text

@@ -500,0 +519,0 @@ `);

@@ -61,3 +61,3 @@

const { src } = options;
// eslint-disable-next-line global-require
const prepareOptions = require('../api/options');

@@ -64,0 +64,0 @@ options = prepareOptions.to.sql(options);

@@ -375,3 +375,3 @@ // Common render functions for toCdl.js, toHdbcds.js and toSql.js

},
'better-sqlite': {
sqlite: {
'$user.id': "session_context( '$user.id' )",

@@ -386,3 +386,3 @@ '$user.locale': "session_context( '$user.locale' )",

},
sqlite: {
'old-sqlite': {
// For sqlite, we render the string-format-time (strftime) function.

@@ -399,7 +399,10 @@ // Because the format of `current_timestamp` is like that: '2021-05-14 09:17:19' whereas

},
plain: {
'$at.from': 'current_timestamp',
'$at.to': 'current_timestamp',
'$valid.from': 'current_timestamp',
'$valid.to': 'current_timestamp',
plain: { // better-sqlite defaults
'$user.id': "session_context( '$user.id' )",
'$user.locale': "session_context( '$user.locale' )",
$tenant: "session_context( '$tenant' )",
'$at.from': "session_context( '$valid.from' )",
'$at.to': "session_context( '$valid.to' )",
'$valid.from': "session_context( '$valid.from' )",
'$valid.to': "session_context( '$valid.to' )",
$now: 'CURRENT_TIMESTAMP',

@@ -429,4 +432,4 @@ },

function variableForDialect( options, variable ) {
const dialect = options.sqlDialect === 'sqlite' && options.betterSqliteSessionVariables
? 'better-sqlite'
const dialect = options.sqlDialect === 'sqlite' && options.betterSqliteSessionVariables === false
? 'old-sqlite'
: options.sqlDialect;

@@ -433,0 +436,0 @@ return variablesToSql[dialect]?.[variable] || variablesToSql.fallback[variable] || null;

'use strict';
// eslint-disable-next-line no-control-regex
const controlCharacters = /[\u{0000}-\u{001F}]/u;

@@ -4,0 +5,0 @@ const highSurrogate = /[\u{D800}-\u{DBFF}]/u;

@@ -48,2 +48,3 @@ 'use strict';

const csnPath = [ ...path ];
const context = {};
if (prop === 'definitions') {

@@ -88,5 +89,5 @@ definitions( parent, 'definitions', parent.definitions );

if (customTransformers[name])
customTransformers[name](node, name, node[name], csnPath, _parent, _prop);
customTransformers[name](node, name, node[name], csnPath, _parent, _prop, context);
else if (options.processAnnotations && customTransformers['@'] && name.charAt(0) === '@')
customTransformers['@'](node, name, node[name], csnPath, _parent, _prop);
customTransformers['@'](node, name, node[name], csnPath, _parent, _prop, context);
trans( node, name, node[name], csnPath );

@@ -115,5 +116,5 @@ }

if (customTransformers[name])
customTransformers[name](node, name, node[name], csnPath, dict);
customTransformers[name](node, name, node[name], csnPath, dict, null, context);
else if (options.processAnnotations && customTransformers['@'] && name.charAt(0) === '@')
customTransformers['@'](node, name, node[name], csnPath, dict);
customTransformers['@'](node, name, node[name], csnPath, dict, null, context);
trans( node, name, node[name], csnPath );

@@ -136,2 +137,3 @@ }

csnPath.push( _prop );
context[`$in_${_prop}`] = true;
for (const name of Object.getOwnPropertyNames( dict ))

@@ -143,2 +145,3 @@ dictEntry( dict, name, dict[name] );

csnPath.pop();
context[`$in_${_prop}`] = undefined;
}

@@ -156,2 +159,3 @@

if (options.processAnnotations) {
context.$annotation = { name: _prop, value: node };
if (isAnnotationExpression(node)) {

@@ -173,2 +177,3 @@ standard(_parent, _prop, node);

}
context.$annotation = undefined;
}

@@ -369,8 +374,8 @@ }

if (Array.isArray(fns)) {
remapped[n].push((parent, name, prop, path, parentParent) => fns.forEach(
fn => fn.bind(that)(parent, name, prop, path, parentParent)
remapped[n].push((parent, name, prop, path, parentParent, opt, context) => fns.forEach(
fn => fn.bind(that)(parent, name, prop, path, parentParent, opt, context)
));
}
else {
remapped[n].push((parent, name, prop, path, parentParent) => fns.bind(that)(parent, name, prop, path, parentParent));
remapped[n].push((parent, name, prop, path, parentParent, opt, context) => fns.bind(that)(parent, name, prop, path, parentParent, opt, context));
}

@@ -381,3 +386,3 @@ }

for (const [ n, fns ] of Object.entries(remapped))
remapped[n] = (parent, name, prop, path, parentParent) => fns.forEach(fn => fn.bind(that)(parent, name, prop, path, parentParent));
remapped[n] = (parent, name, prop, path, parentParent, opt, context) => fns.forEach(fn => fn.bind(that)(parent, name, prop, path, parentParent, opt, context));

@@ -384,0 +389,0 @@

@@ -135,40 +135,4 @@ 'use strict';

function handleManagedAssocSteps( artifact, artifactName ) {
const transformer = {
ref: (refOwner, prop, ref, path) => {
// [<assoc base>.]<managed assoc>.<field>
if (ref.length > 1) {
const { links } = inspectRef(path);
if (links) {
let fkAlias = '';
// eslint-disable-next-line for-direction
for (let i = links.length - 1; i >= 0; i--) {
const link = links[i];
// We found the latest managed assoc path step
if (link.art && link.art.target && link.art.keys &&
// Doesn't work when ref-target (filter condition) or similar is used
!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
const fkRef = ref[i + 1];
const fkName = (!fkAlias ? fkRef : `${fkRef}${pathDelimiter}${fkAlias}`);
const fks = link.art.keys.filter(key => key.ref[0] === fkName);
if (fks.length >= 1) { // after flattening, at most one FK will remain.
// `.as` is set for SQL, but not for OData -> fall back to implicit alias
fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
const source = findSource(links, i - 1) || artifact;
const managedAssocStepName = ref[i];
const newFkName = `${managedAssocStepName}${pathDelimiter}${fkAlias}`;
if (source?.elements[newFkName])
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
}
}
else {
fkAlias = '';
// Ignore last path step and unmanaged associations.
// Structures should have been already flattened.
}
}
}
}
},
};
const transformer = getTransformer();
const inColumnsTransformer = getTransformer(true);
for (const elemName in artifact.elements) {

@@ -191,24 +155,68 @@ const elem = artifact.elements[elemName];

};
if (processOnInQueries)
if (processOnInQueries) {
queryTransformers.columns = (parent, prop, columns, path) => {
for (let i = 0; i < columns.length; i++) {
const column = columns[i];
if (column.ref) {
inColumnsTransformer.ref(column, 'ref', column.ref, path.concat([ 'columns', i ]));
column.ref.forEach((step, index) => {
if (step.where)
transform(step, 'where', step.where, path.concat([ 'columns', i, 'ref', index ]));
});
}
}
};
queryTransformers.on = transform;
applyTransformationsOnNonDictionary(artifact, artifact.query ? 'query' : 'projection', queryTransformers, {}, [ 'definitions', artifactName ]);
}
applyTransformationsOnNonDictionary(artifact, artifact.query ? 'query' : 'projection', queryTransformers, { drillRef: true }, [ 'definitions', artifactName ]);
}
/**
* Find out where the managed association is
*
* @param {Array} links
* @param {number} startIndex
* @returns {object | undefined} CSN definition of the source of the managed association
* @param {boolean} isColumns Whether the transformation is taking place on a column
* @returns {object}
*/
function findSource( links, startIndex ) {
for (let i = startIndex; i >= 0; i--) {
const link = links[i];
// We found the latest assoc step - now check where that points to
if (link.art && link.art.target)
return csn.definitions[link.art.target];
}
function getTransformer( isColumns = false ) {
return {
ref: (refOwner, prop, ref, path) => {
// [<assoc base>.]<managed assoc>.<field>
if (ref.length > 1) {
const { links } = inspectRef(path);
if (links) {
let fkAlias = '';
for (let i = links.length - 1; i >= 0; i--) {
const link = links[i];
// We found the latest managed assoc path step
if (link.art && link.art.target && link.art.keys &&
// Doesn't work when ref-target (filter condition) or similar is used
!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
const fkRef = ref[i + 1];
const fkName = (!fkAlias ? fkRef : `${fkRef}${pathDelimiter}${fkAlias}`);
const fks = link.art.keys.filter(key => key.ref[0] === fkName);
return undefined;
if (fks.length >= 1) { // after flattening, at most one FK will remain.
// `.as` is set for SQL, but not for OData -> fall back to implicit alias
fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
const managedAssocStepName = ref[i];
const newFkName = `${managedAssocStepName}${pathDelimiter}${fkAlias}`;
if (isColumns) {
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
if (!refOwner.as)
refOwner.as = implicitAs(ref);
}
else {
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
}
}
}
else {
fkAlias = '';
// Ignore last path step and unmanaged associations.
// Structures should have been already flattened.
}
}
}
}
},
};
}

@@ -215,0 +223,0 @@ }

@@ -50,3 +50,3 @@ 'use strict';

parent.columns = replaceStar(root, columns, parent.excluding, isComplexQuery);
// FIXME(v5): Remove argument "isComplexOrNestedQuery"; we use path.length > 4 to check
// FIXME(v6): Remove argument "isComplexOrNestedQuery"; we use path.length > 4 to check
// if we're inside the outermost "columns". If so, always prepend a table alias. See #11662

@@ -626,7 +626,2 @@ parent.columns = expand(parent.columns, path.concat('columns'), true, isComplexQuery || path.length > 4);

if (withAlias) {
// TODO: Remove this line in case foreign key annotations should
// be addressed via full path into target instead of using alias
// names. See flattening.js::flattenAllStructStepsInRefs()
// apply transformations on `ref` counterpart comment.
setProp(obj, '$structRef', currentAlias);
obj.as = currentAlias.join(pathDelimiter);

@@ -633,0 +628,0 @@ // alias was implicit - to later distinguish expanded s -> s.a from explicitly written s.a

@@ -15,6 +15,7 @@ 'use strict';

const { csnRefs } = require('../../model/csnRefs');
const { setProp, isBetaEnabled } = require('../../base/model');
const { setProp } = require('../../base/model');
const { forEach } = require('../../utils/objectUtils');
const { transformExpression } = require('./applyTransformations');
const { cloneCsnNonDict } = require('../../model/cloneCsn');
const { EdmTypeFacetNames } = require('../../edm/EdmPrimitiveTypeDefinitions');

@@ -210,5 +211,20 @@ /**

function flattenAllStructStepsInRefs( csn, options, messageFunctions, resolved, pathDelimiter, iterateOptions = {} ) {
const adaptRefs = [];
applyTransformations(csn, getStructStepsFlattener(csn, options, messageFunctions, resolved, pathDelimiter, adaptRefs), [], iterateOptions);
adaptRefs.forEach(fn => fn());
}
/**
* @param {CSN.Model} csn
* @param {CSN.Options} options
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
* @param {WeakMap} resolved Cache for resolved refs
* @param {string} pathDelimiter
* @param {Function[]} adaptRefs
* @returns {object} applyTransformations transformer
*/
function getStructStepsFlattener( csn, options, messageFunctions, resolved, pathDelimiter, adaptRefs ) {
const { inspectRef, effectiveType } = csnRefs(csn);
const { flattenStructStepsInRef } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
const adaptRefs = [];

@@ -234,5 +250,5 @@ /**

applyTransformations(csn, {
const transformer = {
// @ts-ignore
ref: (parent, prop, ref, path) => {
ref: (parent, prop, ref, path, _parent, _prop, context) => {
const { links, art, scope } = inspectRef(path);

@@ -242,30 +258,46 @@ const resolvedLinkTypes = resolveLinkTypes(links);

const lastRef = ref[ref.length - 1];
const fn = () => {
const scopedPath = [ ...parent.$path ];
// TODO: If foreign key annotations should be assigned via
// full path into target, uncomment this line and
// comment/remove setProp in expansion.js
// setProp(parent, '$structRef', parent.ref);
[ parent.ref ] = flattenStructStepsInRef(ref, scopedPath, links, scope, resolvedLinkTypes);
resolved.set(parent, { links, art, scope });
// Explicitly set implicit alias for things that are now flattened - but only in columns
// TODO: Can this be done elegantly during expand phase already?
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
delete parent.as;
delete parent.$implicitAlias;
const fn = (suspend = false, suspendPos = 0,
refFilter = _parent => true) => {
let refChanged = false;
if (refFilter(parent)) {
const scopedPath = [ ...parent.$path ];
// TODO: If foreign key annotations should be assigned via
// full path into target, uncomment this line and
// comment/remove setProp in expansion.js
// setProp(parent, '$structRef', parent.ref);
[ parent.ref, refChanged ] = flattenStructStepsInRef(ref, scopedPath, links, scope, resolvedLinkTypes, suspend, suspendPos, parent.$bparam);
resolved.set(parent, { links, art, scope });
// Explicitly set implicit alias for things that are now flattened - but only in columns
// TODO: Can this be done elegantly during expand phase already?
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
delete parent.as;
delete parent.$implicitAlias;
}
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
!parent.as) {
parent.as = lastRef;
}
}
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
else if (parent.ref[parent.ref.length - 1] !== lastRef &&
(insideColumns(scopedPath) || insideKeys(scopedPath)) &&
!parent.as) {
parent.as = lastRef;
}
return refChanged;
};
// adapt queries later
adaptRefs.push(fn);
if (context?.$annotation) {
const annotation = context.$annotation.value;
adaptRefs.push((...args) => {
const refChanged = fn(...args);
if (refChanged && annotation['='])
annotation['='] = true;
});
}
else {
// adapt queries later
adaptRefs.push(fn);
}
},
}, [], iterateOptions);
};
adaptRefs.forEach(fn => fn());

@@ -290,2 +322,4 @@ /**

}
return transformer;
}

@@ -351,21 +385,24 @@

delete flatElement.key;
transformExpression(flatElement, 'on', {
ref: (_parent, _prop, xpr) => {
const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, -1).join(pathDelimiter);
if (options.transformation !== 'effective') {
const process = endIndex => function processRef(_parent, _prop, xpr) {
const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, endIndex).join(pathDelimiter);
const possibleFlatName = prefix + pathDelimiter + xpr[0];
/*
when element is defined in the current name resolution scope, like
entity E {
key x: Integer;
s : {
y : Integer;
a3 : association to E on a3.x = y;
}
}
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
*/
when element is defined in the current name resolution scope, like
entity E {
key x: Integer;
s : {
y : Integer;
a3 : association to E on a3.x = y;
}
}
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
*/
if (flatElems[possibleFlatName])
xpr[0] = possibleFlatName;
},
});
};
transformExpression(flatElement, 'on', {
ref: process(-1),
});
}
}

@@ -437,33 +474,3 @@ parent[prop].$orderedElements.push([ flatElemName, flatElement ]);

/**
* Link annotate extensions to managed associations as a preparational step
* for later annotation assignment on the final foreignkeys
* This function must be applied on an unmodified, structured CSN in order to
* traverse both the extensions and dictionary trees in corresponding order.
*
* @param {CSN.Model} csn
* @param {object} options
*/
function linkForeignKeyAnnotationExtensionsToAssociation( csn, options ) {
if (isBetaEnabled(options, 'annotateForeignKeys')) {
csn.extensions?.forEach(( ext ) => {
const defName = ext.annotate;
const traverseExtensions = (env, enode) => {
if (env?.target && env?.keys) {
setProp(env, '$fkExtensions', enode);
}
else {
const elements = env?.items?.elements || env?.elements;
if (enode?.elements && elements)
Object.keys(enode.elements).forEach(en => traverseExtensions(elements[en], enode.elements[en]));
}
};
if (ext.annotate)
traverseExtensions(csn.definitions[defName], ext);
});
}
}
/**
* @param {CSN.Model} csn
* @param {CSN.Options} options

@@ -597,3 +604,3 @@ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …

function cloneAndExtendRef( key, base, ref ) {
const clone = cloneCsnNonDict(base, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
const clone = cloneCsnNonDict(base, options );
if (key.ref) {

@@ -614,4 +621,2 @@ // We build a ref that contains the aliased fk - that element will be created later on, so this ref is not resolvable yet

clone.ref = clone.ref.concat(key.ref);
if (clone.$structRef && key.$structRef)
clone.$structRef = clone.$structRef.concat(key.$structRef);
}

@@ -716,30 +721,5 @@

}
}
// assign annotations from fkExtension tree to foreign keys
if (isBetaEnabled(options, 'annotateForeignKeys')) {
const extCollector = {};
fks.forEach(([ _fkn, fk ]) => {
let ext = element.$fkExtensions;
let extKey = elementName;
for (const step of fk.$extensionPath) {
extKey += `.${step}`;
ext = ext?.elements?.[step];
if (!ext)
break;
// collect annotations, lowest wins
// eslint-disable-next-line no-loop-func
Object.entries(ext).forEach(([ k, v ]) => {
if (k[0] === '@') {
fk[k] = v;
extCollector[extKey] = ext;
}
});
}
});
// remove consumed annotations after applying the annotation hierarchy to each fk!
Object.values(extCollector).forEach(ext => Object.keys(ext).forEach((k) => {
if (k[0] === '@')
delete ext[k];
}));
if (options.transformation === 'effective')
delete element.default;
}

@@ -769,7 +749,6 @@ orderedElements.push(...fks);

* @param {string} pathDelimiter
* @param {object} extensionPath
* @param {number} lvl
* @returns {Array[]} First element of every sub-array is the foreign key name, second is the foreign key definition
*/
function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathDelimiter, extensionPath = [], lvl = 0 ) {
function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathDelimiter, lvl = 0 ) {
const special$self = !csn?.definitions?.$self && '$self';

@@ -820,3 +799,3 @@ const isInspectRefResult = !Array.isArray(path);

const result = csnUtils.inspectRef(continuePath);
fks = fks.concat(createForeignKeys(csnUtils, result, result.art, alias, csn, options, pathDelimiter, extensionPath.concat(key.$structRef), lvl + 1));
fks = fks.concat(createForeignKeys(csnUtils, result, result.art, alias, csn, options, pathDelimiter, lvl + 1));
});

@@ -835,3 +814,3 @@ if (!hasKeys)

const continuePath = getContinuePath([ 'elements', elemName ]);
fks = fks.concat(createForeignKeys(csnUtils, continuePath, elem, elemName, csn, options, pathDelimiter, extensionPath.concat(elemName), lvl + 1));
fks = fks.concat(createForeignKeys(csnUtils, continuePath, elem, elemName, csn, options, pathDelimiter, lvl + 1));
}

@@ -843,8 +822,7 @@ });

const newFk = Object.create(null);
setProp(newFk, '$extensionPath', extensionPath);
for (const prop of [ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type' ]) {
[ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${f}`) ].forEach((prop) => {
// copy props from original element to preserve derived types!
if (element[prop] !== undefined)
newFk[prop] = element[prop];
}
});
return [ [ prefix, newFk ] ];

@@ -903,5 +881,5 @@ }

removeLeadingSelf,
linkForeignKeyAnnotationExtensionsToAssociation,
handleManagedAssociationsAndCreateForeignKeys,
getBranches,
getStructStepsFlattener,
};

@@ -73,7 +73,6 @@ 'use strict';

// Clarify: What about $valid?
const atFrom = { ref: [ '$at', 'from' ] };
const atTo = { ref: [ '$at', 'to' ] };
const validFrom = { ref: [ '$valid', 'from' ] };
const validTo = { ref: [ '$valid', 'to' ] };
const cond = [ '(', fromPath, '<', atTo, 'and', toPath, '>', atFrom, ')' ];
const cond = [ '(', fromPath, '<', validTo, 'and', toPath, '>', validFrom, ')' ];

@@ -80,0 +79,0 @@ if (normalizedQuery.query.SELECT.where) { // if there is an existing where-clause, extend it by adding 'and (temporal clause)'

@@ -327,3 +327,2 @@ 'use strict';

*/
// eslint-disable-next-line complexity
function transformViewOrEntity( query, artifact, artName, path ) {

@@ -330,0 +329,0 @@ const ignoreAssociations = options.sqlDialect === 'hana' && options.withHanaAssociations === false;

'use strict';
const { forEachDefinition, getServiceNames, applyAnnotationsFromExtensions } = require('../../model/csnUtils');
const { forEachDefinition, forEachMemberRecursively,
getServiceNames, applyAnnotationsFromExtensions,
transformExpression } = require('../../model/csnUtils');
const { forEach } = require('../../utils/objectUtils');

@@ -61,8 +63,9 @@ const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');

generateDraftForOdata(def, defName, def);
visitedArtifacts[defName] = true;
}, { skipArtifact: isExternalServiceMember });
applyAnnotationsFromExtensions(csn, { override: true, filter: name => filterDict[name] });
rewriteDollarDraft();
return csn;
/**

@@ -88,2 +91,5 @@ * Generate all that is required in ODATA for draft enablement of 'artifact' into the artifact,

if(!visitedArtifacts[artifactName])
visitedArtifacts[artifactName] = artifact;
const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');

@@ -216,4 +222,46 @@ assignAction(draftPrepare, artifact);

}
/*
* After draft decoration, all visited artifacts are supposed to have the draft state elements
* Is/HasActiveEntity, HasDraftEntity. Now, $draft.<postfix> (with postfix defined as magic variable
* in the core compiler builtins) needs to be translated into $self.<postfix>.
*
* It has to be processed after the late 'applyAnnotationsFromExtensions' which could also merge in
* some $draft path expressions.
*/
function rewriteDollarDraft() {
function $draft2$self(member) {
Object.keys(member).forEach(pn => {
if(pn[0] === '@')
transformExpression(member, pn,{
ref: (_parent, _prop, xpr, _path) => {
if(xpr[0] === '$draft') {
xpr[0] = '$self';
}
}
});
});
}
// entity parameters are not substituted as the EDM param entity is not draft enabled
Object.entries(visitedArtifacts).forEach(([artName, art]) => {
$draft2$self(art);
forEachMemberRecursively(art, $draft2$self,
[ 'definitions', artName ],
true, { elementsOnly: true }
);
if(art.actions) {
Object.entries(art.actions).forEach(([actionName, action]) => {
$draft2$self(action);
forEachMemberRecursively(action, $draft2$self,
[ 'definitions', artName, 'actions', actionName ]);
if(action.returns)
$draft2$self(action.returns);
})
}
})
}
}
module.exports = generateDrafts;

@@ -182,5 +182,6 @@ 'use strict';

return {
elements: (parent, prop, elements, path) => {
elements: (parent, prop, elements, path, _parentParent, _dummy, context) => {
const artifact = csn.definitions[path[1]];
if (artifact?.kind === 'entity') {
// Don't process bound actions, as they are still structured
if (artifact?.kind === 'entity' && !context.$in_actions) {
for (const elementName in elements)

@@ -187,0 +188,0 @@ remapAnnotationsOnElement(artifact, elements[elementName]);

@@ -7,2 +7,3 @@ 'use strict';

const transformUtils = require('../transformUtils');
const effectiveFlattening = require('./flattening');
const flattening = require('../db/flattening');

@@ -15,3 +16,2 @@ const types = require('./types');

const associations = require('./associations');
const generateDrafts = require('../draft/db');
const handleExists = require('../db/transformExists');

@@ -71,5 +71,6 @@ const misc = require('./misc');

flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, new WeakMap(), '_');
flattening.flattenElements(csn, options, messageFunctions, '_');
effectiveFlattening.flattenRefs(csn, options, csnUtils, messageFunctions);
flattening.flattenElements(csn, options, messageFunctions, '_', { skipDict: { actions: true } });
resolveTypesInActionsAfterFlattening();

@@ -83,5 +84,4 @@

associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
generateDrafts(csn, options, '_', messageFunctions);
const transformers = mergeTransformers([ misc.attachPersistenceName(csn, options, csnUtils), options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
applyTransformations(csn, transformers, [], { skipIgnore: false });
const transformers = mergeTransformers([ options.addCdsPersistenceName ? misc.attachPersistenceName(csn, options, csnUtils) : {}, options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
applyTransformations(csn, transformers, [], { skipIgnore: false, processAnnotations: true });

@@ -88,0 +88,0 @@ if (!options.resolveProjections)

@@ -21,3 +21,3 @@ 'use strict';

* @param {CSN.Options} options
* @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
* @returns {Function} Callback to resolve types in action returns later - as for them, $self would lead to unresolvable constructs at this point
* so we can call this callback after flattening is done - then we can safely resolve their types.

@@ -29,7 +29,4 @@ */

applyTransformations(csn, {
type: (parent, prop, type, path) => {
const artifact = csn.definitions[path[1]];
// TODO: What about events?
if (!(artifact.kind === 'action' || artifact.kind === 'function'))
resolveType(parent);
type: (parent) => {
resolveType(parent);
},

@@ -43,8 +40,16 @@ }, [ (definitions, artifactName, artifact) => {

later.push(artifact.actions);
} ], { skipDict: { actions: true }, processAnnotations: true });
} ], { skipStandard: { returns: true }, processAnnotations: true });
// TODO: Directly push the .returns into the later so we have a more minimal looping
return function resolveTypesInActions() {
later.forEach(action => applyTransformationsOnDictionary(action, { type: parent => resolveType(parent) }));
later.forEach((a) => {
applyTransformationsOnDictionary(a, {
type: (parent) => {
resolveType(parent);
},
});
});
};
/**

@@ -129,5 +134,4 @@ * Resolve a type to its

module.exports = {
resolve: resolveTypes,
};

@@ -170,4 +170,2 @@ 'use strict';

flattening.linkForeignKeyAnnotationExtensionsToAssociation(csn, options);
// All type refs must be resolved, including external APIs.

@@ -174,0 +172,0 @@ // OData has no 'type of' so 'real' imported OData APIs marked @cds.external are safe.

@@ -172,4 +172,2 @@ 'use strict';

doA2J && flattening.linkForeignKeyAnnotationExtensionsToAssociation(csn, options);
// Check if structured elements and managed associations are compared in an expression

@@ -706,19 +704,2 @@ // and expand these structured elements. This tuple expansion allows all other

});
// restore $fkExtensions and $structRef for foreign key annotations
if (isBetaEnabled(options, 'annotateForeignKeys')) {
forEachDefinition(csn, (oldDef, artName) => {
const newDef = newCsn.definitions[artName];
if(oldDef?.elements) {
Object.entries(oldDef.elements).forEach(([eltName, oldElt]) => {
const newElt = newDef.elements[eltName];
if(oldElt.$fkExtensions)
setProp(newElt, '$fkExtensions', oldElt.$fkExtensions);
oldElt.keys?.forEach((fk, i) => {
if(fk.$structRef && newElt.keys?.[i])
setProp(newElt.keys[i], '$structRef', fk.$structRef);
})
})
}
});
}
csn = newCsn;

@@ -725,0 +706,0 @@ }

'use strict';
const { makeMessageFunction } = require('../base/messages');
const { setProp, isDeprecatedEnabled, isBetaEnabled} = require('../base/model');
const { setProp } = require('../base/model');
const { forEachKey } = require('../utils/objectUtils');

@@ -94,2 +94,3 @@ const { cleanSymbols } = require('../base/cleanSymbols.js');

* @param {boolean} [options.fewerLocalizedViews]
* Default: true
*

@@ -118,3 +119,4 @@ * @param {object} config

options.localizedWithoutCoalesce);
const ignoreAssocToLocalized = !!options.fewerLocalizedViews;
// default is true, hence only check for explicitly disabled option
const ignoreAssocToLocalized = options.fewerLocalizedViews !== false;

@@ -181,3 +183,3 @@ createDirectConvenienceViews(); // 1

copyPersistenceAnnotations(view, art, options);
copyPersistenceAnnotations(view, art);
csn.definitions[viewName] = view;

@@ -758,7 +760,4 @@ }

* @param {CSN.Artifact} source
* @param {CSN.Options} options
*/
function copyPersistenceAnnotations(target, source, options) {
const copyExists = !isBetaEnabled(options, 'v5preview') &&
!isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
function copyPersistenceAnnotations(target, source) {
forEachKey(source, anno => {

@@ -770,3 +769,3 @@ // Note:

// v2/>=v5, `.exists` is never copied.
if (anno === annoPersistenceSkip || (copyExists && anno === '@cds.persistence.exists'))
if (anno === annoPersistenceSkip)
target[anno] = source[anno];

@@ -773,0 +772,0 @@ });

@@ -11,4 +11,3 @@ 'use strict';

applyTransformationsOnNonDictionary } = require('../db/applyTransformations.js');
const { linkForeignKeyAnnotationExtensionsToAssociation,
handleManagedAssociationsAndCreateForeignKeys } = require('../db/flattening');
const { handleManagedAssociationsAndCreateForeignKeys } = require('../db/flattening');
const { cloneCsnNonDict } = require('../../model/cloneCsn');

@@ -297,14 +296,24 @@ const { forEach } = require('../../utils/objectUtils');

ref: (elemref, prop, xpr, path) => {
const { art, scope } = inspectRef(path);
const { links, art, scope } = inspectRef(path);
if (scope !== '$magic' && art) {
const ft = csnUtils.getFinalTypeInfo(art.type);
if (!isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{
anno: refCheck.anno,
elemref,
name: refCheck.eltLocationStr,
'#': 'flatten_builtin'
});
// try to find rightmost 'items', terminate if association comes first.
let i = links.length-1;
const getProp = (propName) => links[i].art?.[propName];
let hasItems = false;
for(; i >= 0 && !getProp('target') && !hasItems; i--) {
hasItems = !!getProp('items')
}
if(!hasItems) {
const ft = csnUtils.getFinalTypeInfo(art.type);
if (!isBuiltinType(ft?.items?.type || ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{
anno: refCheck.anno,
elemref,
name: refCheck.eltLocationStr,
'#': 'flatten_builtin'
});
}
}
}

@@ -426,21 +435,21 @@ }

ref: (elemref, prop, xpr, path) => {
const { links, art } = (elemref._links && elemref._art
? { links: elemref._links, art: elemref._art }
: inspectRef(path) );
const { links, art, scope } = inspectRef(path);
let i = links.length-2;
const getProp = (propName) =>
(links[i].art?.[propName] ||
effectiveType(links[i].art)[propName]);
if (scope !== '$magic' && art) {
let i = links.length-2;
const getProp = (propName) =>
(links[i].art?.[propName] ||
effectiveType(links[i].art)[propName]);
const ft = csnUtils.getFinalTypeInfo(art?.type);
let target = undefined;
for(; i >= 0 && !getProp('items') && !target; i--) {
target = getProp('target');
let target = undefined;
for(; i >= 0 && !getProp('items') && !target; i--) {
target = getProp('target');
}
const ft = csnUtils.getFinalTypeInfo(art.type);
if (target && csn.definitions[target].$flatelements
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
}
}
if (target && csn.definitions[target].$flatelements
&& !isBuiltinType(ft?.type) && refCheck.anno !== 'value') {
error('odata-anno-xpr-ref', path,
{ anno: refCheck.anno, elemref, '#': 'flatten_builtin_type' });
}
}

@@ -554,5 +563,4 @@ }

flattenAllStructStepsInRefs,
linkForeignKeyAnnotationExtensionsToAssociation,
handleManagedAssociationsAndCreateForeignKeys,
getStructRefFlatteningTransformer
};

@@ -11,3 +11,3 @@ 'use strict';

const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
const { getNamespace, copyAnnotations, findAnnotationExpression,
const { getNamespace, copyAnnotations,
forEachDefinition, forEachMember, forEachGeneric, isEdmPropertyRendered } = require('../../model/csnUtils');

@@ -233,18 +233,6 @@ const { isBuiltinType } = require('../../base/builtins');

// TODO: remove the this if and the else clause for the V5 release
if (isBetaEnabled(options, 'v5preview')) {
// We need this check, as we only need to copy the annotions if the type
// is user defined structured type outside of the service
if (!isAnonymous) {
copyAnnotations(typeDef, newType);
}
} else {
// expression annos and their sub annotations are not propagated to type
let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
if (pn[0] === '@')
acc[findAnnotationExpression(typeDef, pn) ? 0 : 1].push(pn);
return acc;
}, [ [], [] ]);
nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
copyAnnotations(typeDef, newType, false, {}, nxprANames);
// Annotations are propagated only from user defined structured
// types that need to be added to a service
if (!isAnonymous) {
copyAnnotations(typeDef, newType);
}

@@ -251,0 +239,0 @@

@@ -269,3 +269,3 @@ 'use strict';

const flatElemName = elemName + pathDelimiter + childName;
const flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
const flatElem = cloneCsnNonDict(childElem, options);
// Don't take over notNull from leaf elements

@@ -272,0 +272,0 @@ delete flatElem.notNull;

@@ -52,19 +52,25 @@ // Util functions for operations usually used with files.

/** @type {function(string, string)} */
readFileAsync: util.promisify(readFile),
readFileAsync: util.promisify( readFile ),
readFile,
readFileSync,
isFileAsync: util.promisify(isFile),
isFile,
isFileSync,
realpathAsync: util.promisify(realpath),
realpath,
realpath: fs.realpath,
realpathNative: fs.realpath.native,
realpathSync,
realpathSyncNative,
};
function realpath( path, cb ) {
return fs.realpath.native(path, cb);
}
function realpathSync( path, cb ) {
try {
cb(null, fs.realpathSync(path));
}
catch (err) {
cb(err, null);
}
}
function realpathSyncNative( path, cb ) {
try {
cb(null, fs.realpathSync.native(path));

@@ -71,0 +77,0 @@ }

@@ -73,3 +73,3 @@ // Custom resolve functionality for the CDS compiler

readFile: _fs.readFile,
realpath: _fs.realpath,
realpath: _fs.realpathNative,
lookupDirs: _getLookupDirectories( options, messageFunctions ),

@@ -94,3 +94,16 @@ };

dep.absname = res;
_fs.realpath( res, cb );
_fs.realpath( res, function realPathResult(realpathErr, modulePath) {
if (realpathErr) {
cb(realpathErr, modulePath);
}
else {
_fs.realpathNative( res, function nativeRealPathResult(nativeErr, nativePath) {
if (!nativeErr)
checkFileCase( dep, modulePath, nativePath, messageFunctions );
// Pass the _native_ path to ensure that we use the actual
// file's path (include case-differences)
cb(realpathErr, nativePath);
});
}
} );
}

@@ -143,3 +156,3 @@ else if (body && typeof body === 'object' && body.realname) {

readFile: _fs.readFileSync,
realpath: _fs.realpathSync,
realpath: _fs.realpathSyncNative,
lookupDirs: _getLookupDirectories( options, messageFunctions ),

@@ -172,7 +185,19 @@ };

dep.absname = result;
_fs.realpathSync( result, (err, modulePath) => {
if (err)
error = err;
else
result = modulePath;
_fs.realpathSync( result, function realPathResult(realpathErr, modulePath) {
if (realpathErr) {
error = realpathErr;
}
else {
_fs.realpathSyncNative( result, function nativeRealPathResult(nativeErr, nativePath) {
if (nativeErr) {
error = nativeErr;
}
else {
checkFileCase(dep, modulePath, nativePath, messageFunctions);
// Use the _native_ path to ensure that we use the actual
// file's path (include case-differences)
result = nativePath;
}
});
}
});

@@ -541,2 +566,28 @@ }

/**
* Check if the given paths for case-differences. If there are case differences
* emit a warning. This can happen on systems with case-insensitive file
* systems. As that is a hard-to-debug issue, we help the user by emitting
* a corresponding warning.
*
* @param {object} dep
* @param {string} realpath
* @param {string} nativeRealpath
* @param {Function} warning
*/
function checkFileCase( dep, realpath, nativeRealpath, { warning } ) {
if (realpath === nativeRealpath)
return;
if (realpath.toLowerCase() !== nativeRealpath.toLowerCase()) {
// safe-guard: in case realpath() resolved symlinks more deeply or sockets/pipes were used,
// which realpath.native() handles differently, don't report a possible false positive.
return;
}
for (const using of dep.usingFroms) {
warning('file-unexpected-case-mismatch', [ using.location, using ], {},
// eslint-disable-next-line max-len
'The imported filename differs on the filesystem; ensure that capitalization matches the actual file\'s name');
}
}
/**
* @typedef {object} ResolveConfig

@@ -543,0 +594,0 @@ * @property {string[]} lookupDirs

@@ -60,4 +60,5 @@ //

// Note: To be able to disable colors in tests, we check the environment
// variable here again.
hasColor = hasColorShell && (process.env.NO_COLOR === undefined);
// variables again.
hasColor = (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0') ||
(hasColorShell && process.env.NO_COLOR === undefined);
break;

@@ -64,0 +65,0 @@ }

{
"name": "@sap/cds-compiler",
"version": "4.9.4",
"version": "5.0.6",
"description": "CDS (Core Data Services) compiler and backends",

@@ -27,7 +27,11 @@ "homepage": "https://cap.cloud.sap/",

"deployHdiHdbcds": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.hdi.hdbcds.js",
"deployHdi": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.hdi.hdbcds.js",
"deployHdbcds": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.hdi.hdbcds.js",
"deployGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.git-diffs.js",
"deployHdbcdsGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.git-diffs.js",
"deployHdiGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.git-diffs.js",
"gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
"coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
"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",
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && 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",
"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",
"tslint": "tsc --pretty -p .",

@@ -64,4 +68,4 @@ "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",

"engines": {
"node": ">=16"
"node": ">=18"
}
}

@@ -17,4 +17,6 @@ {

"type-missing-enum-value",
"type-unexpected-foreign-keys",
"type-unexpected-on-condition",
"wildcard-excluding-one"
]
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc