@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +235 -9
  2. package/bin/cdsc.js +44 -27
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +37 -3
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +37 -123
  7. package/lib/api/options.js +27 -15
  8. package/lib/api/validate.js +34 -9
  9. package/lib/backends.js +9 -89
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +73 -11
  13. package/lib/base/messages.js +86 -30
  14. package/lib/base/model.js +6 -6
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/defaultValues.js +27 -2
  17. package/lib/checks/elements.js +1 -6
  18. package/lib/checks/foreignKeys.js +0 -6
  19. package/lib/checks/managedWithoutKeys.js +17 -0
  20. package/lib/checks/nonexpandableStructured.js +38 -0
  21. package/lib/checks/onConditions.js +9 -45
  22. package/lib/checks/queryNoDbArtifacts.js +25 -7
  23. package/lib/checks/selectItems.js +29 -2
  24. package/lib/checks/types.js +26 -2
  25. package/lib/checks/unknownMagic.js +41 -0
  26. package/lib/checks/utils.js +61 -0
  27. package/lib/checks/validator.js +60 -7
  28. package/lib/compiler/assert-consistency.js +23 -7
  29. package/lib/compiler/base.js +65 -0
  30. package/lib/compiler/builtins.js +30 -1
  31. package/lib/compiler/checks.js +8 -5
  32. package/lib/compiler/definer.js +157 -133
  33. package/lib/compiler/index.js +89 -31
  34. package/lib/compiler/propagator.js +5 -2
  35. package/lib/compiler/resolver.js +375 -185
  36. package/lib/compiler/shared.js +49 -202
  37. package/lib/compiler/utils.js +173 -0
  38. package/lib/edm/annotations/genericTranslation.js +183 -187
  39. package/lib/edm/csn2edm.js +104 -108
  40. package/lib/edm/edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +388 -146
  42. package/lib/edm/edmUtils.js +104 -34
  43. package/lib/gen/Dictionary.json +22 -0
  44. package/lib/gen/language.checksum +1 -1
  45. package/lib/gen/language.interp +28 -1
  46. package/lib/gen/language.tokens +79 -69
  47. package/lib/gen/languageLexer.interp +28 -1
  48. package/lib/gen/languageLexer.js +879 -805
  49. package/lib/gen/languageLexer.tokens +71 -62
  50. package/lib/gen/languageParser.js +5330 -4300
  51. package/lib/json/from-csn.js +110 -52
  52. package/lib/json/to-csn.js +434 -120
  53. package/lib/language/antlrParser.js +15 -3
  54. package/lib/language/errorStrategy.js +1 -0
  55. package/lib/language/genericAntlrParser.js +93 -26
  56. package/lib/language/language.g4 +172 -31
  57. package/lib/main.d.ts +216 -19
  58. package/lib/main.js +32 -7
  59. package/lib/model/api.js +78 -0
  60. package/lib/model/csnRefs.js +413 -149
  61. package/lib/model/csnUtils.js +286 -75
  62. package/lib/model/enrichCsn.js +50 -6
  63. package/lib/model/revealInternalProperties.js +22 -5
  64. package/lib/modelCompare/compare.js +39 -21
  65. package/lib/optionProcessor.js +35 -18
  66. package/lib/render/.eslintrc.json +4 -1
  67. package/lib/render/DuplicateChecker.js +9 -6
  68. package/lib/render/toCdl.js +121 -36
  69. package/lib/render/toHdbcds.js +148 -98
  70. package/lib/render/toSql.js +114 -43
  71. package/lib/render/utils/common.js +8 -13
  72. package/lib/render/utils/sql.js +3 -3
  73. package/lib/sql-identifier.js +6 -1
  74. package/lib/transform/db/assertUnique.js +5 -6
  75. package/lib/transform/db/constraints.js +281 -106
  76. package/lib/transform/db/draft.js +11 -8
  77. package/lib/transform/db/expansion.js +584 -0
  78. package/lib/transform/db/flattening.js +341 -0
  79. package/lib/transform/db/groupByOrderBy.js +2 -2
  80. package/lib/transform/db/transformExists.js +345 -65
  81. package/lib/transform/db/views.js +438 -0
  82. package/lib/transform/forHanaNew.js +131 -793
  83. package/lib/transform/forOdataNew.js +30 -24
  84. package/lib/transform/localized.js +39 -10
  85. package/lib/transform/odata/attachPath.js +19 -4
  86. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  87. package/lib/transform/odata/referenceFlattener.js +60 -39
  88. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  89. package/lib/transform/odata/structuralPath.js +72 -0
  90. package/lib/transform/odata/structureFlattener.js +19 -18
  91. package/lib/transform/odata/typesExposure.js +22 -12
  92. package/lib/transform/transformUtilsNew.js +144 -78
  93. package/lib/transform/translateAssocsToJoins.js +22 -27
  94. package/lib/transform/universalCsnEnricher.js +67 -0
  95. package/lib/utils/file.js +5 -14
  96. package/lib/utils/moduleResolve.js +6 -8
  97. package/lib/utils/term.js +65 -42
  98. package/lib/utils/timetrace.js +48 -26
  99. package/package.json +1 -1
  100. package/lib/json/walker.js +0 -26
  101. package/lib/transform/sqlite +0 -0
  102. package/lib/utils/string.js +0 -17
@@ -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
  }
@@ -194,7 +194,7 @@ module.exports = {
194
194
  'OTHERS',
195
195
  'TIES',
196
196
  ],
197
- // HANA keywords, used to warn in 'toSql' renderer with dialect 'hana' or in 'toHana' renderer (both with 'plain' names only)
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' }, // TODO: Remove if shared.js uses makeMessageFunction()
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-of-v2': { severity: 'Warning' }, // more than 30 chars
104
- 'odata-spec-violation-constraints': { severity: 'Warning' }, // more than 30 chars
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
- namespace: 'Other definition blocks $(NAME) for namespace name',
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
- $tableAlias: 'Duplicate definition of table alias or mixin $(NAME)',
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
  /**
@@ -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
- // TODO: remove property `model`
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.errors.map( m => m.toString() ).join('\n');
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 = model.options || {}, moduleName = null ) {
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 && options.messages || [];
331
-
363
+ let messages = options.messages || [];
364
+ let hasNewError = false;
332
365
  return {
333
- message, error, warning, info, debug,
334
- messages, throwWithError, callTransparently
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 throwWithError(msgs = messages) {
503
- if (!msgs || !msgs.length)
539
+ function throwWithAnyError() {
540
+ if (!messages || !messages.length)
504
541
  return;
505
- reclassifyMessagesForModule(msgs, severities, moduleName); // TODO: no, at the beginning of the module
542
+ reclassifyMessagesForModule( messages, severities, moduleName ); // TODO: no, at the beginning of the module
506
543
  const hasError = options.testMode ? hasNonDowngradableErrors : hasErrors;
507
- if (hasError( msgs, moduleName ))
508
- throw new CompilationError( msgs, options.attachValidNames && model );
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 (in entity:“E”/element:“e”)
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] If true, an additional line (with `|`) will be inserted between message and location.
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 term.asSeverity(severity, severity + msgId) + ' ' + err.message;
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
- // TODO: use ':' before text
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 + '| ' + term.asSeverity(severity, highlighter);
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
- else if (queryProps.includes(csnPath[index + 1]) && !csnPath[index + 2]) {
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