@sap/cds-compiler 6.9.1 → 6.9.3
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 +20 -0
- package/lib/api/main.js +7 -2
- package/lib/api/options.js +1 -0
- package/lib/base/messages.js +23 -2
- package/lib/base/trace.js +74 -20
- package/lib/main.d.ts +12 -0
- package/lib/main.js +18 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,26 @@ we might not list every change in its behavior here.
|
|
|
13
13
|
Productive code should never require a `beta` flag to be set, and
|
|
14
14
|
might use a deprecated flag only for a limited period of time.
|
|
15
15
|
|
|
16
|
+
## Version 6.9.3 - 2026-06-17
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
- **api:** New compiler option `noErrorForUnknownAnnotateTarget` downgrades errors to warnings
|
|
21
|
+
for `annotate` statements with security-relevant annotations (`@restrict`, `@requires`, `@ams`)
|
|
22
|
+
whose target does not exist.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Version 6.9.2 - 2026-05-08
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
- **api:** when the environment variable `CDSC_TRACE_API` is set,
|
|
31
|
+
the compiler writes a trace for calls of API functions;
|
|
32
|
+
it now has more information, and also traces the exit of the API function.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
16
36
|
## Version 6.9.1 - 2026-05-05
|
|
17
37
|
|
|
18
38
|
### Bug Fixes
|
package/lib/api/main.js
CHANGED
|
@@ -1169,7 +1169,7 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
1169
1169
|
* @returns {any} What ever the processor returns
|
|
1170
1170
|
*/
|
|
1171
1171
|
function api( csn, options = {}, ...args ) {
|
|
1172
|
-
trace.
|
|
1172
|
+
trace.call(_name, options, csn);
|
|
1173
1173
|
const originalMessageLength = options.messages?.length;
|
|
1174
1174
|
try {
|
|
1175
1175
|
const messageFunctions = messages.makeMessageFunction(csn, options, _name);
|
|
@@ -1184,6 +1184,7 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
1184
1184
|
timetrace.timetrace.start(_name);
|
|
1185
1185
|
const result = processor( csn, options, messageFunctions, ...args );
|
|
1186
1186
|
timetrace.timetrace.stop(_name);
|
|
1187
|
+
trace.exit(_name, result);
|
|
1187
1188
|
return result;
|
|
1188
1189
|
}
|
|
1189
1190
|
catch (err) {
|
|
@@ -1200,6 +1201,7 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
1200
1201
|
if (originalMessageLength !== undefined)
|
|
1201
1202
|
options.messages.length = originalMessageLength;
|
|
1202
1203
|
|
|
1204
|
+
trace.log( 'recompile CSN and retry backend' );
|
|
1203
1205
|
const messageFunctions = messages.makeMessageFunction( csn, options, _name );
|
|
1204
1206
|
const recompileMsg = messageFunctions.info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {},
|
|
1205
1207
|
'CSN input had to be recompiled' );
|
|
@@ -1211,7 +1213,10 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
1211
1213
|
const xsn = compiler.recompileX(csn, options);
|
|
1212
1214
|
const recompiledCsn = toCsn.compactModel(xsn);
|
|
1213
1215
|
messageFunctions.setModel(recompiledCsn);
|
|
1214
|
-
|
|
1216
|
+
const result = processor( recompiledCsn, options, messageFunctions, ...args );
|
|
1217
|
+
timetrace.timetrace.stop(_name);
|
|
1218
|
+
trace.exit(_name, result);
|
|
1219
|
+
return result;
|
|
1215
1220
|
}
|
|
1216
1221
|
}
|
|
1217
1222
|
}
|
package/lib/api/options.js
CHANGED
package/lib/base/messages.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
const _messageIdsWithExplanation = require('../../share/messages/message-explanations.json').messages;
|
|
17
17
|
const { analyseCsnPath, traverseQuery } = require('../base/csnRefs');
|
|
18
18
|
const { CompilerAssertion } = require('./error');
|
|
19
|
+
const trace = require('./trace');
|
|
19
20
|
const { getArtifactName } = require('../compiler/base');
|
|
20
21
|
const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
21
22
|
const meta = require('./meta');
|
|
@@ -219,6 +220,17 @@ const severitySpecs = {
|
|
|
219
220
|
debug: { name: 'Debug', level: 3 },
|
|
220
221
|
};
|
|
221
222
|
|
|
223
|
+
// Message IDs raised for security-relevant annotate statements with non-existing targets.
|
|
224
|
+
// Downgraded to warning when noErrorForUnknownAnnotateTarget option is set.
|
|
225
|
+
const securityAnnotateTargetIds = new Set([
|
|
226
|
+
'ext-undefined-art-sec',
|
|
227
|
+
'ext-undefined-def-sec',
|
|
228
|
+
'ext-undefined-element-sec',
|
|
229
|
+
'ext-undefined-action-sec',
|
|
230
|
+
'ext-undefined-param-sec',
|
|
231
|
+
'ext-unexpected-returns-sec',
|
|
232
|
+
]);
|
|
233
|
+
|
|
222
234
|
/**
|
|
223
235
|
* Get the reclassified severity of the given message using:
|
|
224
236
|
*
|
|
@@ -258,6 +270,11 @@ function reclassifiedSeverity( msg, options, moduleName ) {
|
|
|
258
270
|
}
|
|
259
271
|
}
|
|
260
272
|
|
|
273
|
+
if (options.noErrorForUnknownAnnotateTarget &&
|
|
274
|
+
severity === 'Error' &&
|
|
275
|
+
securityAnnotateTargetIds.has(msg.messageId))
|
|
276
|
+
severity = 'Warning';
|
|
277
|
+
|
|
261
278
|
if (!options.severities)
|
|
262
279
|
return severity;
|
|
263
280
|
|
|
@@ -567,8 +584,10 @@ function makeMessageFunction( model, options, _moduleName = null ) {
|
|
|
567
584
|
}
|
|
568
585
|
|
|
569
586
|
function throwWithError() {
|
|
570
|
-
if (hasNewError)
|
|
587
|
+
if (hasNewError) {
|
|
588
|
+
trace.log( `stop compilation with ${ messages.length } messages` );
|
|
571
589
|
throw new CompilationError(messages, options.attachValidNames && model);
|
|
590
|
+
}
|
|
572
591
|
}
|
|
573
592
|
|
|
574
593
|
/**
|
|
@@ -583,8 +602,10 @@ function makeMessageFunction( model, options, _moduleName = null ) {
|
|
|
583
602
|
if (!messages || !messages.length)
|
|
584
603
|
return;
|
|
585
604
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
586
|
-
if (hasError( messages, moduleName, options ))
|
|
605
|
+
if (hasError( messages, moduleName, options )) {
|
|
606
|
+
trace.log( `stop compilation with ${ messages.length } messages` );
|
|
587
607
|
throw new CompilationError(messages, options.attachValidNames && model);
|
|
608
|
+
}
|
|
588
609
|
}
|
|
589
610
|
|
|
590
611
|
/**
|
package/lib/base/trace.js
CHANGED
|
@@ -5,33 +5,87 @@ const shouldTraceApi = process?.env?.CDSC_TRACE_API;
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Placeholder for disabled tracing (no-op).
|
|
8
|
-
*
|
|
9
|
-
* @param {string} apiName API name
|
|
10
|
-
* @param {object} options Options passed to the API.
|
|
11
|
-
* @param {...any} [args] Arguments to be logged to stderr
|
|
12
8
|
*/
|
|
13
|
-
|
|
14
|
-
function noOp( apiName, options, ...args ) {
|
|
9
|
+
function noOp() {
|
|
15
10
|
// no-op
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
/**
|
|
19
|
-
* Print
|
|
20
|
-
*
|
|
21
|
-
* @param {string} apiName API name
|
|
22
|
-
* @param {object} options Options passed to the API.
|
|
23
|
-
* @param {...any} [args] Arguments to be logged to stderr
|
|
14
|
+
* Print trace info to stderr when calling an API function
|
|
24
15
|
*/
|
|
25
|
-
function
|
|
26
|
-
const optStr = typeof options === 'object' ? JSON.stringify(options, null, 2) : options;
|
|
27
|
-
const argsStr = args.map(val => JSON.stringify(val)).join(', ');
|
|
28
|
-
const rest = args.length > 0 ? ` | ${ argsStr }` : '';
|
|
29
|
-
// Local require: Only load on-demand, not when tracing is disabled.
|
|
16
|
+
function call( apiName, options, csn, files ) {
|
|
30
17
|
const { version } = require('../../package.json');
|
|
18
|
+
const now = (new Date( Date.now() )).toISOString();
|
|
19
|
+
const args = (files || csn)
|
|
20
|
+
? `${ optionsString( options ) } on ${ filesInfo( files ) || csnInfo( csn ) }`
|
|
21
|
+
: optionsString( options );
|
|
31
22
|
// eslint-disable-next-line no-console
|
|
32
|
-
console.error(
|
|
23
|
+
console.error( 'CDSC_TRACE_API: at %s, call %s() of v%s with options %s',
|
|
24
|
+
now, apiName, version, args );
|
|
33
25
|
}
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Print trace info to stderr when exiting an API function
|
|
29
|
+
*/
|
|
30
|
+
function exit( apiName, result ) {
|
|
31
|
+
const now = (new Date( Date.now() )).toISOString();
|
|
32
|
+
const info = (result?.definitions || result?.extensions)
|
|
33
|
+
? csnInfo( result )
|
|
34
|
+
: `a result of type ${ typeof result }`;
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.error( 'CDSC_TRACE_API: at %s, exit %s() and return %s',
|
|
37
|
+
now, apiName, info );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Print trace info to stderr for miscellaneous use cases
|
|
42
|
+
*/
|
|
43
|
+
function log( info ) {
|
|
44
|
+
const now = (new Date( Date.now() )).toISOString();
|
|
45
|
+
// eslint-disable-next-line no-console
|
|
46
|
+
console.error( 'CDSC_TRACE_API: at %s, %s', now, info );
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function optionsString( obj ) {
|
|
50
|
+
if (!obj || typeof obj !== 'object')
|
|
51
|
+
return obj.toString();
|
|
52
|
+
try {
|
|
53
|
+
if (Array.isArray( obj.messages ) && obj.messages.length) {
|
|
54
|
+
const messages = {};
|
|
55
|
+
for (const msg of obj.messages)
|
|
56
|
+
messages[msg.severity] = (messages[msg.severity] || 0) + 1;
|
|
57
|
+
obj = { ...obj, messages };
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify( obj, null, 2 );
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return err.toString();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function filesInfo( files ) {
|
|
67
|
+
if (!files)
|
|
68
|
+
return files;
|
|
69
|
+
if (!Array.isArray( files ))
|
|
70
|
+
files = Object.keys( files );
|
|
71
|
+
return files.length ? [ 'files', ...files ].join( '\n ') : 'no files';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function csnInfo( csn ) {
|
|
75
|
+
if (!csn || typeof csn !== 'object' || Array.isArray( csn ))
|
|
76
|
+
return `some value of type ${ typeof csn }`;
|
|
77
|
+
try {
|
|
78
|
+
JSON.stringify( csn );
|
|
79
|
+
const defs = csn.definitions ? Object.keys( csn.definitions ).length : 'no';
|
|
80
|
+
const exts = csn.extensions ? csn.extensions.length : 'no';
|
|
81
|
+
const flavor = csn.meta?.flavor || csn.meta?.compilerCsnFlavor || 'unknown';
|
|
82
|
+
return `a CSN of flavor '${ flavor }' with ${ defs } definitions and ${ exts } extensions`;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return `a CORRUPTED CSN (${ err.toString() })`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = (shouldTraceApi)
|
|
90
|
+
? { call, exit, log }
|
|
91
|
+
: { call: noOp, exit: noOp, log: noOp };
|
package/lib/main.d.ts
CHANGED
|
@@ -32,6 +32,18 @@ declare namespace compiler {
|
|
|
32
32
|
* during compilation otherwise.
|
|
33
33
|
*/
|
|
34
34
|
severities?: { [messageId: string]: MessageSeverity}
|
|
35
|
+
/**
|
|
36
|
+
* Downgrade the errors raised for `annotate` statements containing a
|
|
37
|
+
* security-relevant annotation (`@restrict`, `@requires`, `@ams`) but
|
|
38
|
+
* whose target does not exist. Intended as a long-lived migration
|
|
39
|
+
* switch for projects that cannot fix all such statements at once.
|
|
40
|
+
*
|
|
41
|
+
* Explicit per-id entries in `severities` still take precedence over
|
|
42
|
+
* this option.
|
|
43
|
+
*
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
noErrorForUnknownAnnotateTarget?: boolean
|
|
35
47
|
/**
|
|
36
48
|
* Dictionary of beta flag names. This option allows fine-grained control
|
|
37
49
|
* over which beta features should be enabled.
|
package/lib/main.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const lazyload = require('./utils/lazyload')( module );
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const trace = require('./base/trace');
|
|
20
20
|
|
|
21
21
|
const snapi = lazyload('./api/main');
|
|
22
22
|
const csnUtils = lazyload('./model/csnUtils');
|
|
@@ -40,6 +40,7 @@ const meta = lazyload('./base/meta');
|
|
|
40
40
|
const toCsn = lazyload('./json/to-csn');
|
|
41
41
|
|
|
42
42
|
function parseCdl( cdlSource, filename, options = {} ) {
|
|
43
|
+
trace.call( 'parse.cdl', options );
|
|
43
44
|
options = Object.assign( {}, options, { parseCdl: true } );
|
|
44
45
|
const sources = Object.create(null);
|
|
45
46
|
/** @type {XSN.Model} */
|
|
@@ -56,7 +57,7 @@ function parseCdl( cdlSource, filename, options = {} ) {
|
|
|
56
57
|
define( model );
|
|
57
58
|
finalizeParseCdl( model );
|
|
58
59
|
messageFunctions.throwWithError();
|
|
59
|
-
return
|
|
60
|
+
return compactAndTrace( model, 'parse.cdl' );
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function parseCql( cdlSource, filename = '<query>.cds', options = {} ) {
|
|
@@ -75,6 +76,12 @@ function parseExpr( cdlSource, filename = '<expr>.cds', options = {} ) {
|
|
|
75
76
|
return toCsn.compactExpr( xsn );
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
function compactAndTrace( xsn, apiName = 'compile' ) {
|
|
80
|
+
const csn = toCsn.compactModel( xsn );
|
|
81
|
+
trace.exit( apiName, csn );
|
|
82
|
+
return csn;
|
|
83
|
+
}
|
|
84
|
+
|
|
78
85
|
// FIXME: The implementation of those functions that delegate to 'backends'
|
|
79
86
|
// should probably move here
|
|
80
87
|
// ATTENTION: Keep in sync with main.d.ts!
|
|
@@ -82,16 +89,19 @@ module.exports = {
|
|
|
82
89
|
// Compiler
|
|
83
90
|
version: () => meta.version(),
|
|
84
91
|
compile: (filenames, dir, options, fileCache) => { // main function
|
|
85
|
-
|
|
86
|
-
return compiler.compileX(filenames, dir, options, fileCache)
|
|
92
|
+
trace.call( 'compile', options, null, filenames );
|
|
93
|
+
return compiler.compileX( filenames, dir, options, fileCache )
|
|
94
|
+
.then( compactAndTrace );
|
|
87
95
|
},
|
|
88
96
|
compileSync: (filenames, dir, options, fileCache) => { // main function
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
trace.call( 'compileSync', options, null, filenames );
|
|
98
|
+
const xsn = compiler.compileSyncX( filenames, dir, options, fileCache );
|
|
99
|
+
return compactAndTrace( xsn, 'compileSync' );
|
|
91
100
|
},
|
|
92
101
|
compileSources: (sourcesDict, options) => { // main function
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
trace.call( 'compileSources', options, null, sourcesDict );
|
|
103
|
+
const xsn = compiler.compileSourcesX( sourcesDict, options );
|
|
104
|
+
return compactAndTrace( xsn, 'compileSources' );
|
|
95
105
|
},
|
|
96
106
|
compactModel: csn => csn, // for easy v2 migration
|
|
97
107
|
get CompilationError() {
|