@sap/cds-compiler 2.15.4 → 3.0.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 +33 -1590
- package/bin/cdsc.js +36 -33
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +220 -103
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +60 -20
- package/lib/base/messages.js +65 -24
- package/lib/base/model.js +44 -2
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +87 -9
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/extend.js +59 -11
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +13 -13
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +193 -218
- package/lib/compiler/shared.js +47 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/csn2edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +25 -30
- package/lib/edm/edmUtils.js +10 -24
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20632 -22313
- package/lib/json/from-csn.js +56 -49
- package/lib/json/to-csn.js +10 -8
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +303 -229
- package/lib/language/language.g4 +573 -629
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +170 -283
- package/lib/model/revealInternalProperties.js +28 -8
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +12 -21
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +19 -15
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +53 -51
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +10 -12
- package/lib/transform/localized.js +22 -16
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +3 -3
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
|
@@ -45,6 +45,7 @@ const centralMessages = {
|
|
|
45
45
|
'anno-definition': { severity: 'Warning' },
|
|
46
46
|
'anno-duplicate': { severity: 'Error', configurableFor: true }, // does not hurt us
|
|
47
47
|
'anno-duplicate-unrelated-layer': { severity: 'Error', configurableFor: true }, // does not hurt us
|
|
48
|
+
'anno-unstable-array': { severity: 'Warning' },
|
|
48
49
|
'anno-invalid-sql-element': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
49
50
|
'anno-invalid-sql-struct': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
50
51
|
'anno-invalid-sql-kind': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
@@ -67,7 +68,7 @@ const centralMessages = {
|
|
|
67
68
|
'def-unexpected-calcview-assoc': { severity: 'Error' },
|
|
68
69
|
'chained-array-of': { severity: 'Error' },
|
|
69
70
|
'check-proper-type': { severity: 'Error', configurableFor: [ 'compile' ] },
|
|
70
|
-
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.rename' ] },
|
|
71
|
+
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
|
|
71
72
|
|
|
72
73
|
'expr-no-filter': { severity: 'Error', configurableFor: 'deprecated' },
|
|
73
74
|
|
|
@@ -80,6 +81,7 @@ const centralMessages = {
|
|
|
80
81
|
'expected-type': { severity: 'Error' },
|
|
81
82
|
'ref-sloppy-type': { severity: 'Error' },
|
|
82
83
|
'type-unexpected-typeof': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: make it non-config
|
|
84
|
+
'type-ignoring-argument': { severity: 'Error', configurableFor: true },
|
|
83
85
|
'type-expected-builtin': { severity: 'Error', configurableFor: true },
|
|
84
86
|
'expected-actionparam-type': { severity: 'Error' },
|
|
85
87
|
'ref-sloppy-actionparam-type': { severity: 'Error' },
|
|
@@ -116,6 +118,7 @@ const centralMessages = {
|
|
|
116
118
|
'ref-obsolete-parameters': { severity: 'Error', configurableFor: true }, // does not hurt us
|
|
117
119
|
'ref-undefined-param': { severity: 'Error' },
|
|
118
120
|
'ref-rejected-on': { severity: 'Error' },
|
|
121
|
+
'ref-expected-element': { severity: 'Error' },
|
|
119
122
|
|
|
120
123
|
'rewrite-key-not-covered-explicit': { severity: 'Error', configurableFor: 'deprecated' },
|
|
121
124
|
'rewrite-key-not-covered-implicit': { severity: 'Error', configurableFor: 'deprecated' },
|
|
@@ -128,14 +131,11 @@ const centralMessages = {
|
|
|
128
131
|
'service-nested-context': { severity: 'Error', configurableFor: true }, // does not hurt compile, TODO
|
|
129
132
|
'service-nested-service': { severity: 'Error', configurableFor: 'deprecated' }, // not supported yet
|
|
130
133
|
|
|
131
|
-
'syntax-
|
|
132
|
-
'syntax-
|
|
133
|
-
'syntax-
|
|
134
|
-
'syntax-
|
|
135
|
-
'syntax-
|
|
136
|
-
'syntax-csn-expected-translation': { severity: 'Error' }, // TODO: more than 30 chars
|
|
137
|
-
'syntax-csn-required-subproperty': { severity: 'Error' }, // TODO: more than 30 chars
|
|
138
|
-
'syntax-csn-unexpected-property': { severity: 'Error', configurableFor: true }, // is the removed
|
|
134
|
+
'syntax-expected-cardinality': { severity: 'Error' },
|
|
135
|
+
'syntax-expected-length': { severity: 'Error' },
|
|
136
|
+
'syntax-expected-translation': { severity: 'Error' },
|
|
137
|
+
'syntax-required-subproperty': { severity: 'Error' },
|
|
138
|
+
'syntax-unexpected-property': { severity: 'Error', configurableFor: true }, // is the removed
|
|
139
139
|
'syntax-deprecated-ident': { severity: 'Error', configurableFor: true },
|
|
140
140
|
'syntax-fragile-alias': { severity: 'Error', configurableFor: true },
|
|
141
141
|
'syntax-fragile-ident': { severity: 'Error', configurableFor: true },
|
|
@@ -148,6 +148,8 @@ const centralMessages = {
|
|
|
148
148
|
'syntax-missing-escape': { severity: 'Error' },
|
|
149
149
|
|
|
150
150
|
'syntax-expected-integer': { severity: 'Error' },
|
|
151
|
+
'syntax-invalid-masked': { severity: 'Error', configurableFor: true },
|
|
152
|
+
'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
|
|
151
153
|
|
|
152
154
|
'type-managed-composition': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: non-config
|
|
153
155
|
|
|
@@ -193,27 +195,47 @@ for (const oldName in oldMessageIds) {
|
|
|
193
195
|
|
|
194
196
|
// For messageIds, where no text has been provided via code (central def)
|
|
195
197
|
const centralMessageTexts = {
|
|
198
|
+
'api-invalid-option': {
|
|
199
|
+
std: 'Option $(NAME) is deprecated! Use SNAPI options instead',
|
|
200
|
+
magicVars: 'Option “magicVars” is deprecated! Use “variableReplacements” instead. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
|
|
201
|
+
user: 'Option “variableReplacements” expects “$user” instead of “user”. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
|
|
202
|
+
locale: 'Option “variableReplacements” expects “$user.locale” instead of “locale”. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
|
|
203
|
+
'noDollar': 'Option “variableReplacements” does not know $(NAME). Did you forget a leading “$”?'
|
|
204
|
+
},
|
|
205
|
+
|
|
196
206
|
'anno-duplicate': 'Duplicate assignment with $(ANNO)',
|
|
207
|
+
'anno-duplicate-unrelated-layer': 'Duplicate assignment with $(ANNO)',
|
|
208
|
+
'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO) in unrelated layers',
|
|
197
209
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
198
210
|
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
199
211
|
'anno-unexpected-ellipsis-layers': 'No base annotation available to apply $(CODE)',
|
|
200
212
|
'chained-array-of': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
|
|
201
|
-
|
|
202
|
-
'
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
213
|
+
|
|
214
|
+
'name-duplicate-element': {
|
|
215
|
+
'std': 'Generated element $(NAME) conflicts with another element',
|
|
216
|
+
'flatten-element-gen': 'Generated element $(NAME) conflicts with other generated element',
|
|
217
|
+
'flatten-element-exist': 'Flattened name of structured element conflicts with existing element $(NAME)',
|
|
218
|
+
'flatten-fkey-gen': 'Duplicate definition of foreign key element $(NAME) for association $(ART)',
|
|
219
|
+
'flatten-fkey-exists': 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element',
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
'syntax-unexpected-ellipsis': {
|
|
223
|
+
std: 'Expected no more than one $(CODE)',
|
|
224
|
+
'nested-array': 'Unexpected $(CODE) in nested array'
|
|
225
|
+
},
|
|
226
|
+
'syntax-expected-object': 'Expected object for property $(PROP)',
|
|
227
|
+
'syntax-expected-column': 'Expected object or string \'*\' for property $(PROP)',
|
|
228
|
+
'syntax-expected-natnum': 'Expected non-negative number for property $(PROP)',
|
|
229
|
+
'syntax-expected-cardinality': 'Expected non-negative number or string \'*\' for property $(PROP)',
|
|
230
|
+
'syntax-expected-reference': 'Expected non-empty string or object for property $(PROP)',
|
|
231
|
+
'syntax-expected-term': 'Expected non-empty string or object for property $(PROP)',
|
|
210
232
|
'syntax-dollar-ident': {
|
|
211
233
|
std: 'An artifact starting with $(NAME) might shadow a special variable - replace by another name',
|
|
212
234
|
$tableAlias: 'A table alias name starting with $(NAME) might shadow a special variable - replace by another name',
|
|
213
235
|
$tableImplicit: 'The resulting table alias starts with $(NAME) and might shadow a special variable - specify another name with $(KEYWORD)',
|
|
214
236
|
mixin: 'A mixin name starting with $(NAME) might shadow a special variable - replace by another name' ,
|
|
215
237
|
},
|
|
216
|
-
'syntax-
|
|
238
|
+
'syntax-expected-length': {
|
|
217
239
|
std: 'Expected array in $(PROP) to have at least $(N) items',
|
|
218
240
|
one: 'Expected array in $(PROP) to have at least one item',
|
|
219
241
|
suffix: 'With sibling property $(OTHERPROP), expected array in $(PROP) to have at least one item',
|
|
@@ -245,6 +267,20 @@ const centralMessageTexts = {
|
|
|
245
267
|
unknown: 'Unknown argument $(CODE)',
|
|
246
268
|
duplicate: 'Duplicate argument $(CODE)',
|
|
247
269
|
},
|
|
270
|
+
'syntax-invalid-literal': {
|
|
271
|
+
'std': 'Invalid literal',
|
|
272
|
+
'uneven-hex': 'A binary literal must have an even number of characters',
|
|
273
|
+
'invalid-hex': 'A binary literal must only contain characters 0-9, a-f and A-F',
|
|
274
|
+
'time': 'Expected time\'hh:mm:ss\' where hh, mm and the optional ss are numbers',
|
|
275
|
+
'date': 'Expected date\'YYYY-MM-DD\' where YYYY, MM and DD are numbers',
|
|
276
|
+
'timestamp': 'Expected timestamp\'YYYY-MM-DD hh:mm:ss.u…u\' where YYYY, MM, DD, hh, mm, ss and u are numbers (optional 1-7×u)',
|
|
277
|
+
},
|
|
278
|
+
'syntax-unexpected-null': 'Keyword $(KEYWORD) must appear after the enum definition and not before',
|
|
279
|
+
'syntax-unexpected-vocabulary': {
|
|
280
|
+
std: 'Annotations can\'t be defined inside contexts or services',
|
|
281
|
+
service: 'Annotations can\'t be defined inside services',
|
|
282
|
+
context: 'Annotations can\'t be defined inside contexts',
|
|
283
|
+
},
|
|
248
284
|
'ref-undefined-def': {
|
|
249
285
|
std: 'Artifact $(ART) has not been found',
|
|
250
286
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -266,6 +302,10 @@ const centralMessageTexts = {
|
|
|
266
302
|
mixin: 'Do not refer to a mixin like $(ID) in the explicit ON of a redirection',
|
|
267
303
|
alias: 'Do not refer to a source element (via table alias $(ID)) in the explicit ON of a redirection',
|
|
268
304
|
},
|
|
305
|
+
'ref-expected-element': {
|
|
306
|
+
std: 'Expected element reference',
|
|
307
|
+
magicVar: 'Only elements of magic variable $(ID) can be selected',
|
|
308
|
+
},
|
|
269
309
|
'type-unexpected-typeof': {
|
|
270
310
|
std: 'Unexpected $(KEYWORD) for the type reference here',
|
|
271
311
|
type: 'Unexpected $(KEYWORD) in type of a type definition',
|
|
@@ -423,7 +463,7 @@ const centralMessageTexts = {
|
|
|
423
463
|
* Whether the error can be reclassified to a warning or lower.
|
|
424
464
|
* If not `true` then an array is expected with specified modules in which the error is downgradable.
|
|
425
465
|
* Only has an effect if default severity is 'Error'.
|
|
426
|
-
* 'deprecated': severity can only be changed with deprecated.
|
|
466
|
+
* 'deprecated': severity can only be changed with deprecated._downgradableErrors.
|
|
427
467
|
* TODO: Value `true` is temporary. Use an array instead.
|
|
428
468
|
* @property {string[]} [errorFor] Array of module names where the message shall be reclassified to an error.
|
|
429
469
|
* @property {boolean} [throughMessageCall]
|
package/lib/base/messages.js
CHANGED
|
@@ -27,7 +27,7 @@ let test$texts = null;
|
|
|
27
27
|
/**
|
|
28
28
|
* Returns true if at least one of the given messages is of severity "Error".
|
|
29
29
|
*
|
|
30
|
-
* @param {
|
|
30
|
+
* @param {CompileMessage[]} messages
|
|
31
31
|
* @returns {boolean}
|
|
32
32
|
*/
|
|
33
33
|
function hasErrors( messages ) {
|
|
@@ -39,7 +39,7 @@ function hasErrors( messages ) {
|
|
|
39
39
|
* and *cannot* be reclassified to a warning for the given module.
|
|
40
40
|
* Won't detect already downgraded messages.
|
|
41
41
|
*
|
|
42
|
-
* @param {
|
|
42
|
+
* @param {CompileMessage[]} messages
|
|
43
43
|
* @param {string} moduleName
|
|
44
44
|
* @returns {boolean}
|
|
45
45
|
*/
|
|
@@ -319,20 +319,23 @@ function createMessageFunctions( options, moduleName, model = null ) {
|
|
|
319
319
|
* ```
|
|
320
320
|
* @param {object} model
|
|
321
321
|
* @param {CSN.Options} [options]
|
|
322
|
-
* @param {string} [moduleName]
|
|
322
|
+
* @param {string|null} [moduleName]
|
|
323
323
|
*/
|
|
324
324
|
function makeMessageFunction( model, options, moduleName = null ) {
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
if (options.testMode) {
|
|
326
|
+
// ensure message consistency during runtime with --test-mode
|
|
327
327
|
_check$Init( options );
|
|
328
|
+
if (!options.messages)
|
|
329
|
+
throw new CompilerAssertion('makeMessageFunction() expects options.messages to exist in testMode!');
|
|
330
|
+
}
|
|
328
331
|
|
|
329
332
|
const hasMessageArray = !!options.messages;
|
|
330
|
-
const deprecatedDowngradable = isDeprecatedEnabled( options, '
|
|
333
|
+
const deprecatedDowngradable = isDeprecatedEnabled( options, '_downgradableErrors' );
|
|
331
334
|
/**
|
|
332
335
|
* Array of collected compiler messages. Only use it for debugging. Will not
|
|
333
336
|
* contain the messages created during a `callTransparently` call.
|
|
334
337
|
*
|
|
335
|
-
* @type {
|
|
338
|
+
* @type {CompileMessage[]}
|
|
336
339
|
*/
|
|
337
340
|
let messages = options.messages || [];
|
|
338
341
|
/**
|
|
@@ -361,7 +364,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
361
364
|
const [ fileLocation, semanticLocation, definition ] = _normalizeMessageLocation(location);
|
|
362
365
|
const text = messageText( texts || centralMessageTexts[id], textOrArguments );
|
|
363
366
|
|
|
364
|
-
/** @type {
|
|
367
|
+
/** @type {CompileMessage} */
|
|
365
368
|
const msg = new CompileMessage( fileLocation, text, severity, id, semanticLocation, moduleName );
|
|
366
369
|
if (options.internalMsg)
|
|
367
370
|
msg.error = new Error( 'stack' );
|
|
@@ -553,7 +556,7 @@ function makeMessageFunction( model, options, moduleName = null ) {
|
|
|
553
556
|
*
|
|
554
557
|
* @param {Function} callback
|
|
555
558
|
* @param {...any} args
|
|
556
|
-
* @returns {
|
|
559
|
+
* @returns {CompileMessage[]}
|
|
557
560
|
*/
|
|
558
561
|
function callTransparently(callback, ...args) {
|
|
559
562
|
const backup = messages;
|
|
@@ -579,6 +582,22 @@ function _check$Init( options ) {
|
|
|
579
582
|
}
|
|
580
583
|
}
|
|
581
584
|
|
|
585
|
+
/**
|
|
586
|
+
* Check the consistency of the given message and run some basic lint checks. These include:
|
|
587
|
+
*
|
|
588
|
+
* - Long message IDs must be listed centrally.
|
|
589
|
+
* - Messages with the same ID must have the same severity (in a module).
|
|
590
|
+
* - Messages with the same ID must have the same message texts.
|
|
591
|
+
* This ensures that $(PLACEHOLDERS) are used and that we don't accidentally
|
|
592
|
+
* use the same ID for different meanings, i.e. texts.
|
|
593
|
+
*
|
|
594
|
+
* @param {string} id
|
|
595
|
+
* @param {string} moduleName
|
|
596
|
+
* @param {string} severity
|
|
597
|
+
* @param {string|object} texts
|
|
598
|
+
* @param {CSN.Options} options
|
|
599
|
+
* @private
|
|
600
|
+
*/
|
|
582
601
|
function _check$Consistency( id, moduleName, severity, texts, options ) {
|
|
583
602
|
if (id.length > 30 && !centralMessages[id])
|
|
584
603
|
throw new CompilerAssertion( `The message ID "${id}" has more than 30 chars and must be listed centrally` );
|
|
@@ -589,6 +608,16 @@ function _check$Consistency( id, moduleName, severity, texts, options ) {
|
|
|
589
608
|
_check$Texts( id, variant, text );
|
|
590
609
|
}
|
|
591
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Check the consistency of the message severity for the given message ID.
|
|
613
|
+
* Messages with the same ID must have the same severity (in a module).
|
|
614
|
+
* Non-downgradable errors must never be called with a lower severity.
|
|
615
|
+
*
|
|
616
|
+
* @param {string} id
|
|
617
|
+
* @param {string} moduleName
|
|
618
|
+
* @param {string} severity
|
|
619
|
+
* @private
|
|
620
|
+
*/
|
|
592
621
|
function _check$Severities( id, moduleName, severity ) {
|
|
593
622
|
if (!severity) // if just used message(), we are automatically consistent
|
|
594
623
|
return;
|
|
@@ -598,22 +627,34 @@ function _check$Severities( id, moduleName, severity ) {
|
|
|
598
627
|
if (!expected)
|
|
599
628
|
test$severities[id] = severity;
|
|
600
629
|
else if (expected !== severity)
|
|
601
|
-
throw new CompilerAssertion( `
|
|
630
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "${expected}" from previous call, not "${severity}" for message ID "${id}"` );
|
|
602
631
|
return;
|
|
603
632
|
}
|
|
604
633
|
// now try whether the message could be something less than an Error in the module due to user wishes
|
|
605
634
|
if (!isDowngradable( id, moduleName )) { // always an error in module
|
|
606
635
|
if (severity !== 'Error')
|
|
607
|
-
throw new CompilerAssertion( `
|
|
636
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "Error", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
608
637
|
}
|
|
609
638
|
else if (spec.severity === 'Error') {
|
|
610
|
-
throw new CompilerAssertion( `Expecting the use of function message() when message ID "${id}" is a configurable error in module "${moduleName}"` );
|
|
639
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting the use of function message() when message ID "${id}" is a configurable error in module "${moduleName}"` );
|
|
611
640
|
}
|
|
612
641
|
else if (spec.severity !== severity) {
|
|
613
|
-
throw new CompilerAssertion( `
|
|
642
|
+
throw new CompilerAssertion( `Inconsistent severity: Expecting "${spec.severity}", not "${severity}" for message ID "${id}" in module "${moduleName}"` );
|
|
614
643
|
}
|
|
615
644
|
}
|
|
616
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Check the consistency of the message text for the given message ID.
|
|
648
|
+
*
|
|
649
|
+
* Messages with the same ID must have the same message texts.
|
|
650
|
+
* This ensures that $(PLACEHOLDERS) are used and that we don't accidentally
|
|
651
|
+
* use the same ID for different meanings, i.e. texts.
|
|
652
|
+
*
|
|
653
|
+
* @param {string} id
|
|
654
|
+
* @param {string} prop
|
|
655
|
+
* @param {string} value
|
|
656
|
+
* @private
|
|
657
|
+
*/
|
|
617
658
|
function _check$Texts( id, prop, value ) {
|
|
618
659
|
if (!test$texts[id])
|
|
619
660
|
test$texts[id] = Object.create(null);
|
|
@@ -621,7 +662,7 @@ function _check$Texts( id, prop, value ) {
|
|
|
621
662
|
if (!expected)
|
|
622
663
|
test$texts[id][prop] = value;
|
|
623
664
|
else if (expected !== value)
|
|
624
|
-
throw new CompilerAssertion( `Expecting
|
|
665
|
+
throw new CompilerAssertion( `Different texts for the same message ID. Expecting "${expected}", not "${value}" for ID "${id}" and text variant "${prop}"`);
|
|
625
666
|
}
|
|
626
667
|
|
|
627
668
|
const quote = { // could be an option in the future
|
|
@@ -819,7 +860,7 @@ function weakLocation( loc ) {
|
|
|
819
860
|
* Example:
|
|
820
861
|
* <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
|
|
821
862
|
*
|
|
822
|
-
* @param {
|
|
863
|
+
* @param {CompileMessage} err
|
|
823
864
|
* @param {boolean} [normalizeFilename]
|
|
824
865
|
* @param {boolean} [noMessageId]
|
|
825
866
|
* @param {boolean} [noHome]
|
|
@@ -841,7 +882,7 @@ function messageString( err, normalizeFilename, noMessageId, noHome ) {
|
|
|
841
882
|
* Return message hash which is either the message string without the file location,
|
|
842
883
|
* or the full message string if no semantic location is provided.
|
|
843
884
|
*
|
|
844
|
-
* @param {
|
|
885
|
+
* @param {CompileMessage} msg
|
|
845
886
|
* @returns {string} can be used to uniquely identify a message
|
|
846
887
|
*/
|
|
847
888
|
function messageHash(msg) {
|
|
@@ -867,7 +908,7 @@ function messageHash(msg) {
|
|
|
867
908
|
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
868
909
|
* ```
|
|
869
910
|
*
|
|
870
|
-
* @param {
|
|
911
|
+
* @param {CompileMessage} err
|
|
871
912
|
* @param {object} [config = {}]
|
|
872
913
|
* @param {boolean} [config.normalizeFilename] Replace windows `\` with forward slashes `/`.
|
|
873
914
|
* @param {boolean} [config.noMessageId]
|
|
@@ -915,7 +956,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
915
956
|
*
|
|
916
957
|
* @param {string[]} sourceLines The source code split up into lines, e.g. by `splitLines(src)`
|
|
917
958
|
* from `lib/utils/file.js`
|
|
918
|
-
* @param {
|
|
959
|
+
* @param {CompileMessage} err Error object containing all details like line, message, etc.
|
|
919
960
|
* @param {object} [config = {}]
|
|
920
961
|
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
|
|
921
962
|
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
@@ -994,8 +1035,8 @@ function messageContext(sourceLines, err, config) {
|
|
|
994
1035
|
* larger than `b`, and -1 if `a` is smaller than `b`. Messages without a location
|
|
995
1036
|
* are considered larger than messages with a location.
|
|
996
1037
|
*
|
|
997
|
-
* @param {
|
|
998
|
-
* @param {
|
|
1038
|
+
* @param {CompileMessage} a
|
|
1039
|
+
* @param {CompileMessage} b
|
|
999
1040
|
*/
|
|
1000
1041
|
function compareMessage( a, b ) {
|
|
1001
1042
|
const aFile = a.$location && a.$location.file;
|
|
@@ -1029,8 +1070,8 @@ function compareMessage( a, b ) {
|
|
|
1029
1070
|
* location and severity, >0 if `a` is larger than `b`, and <0 if `a` is smaller
|
|
1030
1071
|
* than `b`. See `compareSeverities()` for how severities are compared.
|
|
1031
1072
|
*
|
|
1032
|
-
* @param {
|
|
1033
|
-
* @param {
|
|
1073
|
+
* @param {CompileMessage} a
|
|
1074
|
+
* @param {CompileMessage} b
|
|
1034
1075
|
*/
|
|
1035
1076
|
function compareMessageSeverityAware( a, b ) {
|
|
1036
1077
|
const c = compareSeverities(a.severity, b.severity);
|
|
@@ -1042,7 +1083,7 @@ function compareMessageSeverityAware( a, b ) {
|
|
|
1042
1083
|
* Messages without semantic locations are considered smaller (for syntax errors)
|
|
1043
1084
|
* and (currently - should not happen in v2) larger for other messages.
|
|
1044
1085
|
*
|
|
1045
|
-
* @param {
|
|
1086
|
+
* @param {CompileMessage} msg
|
|
1046
1087
|
*/
|
|
1047
1088
|
function homeSortName( { home, messageId } ) {
|
|
1048
1089
|
return (!home)
|
|
@@ -1061,7 +1102,7 @@ function homeSortName( { home, messageId } ) {
|
|
|
1061
1102
|
* A message is more precise if it is contained in the other or if
|
|
1062
1103
|
* the first does not have an endLine/endCol.
|
|
1063
1104
|
*
|
|
1064
|
-
* @param {
|
|
1105
|
+
* @param {CompileMessage[]} messages
|
|
1065
1106
|
*/
|
|
1066
1107
|
function deduplicateMessages( messages ) {
|
|
1067
1108
|
const seen = new Map();
|
package/lib/base/model.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { forEach } = require("../utils/objectUtils");
|
|
4
|
+
|
|
3
5
|
const queryOps = {
|
|
4
6
|
query: 'select', // TODO: rename to SELECT
|
|
5
7
|
union: 'union',
|
|
@@ -20,7 +22,6 @@ const queryOps = {
|
|
|
20
22
|
const availableBetaFlags = {
|
|
21
23
|
// enabled by --beta-mode
|
|
22
24
|
toRename: true,
|
|
23
|
-
addTextsLanguageAssoc: true,
|
|
24
25
|
assocsWithParams: true,
|
|
25
26
|
hanaAssocRealCardinality: true,
|
|
26
27
|
mapAssocToJoinCardinality: true,
|
|
@@ -58,16 +59,56 @@ function isBetaEnabled( options, feature ) {
|
|
|
58
59
|
* Please do not move this function to the "option processor" code.
|
|
59
60
|
*
|
|
60
61
|
* @param {object} options Options
|
|
61
|
-
* @param {string} [feature] Feature to check for
|
|
62
|
+
* @param {string|null} [feature] Feature to check for
|
|
62
63
|
* @returns {boolean}
|
|
63
64
|
*/
|
|
64
65
|
function isDeprecatedEnabled( options, feature = null ) {
|
|
65
66
|
const { deprecated } = options;
|
|
66
67
|
if (!feature)
|
|
67
68
|
return !!deprecated;
|
|
69
|
+
|
|
68
70
|
return deprecated && typeof deprecated === 'object' && deprecated[feature];
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
const oldDeprecatedFlags_v2 = [
|
|
74
|
+
'createLocalizedViews',
|
|
75
|
+
'downgradableErrors',
|
|
76
|
+
'generatedEntityNameWithUnderscore',
|
|
77
|
+
'longAutoexposed',
|
|
78
|
+
'noElementsExpansion',
|
|
79
|
+
'noInheritedAutoexposeViaComposition',
|
|
80
|
+
'noScopedRedirections',
|
|
81
|
+
'oldVirtualNotNullPropagation',
|
|
82
|
+
'parensAsStrings',
|
|
83
|
+
'projectionAsQuery',
|
|
84
|
+
'redirectInSubQueries',
|
|
85
|
+
'renderVirtualElements',
|
|
86
|
+
'shortAutoexposed',
|
|
87
|
+
'unmanagedUpInComponent',
|
|
88
|
+
'v1KeysForTemporal',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* In cds-compiler v3, we removed old v2 deprecated flags. That can lead to silent
|
|
93
|
+
* errors such as entity/view names changing. To ensure that the user is forced
|
|
94
|
+
* to change their code, emit an error if one of such removed flags was used.
|
|
95
|
+
*
|
|
96
|
+
* @param {CSN.Options} options
|
|
97
|
+
* @param error Error message function returned by makeMessageFunctions().
|
|
98
|
+
*/
|
|
99
|
+
function checkRemovedDeprecatedFlags( options, { error } ) {
|
|
100
|
+
// Assume that we emitted these errors once if a message with this ID was found.
|
|
101
|
+
if (!options.deprecated || options.messages?.some(m => m.messageId === 'api-invalid-deprecated'))
|
|
102
|
+
return;
|
|
103
|
+
|
|
104
|
+
forEach(options.deprecated, (key, val) => {
|
|
105
|
+
if (val && oldDeprecatedFlags_v2.includes(key)) {
|
|
106
|
+
error('api-invalid-deprecated', null, { name: key },
|
|
107
|
+
'Deprecated flag $(NAME) has been removed in CDS compiler v3');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
71
112
|
// Apply function `callback` to all artifacts in dictionary
|
|
72
113
|
// `model.definitions`. See function `forEachGeneric` for details.
|
|
73
114
|
function forEachDefinition( model, callback ) {
|
|
@@ -129,6 +170,7 @@ module.exports = {
|
|
|
129
170
|
isBetaEnabled,
|
|
130
171
|
availableBetaFlags,
|
|
131
172
|
isDeprecatedEnabled,
|
|
173
|
+
checkRemovedDeprecatedFlags,
|
|
132
174
|
queryOps,
|
|
133
175
|
forEachDefinition,
|
|
134
176
|
forEachMember,
|
|
@@ -16,8 +16,8 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
16
16
|
if (!(art.kind === 'action' || art.kind === 'function') && !art.actions)
|
|
17
17
|
return;
|
|
18
18
|
|
|
19
|
-
// const isMultiSchema = this.options.
|
|
20
|
-
// (this.options.
|
|
19
|
+
// const isMultiSchema = this.options.odataFormat === 'structured' &&
|
|
20
|
+
// (this.options.odataProxies || this.options.odataXServiceRefs);
|
|
21
21
|
|
|
22
22
|
const serviceName = this.csnUtils.getServiceName(artName);
|
|
23
23
|
if (!serviceName)
|
|
@@ -85,9 +85,11 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
85
85
|
* @param {string} actKind 'action' or 'function'
|
|
86
86
|
*/
|
|
87
87
|
function checkReturns(returns, currPath, actKind) {
|
|
88
|
-
const finalReturnType = returns.type ? this.csnUtils.
|
|
88
|
+
const finalReturnType = returns.type ? this.csnUtils.getFinalBaseTypeWithProps(returns.type) : returns;
|
|
89
|
+
if (!finalReturnType)
|
|
90
|
+
return; // no type, e.g. `type of V:calculated`; already an error in `checkTypeOfHasProperType()`
|
|
89
91
|
|
|
90
|
-
if (this.csnUtils.isAssocOrComposition(finalReturnType)) {
|
|
92
|
+
if (this.csnUtils.isAssocOrComposition(finalReturnType.type)) {
|
|
91
93
|
this.error(null, currPath, { '#': actKind },
|
|
92
94
|
{
|
|
93
95
|
std: 'An association is not allowed as this artifact\'s return type', // Not used
|
|
@@ -98,7 +100,7 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
98
100
|
|
|
99
101
|
if (finalReturnType.items) // check array return type
|
|
100
102
|
checkReturns.bind(this)(finalReturnType.items, currPath.concat('items'), actKind);
|
|
101
|
-
else // check if return type is user
|
|
103
|
+
else // check if return type is user defined from the current service
|
|
102
104
|
checkUserDefinedType.bind(this)(finalReturnType, returns.type, currPath);
|
|
103
105
|
}
|
|
104
106
|
|
|
@@ -23,7 +23,7 @@ function checkCoreMediaTypeAllowence(member) {
|
|
|
23
23
|
'cds.hana.CLOB': 1,
|
|
24
24
|
'cds.hana.BINARY': 1,
|
|
25
25
|
};
|
|
26
|
-
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.
|
|
26
|
+
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalBaseTypeWithProps(member.type)?.type in allowedCoreMediaTypes)) {
|
|
27
27
|
this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
|
|
28
28
|
'Element annotated with “@Core.MediaType” should be of a type mapped to $(NAMES)');
|
|
29
29
|
}
|
|
@@ -14,6 +14,7 @@ function validateCdsPersistenceAnnotation(artifact, artifactName, prop, path) {
|
|
|
14
14
|
if (artifact.kind === 'entity') {
|
|
15
15
|
// filter for 'table', 'udf', 'calcview' === true
|
|
16
16
|
const persistenceAnnos = [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ];
|
|
17
|
+
// TODO: Why not filter over persistenceAnnos, is shorter!
|
|
17
18
|
const TableUdfCv = Object.keys(artifact).filter(p => persistenceAnnos.includes(p) && artifact[p]);
|
|
18
19
|
if (TableUdfCv.length > 1)
|
|
19
20
|
this.error(null, path, `Annotations ${ TableUdfCv.join(', ') } can't be used in combination`);
|
package/lib/checks/elements.js
CHANGED
|
@@ -36,10 +36,10 @@ function checkPrimaryKey(art) {
|
|
|
36
36
|
*/
|
|
37
37
|
function checkIfPrimaryKeyIsOfGeoType(member, elemFqName, parentIsKey, parentPath) {
|
|
38
38
|
if (member.key || parentIsKey) {
|
|
39
|
-
const finalBaseType = this.csnUtils.
|
|
40
|
-
if (
|
|
39
|
+
const finalBaseType = this.csnUtils.getFinalBaseTypeWithProps(member.type);
|
|
40
|
+
if (isGeoTypeName(finalBaseType?.type)) {
|
|
41
41
|
this.error(null, parentPath || member.$path,
|
|
42
|
-
{ type: finalBaseType, name: elemFqName },
|
|
42
|
+
{ type: finalBaseType.type, name: elemFqName },
|
|
43
43
|
'Type $(TYPE) can\'t be used as primary key in element $(NAME)');
|
|
44
44
|
}
|
|
45
45
|
else if (finalBaseType && this.csnUtils.isStructured(finalBaseType)) {
|
|
@@ -63,7 +63,7 @@ function checkPrimaryKey(art) {
|
|
|
63
63
|
*/
|
|
64
64
|
function checkIfPrimaryKeyIsArray(member, elemFqName, parentIsKey, parentPath) {
|
|
65
65
|
if (member.key || parentIsKey) {
|
|
66
|
-
const finalBaseType = this.csnUtils.
|
|
66
|
+
const finalBaseType = this.csnUtils.getFinalBaseTypeWithProps(member.type);
|
|
67
67
|
if (member.items || (finalBaseType && finalBaseType.items)) {
|
|
68
68
|
this.error(null, parentPath || member.$path, { name: elemFqName },
|
|
69
69
|
'Array-like type in element $(NAME) can\'t be used as primary key');
|
|
@@ -96,10 +96,10 @@ function checkVirtualElement(member) {
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Checks whether managed associations
|
|
99
|
-
* with cardinality 'to many' have an on-condition
|
|
100
|
-
* and if managed associations have foreign keys.
|
|
99
|
+
* with cardinality 'to many' have an on-condition.
|
|
101
100
|
*
|
|
102
101
|
* @param {CSN.Artifact} art The artifact
|
|
102
|
+
* @todo this is a member validator, is it not?
|
|
103
103
|
*/
|
|
104
104
|
function checkManagedAssoc(art) {
|
|
105
105
|
forEachMemberRecursively(art, (member) => {
|
|
@@ -26,7 +26,7 @@ function invalidTarget(member) {
|
|
|
26
26
|
if (!target)
|
|
27
27
|
throw new ModelError(`Expected target ${ mem.target }`);
|
|
28
28
|
if (target.kind !== 'entity') {
|
|
29
|
-
const isAssoc = this.csnUtils.
|
|
29
|
+
const isAssoc = this.csnUtils.getFinalBaseTypeWithProps(member.type)?.type !== 'cds.Composition';
|
|
30
30
|
this.error(
|
|
31
31
|
null,
|
|
32
32
|
member.$path,
|
|
@@ -21,7 +21,7 @@ function nonexpandableStructuredInExpression(parent, name, expression) {
|
|
|
21
21
|
if (_art) {
|
|
22
22
|
_art = resolveArtifactType.call(this, _art);
|
|
23
23
|
// Paths of an expression may end on a structured element only if both operands in the expression end on a structured element
|
|
24
|
-
if (_art
|
|
24
|
+
if (_art?.elements && !validStructuredElement && $scope !== '$self') { // TODO: Use $self to navigate to struct
|
|
25
25
|
this.error(null, expression[i].$path, { elemref: { ref } },
|
|
26
26
|
'Unexpected usage of structured type $(ELEMREF)');
|
|
27
27
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const { hasAnnotationValue, isPersistedOnDatabase, isBuiltinType } = require('../model/csnUtils');
|
|
4
4
|
/**
|
|
5
5
|
* Make sure that all source artifacts and association targets reach the database
|
|
6
|
-
* (otherwise the view can't be activated), but only if the source artifact is NOT activated against the database
|
|
6
|
+
* (otherwise the view can't be activated), but only if the source artifact is NOT activated against the database // <- what does this mean?
|
|
7
|
+
*
|
|
7
8
|
* Check the given query for:
|
|
8
9
|
* - Associations-traversal over skipped/abstract things
|
|
9
10
|
* - Associations (indirectly) using managed associations without foreign keys
|
|
@@ -5,9 +5,13 @@ const { forEachGeneric } = require('../model/csnUtils');
|
|
|
5
5
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Validate select items of a query.
|
|
8
|
+
* Validate select items of a query. If a column reference starts with $self or $projection, it must not contain association steps.
|
|
9
|
+
* Furthermore, for to.hdbcds, window functions are not allowed.
|
|
10
|
+
*
|
|
11
|
+
* For to.hdbcds-hdbcds, structures and managed associations are not allowed as they are not flattened - @see rejectManagedAssociationsAndStructuresForHdbcdsNames
|
|
9
12
|
*
|
|
10
13
|
* @param {CSN.Query} query query object
|
|
14
|
+
* @todo Why do we care about this with $self?
|
|
11
15
|
*/
|
|
12
16
|
function validateSelectItems(query) {
|
|
13
17
|
const { SELECT } = query;
|
package/lib/checks/types.js
CHANGED
|
@@ -129,8 +129,8 @@ function checkTypeOfHasProperType(artOrElement, name, model, error, path, derive
|
|
|
129
129
|
if (!artOrElement.type)
|
|
130
130
|
return;
|
|
131
131
|
|
|
132
|
-
const {
|
|
133
|
-
const typeOfType =
|
|
132
|
+
const { getFinalBaseTypeWithProps } = getUtils(model);
|
|
133
|
+
const typeOfType = getFinalBaseTypeWithProps(artOrElement.type);
|
|
134
134
|
|
|
135
135
|
if (typeOfType === null) {
|
|
136
136
|
if (artOrElement.type.ref) {
|
|
@@ -157,6 +157,7 @@ function checkTypeOfHasProperType(artOrElement, name, model, error, path, derive
|
|
|
157
157
|
* @param {CSN.Path} path the path to the element or the artifact
|
|
158
158
|
* @param {string} name of the element or the artifact which is dubious
|
|
159
159
|
* @param {boolean} isElement indicates whether we are dealing with an element or an artifact
|
|
160
|
+
* @todo Rename, is an error not a warning
|
|
160
161
|
*/
|
|
161
162
|
function warnAboutMissingType(error, path, name, isElement = false) {
|
|
162
163
|
error('check-proper-type', path, { art: name, '#': isElement ? 'elm' : 'std' }, {
|
|
@@ -173,6 +174,7 @@ function warnAboutMissingType(error, path, name, isElement = false) {
|
|
|
173
174
|
*
|
|
174
175
|
* @param {CSN.Artifact} artifact the artifact to check
|
|
175
176
|
* @returns {boolean} indicates whether the artifact has type information
|
|
177
|
+
* @todo What is the point of isBuiltinType here if we check for artifact.type at the end?
|
|
176
178
|
*/
|
|
177
179
|
function hasArtifactTypeInformation(artifact) {
|
|
178
180
|
// When is what property set?
|
package/lib/checks/utils.js
CHANGED
|
@@ -31,7 +31,7 @@ function otherSideIsExpandableStructure(on, startIndex) {
|
|
|
31
31
|
return false;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Artifact is structured or a managed association/
|
|
34
|
+
* Artifact is structured or a managed association/composition
|
|
35
35
|
*
|
|
36
36
|
* @param {CSN.Artifact} art Artifact
|
|
37
37
|
* @returns {boolean} True if expandable
|
|
@@ -49,7 +49,7 @@ function otherSideIsExpandableStructure(on, startIndex) {
|
|
|
49
49
|
*/
|
|
50
50
|
function resolveArtifactType(art) {
|
|
51
51
|
if (art && art.type && !isBuiltinType(art.type))
|
|
52
|
-
return this.
|
|
52
|
+
return this.csnUtils.getFinalBaseTypeWithProps(art.type);
|
|
53
53
|
|
|
54
54
|
return art;
|
|
55
55
|
}
|