@sap/cds-compiler 2.10.4 → 2.11.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 +50 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +4 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +9 -23
- package/lib/api/options.js +12 -4
- package/lib/api/validate.js +23 -2
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/message-registry.js +10 -2
- package/lib/base/messages.js +23 -9
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +7 -0
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +28 -1
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +58 -91
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +93 -34
- package/lib/compiler/shared.js +29 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +31 -36
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +169 -34
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/genericAntlrParser.js +72 -14
- package/lib/language/language.g4 +73 -0
- package/lib/main.d.ts +136 -17
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +108 -31
- package/lib/model/csnUtils.js +63 -29
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +29 -18
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +9 -3
- package/lib/render/toHdbcds.js +16 -36
- package/lib/render/toSql.js +23 -5
- package/lib/transform/db/constraints.js +278 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +17 -1
- package/lib/transform/db/transformExists.js +61 -2
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +56 -435
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/transformUtilsNew.js +10 -0
- package/lib/transform/translateAssocsToJoins.js +5 -13
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/lib/main.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
//
|
|
5
5
|
// These types are improved step by step and use a lot any types at the moment.
|
|
6
6
|
|
|
7
|
+
// Author's note: All "options" interfaces should actually be types. However, due to
|
|
8
|
+
// https://github.com/TypeStrong/typedoc/issues/1519 we can't use
|
|
9
|
+
// intersection types at the moment.
|
|
10
|
+
|
|
7
11
|
export = compiler;
|
|
8
12
|
|
|
9
13
|
declare namespace compiler {
|
|
@@ -11,7 +15,7 @@ declare namespace compiler {
|
|
|
11
15
|
/**
|
|
12
16
|
* Options used by the core compiler and all backends.
|
|
13
17
|
*/
|
|
14
|
-
export
|
|
18
|
+
export interface Options {
|
|
15
19
|
[option: string]: any,
|
|
16
20
|
|
|
17
21
|
/**
|
|
@@ -57,6 +61,102 @@ declare namespace compiler {
|
|
|
57
61
|
dictionaryPrototype?: any
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Options used by OData backends. Includes options for the OData
|
|
66
|
+
* transformer as well as for rendering EDM and EDMX.
|
|
67
|
+
*/
|
|
68
|
+
export interface ODataOptions extends Options {
|
|
69
|
+
/**
|
|
70
|
+
* OData version for output files. Either 'v4' or 'v2'.
|
|
71
|
+
*
|
|
72
|
+
* @default 'v4'
|
|
73
|
+
*/
|
|
74
|
+
odataVersion?: string | 'v4' | 'v2'
|
|
75
|
+
/**
|
|
76
|
+
* Whether to generate OData as flat or as structured.
|
|
77
|
+
* Structured is only supported for OData v4.
|
|
78
|
+
*
|
|
79
|
+
* @default 'flat'
|
|
80
|
+
*/
|
|
81
|
+
odataFormat?: string | 'flat' | 'structured'
|
|
82
|
+
/**
|
|
83
|
+
* Naming mode used by the corresponding SQL.
|
|
84
|
+
*
|
|
85
|
+
* @default 'plain'
|
|
86
|
+
*/
|
|
87
|
+
sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
|
|
88
|
+
/**
|
|
89
|
+
* If `true`, `cds.Compositions` are rendered as `edm:NavigationProperty` with the additional
|
|
90
|
+
* attribute `ContainsTarget="true"` and all contained entities (composition targets) have no
|
|
91
|
+
* `edm.EntitySet`.
|
|
92
|
+
*
|
|
93
|
+
* @note Only available for OData v4 EDM(X) rendering.
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
odataContainment?: boolean
|
|
97
|
+
/**
|
|
98
|
+
* If `true`, render generated foreign keys for managed associations.
|
|
99
|
+
* By default foreign keys are never visible in structured OData APIs.
|
|
100
|
+
*
|
|
101
|
+
* @note Only available for structured OData v4 EDM(X) rendering.
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
odataForeignKeys?: boolean
|
|
105
|
+
/**
|
|
106
|
+
* If `true`, association targets outside of the current service are added as
|
|
107
|
+
* `edm.EntityType` that only exposes their primary keys and have no `edm.EntitySet`.
|
|
108
|
+
* If the original association target is a service member, a corresponding `edm.Schema`
|
|
109
|
+
* representing the namespace of that service is added to `edm.Services`. All association
|
|
110
|
+
* targets that are no service members are collected in an `edm.Schema` with namespace `root`.
|
|
111
|
+
*
|
|
112
|
+
* @note Only valid for structured OData v4 EDM(X) rendering.
|
|
113
|
+
* @default false
|
|
114
|
+
* @since v2.1.0
|
|
115
|
+
*/
|
|
116
|
+
odataProxies?: boolean
|
|
117
|
+
/**
|
|
118
|
+
* This option is an extension to `odataProxies`.
|
|
119
|
+
* If `true`, an `edm:Reference` instead of a proxy `edm.EntityType` is rendered for each
|
|
120
|
+
* association target that is a service member outside the current service instead of proxies.
|
|
121
|
+
*
|
|
122
|
+
* @note Only valid for structured OData v4 EDM(X) rendering.
|
|
123
|
+
* @default false
|
|
124
|
+
* @since v2.1.0
|
|
125
|
+
*/
|
|
126
|
+
odataXServiceRefs?: boolean
|
|
127
|
+
/**
|
|
128
|
+
* The OData specification requires that all primary keys of the principal must be used as
|
|
129
|
+
* referential constraints. If an association is modelled with only a partial key, no
|
|
130
|
+
* referential constraints are added. If `true`, partial constraints are rendered for
|
|
131
|
+
* backwards compatibility and mocking scenarios. A spec violation warning is raised for
|
|
132
|
+
* each incomplete constraint.
|
|
133
|
+
*
|
|
134
|
+
* @note Only valid for OData v2 CSN transformation.
|
|
135
|
+
* @default false
|
|
136
|
+
* @since v2.2.6
|
|
137
|
+
*/
|
|
138
|
+
odataV2PartialConstr?: boolean
|
|
139
|
+
/**
|
|
140
|
+
* Service name for which EDMX or EDM shall be rendered.
|
|
141
|
+
*
|
|
142
|
+
* @note Only available for `to.edmx()` and `to.edm()`. For `to.edmx.all()`
|
|
143
|
+
* and `to.edm.all()`, use `serviceNames` instead.
|
|
144
|
+
*
|
|
145
|
+
* @see serviceNames
|
|
146
|
+
*/
|
|
147
|
+
service?: string
|
|
148
|
+
/**
|
|
149
|
+
* Array of service names for which EDMX or EDM shall be rendered.
|
|
150
|
+
* If unspecified, all services are rendered.
|
|
151
|
+
*
|
|
152
|
+
* @note Only available for `to.edmx.all()` and `to.edm.all()`. For `to.edmx()`
|
|
153
|
+
* and `to.edm()`, use `service` instead.
|
|
154
|
+
*
|
|
155
|
+
* @see service
|
|
156
|
+
*/
|
|
157
|
+
serviceNames?: string[]
|
|
158
|
+
}
|
|
159
|
+
|
|
60
160
|
/**
|
|
61
161
|
* The compiler's package version.
|
|
62
162
|
* For more details on versioning and SemVer, see `doc/Versioning.md`
|
|
@@ -83,6 +183,7 @@ declare namespace compiler {
|
|
|
83
183
|
* {@link CompilationError} containing a vector of individual errors.
|
|
84
184
|
*
|
|
85
185
|
* @param filenames Array of files that should be compiled.
|
|
186
|
+
* @param dir Working directory. Relative paths in `filenames` will be resolved relatively to this directory.
|
|
86
187
|
* @param options Compiler options. If you do not set `messages`, they will be printed to console.
|
|
87
188
|
* @param fileCache A dictionary of absolute file names to the file content with values:
|
|
88
189
|
* - false: the file does not exist
|
|
@@ -138,7 +239,7 @@ declare namespace compiler {
|
|
|
138
239
|
* _Note_: Sorting is done in-place.
|
|
139
240
|
*
|
|
140
241
|
* Example of sorted messages:
|
|
141
|
-
* ```
|
|
242
|
+
* ```
|
|
142
243
|
* A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
|
|
143
244
|
* A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
|
|
144
245
|
* B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
|
|
@@ -188,10 +289,11 @@ declare namespace compiler {
|
|
|
188
289
|
* form (i.e. one line)
|
|
189
290
|
*
|
|
190
291
|
* Example:
|
|
191
|
-
* ```
|
|
292
|
+
* ```
|
|
192
293
|
* <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
|
|
193
294
|
* ```
|
|
194
295
|
*
|
|
296
|
+
* @param msg Compiler message which shall be stringified.
|
|
195
297
|
* @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
|
|
196
298
|
* @param noMessageId If true, the message ID will _not_ be part of the string.
|
|
197
299
|
* @param noHome If true, the semantic location will _not_ be part of the string.
|
|
@@ -201,26 +303,30 @@ declare namespace compiler {
|
|
|
201
303
|
/**
|
|
202
304
|
* Returns a message string with file- and semantic location if present
|
|
203
305
|
* in multiline form.
|
|
204
|
-
* The error (+ message id)
|
|
205
|
-
* run on a TTY.
|
|
306
|
+
* The error (+ message id) can colored according to their severity.
|
|
206
307
|
*
|
|
207
308
|
* Example:
|
|
208
|
-
* ```
|
|
209
|
-
* Error[message-id]: Can't find type `nu` in this scope
|
|
309
|
+
* ```
|
|
310
|
+
* Error[message-id]: Can't find type `nu` in this scope
|
|
210
311
|
* |
|
|
211
|
-
* <source>.cds:3:11, at entity:“E”
|
|
312
|
+
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
212
313
|
* ```
|
|
213
314
|
*
|
|
214
315
|
* @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
|
|
215
316
|
* @param config.noMessageId If true, no messages id (in brackets) will be shown.
|
|
216
317
|
* @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
|
|
217
318
|
* @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
|
|
319
|
+
* @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
320
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
321
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
322
|
+
* unset.
|
|
218
323
|
*/
|
|
219
324
|
export function messageStringMultiline(msg: CompileMessage, config?: {
|
|
220
325
|
normalizeFilename?: boolean
|
|
221
326
|
noMessageId?: boolean
|
|
222
327
|
hintExplanation?: boolean
|
|
223
328
|
withLineSpacer?: boolean
|
|
329
|
+
color?: boolean | 'auto'
|
|
224
330
|
}): string;
|
|
225
331
|
|
|
226
332
|
/**
|
|
@@ -233,16 +339,24 @@ declare namespace compiler {
|
|
|
233
339
|
* All lines are prepended by a pipe (`|`) and show the corresponding line number.
|
|
234
340
|
*
|
|
235
341
|
* Example Output:
|
|
236
|
-
* ```
|
|
342
|
+
* ```
|
|
237
343
|
* |
|
|
238
344
|
* 13 | num * nu
|
|
239
345
|
* | ^^
|
|
240
346
|
* ```
|
|
241
347
|
*
|
|
242
|
-
* @param sourceLines
|
|
243
|
-
* @param msg
|
|
348
|
+
* @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
|
|
349
|
+
* @param msg Message whose location is used to print the message context.
|
|
350
|
+
* @param config Configuration for the message context.
|
|
351
|
+
* @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
352
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
353
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
354
|
+
* unset.
|
|
355
|
+
|
|
244
356
|
*/
|
|
245
|
-
export function messageContext(sourceLines: string[], msg: CompileMessage
|
|
357
|
+
export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
|
|
358
|
+
color?: boolean | 'auto'
|
|
359
|
+
}): string;
|
|
246
360
|
|
|
247
361
|
/**
|
|
248
362
|
* Get an explanatory text for a complicated compiler message with ID
|
|
@@ -316,21 +430,26 @@ declare namespace compiler {
|
|
|
316
430
|
* @alias for
|
|
317
431
|
*/
|
|
318
432
|
export namespace For {
|
|
319
|
-
|
|
433
|
+
/**
|
|
434
|
+
* Transform the given (generic) CSN into one that is used for OData.
|
|
435
|
+
* Changes include flattening, type resolution and more, according to
|
|
436
|
+
* the provided options.
|
|
437
|
+
*/
|
|
438
|
+
function odata(csn: CSN, options: ODataOptions): any;
|
|
320
439
|
}
|
|
321
440
|
|
|
322
441
|
export namespace to {
|
|
323
442
|
function cdl(csn: CSN, options: Options): object;
|
|
324
443
|
function sql(csn: CSN, options: Options): any;
|
|
325
444
|
|
|
326
|
-
function edm(csn: CSN, options:
|
|
445
|
+
function edm(csn: CSN, options: ODataOptions): any;
|
|
327
446
|
namespace edm {
|
|
328
|
-
function all(csn: CSN, options:
|
|
447
|
+
function all(csn: CSN, options: ODataOptions): any;
|
|
329
448
|
}
|
|
330
449
|
|
|
331
|
-
function edmx(csn: CSN, options:
|
|
450
|
+
function edmx(csn: CSN, options: ODataOptions): any;
|
|
332
451
|
namespace edmx {
|
|
333
|
-
function all(csn: CSN, options:
|
|
452
|
+
function all(csn: CSN, options: ODataOptions): any;
|
|
334
453
|
}
|
|
335
454
|
|
|
336
455
|
function hdbcds(csn: CSN, options: Options): any;
|
package/lib/main.js
CHANGED
|
@@ -21,6 +21,7 @@ const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, dedupli
|
|
|
21
21
|
|
|
22
22
|
const parseLanguage = require('./language/antlrParser');
|
|
23
23
|
const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
|
|
24
|
+
const { fns } = require('./compiler/shared');
|
|
24
25
|
const { define } = require('./compiler/definer');
|
|
25
26
|
|
|
26
27
|
// The compiler version (taken from package.json)
|
|
@@ -43,13 +44,14 @@ const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
|
|
|
43
44
|
function parseCdl( cdl, filename, options = {} ) {
|
|
44
45
|
options = Object.assign( {}, options, { parseCdl: true } );
|
|
45
46
|
const sources = Object.create(null);
|
|
46
|
-
const model = { sources, options };
|
|
47
|
+
const model = { sources, options, $functions: {}, $volatileFunctions: {} };
|
|
47
48
|
const messageFunctions = createMessageFunctions( options, 'parse', model );
|
|
48
49
|
model.$messageFunctions = messageFunctions;
|
|
49
50
|
|
|
50
51
|
const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
|
|
51
52
|
messageFunctions );
|
|
52
53
|
sources[filename] = xsn;
|
|
54
|
+
fns( model );
|
|
53
55
|
define( model );
|
|
54
56
|
messageFunctions.throwWithError();
|
|
55
57
|
return compactModel( model );
|
package/lib/model/api.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - the ‹property name› (might be useful if the same function is used for several props)
|
|
16
16
|
*/
|
|
17
17
|
const defaultFunctions = {
|
|
18
|
-
'@': () => {
|
|
18
|
+
'@': () => { /* do not traverse annotation assignments */ },
|
|
19
19
|
args: dictionary,
|
|
20
20
|
elements: dictionary,
|
|
21
21
|
enum: dictionary,
|
|
@@ -23,7 +23,7 @@ const defaultFunctions = {
|
|
|
23
23
|
actions: dictionary,
|
|
24
24
|
mixin: dictionary,
|
|
25
25
|
definitions: dictionary,
|
|
26
|
-
'$': () => {
|
|
26
|
+
'$': () => { /* do not traverse properties starting with '$' */},
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -225,17 +225,19 @@ function csnRefs( csn ) {
|
|
|
225
225
|
const cachedType = getCache( art, '_effectiveType' );
|
|
226
226
|
if (cachedType !== undefined)
|
|
227
227
|
return cachedType;
|
|
228
|
-
else if (!art.type && !art.$origin ||
|
|
229
|
-
art.elements || art.target || art.targetAspect || art.enum)
|
|
230
|
-
return setCache( art, '_effectiveType', art );
|
|
231
228
|
|
|
232
229
|
const chain = [];
|
|
233
|
-
|
|
230
|
+
let origin;
|
|
231
|
+
while (getCache( art, '_effectiveType' ) === undefined &&
|
|
232
|
+
(origin = cached( art, '_origin', getOriginRaw )) &&
|
|
234
233
|
!art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
|
|
235
234
|
chain.push( art );
|
|
236
235
|
setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
237
|
-
art =
|
|
236
|
+
art = origin;
|
|
238
237
|
}
|
|
238
|
+
if (!chain.length)
|
|
239
|
+
return setCache( art, '_effectiveType', art );
|
|
240
|
+
|
|
239
241
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
240
242
|
throw new Error( 'Circular type reference');
|
|
241
243
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
@@ -251,10 +253,16 @@ function csnRefs( csn ) {
|
|
|
251
253
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
252
254
|
// elements of array items (that is the task of the core compiler /
|
|
253
255
|
// semantic check)
|
|
254
|
-
while (type.items)
|
|
256
|
+
while (type.items) {
|
|
257
|
+
cached( type, '$origin', _a => setImplicitOrigin( type, origin ) );
|
|
255
258
|
type = effectiveType( type.items );
|
|
259
|
+
}
|
|
256
260
|
// cannot navigate along targetAspect!
|
|
257
|
-
|
|
261
|
+
const env = (type.target) ? csn.definitions[type.target] : type;
|
|
262
|
+
const origin = cached( env, '_origin', getOriginRaw );
|
|
263
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
264
|
+
cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
|
|
265
|
+
return env;
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
/**
|
|
@@ -279,38 +287,102 @@ function csnRefs( csn ) {
|
|
|
279
287
|
function artifactPathRef( ref ) {
|
|
280
288
|
const [ head, ...tail ] = ref.ref;
|
|
281
289
|
let art = csn.definitions[pathId( head )];
|
|
282
|
-
for (const elem of tail)
|
|
283
|
-
|
|
290
|
+
for (const elem of tail) {
|
|
291
|
+
const env = navigationEnv( art );
|
|
292
|
+
art = env.elements[pathId( elem )];
|
|
293
|
+
}
|
|
284
294
|
return art;
|
|
285
295
|
}
|
|
286
296
|
|
|
287
|
-
function getOrigin(
|
|
288
|
-
const
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
297
|
+
function getOrigin( art, alsoType ) {
|
|
298
|
+
const origin = cached( art, '_origin', getOriginRaw );
|
|
299
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
300
|
+
cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
301
|
+
return art.type && !alsoType ? undefined : origin;
|
|
292
302
|
}
|
|
293
303
|
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
304
|
+
function getOriginRaw( art ) {
|
|
305
|
+
if (art.type) // TODO: make robust against "linked" = only direct
|
|
306
|
+
return artifactRef( art.type, BUILTIN_TYPE );
|
|
307
|
+
if (!art.$origin) // implicit $origin should have been set
|
|
308
|
+
return null;
|
|
309
|
+
// art.$origin must not be a string here - shortened refs should already
|
|
310
|
+
// have been used to set the _origin cache
|
|
311
|
+
if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
|
|
312
|
+
return cached( art.$origin, '_origin', getOriginRaw );
|
|
313
|
+
const [ head, ...tail ] = art.$origin;
|
|
314
|
+
let origin = csn.definitions[head];
|
|
315
|
+
// allow shorter $origin ref for actions/functions, just using a string:
|
|
316
|
+
let isAction = art.kind === 'action' || art.kind === 'function';
|
|
317
|
+
for (const elem of tail) {
|
|
318
|
+
origin = originNavigation( origin, elem, isAction );
|
|
319
|
+
isAction = false;
|
|
320
|
+
}
|
|
321
|
+
return origin;
|
|
300
322
|
}
|
|
301
323
|
|
|
302
|
-
function originNavigation( art, elem ) {
|
|
324
|
+
function originNavigation( art, elem, isAction ) {
|
|
303
325
|
if (typeof elem !== 'string') {
|
|
304
326
|
if (elem.action)
|
|
305
|
-
return art.actions[elem.action]
|
|
327
|
+
return art.actions[elem.action];
|
|
306
328
|
if (elem.param)
|
|
307
|
-
return
|
|
329
|
+
return art.params[elem.param];
|
|
330
|
+
if (elem.returns)
|
|
331
|
+
return art.returns;
|
|
308
332
|
}
|
|
309
|
-
if (
|
|
333
|
+
if (isAction)
|
|
334
|
+
return art.actions[elem];
|
|
335
|
+
// TODO: if we use effectiveType(), we might have more implicit prototypes
|
|
336
|
+
// we might then need a function like effectiveArtifact,
|
|
337
|
+
// which cares about actions/params
|
|
338
|
+
// let origin = cached( art, '_origin', getOriginRaw );
|
|
339
|
+
// while (art.items) {
|
|
340
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
341
|
+
// art = art.items;
|
|
342
|
+
// origin = cached( art, '_origin', getOriginRaw );
|
|
343
|
+
// }
|
|
344
|
+
// if (origin)
|
|
345
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
346
|
+
return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// From the current CSN object, set implicit origin for the next navigation step
|
|
350
|
+
// Currently (TODO: ?) `elements` only, i.e. what is needed for name resolution.
|
|
351
|
+
function setImplicitOrigin( art, origin ) {
|
|
352
|
+
setMembersImplicit( art.actions, origin.actions );
|
|
353
|
+
setMembersImplicit( art.params, origin.params );
|
|
354
|
+
if (art.returns) {
|
|
310
355
|
art = art.returns;
|
|
311
|
-
|
|
356
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
357
|
+
return true; // not implicit or shortened
|
|
358
|
+
origin = effectiveType( origin.returns );
|
|
359
|
+
setCache( art, '_origin', origin );
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
while (art.items) {
|
|
312
363
|
art = art.items;
|
|
313
|
-
|
|
364
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
365
|
+
return true; // not implicit or shortened
|
|
366
|
+
origin = effectiveType( origin.items );
|
|
367
|
+
setCache( art, '_origin', origin );
|
|
368
|
+
}
|
|
369
|
+
setMembersImplicit( art.elements, origin.elements );
|
|
370
|
+
// The enum base type is _not_ where we find the origins of the enum symbols.
|
|
371
|
+
// A derived type of an enum type with individual annotations on a symbol
|
|
372
|
+
// has both 'type' and '$origin'.
|
|
373
|
+
if (!art.type || art.$origin)
|
|
374
|
+
setMembersImplicit( art.enum, origin.enum );
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function setMembersImplicit( members, originMembers ) {
|
|
379
|
+
if (!members)
|
|
380
|
+
return;
|
|
381
|
+
for (const name in members) {
|
|
382
|
+
const elem = members[name];
|
|
383
|
+
if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
|
|
384
|
+
setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
|
|
385
|
+
}
|
|
314
386
|
}
|
|
315
387
|
|
|
316
388
|
/**
|
|
@@ -371,6 +443,9 @@ function csnRefs( csn ) {
|
|
|
371
443
|
if (semantics.dynamic === 'global' || ref.global)
|
|
372
444
|
return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
|
|
373
445
|
|
|
446
|
+
const origin = cached( main, '_origin', getOriginRaw )
|
|
447
|
+
if (origin)
|
|
448
|
+
cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
|
|
374
449
|
cached( main, '$queries', allQueries );
|
|
375
450
|
let qcache = query && cache.get( query.projection || query );
|
|
376
451
|
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
@@ -387,7 +462,7 @@ function csnRefs( csn ) {
|
|
|
387
462
|
while (cache) {
|
|
388
463
|
const alias = tryAlias && cache.$aliases[head];
|
|
389
464
|
if (alias)
|
|
390
|
-
return resolvePath( path, alias._select || alias, 'alias', cache.$queryNumber );
|
|
465
|
+
return resolvePath( path, alias._select || alias._ref, 'alias', cache.$queryNumber );
|
|
391
466
|
const mixin = cache._select.mixin && cache._select.mixin[head];
|
|
392
467
|
if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
|
|
393
468
|
return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
|
|
@@ -481,7 +556,8 @@ function csnRefs( csn ) {
|
|
|
481
556
|
if (query.ref) { // ref in from
|
|
482
557
|
// console.log('SQ:',query,cache.get(query))
|
|
483
558
|
const as = query.as || implicitAs( query.ref );
|
|
484
|
-
|
|
559
|
+
const _ref = fromRef( query );
|
|
560
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
485
561
|
}
|
|
486
562
|
else {
|
|
487
563
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -607,7 +683,7 @@ function queryOrMain( query, main ) {
|
|
|
607
683
|
*
|
|
608
684
|
* @param {CSN.Query} query
|
|
609
685
|
* @param {CSN.QuerySelect} fromSelect
|
|
610
|
-
* @param {CSN.Query} parentQuery
|
|
686
|
+
* @param {CSN.Query} parentQuery
|
|
611
687
|
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
|
|
612
688
|
*/
|
|
613
689
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
@@ -632,8 +708,9 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
|
632
708
|
|
|
633
709
|
/**
|
|
634
710
|
* @param {CSN.QueryFrom} from
|
|
635
|
-
* @param {CSN.QuerySelect}
|
|
636
|
-
* @param {
|
|
711
|
+
* @param {CSN.QuerySelect} fromSelect
|
|
712
|
+
* @param {CSN.Query} parentQuery
|
|
713
|
+
* @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
|
|
637
714
|
*/
|
|
638
715
|
function traverseFrom( from, fromSelect, parentQuery, callback ) {
|
|
639
716
|
if (from.ref) {
|