@sap/cds-compiler 4.7.6 → 4.8.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 +37 -2
- package/bin/cdsc.js +15 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +5 -1
- package/lib/api/main.js +61 -23
- package/lib/api/options.js +40 -0
- package/lib/base/builtins.js +89 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +50 -33
- package/lib/base/messages.js +71 -16
- package/lib/base/model.js +0 -2
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +6 -22
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +61 -11
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +2 -2
- package/lib/compiler/index.js +9 -9
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +3 -0
- package/lib/compiler/resolve.js +6 -20
- package/lib/compiler/shared.js +1 -1
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +113 -55
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +3 -3
- package/lib/edm/edmInboundChecks.js +24 -5
- package/lib/edm/edmPreprocessor.js +46 -20
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +9 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1941 -1850
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +8 -7
- package/lib/json/to-csn.js +12 -7
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +9 -10
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/main.js +8 -1
- package/lib/model/cloneCsn.js +15 -6
- package/lib/model/csnRefs.js +141 -35
- package/lib/model/csnUtils.js +1 -4
- package/lib/model/enrichCsn.js +1 -1
- package/lib/modelCompare/compare.js +106 -92
- package/lib/optionProcessor.js +23 -1
- package/lib/render/toCdl.js +3 -2
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/main.js +6 -3
- package/lib/transform/effective/misc.js +18 -8
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +8 -7
- package/lib/transform/forRelationalDB.js +103 -112
- package/lib/transform/odata/flattening.js +82 -44
- package/lib/transform/odata/toFinalBaseType.js +9 -25
- package/lib/transform/odata/typesExposure.js +28 -15
- package/lib/transform/parseExpr.js +0 -3
- package/lib/transform/transformUtils.js +12 -8
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
package/lib/json/csnVersion.js
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
// (Note: the SQL name mapping mode is not reflected in the content of the csn, the version only
|
|
9
9
|
// signals which default name mapping a backend has to use)
|
|
10
10
|
// Historic versions:
|
|
11
|
-
// 0.0.1 : Used by HANA CDS for its CSN output
|
|
12
|
-
//
|
|
11
|
+
// 0.0.1 : Used by SAP HANA CDS for its CSN output
|
|
12
|
+
// (incomplete, not well defined, quite different from CDX ...)
|
|
13
|
+
// 0.0.2 : CDX in the initial versions with old-style CSN, default for SQL
|
|
14
|
+
// name mapping is 'quoted'
|
|
13
15
|
// 0.0.99 : Like 0.0.2, but with new-style CSN
|
|
14
16
|
// Versions that are currently produced by compiler:
|
|
15
17
|
// 0.1.0 : Like 0.0.2, default for SQL name mapping is 'plain'
|
|
@@ -21,15 +23,16 @@
|
|
|
21
23
|
const newCSNVersions = [ '0.1.99', '0.2', '0.2.0', '1.0', '2.0' ];
|
|
22
24
|
|
|
23
25
|
// Check if new-style CSN is requested, i.e. versions >= 0.1.99
|
|
24
|
-
function isNewCSN(csn, options) {
|
|
26
|
+
function isNewCSN( csn, options ) {
|
|
25
27
|
return !( options?.newCsn === false ||
|
|
26
28
|
(csn.version && !newCSNVersions.includes(csn.version.csn)) ||
|
|
27
29
|
(csn.$version && !newCSNVersions.includes(csn.$version)));
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
function checkCSNVersion(csn, options) {
|
|
32
|
+
function checkCSNVersion( csn, options ) {
|
|
31
33
|
if (!isNewCSN(csn, options)) {
|
|
32
34
|
// the new transformer works only with new CSN
|
|
35
|
+
// eslint-disable-next-line global-require
|
|
33
36
|
const { makeMessageFunction } = require('../base/messages');
|
|
34
37
|
const { error, throwWithAnyError } = makeMessageFunction(csn, options);
|
|
35
38
|
|
package/lib/json/from-csn.js
CHANGED
|
@@ -119,10 +119,11 @@
|
|
|
119
119
|
*/
|
|
120
120
|
|
|
121
121
|
const { dictAdd } = require('../base/dictionaries');
|
|
122
|
-
|
|
123
|
-
const { isAnnotationExpression
|
|
122
|
+
const { quotedLiteralPatterns } = require('../compiler/builtins');
|
|
123
|
+
const { isAnnotationExpression } = require('../base/builtins');
|
|
124
124
|
const { CompilerAssertion } = require('../base/error');
|
|
125
|
-
const {
|
|
125
|
+
const { Location } = require('../base/location');
|
|
126
|
+
const { XsnSource } = require('../compiler/xsn-model');
|
|
126
127
|
|
|
127
128
|
const $location = Symbol.for('cds.$location');
|
|
128
129
|
|
|
@@ -1997,7 +1998,7 @@ function replaceZeroValue( spec, msgVariant, newValue ) {
|
|
|
1997
1998
|
function location( enforceJsonPos ) {
|
|
1998
1999
|
return !enforceJsonPos && dollarLocations.length &&
|
|
1999
2000
|
dollarLocations[dollarLocations.length - 1] || {
|
|
2000
|
-
__proto__:
|
|
2001
|
+
__proto__: Location.prototype,
|
|
2001
2002
|
file: csnFilename,
|
|
2002
2003
|
line: virtualLine,
|
|
2003
2004
|
col: 0,
|
|
@@ -2010,7 +2011,7 @@ function pushLocation( obj ) {
|
|
|
2010
2011
|
if (loc === undefined)
|
|
2011
2012
|
return;
|
|
2012
2013
|
if (loc && typeof loc === 'object' && !Array.isArray( loc )) {
|
|
2013
|
-
dollarLocations.push( loc.line ? { __proto__:
|
|
2014
|
+
dollarLocations.push( loc.line ? { __proto__: Location.prototype, ...loc } : null );
|
|
2014
2015
|
return;
|
|
2015
2016
|
}
|
|
2016
2017
|
else if (!loc || typeof loc !== 'string') {
|
|
@@ -2028,7 +2029,7 @@ function pushLocation( obj ) {
|
|
|
2028
2029
|
const column = m[2] && Number( m[2] ) || 0;
|
|
2029
2030
|
const file = loc.substring( 0, m.index );
|
|
2030
2031
|
dollarLocations.push({
|
|
2031
|
-
__proto__:
|
|
2032
|
+
__proto__: Location.prototype, file, line, col: column,
|
|
2032
2033
|
} );
|
|
2033
2034
|
}
|
|
2034
2035
|
}
|
|
@@ -2123,7 +2124,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
|
|
|
2123
2124
|
}
|
|
2124
2125
|
column = end - eol + 1;
|
|
2125
2126
|
}
|
|
2126
|
-
const loc = new
|
|
2127
|
+
const loc = new Location(
|
|
2127
2128
|
filename,
|
|
2128
2129
|
line,
|
|
2129
2130
|
column
|
package/lib/json/to-csn.js
CHANGED
|
@@ -232,13 +232,15 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
232
232
|
if (using.length)
|
|
233
233
|
csn.requires = using;
|
|
234
234
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
if (!isBetaEnabled(options, 'v5preview')) {
|
|
236
|
+
// 'namespace' for complete model is 'namespace' of first source
|
|
237
|
+
// (not a really useful property at all, avoids XSN inspection by Umbrella)
|
|
238
|
+
for (const first in srcDict) {
|
|
239
|
+
const { namespace } = srcDict[first];
|
|
240
|
+
if (namespace?.path)
|
|
241
|
+
csn.namespace = pathName( namespace.path );
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
242
244
|
}
|
|
243
245
|
set( 'definitions', csn, model );
|
|
244
246
|
if (Object.keys(model.vocabularies || {}).length > 0)
|
|
@@ -1408,6 +1410,9 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1408
1410
|
setHidden( col, '$parens', parens.length );
|
|
1409
1411
|
addLocation( (parens ? parens[parens.length - 1] : elem.value.location), col );
|
|
1410
1412
|
}
|
|
1413
|
+
else if (elem.name && !elem.name.$inferred) {
|
|
1414
|
+
addLocation( elem.name.location, col );
|
|
1415
|
+
}
|
|
1411
1416
|
cols.push( extra( col, elem ) );
|
|
1412
1417
|
}
|
|
1413
1418
|
|
|
@@ -12,7 +12,7 @@ const antlr4 = require('antlr4');
|
|
|
12
12
|
|
|
13
13
|
const { CompileMessage } = require('../base/messages');
|
|
14
14
|
const errorStrategy = require('./errorStrategy');
|
|
15
|
-
const { XsnSource } = require('../compiler/
|
|
15
|
+
const { XsnSource } = require('../compiler/xsn-model');
|
|
16
16
|
|
|
17
17
|
const Parser = require('../gen/languageParser').default;
|
|
18
18
|
const Lexer = require('../gen/languageLexer').default;
|
|
@@ -14,14 +14,13 @@ const locUtils = require('../base/location');
|
|
|
14
14
|
const { parseDocComment } = require('./docCommentParser');
|
|
15
15
|
const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
|
|
16
16
|
const {
|
|
17
|
-
functionsWithoutParens,
|
|
18
17
|
specialFunctions,
|
|
19
18
|
quotedLiteralPatterns,
|
|
20
19
|
} = require('../compiler/builtins');
|
|
20
|
+
const { functionsWithoutParens } = require('../base/builtins');
|
|
21
|
+
const { Location } = require('../base/location');
|
|
21
22
|
const { pathName } = require('../compiler/utils');
|
|
22
|
-
const {
|
|
23
|
-
XsnArtifact, XsnName, CsnLocation, XsnSource,
|
|
24
|
-
} = require('../compiler/classes');
|
|
23
|
+
const { XsnArtifact, XsnName, XsnSource } = require('../compiler/xsn-model');
|
|
25
24
|
const { isBetaEnabled } = require('../base/model');
|
|
26
25
|
const { weakLocation } = require('../base/location');
|
|
27
26
|
const { normalizeNewLine, normalizeNumberString } = require('./textUtils');
|
|
@@ -500,10 +499,10 @@ function handleDuplicateExtension( ext, name, numDefines ) {
|
|
|
500
499
|
* Return start location of `token`, or the first token matched by the current
|
|
501
500
|
* rule if `token` is undefined
|
|
502
501
|
*
|
|
503
|
-
* @returns {
|
|
502
|
+
* @returns {Location}
|
|
504
503
|
*/
|
|
505
504
|
function startLocation( token = this._ctx.start ) {
|
|
506
|
-
return new
|
|
505
|
+
return new Location(
|
|
507
506
|
this.filename,
|
|
508
507
|
token.line,
|
|
509
508
|
token.column + 1
|
|
@@ -516,7 +515,7 @@ function startLocation( token = this._ctx.start ) {
|
|
|
516
515
|
*
|
|
517
516
|
* @param {object} token
|
|
518
517
|
* @param {object} endToken
|
|
519
|
-
* @return {
|
|
518
|
+
* @return {Location}
|
|
520
519
|
*/
|
|
521
520
|
function tokenLocation( token, endToken = null ) {
|
|
522
521
|
if (!token)
|
|
@@ -528,7 +527,7 @@ function tokenLocation( token, endToken = null ) {
|
|
|
528
527
|
const endLine = endToken.line;
|
|
529
528
|
// after the last char (special for EOF?)
|
|
530
529
|
const endCol = endToken.stop - endToken.start + endToken.column + 2;
|
|
531
|
-
const loc = new
|
|
530
|
+
const loc = new Location( this.filename, token.line, token.column + 1, endLine, endCol );
|
|
532
531
|
|
|
533
532
|
// This check is done for performance reason. No need to access a token's
|
|
534
533
|
// data if we know that it spans only one single line.
|
|
@@ -629,7 +628,7 @@ function secureParens( expr ) {
|
|
|
629
628
|
return {
|
|
630
629
|
op: { val: 'xpr', location: this.startLocation() },
|
|
631
630
|
args: [ expr ],
|
|
632
|
-
location: { __proto__:
|
|
631
|
+
location: { __proto__: Location.prototype, ...expr.location },
|
|
633
632
|
$parens,
|
|
634
633
|
};
|
|
635
634
|
}
|
|
@@ -1346,7 +1345,7 @@ function argsExpression( args, nary ) {
|
|
|
1346
1345
|
return args[0];
|
|
1347
1346
|
const $parens = args[0]?.$parens;
|
|
1348
1347
|
const loc = ($parens) ? $parens[$parens.length - 1] : args[0]?.location;
|
|
1349
|
-
const location = loc ? { __proto__:
|
|
1348
|
+
const location = loc ? { __proto__: Location.prototype, ...loc } : this.startLocation();
|
|
1350
1349
|
// console.log('AE:',args);
|
|
1351
1350
|
const op = {
|
|
1352
1351
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -6,7 +6,7 @@ const {
|
|
|
6
6
|
cdlNewLineRegEx,
|
|
7
7
|
} = require('./textUtils');
|
|
8
8
|
const { CompilerAssertion } = require('../base/error');
|
|
9
|
-
const {
|
|
9
|
+
const { Location } = require('../base/location');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Strips and counts the indentation from the given string.
|
|
@@ -463,7 +463,7 @@ class MultiLineStringParser {
|
|
|
463
463
|
*/
|
|
464
464
|
_locationForCharacters(i, width) {
|
|
465
465
|
return {
|
|
466
|
-
__proto__:
|
|
466
|
+
__proto__: Location.prototype,
|
|
467
467
|
file: this.parser.filename,
|
|
468
468
|
line: this.token.line + this._lineInString,
|
|
469
469
|
endLine: this.token.line + this._lineInString,
|
|
@@ -45,7 +45,7 @@ function isWhitespaceCharacterNoNewline( char ) {
|
|
|
45
45
|
* @return {string}
|
|
46
46
|
*/
|
|
47
47
|
function normalizeNewLine( str ) {
|
|
48
|
-
// Note: cdlNewLineRegEx does not have `g
|
|
48
|
+
// Note: cdlNewLineRegEx does not have `g`.
|
|
49
49
|
return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
|
|
50
50
|
}
|
|
51
51
|
|
package/lib/main.d.ts
CHANGED
|
@@ -246,6 +246,15 @@ declare namespace compiler {
|
|
|
246
246
|
* @see service
|
|
247
247
|
*/
|
|
248
248
|
serviceNames?: string[]
|
|
249
|
+
/**
|
|
250
|
+
* If set, certain OData errors that are not relevant for OpenAPI generation
|
|
251
|
+
* are downgraded to warnings when generating EDM JSON.
|
|
252
|
+
*
|
|
253
|
+
* @default true
|
|
254
|
+
* @since v4.8.0
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
edm4OpenAPI?: boolean
|
|
249
258
|
}
|
|
250
259
|
|
|
251
260
|
/**
|
|
@@ -951,6 +960,20 @@ declare namespace compiler {
|
|
|
951
960
|
*/
|
|
952
961
|
const keywords: string[];
|
|
953
962
|
}
|
|
963
|
+
namespace h2 {
|
|
964
|
+
/**
|
|
965
|
+
* Immutable list of reserved keywords for H2. The list is used by {@link to.sql}.
|
|
966
|
+
* Taken from <http://www.h2database.com/html/advanced.html#keywords>.
|
|
967
|
+
*/
|
|
968
|
+
const keywords: string[];
|
|
969
|
+
}
|
|
970
|
+
namespace postgres {
|
|
971
|
+
/**
|
|
972
|
+
* Immutable list of reserved keywords for PostgreSQL. The list is used by {@link to.sql}.
|
|
973
|
+
* Taken from <https://www.postgresql.org/docs/current/sql-keywords-appendix.html>.
|
|
974
|
+
*/
|
|
975
|
+
const keywords: string[];
|
|
976
|
+
}
|
|
954
977
|
|
|
955
978
|
/**
|
|
956
979
|
* If the given `name` requires quoting for SQL dialect `dialect`,
|
package/lib/main.js
CHANGED
|
@@ -26,7 +26,7 @@ const parseLanguage = lazyload('./language/antlrParser');
|
|
|
26
26
|
const compiler = lazyload('./compiler');
|
|
27
27
|
const shared = lazyload('./compiler/shared');
|
|
28
28
|
const define = lazyload('./compiler/define');
|
|
29
|
-
const builtins = lazyload('./
|
|
29
|
+
const builtins = lazyload('./base/builtins');
|
|
30
30
|
const base = lazyload('./compiler/base');
|
|
31
31
|
const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
|
|
32
32
|
|
|
@@ -126,6 +126,7 @@ module.exports = {
|
|
|
126
126
|
// SNAPI
|
|
127
127
|
for: {
|
|
128
128
|
odata: (...args) => snapi.odata(...args),
|
|
129
|
+
java: (...args) => snapi.java(...args),
|
|
129
130
|
effective: (...args) => snapi.for_effective(...args),
|
|
130
131
|
},
|
|
131
132
|
to: {
|
|
@@ -141,6 +142,12 @@ module.exports = {
|
|
|
141
142
|
sqlite: {
|
|
142
143
|
keywords: Object.freeze([ ...keywords.sqlite ] )
|
|
143
144
|
},
|
|
145
|
+
postgres: {
|
|
146
|
+
keywords: Object.freeze([ ...keywords.postgres ] )
|
|
147
|
+
},
|
|
148
|
+
h2: {
|
|
149
|
+
keywords: Object.freeze([ ...keywords.h2 ] )
|
|
150
|
+
},
|
|
144
151
|
smartId: (...args) => sqlIdentifier.smartId(...args),
|
|
145
152
|
smartFunctionId: (...args) => sqlIdentifier.smartFuncId(...args),
|
|
146
153
|
delimitedId: (...args) => sqlIdentifier.delimitedId(...args),
|
package/lib/model/cloneCsn.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { csnPropertyOrder } = require('../json/to-csn');
|
|
4
4
|
const { ModelError } = require('../base/error');
|
|
5
5
|
const { setHidden } = require('../utils/objectUtils');
|
|
6
|
-
const { isAnnotationExpression } = require('../
|
|
6
|
+
const { isAnnotationExpression } = require('../base/builtins');
|
|
7
7
|
|
|
8
8
|
const csnDictionaries = [
|
|
9
9
|
'args',
|
|
@@ -21,16 +21,22 @@ function shallowCopy( val, _options, _sort ) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const internalCsnProps = {
|
|
24
|
+
__proto__: null,
|
|
24
25
|
$sources: shallowCopy,
|
|
25
26
|
$location: shallowCopy,
|
|
26
27
|
$path: shallowCopy,
|
|
27
28
|
$paths: shallowCopy,
|
|
28
29
|
elements: cloneCsnDict,
|
|
29
30
|
$tableConstraints: shallowCopy,
|
|
31
|
+
$default: shallowCopy, // used for HANA CSN migrations
|
|
32
|
+
$notNull: shallowCopy, // used for HANA CSN migrations
|
|
33
|
+
};
|
|
34
|
+
const internalEnumerableCsnProps = {
|
|
35
|
+
__proto__: null,
|
|
36
|
+
$tableConstraints: shallowCopy, // enumerable for HANA CSN for migrations
|
|
30
37
|
};
|
|
31
38
|
const internalCsnPropertyNames = Object.keys(internalCsnProps);
|
|
32
39
|
|
|
33
|
-
|
|
34
40
|
/**
|
|
35
41
|
* Deeply clone the given CSN model and return it.
|
|
36
42
|
* In testMode (or with testSortCsn), definitions are sorted.
|
|
@@ -58,18 +64,21 @@ function cloneCsn( csn, options, sort ) {
|
|
|
58
64
|
const r = {};
|
|
59
65
|
for (const n of keys) {
|
|
60
66
|
const val = csn[n];
|
|
61
|
-
if (
|
|
62
|
-
r[n] = val;
|
|
63
|
-
}
|
|
64
|
-
else if (n.charAt(0) === '@') {
|
|
67
|
+
if (n.charAt(0) === '@') {
|
|
65
68
|
r[n] = cloneAnnotationValue(val, options, false); // TODO: pass 'sort'
|
|
66
69
|
}
|
|
70
|
+
else if (!val || typeof val !== 'object') {
|
|
71
|
+
r[n] = val;
|
|
72
|
+
}
|
|
67
73
|
else if (csnDictionaries.includes(n) && !Array.isArray(val)) {
|
|
68
74
|
const sortDict = n === 'definitions' &&
|
|
69
75
|
(!options || options.testMode || options.testSortCsn);
|
|
70
76
|
// Array check for property `args` which may either be a dictionary or an array.
|
|
71
77
|
r[n] = cloneCsnDict(val, options, sort, sortDict);
|
|
72
78
|
}
|
|
79
|
+
else if (n in internalEnumerableCsnProps) {
|
|
80
|
+
r[n] = internalEnumerableCsnProps[n](val, options, sort);
|
|
81
|
+
}
|
|
73
82
|
else {
|
|
74
83
|
r[n] = cloneCsn(val, options, sort);
|
|
75
84
|
}
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -171,15 +171,14 @@
|
|
|
171
171
|
// hierarchy, query number, table aliases and links from a column to its
|
|
172
172
|
// respective inferred element.
|
|
173
173
|
|
|
174
|
-
// TODO: some `name` property would be useful (also set with `initDefinition`)
|
|
175
|
-
|
|
176
174
|
// Properties in cache:
|
|
177
175
|
//
|
|
178
176
|
// - _effectiveType on def/member/items: cached result of effectiveType()
|
|
179
177
|
// - _origin on def/member/items: the "prototype"
|
|
180
178
|
// - $origin on def/member/items: whether implicit _origin refs have been set for direct members
|
|
181
179
|
// - _parent: currently just use to allow ref to `up_` in anonymous aspect
|
|
182
|
-
// for managed compositions
|
|
180
|
+
// for managed compositions;
|
|
181
|
+
// in queries always the main artifact (see `$next` for name resolution)
|
|
183
182
|
// - _env on non-string path item: environment provided by the ref so far,
|
|
184
183
|
// next path item is element in it
|
|
185
184
|
// - _ref on non-string `type` or `from` ref, or on alias: the referred def/elem
|
|
@@ -193,10 +192,11 @@
|
|
|
193
192
|
|
|
194
193
|
'use strict';
|
|
195
194
|
|
|
195
|
+
|
|
196
196
|
const BUILTIN_TYPE = {};
|
|
197
|
-
const { locationString } = require('../base/location');
|
|
197
|
+
const { SemanticLocation, locationString } = require('../base/location');
|
|
198
198
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
199
|
-
const { isAnnotationExpression } = require('../
|
|
199
|
+
const { isAnnotationExpression } = require('../base/builtins');
|
|
200
200
|
|
|
201
201
|
// Properties in which artifact or members are defined - next property in the
|
|
202
202
|
// "csnPath" is the name or index of that property; 'args' (its value can be a
|
|
@@ -244,11 +244,13 @@ function justDollar() {
|
|
|
244
244
|
* @param {boolean|string} [universalReady]
|
|
245
245
|
*/
|
|
246
246
|
function csnRefs( csn, universalReady ) {
|
|
247
|
+
// some users exchange the dict while using csn-refs !?! see test/testDraft.js
|
|
248
|
+
// const { definitions } = csn;
|
|
247
249
|
const cache = new WeakMap();
|
|
248
250
|
setCache( BUILTIN_TYPE, '_origin', null );
|
|
249
251
|
if (universalReady === 'init-all') {
|
|
250
|
-
for (const
|
|
251
|
-
initDefinition(
|
|
252
|
+
for (const name of Object.keys( csn.definitions || {}))
|
|
253
|
+
initDefinition( name );
|
|
252
254
|
}
|
|
253
255
|
// Functions which set the new `baseEnv`:
|
|
254
256
|
resolveRef.expandInline = function resolveExpandInline( ref, ...args ) {
|
|
@@ -275,6 +277,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
275
277
|
initDefinition,
|
|
276
278
|
dropDefinitionCache,
|
|
277
279
|
targetAspect,
|
|
280
|
+
msgLocations,
|
|
278
281
|
__getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
|
|
279
282
|
};
|
|
280
283
|
|
|
@@ -324,9 +327,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
324
327
|
const target = (staticAssoc ? targetAspect( env ) : env.target || env.targetAspect);
|
|
325
328
|
if (typeof target !== 'string')
|
|
326
329
|
return target || env;
|
|
327
|
-
|
|
328
|
-
initDefinition( def );
|
|
329
|
-
return def;
|
|
330
|
+
return initDefinition( target );
|
|
330
331
|
}
|
|
331
332
|
|
|
332
333
|
/**
|
|
@@ -342,18 +343,19 @@ function csnRefs( csn, universalReady ) {
|
|
|
342
343
|
if (typeof ref === 'string') {
|
|
343
344
|
const main = csn.definitions[ref];
|
|
344
345
|
if (main)
|
|
345
|
-
return initDefinition(
|
|
346
|
+
return initDefinition( ref );
|
|
347
|
+
// notFound only meant for builtins and $self
|
|
348
|
+
if (notFound !== undefined)
|
|
349
|
+
return notFound;
|
|
346
350
|
}
|
|
347
351
|
else {
|
|
348
352
|
const art = cached( ref, '_ref', artifactPathRef );
|
|
349
353
|
if (art)
|
|
350
354
|
return art;
|
|
355
|
+
// Backend bug workaround, TODO: delete next 2 lines
|
|
356
|
+
if (notFound !== undefined)
|
|
357
|
+
return notFound;
|
|
351
358
|
}
|
|
352
|
-
if (notFound !== undefined && typeof ref === 'string')
|
|
353
|
-
return notFound; // is only meant for builtins and $self
|
|
354
|
-
// Backend bug workaround, TODO: delete next 2 lines
|
|
355
|
-
if (notFound !== undefined)
|
|
356
|
-
return notFound;
|
|
357
359
|
throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
|
|
358
360
|
}
|
|
359
361
|
|
|
@@ -367,8 +369,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
367
369
|
|
|
368
370
|
function artifactPathRef( ref ) {
|
|
369
371
|
const [ head, ...tail ] = ref.ref;
|
|
370
|
-
let art =
|
|
371
|
-
initDefinition( art );
|
|
372
|
+
let art = initDefinition( pathId( head ) );
|
|
372
373
|
for (const elem of tail) {
|
|
373
374
|
const env = navigationEnv( art );
|
|
374
375
|
art = env.elements[pathId( elem )];
|
|
@@ -378,8 +379,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
378
379
|
|
|
379
380
|
function artifactFromRef( ref, noLast ) {
|
|
380
381
|
const [ head, ...tail ] = ref.ref;
|
|
381
|
-
let art =
|
|
382
|
-
initDefinition( art );
|
|
382
|
+
let art = initDefinition( pathId( head ) );
|
|
383
383
|
for (const elem of tail) {
|
|
384
384
|
const env = navigationEnv( art );
|
|
385
385
|
art = env.elements[pathId( elem )];
|
|
@@ -401,9 +401,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
401
401
|
const targetName = refCtx !== 'keys_origin' && art.target ||
|
|
402
402
|
art.$origin && art.$origin.target ||
|
|
403
403
|
art.cast.target;
|
|
404
|
-
|
|
405
|
-
initDefinition( target );
|
|
406
|
-
return target;
|
|
404
|
+
return initDefinition( targetName );
|
|
407
405
|
}
|
|
408
406
|
|
|
409
407
|
function getOrigin( art ) {
|
|
@@ -437,9 +435,8 @@ function csnRefs( csn, universalReady ) {
|
|
|
437
435
|
if (!Array.isArray( $origin )) // anonymous prototype in $origin
|
|
438
436
|
return getOriginExplicit( $origin.$origin );
|
|
439
437
|
const [ head, ...tail ] = $origin;
|
|
440
|
-
const main = csn.definitions[head];
|
|
441
438
|
// if (!main) throw Error(JSON.stringify({$origin,csn}))
|
|
442
|
-
initDefinition(
|
|
439
|
+
const main = initDefinition( head );
|
|
443
440
|
return tail.reduce( originNavigation, main );
|
|
444
441
|
}
|
|
445
442
|
|
|
@@ -483,8 +480,13 @@ function csnRefs( csn, universalReady ) {
|
|
|
483
480
|
}
|
|
484
481
|
|
|
485
482
|
function initDefinition( main ) {
|
|
483
|
+
const name = typeof main === 'string' && main;
|
|
484
|
+
if (name) {
|
|
485
|
+
main = csn.definitions[name];
|
|
486
|
+
setCache( main, '$name', name );
|
|
487
|
+
}
|
|
486
488
|
// TODO: some --test-mode check that the argument is in ‹csn›.definitions ?
|
|
487
|
-
if (getCache( main, '$queries' ) !== undefined) // already computed
|
|
489
|
+
if (!main || getCache( main, '$queries' ) !== undefined) // already computed
|
|
488
490
|
return main;
|
|
489
491
|
traverseDef( main, null, null, null, initNode );
|
|
490
492
|
const queries = cached( main, '$queries', allQueries );
|
|
@@ -650,7 +652,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
650
652
|
const up = getCache( parent, '_parent' );
|
|
651
653
|
const target = up && typeof up.target === 'string' && csn.definitions[up.target];
|
|
652
654
|
if (target && target.elements) {
|
|
653
|
-
initDefinition( target );
|
|
655
|
+
initDefinition( up.target );
|
|
654
656
|
art = target.elements.up_;
|
|
655
657
|
}
|
|
656
658
|
}
|
|
@@ -734,6 +736,110 @@ function csnRefs( csn, universalReady ) {
|
|
|
734
736
|
};
|
|
735
737
|
}
|
|
736
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Return [ Location, SemanticLocation ] from `csnPath`.
|
|
741
|
+
*/
|
|
742
|
+
function msgLocations( csnPath ) {
|
|
743
|
+
let location = csn?.$location;
|
|
744
|
+
const artifact = new SemanticLocation();
|
|
745
|
+
let obj = csn;
|
|
746
|
+
let index = 0;
|
|
747
|
+
let inlinePathIndex = null;
|
|
748
|
+
if (typeof csnPath[0] === 'object')
|
|
749
|
+
startPath( csnPath[0] );
|
|
750
|
+
|
|
751
|
+
/* eslint-disable no-return-assign */
|
|
752
|
+
const pathFunctions = {
|
|
753
|
+
definitions: name => absolute( name, 'type' ),
|
|
754
|
+
vocabularies: name => absolute( name, 'annotation' ),
|
|
755
|
+
extensions,
|
|
756
|
+
projection,
|
|
757
|
+
SELECT: projection,
|
|
758
|
+
// TODO: alias
|
|
759
|
+
mixin: name => nameInProp( name, 'mixin' ),
|
|
760
|
+
actions: name => nameInProp( name, 'action' ),
|
|
761
|
+
params: name => nameInProp( name, 'param' ),
|
|
762
|
+
returns: () => (artifact.param = ''),
|
|
763
|
+
elements: name => elements( name, artifact.select == null ? null : 'element' ),
|
|
764
|
+
columns: elements,
|
|
765
|
+
expand: elements,
|
|
766
|
+
inline: elements,
|
|
767
|
+
keys: pos => elements( pos, 'key' ),
|
|
768
|
+
enum: name => elements( name, 'enum' ),
|
|
769
|
+
item: () => (artifact.innerKind = 'item'),
|
|
770
|
+
// targetAspect: () => (artifact.innerKind = 'aspect')
|
|
771
|
+
'@': suffix,
|
|
772
|
+
}; /* eslint-enable no-return-assign */
|
|
773
|
+
|
|
774
|
+
while (obj && index < csnPath.length) {
|
|
775
|
+
const step = csnPath[index++];
|
|
776
|
+
obj = obj[step];
|
|
777
|
+
const fn = pathFunctions[step] || pathFunctions[step.charAt( 0 )];
|
|
778
|
+
if (fn)
|
|
779
|
+
fn( csnPath[index] );
|
|
780
|
+
if (obj?.$location)
|
|
781
|
+
location = obj.$location;
|
|
782
|
+
}
|
|
783
|
+
return [ location, artifact ];
|
|
784
|
+
|
|
785
|
+
function startPath( art ) {
|
|
786
|
+
const parent = getCache( art, '_parent' );
|
|
787
|
+
if (parent) {
|
|
788
|
+
if (!art.SELECT && !art.projection)
|
|
789
|
+
throw new CompilerAssertion( 'CSN path starts with object other than def or query' );
|
|
790
|
+
}
|
|
791
|
+
obj = csn.definitions;
|
|
792
|
+
absolute( getCache( parent || art, '$name' ), 'type' );
|
|
793
|
+
obj = art;
|
|
794
|
+
location = art.$location || parent?.$location || csn.$location;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function absolute( name, defaultKind ) {
|
|
798
|
+
obj = obj[name];
|
|
799
|
+
artifact.mainKind = obj.kind || defaultKind;
|
|
800
|
+
artifact.absolute = name;
|
|
801
|
+
++index;
|
|
802
|
+
}
|
|
803
|
+
function extensions( pos ) {
|
|
804
|
+
obj = obj[pos];
|
|
805
|
+
artifact.mainKind = obj.annotate ? 'annotate' : 'extend';
|
|
806
|
+
artifact.absolute = obj.annotate || obj.extend;
|
|
807
|
+
++index;
|
|
808
|
+
}
|
|
809
|
+
function projection() {
|
|
810
|
+
let select = getCache( obj, '$queryNumber' );
|
|
811
|
+
if (select === 1) {
|
|
812
|
+
const parent = getCache( obj, '_parent' );
|
|
813
|
+
if (parent && getCache( parent, '$queries' )?.length === 1)
|
|
814
|
+
select = 0;
|
|
815
|
+
}
|
|
816
|
+
artifact.select = select;
|
|
817
|
+
}
|
|
818
|
+
function nameInProp( name, prop ) {
|
|
819
|
+
obj = obj[name];
|
|
820
|
+
artifact[prop] = name;
|
|
821
|
+
++index;
|
|
822
|
+
}
|
|
823
|
+
function elements( name, kind ) {
|
|
824
|
+
obj = obj[name];
|
|
825
|
+
const elem = (typeof name === 'string') ? name : !obj.inline && columnAlias( obj );
|
|
826
|
+
if (obj.inline) { // inline
|
|
827
|
+
inlinePathIndex ??= artifact.element.length;
|
|
828
|
+
}
|
|
829
|
+
else if (inlinePathIndex != null) { // inline before: remove inline col indexes
|
|
830
|
+
if (elem)
|
|
831
|
+
artifact.element.length = inlinePathIndex;
|
|
832
|
+
inlinePathIndex = null;
|
|
833
|
+
}
|
|
834
|
+
artifact.element.push( elem || name + 1);
|
|
835
|
+
artifact.innerKind = kind || undefined;
|
|
836
|
+
++index;
|
|
837
|
+
}
|
|
838
|
+
function suffix( prop ) {
|
|
839
|
+
artifact.suffix = prop;
|
|
840
|
+
obj = null; // stop
|
|
841
|
+
}
|
|
842
|
+
}
|
|
737
843
|
/**
|
|
738
844
|
* Get the array of all (sub-)queries (value of the `SELECT`/`projection`
|
|
739
845
|
* property) inside the given `main` artifact (of `main.query`).
|
|
@@ -1021,16 +1127,18 @@ function implicitAs( ref ) {
|
|
|
1021
1127
|
function startCsnPath( csnPath, csn ) {
|
|
1022
1128
|
const head = csnPath[0];
|
|
1023
1129
|
if (typeof head !== 'string') {
|
|
1024
|
-
const {
|
|
1130
|
+
const {
|
|
1131
|
+
main, parent, art, query,
|
|
1132
|
+
} = head;
|
|
1025
1133
|
return {
|
|
1026
|
-
index: 1, main, parent, art,
|
|
1134
|
+
index: 1, main, parent, art, query,
|
|
1027
1135
|
};
|
|
1028
1136
|
}
|
|
1029
|
-
if (csnPath.length < 2 ||
|
|
1137
|
+
if (csnPath.length < 2 || head !== 'definitions' && head !== 'vocabularies')
|
|
1030
1138
|
throw new CompilerAssertion( 'References outside definitions and vocabularies not supported yet');
|
|
1031
|
-
const art = csn[
|
|
1139
|
+
const art = csn[head][csnPath[1]];
|
|
1032
1140
|
return {
|
|
1033
|
-
index: 2, main: art, parent: art, art,
|
|
1141
|
+
index: 2, main: art, parent: art, art, query: null,
|
|
1034
1142
|
};
|
|
1035
1143
|
}
|
|
1036
1144
|
|
|
@@ -1041,8 +1149,6 @@ function startCsnPath( csnPath, csn ) {
|
|
|
1041
1149
|
* @param {any} resolve
|
|
1042
1150
|
*/
|
|
1043
1151
|
function analyseCsnPath( csnPath, csn, resolve ) {
|
|
1044
|
-
/** @type {object} */
|
|
1045
|
-
let query = null;
|
|
1046
1152
|
/** @type {any} */
|
|
1047
1153
|
let refCtx = null;
|
|
1048
1154
|
/** @type {boolean|string|number} */
|
|
@@ -1051,7 +1157,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
1051
1157
|
let baseCtx = null;
|
|
1052
1158
|
let baseEnv = null;
|
|
1053
1159
|
let {
|
|
1054
|
-
index, main, parent, art,
|
|
1160
|
+
index, main, parent, art, query,
|
|
1055
1161
|
} = startCsnPath( csnPath, csn );
|
|
1056
1162
|
let obj = art;
|
|
1057
1163
|
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
applyTransformationsOnNonDictionary,
|
|
8
8
|
applyTransformationsOnDictionary,
|
|
9
9
|
} = require('../transform/db/applyTransformations');
|
|
10
|
-
const { isBuiltinType,
|
|
10
|
+
const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
|
|
11
11
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
12
12
|
const { typeParameters } = require('../compiler/builtins');
|
|
13
13
|
const { forEach } = require('../utils/objectUtils');
|
|
@@ -1414,9 +1414,6 @@ function findAnnotationExpression( node, prop ) {
|
|
|
1414
1414
|
|
|
1415
1415
|
module.exports = {
|
|
1416
1416
|
getUtils,
|
|
1417
|
-
isBuiltinType,
|
|
1418
|
-
isMagicVariable,
|
|
1419
|
-
isAnnotationExpression,
|
|
1420
1417
|
applyAnnotationsFromExtensions,
|
|
1421
1418
|
forEachGeneric,
|
|
1422
1419
|
forEachDefinition,
|