@sap/cds-compiler 6.0.12 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/bin/cdsc.js +1 -1
- package/lib/api/options.js +2 -0
- package/lib/base/messages.js +1 -1
- package/lib/base/node-helpers.js +10 -2
- package/lib/checks/assocOutsideService.js +3 -1
- package/lib/compiler/index.js +10 -1
- package/lib/compiler/shared.js +0 -1
- package/lib/edm/annotations/genericTranslation.js +9 -0
- package/lib/gen/BaseParser.js +10 -10
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +7 -7
- package/lib/gen/Dictionary.json +0 -7
- package/lib/json/to-csn.js +8 -4
- package/lib/main.d.ts +10 -2
- package/lib/optionProcessor.js +6 -1
- package/lib/transform/db/rewriteCalculatedElements.js +3 -1
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/draft/odata.js +14 -4
- package/lib/transform/transformUtils.js +2 -2
- package/lib/utils/file.js +5 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,31 @@ Note: `beta` fixes, changes and features are usually not listed in this ChangeLo
|
|
|
8
8
|
but in [doc/CHANGELOG_BETA.md](doc/CHANGELOG_BETA.md).
|
|
9
9
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
10
10
|
|
|
11
|
+
## Version 6.1.0 - 2025-06-27
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- for.odata:
|
|
16
|
+
+ Introduce a new option `addAnnotationAddressViaNavigationPath` to annotate services
|
|
17
|
+
containing draft-enabled entities with `@Common.AddressViaNavigationPath`.
|
|
18
|
+
+ Introduce a new option `draftMessages` that enhances the draft generation logic.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Update OData vocabularies: Capabilities, Common
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- compiler: The ternary condition operator `…?…:…` is now right-associative as usual
|
|
27
|
+
(in v5, chaining it like in `…?…:…?…:…` was not possible without parentheses).
|
|
28
|
+
|
|
29
|
+
## Version 6.0.14 - 2025-06-18
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- to.sql: Fix error when calculated element refers to a localized element.
|
|
34
|
+
- to.edm(x): Correctly handle `PropertyPath` in a collection when using expressions as annotation values
|
|
35
|
+
|
|
11
36
|
## Version 6.0.12 - 2025-06-06
|
|
12
37
|
|
|
13
38
|
### Changed
|
package/bin/cdsc.js
CHANGED
|
@@ -299,7 +299,7 @@ function displayUsage( error, helpText, code ) {
|
|
|
299
299
|
*/
|
|
300
300
|
async function createTemporaryFileFromStdin() {
|
|
301
301
|
const contents = await readStream(process.stdin);
|
|
302
|
-
const file = tmpFilePath('cds-compiler-stdin
|
|
302
|
+
const file = tmpFilePath('cds-compiler-stdin.cds');
|
|
303
303
|
await fs.promises.writeFile(file, contents);
|
|
304
304
|
return file;
|
|
305
305
|
}
|
package/lib/api/options.js
CHANGED
|
@@ -36,6 +36,7 @@ const publicOptionsNewAPI = [
|
|
|
36
36
|
'booleanEquality',
|
|
37
37
|
'dollarNowAsTimestamp',
|
|
38
38
|
// ODATA
|
|
39
|
+
'addAnnotationAddressViaNavigationPath',
|
|
39
40
|
'odataOpenapiHints',
|
|
40
41
|
'edm4OpenAPI',
|
|
41
42
|
'odataVersion',
|
|
@@ -48,6 +49,7 @@ const publicOptionsNewAPI = [
|
|
|
48
49
|
'odataV2PartialConstr',
|
|
49
50
|
'odataVocabularies',
|
|
50
51
|
'odataNoCreator',
|
|
52
|
+
'draftMessages',
|
|
51
53
|
'service',
|
|
52
54
|
'serviceNames',
|
|
53
55
|
// to.cdl
|
package/lib/base/messages.js
CHANGED
|
@@ -193,7 +193,7 @@ class CompileMessage {
|
|
|
193
193
|
this.validNames = null;
|
|
194
194
|
this.home = home; // semantic location, e.g. 'entity:"E"/element:"x"'
|
|
195
195
|
this.severity = severity;
|
|
196
|
-
|
|
196
|
+
this.messageId = id;
|
|
197
197
|
Object.defineProperty( this, '$module', { value: moduleName, configurable: true } );
|
|
198
198
|
// Uncomment when running TypeScript linter
|
|
199
199
|
// this.messageId = id;
|
package/lib/base/node-helpers.js
CHANGED
|
@@ -19,9 +19,17 @@ class PromiseAllError extends Error {
|
|
|
19
19
|
*
|
|
20
20
|
* This function only works as intended if no promise in `promises` fulfill
|
|
21
21
|
* with a value which is an instance of Error.
|
|
22
|
+
*
|
|
23
|
+
* @param {Promise[]} promises
|
|
24
|
+
* @param {(e: Error) => boolean} shouldImmediatelyReject
|
|
25
|
+
* Determine on a per-promise basis whether we should immediately abort all promises.
|
|
22
26
|
*/
|
|
23
|
-
function promiseAllDoNotRejectImmediately( promises ) {
|
|
24
|
-
return Promise.all( promises.map( p => p.catch(e =>
|
|
27
|
+
function promiseAllDoNotRejectImmediately( promises, shouldImmediatelyReject = _error => false ) {
|
|
28
|
+
return Promise.all( promises.map( p => p.catch((e) => {
|
|
29
|
+
if (shouldImmediatelyReject(e))
|
|
30
|
+
throw e;
|
|
31
|
+
return e;
|
|
32
|
+
}) ) )
|
|
25
33
|
.then( values => (values.some(e => e instanceof Error)
|
|
26
34
|
? Promise.reject( new PromiseAllError(
|
|
27
35
|
values, 'At least one promise has been rejected'
|
|
@@ -21,7 +21,9 @@ function assertNoAssocUsageOutsideOfService( parent, prop, ref, path, grandparen
|
|
|
21
21
|
return;
|
|
22
22
|
|
|
23
23
|
const { _links } = parent;
|
|
24
|
-
|
|
24
|
+
// session variables can't have assoc steps, _links of 1 can't have assoc steps
|
|
25
|
+
// TODO: (typeof parentProp === 'number' && path[path.length - 2] === 'on') - ignore on-conditions, as they are cut off anyway
|
|
26
|
+
if (parent.$scope === '$magic' || _links?.length <= 1 )
|
|
25
27
|
return;
|
|
26
28
|
|
|
27
29
|
for (let i = 0; i < _links.length - 1; i++) {
|
package/lib/compiler/index.js
CHANGED
|
@@ -148,6 +148,8 @@ function parserForFile( source, ext, options ) {
|
|
|
148
148
|
// - { realname: fs.realpath(filename) }: if filename is not canonicalized
|
|
149
149
|
//
|
|
150
150
|
function compileX( filenames, dir = '', options = {}, fileCache = Object.create( null ) ) {
|
|
151
|
+
options.abortSignal?.throwIfAborted();
|
|
152
|
+
|
|
151
153
|
// A non-proper dictionary (i.e. with prototype) is safe if the keys are
|
|
152
154
|
// absolute file names - they start with `/` or `\` or similar
|
|
153
155
|
// if (Object.getPrototypeOf( fileCache ))
|
|
@@ -163,7 +165,10 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
163
165
|
input = processedInput;
|
|
164
166
|
model.sources = input.sources;
|
|
165
167
|
} )
|
|
166
|
-
.then( () => promiseAllDoNotRejectImmediately(
|
|
168
|
+
.then( () => promiseAllDoNotRejectImmediately(
|
|
169
|
+
input.files.map( readAndParse ),
|
|
170
|
+
e => e?.name === 'AbortError' // reject immediately if user wants to abort
|
|
171
|
+
))
|
|
167
172
|
.then( testInvocation, (reason) => {
|
|
168
173
|
// do not reject with PromiseAllError, use InvocationError:
|
|
169
174
|
const errs = reason.valuesOrErrors?.filter( e => e instanceof Error ) || [ reason ];
|
|
@@ -176,6 +181,7 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
176
181
|
all = all.then( readDependencies );
|
|
177
182
|
|
|
178
183
|
return all.then( () => {
|
|
184
|
+
options.abortSignal?.throwIfAborted();
|
|
179
185
|
moduleLayers.setLayers( input.sources );
|
|
180
186
|
return compileDoX( model );
|
|
181
187
|
} );
|
|
@@ -193,6 +199,9 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
193
199
|
sources[filename] = { location: new Location( rel ) };
|
|
194
200
|
|
|
195
201
|
const source = await cdsFs( fileCache, options.traceFs ).readFileAsync( filename, 'utf8' );
|
|
202
|
+
// before running our compute-heavy parsing, check if user aborted
|
|
203
|
+
options.abortSignal?.throwIfAborted();
|
|
204
|
+
|
|
196
205
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
197
206
|
sources[filename] = ast;
|
|
198
207
|
ast.location = new Location( rel );
|
package/lib/compiler/shared.js
CHANGED
|
@@ -527,7 +527,6 @@ function fns( model ) {
|
|
|
527
527
|
if (traverseTypedExpr( args[0], exprCtx, user, null, callback ) === traverseExpr.STOP)
|
|
528
528
|
return null;
|
|
529
529
|
return args.slice( 1 );
|
|
530
|
-
// TODO: adopt if we extend this to ?:?:…
|
|
531
530
|
}
|
|
532
531
|
|
|
533
532
|
function traverseCaseWhen( args, exprCtx, user, type, callback ) {
|
|
@@ -1442,6 +1442,15 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1442
1442
|
{ anno: msg.anno(), '#': 'enuminCollection' });
|
|
1443
1443
|
}
|
|
1444
1444
|
else if (value.$edmJson) {
|
|
1445
|
+
if (dTypeName === 'Edm.PropertyPath') {
|
|
1446
|
+
const dPropType = dTypeName.replace(/^Edm\./, '');
|
|
1447
|
+
if (!value.$edmJson[`$${ dPropType }`]) {
|
|
1448
|
+
// Needs to adapt the property type as per the dictionary, because expression refs are always
|
|
1449
|
+
// generated as kind $Path earlier on (edmJson::transform.ref)
|
|
1450
|
+
value.$edmJson[`$${ dPropType }`] = value.$edmJson.$Path;
|
|
1451
|
+
delete value.$edmJson.$Path;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1445
1454
|
newCollection.append(handleEdmJson(value.$edmJson, msg));
|
|
1446
1455
|
}
|
|
1447
1456
|
else {
|
package/lib/gen/BaseParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Base class for generated parser, for redepage v0.2.
|
|
1
|
+
// Base class for generated parser, for redepage v0.2.5
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -53,7 +53,7 @@ class BaseParser {
|
|
|
53
53
|
s: this.s,
|
|
54
54
|
stack: this.stack,
|
|
55
55
|
dynamic_: this.dynamic_,
|
|
56
|
-
prec_: this.prec_
|
|
56
|
+
prec_: this.prec_ // TODO: necessary?
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -433,13 +433,13 @@ class BaseParser {
|
|
|
433
433
|
this.s = cmd[1];
|
|
434
434
|
if (cmd[0] !== (lk1 ? 'ck' : 'ci')) { // make the std case fast
|
|
435
435
|
// TODO: also not with lean condition
|
|
436
|
-
let match1 = this._pred_next( 'Id', lk1, 'P' ); //
|
|
436
|
+
let match1 = this._pred_next( 'Id', lk1, 'P' ); // first step of `K`/`I` prediction
|
|
437
437
|
if (!match1) {
|
|
438
438
|
if (lk1 || match1 === false) // assert for correct code generation
|
|
439
439
|
throw Error( `Cannot match first prediction token in rule at state ${ saved.s }` );
|
|
440
|
-
if (match1 == null) {
|
|
440
|
+
if (match1 == null) { // TODO: just return true, rule exit prediction will do it
|
|
441
441
|
this._traceSubPush( 0 ); // TODO: make _pred_next push this
|
|
442
|
-
match1 = this._matchesInFollow( 'Id', lk1, 'I' );
|
|
442
|
+
match1 = this._matchesInFollow( 'Id', lk1, 'I' );
|
|
443
443
|
}
|
|
444
444
|
else {
|
|
445
445
|
this._traceSubPush( false );
|
|
@@ -452,10 +452,12 @@ class BaseParser {
|
|
|
452
452
|
|
|
453
453
|
this._traceSubPush( '' ); // between the two tokens
|
|
454
454
|
++this.tokenIdx; // for user lookahead fns and conditions
|
|
455
|
-
|
|
455
|
+
const mode = lk1 ? 'K' : 'I';
|
|
456
|
+
let match2 = this._pred_next( lt2, lk2, mode );
|
|
456
457
|
if (match2 == null) {
|
|
457
458
|
this._traceSubPush( 0 ); // TODO: make _pred_next push this
|
|
458
|
-
match2 = !!this._matchesInFollow( lt2, lk2,
|
|
459
|
+
match2 = !!this._matchesInFollow( lt2, lk2, mode );
|
|
460
|
+
// TODO: we might use mode 'E' in _matchesInFollow (depends on caching)
|
|
459
461
|
}
|
|
460
462
|
else {
|
|
461
463
|
this._traceSubPush( match2 );
|
|
@@ -485,8 +487,6 @@ class BaseParser {
|
|
|
485
487
|
* condition is listed in `this.leanConditions`.
|
|
486
488
|
*/
|
|
487
489
|
_pred_next( type, keyword, mode ) { // mode = P | K | I | E | R | M
|
|
488
|
-
// TODO mode: really distinguish between K | I | E | R ?
|
|
489
|
-
// Probably not: would not work with caching? → P, P -> F
|
|
490
490
|
const properCall = (mode === 'P');
|
|
491
491
|
const lean = (mode !== 'M'); // TODO: extra method with conditions ?
|
|
492
492
|
// TODO: if false, use condition in this.leanConditions
|
|
@@ -609,6 +609,7 @@ class BaseParser {
|
|
|
609
609
|
this._traceSubPush( match == null ? 0 : match === (mode !== 'R') );
|
|
610
610
|
// successfully matching a keyword in giR() means unsuccessful match as
|
|
611
611
|
// reserved identifer
|
|
612
|
+
// TODO: this.stack ?
|
|
612
613
|
}
|
|
613
614
|
this.dynamic_ = dynamic_;
|
|
614
615
|
this.s = savedState;
|
|
@@ -773,7 +774,6 @@ class BaseParser {
|
|
|
773
774
|
token.keyword = keyword;
|
|
774
775
|
Object.assign( this, saved );
|
|
775
776
|
this.trace = trace;
|
|
776
|
-
// TODO: also trace M(…) collection, extra line for each token, with condition
|
|
777
777
|
return expecting;
|
|
778
778
|
}
|
|
779
779
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7cfe4871a952fbc999cd1e8b06300f7f
|
package/lib/gen/CdlParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Parser generated by redepage v0.2.
|
|
1
|
+
// Parser generated by redepage v0.2.5
|
|
2
2
|
'use strict;'
|
|
3
3
|
const { XsnSource, XsnArtifact, XsnName } = require( '../compiler/xsn-model' )
|
|
4
4
|
const AstBuildingParser = require('../parsers/AstBuildingParser')
|
|
@@ -1164,7 +1164,7 @@ Id:[656,673],
|
|
|
1164
1164
|
'||':['c',660,,'precLeft_',20],
|
|
1165
1165
|
and:['ck',660,,'precLeft_',4],
|
|
1166
1166
|
or:['ck',660,,'precLeft_',2],
|
|
1167
|
-
'?':['c',657,,'
|
|
1167
|
+
'?':['c',657,,'precRight_',0],
|
|
1168
1168
|
'<':['c',659,,'precNone_',10],'=':'<','>':'<','!=':'<','<=':'<','<>':'<','>=':'<',
|
|
1169
1169
|
'==':['c',660,,'precNone_',10],
|
|
1170
1170
|
is:['ck',661,,'precNone_',10],
|
|
@@ -1786,7 +1786,7 @@ case 85:switch(this.lk()){
|
|
|
1786
1786
|
case'limit':case'order':this.orderByLimitOffset({query},86);continue
|
|
1787
1787
|
default:this.s=86;continue
|
|
1788
1788
|
}
|
|
1789
|
-
case 86:this.s=87;{this.afterBrace('normal')
|
|
1789
|
+
case 86:this.s=87;{this.afterBrace('normal')}continue;
|
|
1790
1790
|
case 87:switch(this.lk()){
|
|
1791
1791
|
case'actions':this.actionsBlock({art:$.art},0);continue
|
|
1792
1792
|
default:this.gr([';']);continue
|
|
@@ -3087,7 +3087,7 @@ return this.exit_()
|
|
|
3087
3087
|
}
|
|
3088
3088
|
targetCardinality($,$next,$startState){
|
|
3089
3089
|
this.rule_($startState??436,$next)
|
|
3090
|
-
|
|
3090
|
+
if (!$.atAlt) $.card.sourceMax = $.card.targetMax
|
|
3091
3091
|
for(;;)switch(this.s){
|
|
3092
3092
|
case 436:switch(this.l()){
|
|
3093
3093
|
case'*':if(this.c(0)){ $.card.targetMax = this.valueWithLocation(); }continue
|
|
@@ -3232,7 +3232,7 @@ case 473:switch(this.lk()){
|
|
|
3232
3232
|
case'excluding':this.excludingClause({query:$.query},480);continue
|
|
3233
3233
|
default:this.s=480;continue
|
|
3234
3234
|
}
|
|
3235
|
-
case 474:this.s=475;{this.inSelectItem('sqlStyle')
|
|
3235
|
+
case 474:this.s=475;{this.inSelectItem('sqlStyle')}continue;
|
|
3236
3236
|
case 475:switch(this.l()){
|
|
3237
3237
|
case'*':if(this.c(476)){ $.query.columns = [ this.valueWithLocation() ]; }continue
|
|
3238
3238
|
case'Id':case'#':case'(':case'+':case'-':case':':case'?':case'@':case'{':case'Number':case'String':case'QuotedLiteral':this.selectItemDef({columns:($.query.columns = [])},476);continue
|
|
@@ -3578,7 +3578,7 @@ case 570:switch(this.lk()){
|
|
|
3578
3578
|
case'virtual':if(this.lP(['Id','#','(','+','-',':','?','{','key','not','case','cast','null','true','false','Number','String','exists','QuotedLiteral'])&&this.gc(571,'modifierRestriction')&&this.ck(571)){ art.virtual = this.valueWithLocation( true ); }continue
|
|
3579
3579
|
default:this.s=571;continue
|
|
3580
3580
|
}
|
|
3581
|
-
case 571:this.s=572;{this.columnExpr('key')
|
|
3581
|
+
case 571:this.s=572;{this.columnExpr('key')}continue;
|
|
3582
3582
|
case 572:switch(this.lk()){
|
|
3583
3583
|
case'key':if(this.gc(573,'modifierRestriction')&&this.ck(573)){ art.key = this.valueWithLocation( true ); }continue
|
|
3584
3584
|
default:this.s=573;continue
|
|
@@ -3860,7 +3860,7 @@ case'+':case'-':if(this.gc(0,'precLeft_',22)&&this.c(660)){ $.expr = this.applyO
|
|
|
3860
3860
|
case'||':if(this.gc(0,'precLeft_',20)&&this.c(660)){ $.expr = this.applyOpToken( $.expr, 'nary' ); }continue
|
|
3861
3861
|
case'and':if(this.gc(0,'precLeft_',4)&&this.ck(660)){ $.expr = this.applyOpToken( $.expr, 'nary' ); }continue
|
|
3862
3862
|
case'or':if(this.gc(0,'precLeft_',2)&&this.ck(660)){ $.expr = this.applyOpToken( $.expr, 'nary' ); }continue
|
|
3863
|
-
case'?':if(this.gc(0,'
|
|
3863
|
+
case'?':if(this.gc(0,'precRight_',0)&&this.c(657)){ $.expr = this.applyOpToken( $.expr, '?:' ); }continue
|
|
3864
3864
|
case'<':case'=':case'>':case'!=':case'<=':case'<>':case'>=':if(this.gc(0,'precNone_',10)&&this.c(659)){ $.expr = this.applyOpToken( $.expr ); }continue
|
|
3865
3865
|
case'==':if(this.gc(0,'precNone_',10)&&this.c(660)){ $.expr = this.applyOpToken( $.expr ); }continue
|
|
3866
3866
|
case'is':if(this.gc(0,'precNone_',10)&&this.ck(661)){ $.expr = this.applyOpToken( $.expr ); }continue
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -3231,13 +3231,6 @@
|
|
|
3231
3231
|
"ValidationFunction": "Common.QualifiedName"
|
|
3232
3232
|
}
|
|
3233
3233
|
},
|
|
3234
|
-
"Common.DraftUserAccessType": {
|
|
3235
|
-
"$kind": "ComplexType",
|
|
3236
|
-
"Properties": {
|
|
3237
|
-
"UserAccessRole": "Edm.String",
|
|
3238
|
-
"UserID": "Edm.String"
|
|
3239
|
-
}
|
|
3240
|
-
},
|
|
3241
3234
|
"Common.EffectType": {
|
|
3242
3235
|
"$deprecated": true,
|
|
3243
3236
|
"$deprecationText": "All side effects are essentially value changes, differentiation not needed.",
|
package/lib/json/to-csn.js
CHANGED
|
@@ -1319,11 +1319,15 @@ function flattenInternalXpr( array, xprOp ) {
|
|
|
1319
1319
|
function ternaryOperator( node ) {
|
|
1320
1320
|
const rargs = [
|
|
1321
1321
|
'case',
|
|
1322
|
-
'when', exprInternal(node.args[0]),
|
|
1323
|
-
'then', exprInternal(node.args[2]),
|
|
1324
|
-
'else', exprInternal(node.args[4]),
|
|
1325
|
-
'end',
|
|
1322
|
+
'when', exprInternal( node.args[0] ),
|
|
1323
|
+
'then', exprInternal( node.args[2] ),
|
|
1326
1324
|
];
|
|
1325
|
+
let right = node.args[4];
|
|
1326
|
+
for (; right.op?.val === '?:' && !right.$parens?.length; right = right.args[4]) {
|
|
1327
|
+
rargs.push( 'when', exprInternal( right.args[0] ),
|
|
1328
|
+
'then', exprInternal( right.args[2] ) );
|
|
1329
|
+
}
|
|
1330
|
+
rargs.push( 'else', exprInternal( right ), 'end' );
|
|
1327
1331
|
|
|
1328
1332
|
if (node.$parens?.length)
|
|
1329
1333
|
return { xpr: flattenInternalXpr( rargs, 'xpr' ) };
|
package/lib/main.d.ts
CHANGED
|
@@ -158,6 +158,16 @@ declare namespace compiler {
|
|
|
158
158
|
* @since v4.2.0
|
|
159
159
|
*/
|
|
160
160
|
moduleLookupDirectories?: string[]
|
|
161
|
+
/**
|
|
162
|
+
* An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
|
|
163
|
+
* that can be used to abort the compilation.
|
|
164
|
+
* Used for any `async` task, i.e. at the moment for reading/parsing files.
|
|
165
|
+
*
|
|
166
|
+
* Note that this flag has no effect on _synchronous_ compilation functions.
|
|
167
|
+
*
|
|
168
|
+
* @since v6.1
|
|
169
|
+
*/
|
|
170
|
+
abortSignal?: AbortSignal
|
|
161
171
|
/**
|
|
162
172
|
* Option for {@link compileSources}. If set, all objects inside the
|
|
163
173
|
* provided sources dictionary are interpreted as XSN structures instead
|
|
@@ -1558,8 +1568,6 @@ declare namespace compiler {
|
|
|
1558
1568
|
constructor(location: Location, msg: string, severity?: MessageSeverity, id?: string | null, home?: string | null, moduleName?: string | null);
|
|
1559
1569
|
/**
|
|
1560
1570
|
* Optional ID of the message. Can be used to reclassify messages.
|
|
1561
|
-
*
|
|
1562
|
-
* @note This property is non-enumerable as message IDs are not finalized, yet.
|
|
1563
1571
|
*/
|
|
1564
1572
|
messageId?: string
|
|
1565
1573
|
|
package/lib/optionProcessor.js
CHANGED
|
@@ -263,6 +263,8 @@ optionProcessor.command('O, toOdata')
|
|
|
263
263
|
.option(' --odata-v2-partial-constr')
|
|
264
264
|
.option(' --odata-vocabularies <list>')
|
|
265
265
|
.option(' --odata-no-creator')
|
|
266
|
+
.option(' --draft-messages')
|
|
267
|
+
.option(' --add-annotation-AddressViaNavigationPath')
|
|
266
268
|
.option('-c, --csn')
|
|
267
269
|
.option('-f, --odata-format <format>', { valid: [ 'flat', 'structured' ] })
|
|
268
270
|
.option('-n, --sql-mapping <style>', { valid: [ 'plain', 'quoted', 'hdbcds' ], aliases: [ '--names' ] })
|
|
@@ -297,7 +299,10 @@ optionProcessor.command('O, toOdata')
|
|
|
297
299
|
--odata-vocabularies <list> JSON array of adhoc vocabulary definitions
|
|
298
300
|
{ prefix: { alias, ns, uri }, ... }
|
|
299
301
|
--odata-no-creator Omit creator identification in API
|
|
300
|
-
|
|
302
|
+
--draft-messages Add draft messages as part of the draft creation
|
|
303
|
+
--add-annotation-AddressViaNavigationPath Add annotation "@Common.AddressViaNavigationPath" to the services
|
|
304
|
+
containing draft enabled entitties
|
|
305
|
+
-n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
|
|
301
306
|
the corresponding database name (see "--sql-mapping" for "toSql")
|
|
302
307
|
plain : (default) Names in uppercase and flattened with underscores
|
|
303
308
|
quoted : Names in original case as in CDL. Entity names with dots,
|
|
@@ -697,8 +697,10 @@ function removeDummyValueInEntity( artifact, path, options ) {
|
|
|
697
697
|
function dummifyInEntity( artifact, path ) {
|
|
698
698
|
applyTransformationsOnDictionary(artifact.elements, {
|
|
699
699
|
value: (parent, _prop, value) => {
|
|
700
|
-
if (!value.stored)
|
|
700
|
+
if (!value.stored) {
|
|
701
701
|
parent.value = { val: 'DUMMY' };
|
|
702
|
+
delete parent.localized;
|
|
703
|
+
}
|
|
702
704
|
},
|
|
703
705
|
}, {}, path);
|
|
704
706
|
}
|
|
@@ -232,7 +232,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
232
232
|
draftAdministrativeData.DraftAdministrativeData.notNull = true;
|
|
233
233
|
addElement(draftAdministrativeData, draftsArtifact, artifactName);
|
|
234
234
|
|
|
235
|
-
if (isBetaEnabled(options, 'draftMessages')) {
|
|
235
|
+
if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
|
|
236
236
|
const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
|
|
237
237
|
addElement(draftMessages, draftsArtifact, artifactName);
|
|
238
238
|
}
|
|
@@ -59,7 +59,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
59
59
|
const filterDict = Object.create(null);
|
|
60
60
|
|
|
61
61
|
// validate the 'DRAFT.DraftAdministrativeData_DraftMessage' type if already present in the model
|
|
62
|
-
if (isBetaEnabled(options, 'draftMessages')) {
|
|
62
|
+
if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
|
|
63
63
|
const draftAdminDataMessagesType = csn.definitions['DRAFT.DraftAdministrativeData_DraftMessage'];
|
|
64
64
|
if (draftAdminDataMessagesType && !isValidDraftAdminDataMessagesType(draftAdminDataMessagesType)) {
|
|
65
65
|
error(null, [ 'definitions', 'DRAFT.DraftAdministrativeData_DraftMessage' ], { name: 'DRAFT.DraftAdministrativeData_DraftMessage' },
|
|
@@ -180,7 +180,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
180
180
|
// ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
|
|
181
181
|
siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
|
|
182
182
|
|
|
183
|
-
if (isBetaEnabled(options, 'draftMessages')) {
|
|
183
|
+
if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
|
|
184
184
|
const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
|
|
185
185
|
addElement(draftMessages, artifact, artifactName);
|
|
186
186
|
|
|
@@ -189,8 +189,11 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
189
189
|
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', ['DraftMessages'] );
|
|
190
190
|
}
|
|
191
191
|
setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: ['DraftMessages'] });
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
setAnnotationAddressViaNavigationPath(artifactName, services);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (options.addAnnotationAddressViaNavigationPath) {
|
|
196
|
+
setAnnotationAddressViaNavigationPath(artifactName, services);
|
|
194
197
|
}
|
|
195
198
|
|
|
196
199
|
// Iterate elements
|
|
@@ -291,6 +294,13 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
291
294
|
}
|
|
292
295
|
})
|
|
293
296
|
}
|
|
297
|
+
|
|
298
|
+
// Set the @Common.AddressViaNavigationPath annotation to the service of
|
|
299
|
+
// the current artifact, if not set already
|
|
300
|
+
function setAnnotationAddressViaNavigationPath(artifactName, services) {
|
|
301
|
+
const service = csn.definitions[getServiceOfArtifact(artifactName, services)];
|
|
302
|
+
setAnnotation(service, '@Common.AddressViaNavigationPath', true);
|
|
303
|
+
}
|
|
294
304
|
}
|
|
295
305
|
|
|
296
306
|
module.exports = generateDrafts;
|
|
@@ -402,7 +402,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
402
402
|
if (!draftAdminDataEntity) {
|
|
403
403
|
draftAdminDataEntity = createAndAddDraftAdminDataEntity();
|
|
404
404
|
model.definitions['DRAFT.DraftAdministrativeData'] = draftAdminDataEntity;
|
|
405
|
-
if (isBetaEnabled(options, 'draftMessages')
|
|
405
|
+
if ((isBetaEnabled(options, 'draftMessages') || options.draftMessages)
|
|
406
406
|
&& options.transformation === 'odata'
|
|
407
407
|
&& !model.definitions['DRAFT.DraftAdministrativeData_DraftMessage']) {
|
|
408
408
|
model.definitions['DRAFT.DraftAdministrativeData_DraftMessage'] = createDraftAdminDataMessagesType();
|
|
@@ -475,7 +475,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
|
475
475
|
draftIsProcessedByMe.DraftIsProcessedByMe['@Common.Label'] = '{i18n>Draft_DraftIsProcessedByMe}';
|
|
476
476
|
addElement(draftIsProcessedByMe, artifact, artifactName);
|
|
477
477
|
|
|
478
|
-
if (isBetaEnabled(options, 'draftMessages')) {
|
|
478
|
+
if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
|
|
479
479
|
const messages = { DraftMessages: { } };
|
|
480
480
|
if (options.transformation === 'odata') {
|
|
481
481
|
messages.DraftMessages = { items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } };
|
package/lib/utils/file.js
CHANGED
|
@@ -32,18 +32,14 @@ function fileExtension( filename ) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Create a temporary file path using the system's temporary folder
|
|
36
|
-
* consisting of the given name/extension and a random string.
|
|
35
|
+
* Create a temporary file path using the system's temporary folder.
|
|
37
36
|
*
|
|
38
|
-
* @param {string}
|
|
39
|
-
* @param {string} extension
|
|
37
|
+
* @param {string} filename
|
|
40
38
|
* @returns {string}
|
|
41
39
|
*/
|
|
42
|
-
function tmpFilePath(
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const filename = `${ name }-${ id }.${ extension }`;
|
|
46
|
-
return path.join(os.tmpdir(), filename);
|
|
40
|
+
function tmpFilePath( filename ) {
|
|
41
|
+
const dir = fs.mkdtempSync(fs.realpathSync(os.tmpdir()) + path.sep);
|
|
42
|
+
return path.join(dir, filename);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
/**
|