@sap/cds-compiler 2.5.2 → 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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/base/dictionaries.js
CHANGED
|
@@ -28,6 +28,7 @@ function dictAdd( dict, name, entry, duplicateCallback ) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function dictForEach( dict, callback ) {
|
|
31
|
+
// TODO: probably define an extra dictForEachArray()
|
|
31
32
|
for (const name in dict) {
|
|
32
33
|
const entry = dict[name];
|
|
33
34
|
if (Array.isArray(entry)) {
|
|
@@ -35,7 +36,7 @@ function dictForEach( dict, callback ) {
|
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
38
|
callback( entry );
|
|
38
|
-
if (Array.isArray(entry.$duplicates))
|
|
39
|
+
if (Array.isArray( entry.$duplicates ))
|
|
39
40
|
entry.$duplicates.forEach( callback );
|
|
40
41
|
}
|
|
41
42
|
}
|
package/lib/base/keywords.js
CHANGED
|
@@ -194,7 +194,7 @@ module.exports = {
|
|
|
194
194
|
'OTHERS',
|
|
195
195
|
'TIES',
|
|
196
196
|
],
|
|
197
|
-
// HANA keywords, used
|
|
197
|
+
// HANA keywords, used for smart quoting in to-hdi.plain
|
|
198
198
|
// Taken from https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/28bcd6af3eb6437892719f7c27a8a285.html
|
|
199
199
|
// Better use keywords in ptime/query/parser/syntax/qp_keyword.cc minus those
|
|
200
200
|
// in rule unreserved_keyword_column (=…_common - "CONSTRAINT") in
|
|
@@ -673,5 +673,35 @@ module.exports = {
|
|
|
673
673
|
'WITHIN',
|
|
674
674
|
'XMLTABLE',
|
|
675
675
|
'YEAR'
|
|
676
|
-
]
|
|
676
|
+
],
|
|
677
|
+
// HANA CDS keywords, used for smart quoting in to-hdbcds.plain
|
|
678
|
+
hdbcds: [
|
|
679
|
+
'ALL', 'ALTER', 'AS',
|
|
680
|
+
'BEFORE', 'BEGIN', 'BOTH',
|
|
681
|
+
'CASE', 'CHAR', 'CONDITION',
|
|
682
|
+
'CONNECT', 'CROSS', 'CUBE',
|
|
683
|
+
'CURRENT_CONNECTION', 'CURRENT_DATE', 'CURRENT_SCHEMA',
|
|
684
|
+
'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER',
|
|
685
|
+
'CURRENT_UTCDATE', 'CURRENT_UTCTIME', 'CURRENT_UTCTIMESTAMP',
|
|
686
|
+
'CURRVAL', 'CURSOR', 'DECLARE',
|
|
687
|
+
'DISTINCT', 'ELSE', 'ELSEIF',
|
|
688
|
+
'ELSIF', 'END', 'EXCEPT',
|
|
689
|
+
'EXCEPTION', 'EXEC', 'FOR',
|
|
690
|
+
'FROM', 'FULL', 'GROUP',
|
|
691
|
+
'HAVING', 'IF', 'IN',
|
|
692
|
+
'INNER', 'INOUT', 'INTERSECT',
|
|
693
|
+
'INTO', 'IS', 'JOIN',
|
|
694
|
+
'LEADING', 'LEFT', 'LIMIT',
|
|
695
|
+
'LOOP', 'MINUS', 'NATURAL',
|
|
696
|
+
'NEXTVAL', 'NULL', 'ON',
|
|
697
|
+
'ORDER', 'OUT', 'OUTER',
|
|
698
|
+
'PRIOR', 'RETURN', 'RETURNS',
|
|
699
|
+
'REVERSE', 'RIGHT', 'ROLLUP',
|
|
700
|
+
'ROWID', 'SELECT', 'SET',
|
|
701
|
+
'SQL', 'START', 'SYSDATE',
|
|
702
|
+
'SYSTIME', 'SYSTIMESTAMP', 'SYSUUID',
|
|
703
|
+
'TOP', 'TRAILING', 'UNION',
|
|
704
|
+
'USING', 'VALUES', 'WHEN',
|
|
705
|
+
'WHERE', 'WHILE', 'WITH'
|
|
706
|
+
]
|
|
677
707
|
}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
// Central registry for messages.
|
|
2
2
|
|
|
3
|
+
// `centralMessages` contains all details of a message-id except its standard texts
|
|
4
|
+
// (`standardTexts` exists for that). Only `severity` is required, all other
|
|
5
|
+
// properties are optional.
|
|
6
|
+
|
|
7
|
+
// The user can specify "severity wishes" via the option `severities`. Errors
|
|
8
|
+
// that don't have a `configurableFor` property cannot be reclassified by
|
|
9
|
+
// users. If a module is used that is _not_ listed in `configurableFor` (if it
|
|
10
|
+
// is an array) property of the message then the message cannot be
|
|
11
|
+
// reclassified.
|
|
12
|
+
|
|
13
|
+
// We also allow `configurableFor` to have value `true` for errors which are
|
|
14
|
+
// always configurable; useful for issues like deprecated syntax variants which
|
|
15
|
+
// do not affect the compiler or CSN processors. Temporarily, we also allow
|
|
16
|
+
// value `deprecated` for errors which are only configurable if the option
|
|
17
|
+
// `deprecated.downgradableErrors` is set.
|
|
18
|
+
|
|
19
|
+
// Messages other than errors can always be reclassified by the user except if
|
|
20
|
+
// the module is listed in the message's `errorFor` property.
|
|
21
|
+
|
|
22
|
+
// __NEW__: If the future `poc` (proof of concept) or `sloppy` option is set,
|
|
23
|
+
// the module name `compile` is added to all configurable messages, i.e. to all
|
|
24
|
+
// `configurableFor` arrays. (module `compile` includes all parsers and the
|
|
25
|
+
// core compiler). This allows creators of _non-productive models_ to
|
|
26
|
+
// reclassify errors which usually cannot be reclassified, and continue the
|
|
27
|
+
// compilation but has the side effect that the result may be unstable, hence
|
|
28
|
+
// "sloppy": with an upcoming _minor_ version of the compiler, the compilation
|
|
29
|
+
// might lead to an error anyway or the compiled CSN might look different.
|
|
30
|
+
|
|
3
31
|
'use strict';
|
|
4
32
|
|
|
5
33
|
/**
|
|
@@ -59,13 +87,15 @@ const centralMessages = {
|
|
|
59
87
|
'param-default': { severity: 'Error', configurableFor: 'deprecated' }, // not supported yet
|
|
60
88
|
|
|
61
89
|
'query-undefined-element': { severity: 'Error' },
|
|
90
|
+
'query-unexpected-assoc-hdbcds': { severity: 'Error' },
|
|
91
|
+
'query-unexpected-structure-hdbcds': { severity: 'Error' },
|
|
62
92
|
|
|
63
93
|
'recalculated-localized': { severity: 'Info' }, // KEEP: Downgrade in lib/transform/translateAssocsToJoins.js
|
|
64
94
|
'redirected-implicitly-ambiguous': { severity: 'Error', configurableFor: true }, // does not hurt us - TODO: ref-ambiguous-target
|
|
65
95
|
'type-ambiguous-target': { severity: 'Warning' },
|
|
66
96
|
|
|
67
97
|
'ref-autoexposed': { severity: 'Error', configurableFor: 'deprecated' },
|
|
68
|
-
'ref-undefined-art': { severity: 'Error' },
|
|
98
|
+
'ref-undefined-art': { severity: 'Error' },
|
|
69
99
|
'ref-undefined-def': { severity: 'Error' },
|
|
70
100
|
'ref-undefined-var': { severity: 'Error' },
|
|
71
101
|
'ref-undefined-element': { severity: 'Error' },
|
|
@@ -88,6 +118,7 @@ const centralMessages = {
|
|
|
88
118
|
'syntax-anno-after-params': { severity: 'Error', configurableFor: true }, // does not hurt
|
|
89
119
|
'syntax-anno-after-struct': { severity: 'Error', configurableFor: true }, // does not hurt
|
|
90
120
|
'syntax-csn-expected-cardinality': { severity: 'Error' }, // TODO: more than 30 chars
|
|
121
|
+
'syntax-csn-expected-length': { severity: 'Error' },
|
|
91
122
|
'syntax-csn-expected-translation': { severity: 'Error' }, // TODO: more than 30 chars
|
|
92
123
|
'syntax-csn-required-subproperty': { severity: 'Error' }, // TODO: more than 30 chars
|
|
93
124
|
'syntax-csn-unexpected-property': { severity: 'Error', configurableFor: true }, // is the removed
|
|
@@ -100,8 +131,13 @@ const centralMessages = {
|
|
|
100
131
|
'unexpected-keys-for-composition': { severity: 'Error' }, // TODO: more than 30 chars
|
|
101
132
|
'unmanaged-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing
|
|
102
133
|
'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
|
|
103
|
-
'odata-spec-violation-array
|
|
104
|
-
'odata-spec-violation-constraints': { severity: '
|
|
134
|
+
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
135
|
+
'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
|
|
136
|
+
'odata-spec-violation-type': { severity: 'Error', configurableFor: [ 'to.edmx' ] },
|
|
137
|
+
'odata-spec-violation-no-key': { severity: 'Warning' },
|
|
138
|
+
'odata-spec-violation-key-array': { severity: 'Error' }, // more than 30 chars
|
|
139
|
+
'odata-spec-violation-key-null': { severity: 'Error' }, // more than 30 chars
|
|
140
|
+
'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
|
|
105
141
|
'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
|
|
106
142
|
'odata-spec-violation-namespace-name': { severity: 'Warning' }, // more than 30 chars
|
|
107
143
|
};
|
|
@@ -126,6 +162,11 @@ const centralMessageTexts = {
|
|
|
126
162
|
$tableImplicit: 'The resulting table alias starts with $(NAME) and might shadow a special variable - specify another name with $(KEYWORD)',
|
|
127
163
|
mixin: 'A mixin name starting with $(NAME) might shadow a special variable - replace by another name' ,
|
|
128
164
|
},
|
|
165
|
+
'syntax-csn-expected-length': {
|
|
166
|
+
std: 'Expected array in $(PROP) to have at least $(N) items',
|
|
167
|
+
one: 'Expected array in $(PROP) to have at least one item',
|
|
168
|
+
suffix: 'With sibling property $(OTHERPROP), expected array in $(PROP) to have at least one item',
|
|
169
|
+
},
|
|
129
170
|
'ref-undefined-def': {
|
|
130
171
|
std: 'Artifact $(ART) has not been found',
|
|
131
172
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -169,13 +210,13 @@ const centralMessageTexts = {
|
|
|
169
210
|
'duplicate-definition': {
|
|
170
211
|
std: 'Duplicate definition of $(NAME)',
|
|
171
212
|
absolute: 'Duplicate definition of artifact $(NAME)',
|
|
172
|
-
|
|
213
|
+
annotation: 'Duplicate definition of annotation vocabulary $(NAME)',
|
|
173
214
|
element: 'Duplicate definition of element $(NAME)',
|
|
174
215
|
enum: 'Duplicate definition of enum $(NAME)',
|
|
175
216
|
key: 'Duplicate definition of key $(NAME)',
|
|
176
217
|
action: 'Duplicate definition of action or function $(NAME)',
|
|
177
218
|
param: 'Duplicate definition of parameter $(NAME)',
|
|
178
|
-
|
|
219
|
+
alias: 'Duplicate definition of table alias or mixin $(NAME)',
|
|
179
220
|
},
|
|
180
221
|
|
|
181
222
|
// TODO: rename to ref-expected-XYZ
|
|
@@ -191,6 +232,10 @@ const centralMessageTexts = {
|
|
|
191
232
|
'expected-target': 'An entity or an aspect is expected here',
|
|
192
233
|
'extend-columns': 'Artifact $(ART) can\'t be extended with columns, only projections can',
|
|
193
234
|
'extend-repeated-intralayer': 'Unstable element order due to repeated extensions in same layer',
|
|
235
|
+
|
|
236
|
+
'query-unexpected-assoc-hdbcds': 'Publishing a managed association in a view is not possible for “hdbcds” naming mode',
|
|
237
|
+
'query-unexpected-structure-hdbcds': 'Publishing a structured element in a view is not possible for “hdbcds” naming mode',
|
|
238
|
+
|
|
194
239
|
'ref-sloppy-type': 'A type or an element is expected here',
|
|
195
240
|
'ref-sloppy-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
196
241
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
@@ -204,13 +249,30 @@ const centralMessageTexts = {
|
|
|
204
249
|
},
|
|
205
250
|
|
|
206
251
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
207
|
-
'odata-spec-violation-array-of-v2': 'EDM $(LITERAL) must not have an attribute $(PROP) with the value $(NAME)',
|
|
208
|
-
'odata-spec-violation-param-v2' : 'Type of EDM FunctionImport Parameter must be an EDM SimpleType or ComplexType',
|
|
209
|
-
'odata-spec-violation-returns-v2': 'EDM FunctionImport must return EDM SimpleType, ComplexType, EntityType or a collection of one of these',
|
|
210
|
-
'odata-spec-violation-assoc-v2': 'EDM ComplexType $(NAME) must not contain an EDM NavigationProperty',
|
|
211
|
-
'odata-spec-violation-attribute': 'EDM Property $(NAME) has no attribute $(PROP)',
|
|
212
|
-
'odata-spec-violation-property-name': 'EDM Property $(NAME) must not have the same name as the declaring $(TYPE)',
|
|
213
252
|
|
|
253
|
+
// OData version dependent messages
|
|
254
|
+
'odata-spec-violation-array': 'Unexpected array type for $(API)',
|
|
255
|
+
'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for $(API)',
|
|
256
|
+
'odata-spec-violation-returns': 'Expected $(KIND) to return one or many values of scalar, complex, entity or view type for $(API)',
|
|
257
|
+
'odata-spec-violation-assoc': 'Unexpected association in structured type for $(API)',
|
|
258
|
+
'odata-spec-violation-constraints': 'Partial referential constraints produced for $(API)',
|
|
259
|
+
// version independent messages
|
|
260
|
+
'odata-spec-violation-key-array': {
|
|
261
|
+
std: 'Unexpected array type for element $(NAME)',
|
|
262
|
+
scalar: 'Unexpected array type'
|
|
263
|
+
},
|
|
264
|
+
'odata-spec-violation-key-null': {
|
|
265
|
+
std: 'Expected key element $(NAME) to be not nullable', // structured
|
|
266
|
+
scalar: 'Expected key element to be not nullable' // flat
|
|
267
|
+
},
|
|
268
|
+
'odata-spec-violation-key-type': {
|
|
269
|
+
std: 'Unexpected $(TYPE) mapped to $(ID) as type for key element $(NAME)', // structured
|
|
270
|
+
scalar: 'Unexpected $(TYPE) mapped to $(ID) as type for key element' // flat
|
|
271
|
+
},
|
|
272
|
+
'odata-spec-violation-no-key': 'Expected entity to have a primary key',
|
|
273
|
+
'odata-spec-violation-type': 'Expected element to have a type',
|
|
274
|
+
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
|
|
275
|
+
'odata-spec-violation-namespace': 'Expected service name not to be one of the reserved names $(NAMES)',
|
|
214
276
|
}
|
|
215
277
|
|
|
216
278
|
/**
|
package/lib/base/messages.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Functions and classes for syntax messages
|
|
2
2
|
|
|
3
|
+
// See internalDoc/ReportingMessages.md and lib/base/message-registry.js for details.
|
|
4
|
+
|
|
3
5
|
'use strict';
|
|
4
6
|
|
|
5
|
-
const term = require('../utils/term');
|
|
7
|
+
const { term } = require('../utils/term');
|
|
6
8
|
const { locationString } = require('./location');
|
|
7
9
|
const { isDeprecatedEnabled } = require('./model');
|
|
8
10
|
const { centralMessages, centralMessageTexts } = require('./message-registry');
|
|
@@ -13,6 +15,8 @@ const { analyseCsnPath, traverseQuery } = require('../model/csnRefs');
|
|
|
13
15
|
const fs = require('fs');
|
|
14
16
|
const path = require('path');
|
|
15
17
|
|
|
18
|
+
// term instance for messages
|
|
19
|
+
const colorTerm = term();
|
|
16
20
|
|
|
17
21
|
// Functions ensuring message consistency during runtime with --test-mode
|
|
18
22
|
|
|
@@ -92,14 +96,17 @@ class CompilationError extends Error {
|
|
|
92
96
|
|
|
93
97
|
/** @type {boolean} model */
|
|
94
98
|
this.hasBeenReported = false; // TODO: remove this bin/cdsc.js specifics
|
|
95
|
-
//
|
|
99
|
+
// property `model` is only set with options.attachValidNames:
|
|
96
100
|
Object.defineProperty( this, 'model', { value: model || undefined, configurable: true } );
|
|
97
101
|
}
|
|
98
102
|
toString() { // does not really help -> set message
|
|
99
103
|
return this.message.includes('\n')
|
|
100
104
|
? this.message
|
|
101
|
-
: this.message + '\n' + this.
|
|
105
|
+
: this.message + '\n' + this.messages.map( m => m.toString() ).join('\n');
|
|
102
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* @deprecated Use `.messages` instead.
|
|
109
|
+
*/
|
|
103
110
|
get errors() {
|
|
104
111
|
return this.messages;
|
|
105
112
|
}
|
|
@@ -210,6 +217,7 @@ const severitySpecs = {
|
|
|
210
217
|
* @returns {CSN.MessageSeverity}
|
|
211
218
|
*
|
|
212
219
|
* TODO: we should pass options as usual
|
|
220
|
+
* TODO: should be part of the returned function
|
|
213
221
|
*/
|
|
214
222
|
function reclassifiedSeverity( id, severity, severities, moduleName, deprecatedDowngradable ) {
|
|
215
223
|
const spec = centralMessages[id] || { severity };
|
|
@@ -294,6 +302,30 @@ function searchForLocation( model, path ) {
|
|
|
294
302
|
return lastLocation;
|
|
295
303
|
}
|
|
296
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Create the `message` functions to emit messages.
|
|
307
|
+
* See internalDoc/ReportingMessages.md for detail
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```
|
|
311
|
+
* const { createMessageFunctions } = require(‘../base/messages’);
|
|
312
|
+
* function module( …, options ) {
|
|
313
|
+
* const { message, info, throwWithError } = createMessageFunctions( options, moduleName );
|
|
314
|
+
* // [...]
|
|
315
|
+
* message( 'message-id', <location>, <text-arguments>, <severity>, <text> );
|
|
316
|
+
* info( 'message-id', <location>, [<text-arguments>,] <text> );
|
|
317
|
+
* // [...]
|
|
318
|
+
* throwWithError();
|
|
319
|
+
* }
|
|
320
|
+
* ```
|
|
321
|
+
* @param {CSN.Options} [options]
|
|
322
|
+
* @param {string} [moduleName]
|
|
323
|
+
* @param {object} [model=null] the CSN or XSN model, used for convenience
|
|
324
|
+
*/
|
|
325
|
+
function createMessageFunctions( options, moduleName, model = null ) {
|
|
326
|
+
return makeMessageFunction( model, options, moduleName, true );
|
|
327
|
+
}
|
|
328
|
+
|
|
297
329
|
/**
|
|
298
330
|
* Create the `message` function to emit messages.
|
|
299
331
|
*
|
|
@@ -312,8 +344,9 @@ function searchForLocation( model, path ) {
|
|
|
312
344
|
* @param {object} model
|
|
313
345
|
* @param {CSN.Options} [options]
|
|
314
346
|
* @param {string} [moduleName]
|
|
347
|
+
* @param {boolean} [throwOnlyWithNew=false] behave like createMessageFunctions
|
|
315
348
|
*/
|
|
316
|
-
function makeMessageFunction( model, options =
|
|
349
|
+
function makeMessageFunction( model, options, moduleName = null, throwOnlyWithNew = false ) {
|
|
317
350
|
// ensure message consistency during runtime with --test-mode
|
|
318
351
|
if (options.testMode)
|
|
319
352
|
_check$Init( options );
|
|
@@ -327,11 +360,12 @@ function makeMessageFunction( model, options = model.options || {}, moduleName =
|
|
|
327
360
|
*
|
|
328
361
|
* @type {CSN.Message[]}
|
|
329
362
|
*/
|
|
330
|
-
let messages = options
|
|
331
|
-
|
|
363
|
+
let messages = options.messages || [];
|
|
364
|
+
let hasNewError = false;
|
|
332
365
|
return {
|
|
333
|
-
message, error, warning, info, debug,
|
|
334
|
-
|
|
366
|
+
message, error, warning, info, debug, messages,
|
|
367
|
+
throwWithError: (throwOnlyWithNew ? throwWithError : throwWithAnyError),
|
|
368
|
+
callTransparently, moduleName,
|
|
335
369
|
};
|
|
336
370
|
|
|
337
371
|
function _message(id, location, textOrArguments, severity, texts = null) {
|
|
@@ -342,7 +376,6 @@ function makeMessageFunction( model, options = model.options || {}, moduleName =
|
|
|
342
376
|
texts = { std: textOrArguments };
|
|
343
377
|
textOrArguments = {};
|
|
344
378
|
}
|
|
345
|
-
|
|
346
379
|
if (id) {
|
|
347
380
|
if (options.testMode && !options.$recompile)
|
|
348
381
|
_check$Consistency( id, moduleName, severity, texts, options )
|
|
@@ -360,6 +393,8 @@ function makeMessageFunction( model, options = model.options || {}, moduleName =
|
|
|
360
393
|
msg.$location.address = { definition };
|
|
361
394
|
|
|
362
395
|
messages.push( msg );
|
|
396
|
+
hasNewError = hasNewError || msg.severity === 'Error' &&
|
|
397
|
+
!(options.testMode && msg.messageId && isDowngradable( msg.messageId, moduleName ));
|
|
363
398
|
if (!hasMessageArray)
|
|
364
399
|
console.error( messageString( msg ) );
|
|
365
400
|
return msg;
|
|
@@ -488,6 +523,11 @@ function makeMessageFunction( model, options = model.options || {}, moduleName =
|
|
|
488
523
|
return _message(id, location, textOrArguments, 'Debug', texts);
|
|
489
524
|
}
|
|
490
525
|
|
|
526
|
+
function throwWithError() {
|
|
527
|
+
if (hasNewError)
|
|
528
|
+
throw new CompilationError( messages, options.attachValidNames && model );
|
|
529
|
+
}
|
|
530
|
+
|
|
491
531
|
/**
|
|
492
532
|
* Throws a CompilationError exception if there is at least one error message
|
|
493
533
|
* in the model's messages after reclassifying existing messages according to
|
|
@@ -495,22 +535,20 @@ function makeMessageFunction( model, options = model.options || {}, moduleName =
|
|
|
495
535
|
* If `--test-mode` is enabled, this function will only throw if the
|
|
496
536
|
* error *cannot* be downgraded to a warning. This is done to ensure that
|
|
497
537
|
* developers do not rely on certain errors leading to an exception.
|
|
498
|
-
*
|
|
499
|
-
* @param {CSN.Message[]} [msgs] Which messages to check for. Default: The ones of
|
|
500
|
-
* this makeMessageFunction() scope.
|
|
501
538
|
*/
|
|
502
|
-
function
|
|
503
|
-
if (!
|
|
539
|
+
function throwWithAnyError() {
|
|
540
|
+
if (!messages || !messages.length)
|
|
504
541
|
return;
|
|
505
|
-
reclassifyMessagesForModule(
|
|
542
|
+
reclassifyMessagesForModule( messages, severities, moduleName ); // TODO: no, at the beginning of the module
|
|
506
543
|
const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
|
|
507
|
-
if (hasError(
|
|
508
|
-
throw new CompilationError(
|
|
544
|
+
if (hasError( messages, moduleName ))
|
|
545
|
+
throw new CompilationError( messages, options.attachValidNames && model );
|
|
509
546
|
}
|
|
510
547
|
|
|
511
548
|
/**
|
|
512
549
|
* Collects all messages during the call of the callback function instead of
|
|
513
550
|
* storing them in the model. Returns the collected messages.
|
|
551
|
+
* Not yet in use.
|
|
514
552
|
*
|
|
515
553
|
* @param {Function} callback
|
|
516
554
|
* @param {...any} args
|
|
@@ -613,6 +651,8 @@ const paramsTransform = {
|
|
|
613
651
|
// more complex convenience:
|
|
614
652
|
names: transformManyWith( quoted ),
|
|
615
653
|
number: n => n,
|
|
654
|
+
line: l => l,
|
|
655
|
+
col: c => c,
|
|
616
656
|
literal: l => l,
|
|
617
657
|
art: transformArg,
|
|
618
658
|
service: transformArg,
|
|
@@ -815,19 +855,26 @@ function messageHash(msg) {
|
|
|
815
855
|
*
|
|
816
856
|
* Example:
|
|
817
857
|
* ```txt
|
|
818
|
-
* Error[message-id]: Can't find type `nu` in this scope
|
|
858
|
+
* Error[message-id]: Can't find type `nu` in this scope
|
|
819
859
|
* |
|
|
820
|
-
* <source>.cds:3:11, at entity:“E”
|
|
860
|
+
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
821
861
|
* ```
|
|
862
|
+
*
|
|
822
863
|
* @param {CSN.Message} err
|
|
823
864
|
* @param {object} [config = {}]
|
|
824
865
|
* @param {boolean} [config.normalizeFilename] Replace windows `\` with forward slashes `/`.
|
|
825
866
|
* @param {boolean} [config.noMessageId]
|
|
826
867
|
* @param {boolean} [config.hintExplanation] If true, messages with explanations will get a "…" marker.
|
|
827
|
-
* @param {boolean} [config.withLineSpacer]
|
|
868
|
+
* @param {boolean} [config.withLineSpacer] If true, an additional line (with `|`) will be inserted between message and location.
|
|
869
|
+
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
870
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
871
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
872
|
+
* unset.
|
|
828
873
|
* @returns {string}
|
|
829
874
|
*/
|
|
830
875
|
function messageStringMultiline( err, config = {} ) {
|
|
876
|
+
colorTerm.changeColorMode(config ? config.color : 'auto');
|
|
877
|
+
|
|
831
878
|
const explainHelp = (config.hintExplanation && hasMessageExplanation(err.messageId)) ? '…' : '';
|
|
832
879
|
const msgId = (err.messageId && !config.noMessageId) ? `[${ err.messageId }${ explainHelp }]` : '';
|
|
833
880
|
const home = !err.home ? '' : ('at ' + err.home);
|
|
@@ -840,7 +887,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
840
887
|
location += ', '
|
|
841
888
|
}
|
|
842
889
|
else if (!home)
|
|
843
|
-
return
|
|
890
|
+
return colorTerm.severity(severity, severity + msgId) + ' ' + err.message;
|
|
844
891
|
|
|
845
892
|
let lineSpacer = '';
|
|
846
893
|
if (config.withLineSpacer) {
|
|
@@ -848,8 +895,7 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
848
895
|
lineSpacer = `\n ${ ' '.repeat( additionalIndent ) }|`;
|
|
849
896
|
}
|
|
850
897
|
|
|
851
|
-
|
|
852
|
-
return term.asSeverity(severity, severity + msgId) + ': ' + err.message + lineSpacer + '\n ' + location + home;
|
|
898
|
+
return colorTerm.severity(severity, severity + msgId) + ': ' + err.message + lineSpacer + '\n ' + location + home;
|
|
853
899
|
}
|
|
854
900
|
|
|
855
901
|
/**
|
|
@@ -863,9 +909,15 @@ function messageStringMultiline( err, config = {} ) {
|
|
|
863
909
|
* @param {string[]} sourceLines The source code split up into lines, e.g. by `splitLines(src)`
|
|
864
910
|
* from `lib/utils/file.js`
|
|
865
911
|
* @param {CSN.Message} err Error object containing all details like line, message, etc.
|
|
912
|
+
* @param {object} [config = {}]
|
|
913
|
+
* @param {boolean | 'auto'} [config.color] If true, ANSI escape codes will be used for coloring the `^`. If false, no
|
|
914
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
915
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
916
|
+
* unset.
|
|
866
917
|
* @returns {string}
|
|
867
918
|
*/
|
|
868
|
-
function messageContext(sourceLines, err) {
|
|
919
|
+
function messageContext(sourceLines, err, config) {
|
|
920
|
+
colorTerm.changeColorMode(config ? config.color : 'auto');
|
|
869
921
|
const MAX_COL_LENGTH = 100;
|
|
870
922
|
|
|
871
923
|
const loc = err.$location;
|
|
@@ -917,7 +969,7 @@ function messageContext(sourceLines, err) {
|
|
|
917
969
|
// Indicate that the error is further to the right.
|
|
918
970
|
if (endColumn === MAX_COL_LENGTH)
|
|
919
971
|
highlighter = highlighter.replace(' ^', '..^');
|
|
920
|
-
msg += indent + '| ' +
|
|
972
|
+
msg += indent + '| ' + colorTerm.severity(severity, highlighter);
|
|
921
973
|
|
|
922
974
|
} else if (maxLine !== endLine) {
|
|
923
975
|
// error spans more lines which we don't print
|
|
@@ -993,12 +1045,12 @@ function homeSortName( { home, messageId } ) {
|
|
|
993
1045
|
|
|
994
1046
|
/**
|
|
995
1047
|
* Removes duplicate messages from the given messages array without destroying
|
|
996
|
-
* references to the array.
|
|
1048
|
+
* references to the array, i.e. removes them in-place.
|
|
997
1049
|
*
|
|
998
|
-
* Does NOT keep the original order!
|
|
1050
|
+
* _Note_: Does NOT keep the original order!
|
|
999
1051
|
*
|
|
1000
1052
|
* Two messages are the same if they have the same message hash. See messageHash().
|
|
1001
|
-
* If one of the two is more precise then it replaces the other.
|
|
1053
|
+
* If one of the two is more precise, then it replaces the other.
|
|
1002
1054
|
* A message is more precise if it is contained in the other or if
|
|
1003
1055
|
* the first does not have an endLine/endCol.
|
|
1004
1056
|
*
|
|
@@ -1197,7 +1249,10 @@ function constructSemanticLocationFromCsnPath(csnPath, model) {
|
|
|
1197
1249
|
if (!csnPath[index + 1]) {
|
|
1198
1250
|
result += select();
|
|
1199
1251
|
}
|
|
1200
|
-
|
|
1252
|
+
// only print last query prop for paths like
|
|
1253
|
+
// [... 'query', 'SELECT', 'from', 'SELECT', 'elements', 'struct'] -> select:2/element:"struct"
|
|
1254
|
+
// no from in the semantic location in this case
|
|
1255
|
+
else if (queryProps.includes(csnPath[index + 1]) && (!csnPath[index + 2] || query.isOnlySelect)) {
|
|
1201
1256
|
const clause = csnPath[index + 1];
|
|
1202
1257
|
result += select();
|
|
1203
1258
|
result += `/${ clause }`;
|
|
@@ -1266,7 +1321,7 @@ function constructSemanticLocationFromCsnPath(csnPath, model) {
|
|
|
1266
1321
|
if (currentThing.as)
|
|
1267
1322
|
result += `:${ _quoted(currentThing.as) }`;
|
|
1268
1323
|
else
|
|
1269
|
-
result += inRef ? `:${ _quoted(currentThing) }` : currentThing.ref ? `:${ _quoted(currentThing.ref.join('.')) }` : '';
|
|
1324
|
+
result += inRef ? `:${ _quoted(currentThing) }` : currentThing.ref ? `:${ _quoted(currentThing.ref.map(r => r.id ? r.id : r).join('.')) }` : '';
|
|
1270
1325
|
|
|
1271
1326
|
break;
|
|
1272
1327
|
}
|
|
@@ -1380,6 +1435,7 @@ module.exports = {
|
|
|
1380
1435
|
messageStringMultiline,
|
|
1381
1436
|
messageContext,
|
|
1382
1437
|
searchName,
|
|
1438
|
+
createMessageFunctions,
|
|
1383
1439
|
makeMessageFunction,
|
|
1384
1440
|
artName,
|
|
1385
1441
|
handleMessages,
|
package/lib/base/model.js
CHANGED
|
@@ -19,8 +19,6 @@ const queryOps = {
|
|
|
19
19
|
*/
|
|
20
20
|
const availableBetaFlags = {
|
|
21
21
|
// enabled by --beta-mode
|
|
22
|
-
keylessManagedAssoc: true,
|
|
23
|
-
foreignKeyConstraints: true,
|
|
24
22
|
toRename: true,
|
|
25
23
|
addTextsLanguageAssoc: true,
|
|
26
24
|
assocsWithParams: true,
|
|
@@ -28,9 +26,8 @@ const availableBetaFlags = {
|
|
|
28
26
|
mapAssocToJoinCardinality: true,
|
|
29
27
|
ignoreAssocPublishingInUnion: true,
|
|
30
28
|
nestedProjections: true,
|
|
29
|
+
enableUniversalCsn: true,
|
|
31
30
|
// disabled by --beta-mode
|
|
32
|
-
pretransformedCSN: false,
|
|
33
|
-
renderSQL: false,
|
|
34
31
|
nestedServices: false,
|
|
35
32
|
};
|
|
36
33
|
|
|
@@ -56,15 +53,18 @@ function isBetaEnabled( options, feature ) {
|
|
|
56
53
|
/**
|
|
57
54
|
* Test for deprecated feature, stored in option `deprecated`.
|
|
58
55
|
* With that, the value of `deprecated` is a dictionary of feature=>Boolean.
|
|
56
|
+
* If no `feature` is provided, checks if any deprecated option is set.
|
|
59
57
|
*
|
|
60
58
|
* Please do not move this function to the "option processor" code.
|
|
61
59
|
*
|
|
62
60
|
* @param {object} options Options
|
|
63
|
-
* @param {string} feature Feature to check for
|
|
61
|
+
* @param {string} [feature] Feature to check for
|
|
64
62
|
* @returns {boolean}
|
|
65
63
|
*/
|
|
66
|
-
function isDeprecatedEnabled( options, feature ) {
|
|
64
|
+
function isDeprecatedEnabled( options, feature = null ) {
|
|
67
65
|
const { deprecated } = options;
|
|
66
|
+
if(!feature)
|
|
67
|
+
return !!deprecated;
|
|
68
68
|
return deprecated && typeof deprecated === 'object' && deprecated[feature];
|
|
69
69
|
}
|
|
70
70
|
|