@sap/cds-compiler 2.13.8 → 2.15.6
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 +128 -4
- package/bin/cdsc.js +112 -37
- package/lib/api/main.js +63 -22
- package/lib/api/options.js +2 -3
- package/lib/api/validate.js +6 -6
- package/lib/base/message-registry.js +100 -17
- package/lib/base/messages.js +85 -64
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/validator.js +2 -4
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +11 -0
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +59 -11
- package/lib/compiler/extend.js +20 -3
- package/lib/compiler/finalize-parse-cdl.js +26 -20
- package/lib/compiler/index.js +75 -26
- package/lib/compiler/populate.js +36 -17
- package/lib/compiler/propagator.js +4 -1
- package/lib/compiler/resolve.js +104 -16
- package/lib/compiler/shared.js +61 -27
- package/lib/compiler/tweak-assocs.js +7 -1
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +216 -98
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +499 -423
- package/lib/edm/edmUtils.js +22 -22
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageParser.js +4636 -4368
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +0 -2
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +47 -2
- package/lib/language/language.g4 +59 -27
- package/lib/main.d.ts +19 -1
- package/lib/main.js +6 -0
- package/lib/model/csnRefs.js +33 -6
- package/lib/model/csnUtils.js +193 -75
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +2 -2
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +62 -26
- package/lib/render/toCdl.js +844 -679
- package/lib/render/toHdbcds.js +189 -243
- package/lib/render/toSql.js +180 -198
- package/lib/render/utils/common.js +131 -15
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/constraints.js +3 -1
- package/lib/transform/db/expansion.js +15 -10
- package/lib/transform/db/flattening.js +94 -64
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +6 -3
- package/lib/transform/forHanaNew.js +43 -26
- package/lib/transform/forOdataNew.js +43 -42
- package/lib/transform/localized.js +12 -7
- package/lib/transform/odata/toFinalBaseType.js +8 -6
- package/lib/transform/odata/typesExposure.js +145 -197
- package/lib/transform/transformUtilsNew.js +9 -12
- package/lib/transform/translateAssocsToJoins.js +5 -1
- package/lib/transform/universalCsn/coreComputed.js +5 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
- package/lib/utils/moduleResolve.js +13 -6
- package/package.json +1 -1
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
package/CHANGELOG.md
CHANGED
|
@@ -7,27 +7,151 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 2.15.6 - 2022-07-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Annotations on sub-elements were lost during re-compilation.
|
|
15
|
+
|
|
16
|
+
## Version 2.15.4 - 2022-06-09
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- for.odata:
|
|
21
|
+
+ Fix derived type to scalar type resolution with intermediate `many`.
|
|
22
|
+
- to.edm(x):
|
|
23
|
+
+ (V4 structured) Fix key paths in combination with `--odata-foreign-keys`.
|
|
24
|
+
+ Add `Edm.PrimitiveType` to `@odata.Type`.
|
|
25
|
+
+ (V4 JSON) Render constant expressions for `Edm.Stream` and `Edm.Untyped`.
|
|
26
|
+
+ Fix a bug in target path calculation for `NavigationPropertyBinding`s to external references.
|
|
27
|
+
+ Render inner annotations even if `$value` is missing.
|
|
28
|
+
- Update OData vocabularies 'Common', 'UI'.
|
|
29
|
+
|
|
30
|
+
## Version 2.15.2 - 2022-05-12
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Option `cdsHome` can be used instead of `global.cds.home` to specify the path to `@sap/cds/`.
|
|
35
|
+
- to.edm(x):
|
|
36
|
+
+ Set anonymous nested proxy key elements to `Nullable:false` until first named type is reached.
|
|
37
|
+
+ Enforce `odata-spec-violation-key-null` on explicit foreign keys of managed primary key associations.
|
|
38
|
+
+ Proxies/service cross references are no longer created for associations with arbitrary ON conditions.
|
|
39
|
+
Only managed or `$self` backlink association targets are proxy/service cross reference candidates.
|
|
40
|
+
+ Explicit foreign keys of a managed association that are not a primary key in the target are exposed in the the proxy.
|
|
41
|
+
+ If an association is primary key, the resulting navigation property is set to `Nullable:false` in structured mode.
|
|
42
|
+
|
|
43
|
+
## Version 2.15.0 - 2022-05-06
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- A new warning is emitted if `excluding` is used without a wildcard, since this does
|
|
48
|
+
not have any effect.
|
|
49
|
+
- All scalar types can now take named arguments, e.g. `MyString(length: 10)`.
|
|
50
|
+
For custom scalar types, one unnamed arguments is interpreted as length, two arguments are interpreted
|
|
51
|
+
as precision and scale, e.g. `MyDecimal(3,3)`.
|
|
52
|
+
- If the type `sap.common.Locale` exists, it will be used as type for the `locale` element
|
|
53
|
+
of generated texts entities. The type must be a `cds.String`.
|
|
54
|
+
- to.cdl: Extend statements (from `extensions`) can now be rendered.
|
|
55
|
+
- Add OData vocabulary 'Hierarchy'.
|
|
56
|
+
- CDL: New associations can be published in queries, e.g. `assoc : Association to Target on assoc.id = id`
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- to.edm(x):
|
|
61
|
+
+ perform inbound qualification and spec violation checks as well as most/feasible EDM preprocessing steps
|
|
62
|
+
on requested services only.
|
|
63
|
+
+ Open up `@odata { Type, MaxLength, Precision, Scale, SRID }` annotation.
|
|
64
|
+
The annotations behavior is defined as follows:
|
|
65
|
+
+ The element/parameter must have a scalar CDS type. The annotation is not applied on named types
|
|
66
|
+
(With the V2 exception where derived type chains terminating in a scalar type are resolved).
|
|
67
|
+
+ The value of `@odata.Type` must be a valid `EDM` type for the rendered protocol version.
|
|
68
|
+
+ If `@odata.Type` can be applied, all canonic type facets (`MaxLength`, `Precision`, `Scale`, `SRID`) are
|
|
69
|
+
removed from the Edm Node and the new facets `@odata { MaxLength, Precision, Scale, SRID }` are applied.
|
|
70
|
+
Non Edm type conformant facets are ignored (eg. `@odata { Type: 'Edm.Decimal', MaxLength: 10, SRID: 0 }`).
|
|
71
|
+
+ Type facet values are not evaluated.
|
|
72
|
+
+ V2: Propagate `@Core.MediaType` annotation from stream element to entity type if not set.
|
|
73
|
+
- to.edm: Render constant expressions in short notation.
|
|
74
|
+
- Update OData Vocabularies: 'Common', 'Graph', 'Validation'.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- to.cdl:
|
|
79
|
+
+ Annotations of elements of action `returns` are now rendered as `annotate` statements.
|
|
80
|
+
+ Annotations on columns (query sub-elements) were not always rendered.
|
|
81
|
+
+ Doc comments on bound actions were rendered twice.
|
|
82
|
+
+ Unapplied annotations for action parameters were not rendered.
|
|
83
|
+
+ Unions and joins are correctly put into parentheses.
|
|
84
|
+
+ Add parentheses around certain expressions in function bodies that require it, such as `fct((1=1))`.
|
|
85
|
+
- to.edm(x):
|
|
86
|
+
+ Fix a bug in top level and derived type `items` exposure leading to undefined type rendering.
|
|
87
|
+
+ Fix a naming bug in type exposure leading to false reuse types, disguising invididual type
|
|
88
|
+
modifications (such as annotations, (auto-)redirections, element extensions).
|
|
89
|
+
+ Ignore `@Aggregation.default`.
|
|
90
|
+
+ Consolidate message texts and formatting.
|
|
91
|
+
+ Fix navigation property binding in cross service rendering mode.
|
|
92
|
+
+ Remove partner attribute in proxy/cross service navigations.
|
|
93
|
+
- Core engine (function `compile`):
|
|
94
|
+
+ Annotations for new columns inside `extend projection` blocks were not used.
|
|
95
|
+
+ Extending an unknown select item resulted in a crash.
|
|
96
|
+
+ Extending a context/service with columns now correctly emits an error.
|
|
97
|
+
+ Unmanaged `redirected to` in queries did not check whether the source is an association.
|
|
98
|
+
- parseCdl: `extend <art> with enum {...}` incorrectly threw a compiler error.
|
|
99
|
+
- API: `compile()` used a synchronous call `fs.realpathSync()` on the input filename array.
|
|
100
|
+
Now the asynchronous `fs.realpath()` is used.
|
|
101
|
+
- On-conditions in localized convenience views may be incorrectly rewritten if an element
|
|
102
|
+
has the same as a localized entity.
|
|
103
|
+
- to.sql/hdi/hdbcds:
|
|
104
|
+
+ No referential constraint is generated for an association if its parent
|
|
105
|
+
or target entity are annotated with `@cds.persistence.exists: true`.
|
|
106
|
+
+ Fix rendering of virtual elements in subqueries
|
|
107
|
+
+ Correctly process subqueries in JOINs
|
|
108
|
+
- to.sql/hdi: Queries with `UNION`, `INTERSECT` and similar in expressions are now enclosed in parentheses.
|
|
109
|
+
|
|
110
|
+
## Version 2.14.0 - 2022-04-08
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- cdsc:
|
|
115
|
+
+ `--quiet` can now be used to suppress compiler output, including messages.
|
|
116
|
+
+ `--options <file.json>` can be used to load compiler options. A JSON file is expected. Is compatible to CDS `package.json`
|
|
117
|
+
and `.cdsrc.json` by first looking for `cdsc` key in `cds`, then for a `cdsc` key and otherwise uses the full JSON file.
|
|
118
|
+
+ `--[error|warn|info|debug] id1,id2` can be used to reclassify specific messages.
|
|
119
|
+
- Add OData Vocabularies: 'DataIntegration', 'JSON'.
|
|
120
|
+
|
|
121
|
+
### Changed
|
|
122
|
+
|
|
123
|
+
- Update OData Vocabularies: 'UI'.
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
|
|
127
|
+
- to.cdl:
|
|
128
|
+
+ Delimited identifiers as the last elements of arrays in annotation values are now
|
|
129
|
+
rendered with spaces in between, to avoid accidentally escaping `]`.
|
|
130
|
+
+ Identifiers in includes and redirection targets were not quoted if they are reserved keywords.
|
|
131
|
+
- to.edm(x): Correctly rewrite `@Capabilities.ReadRestrictions.ReadByKeyRestrictions` into
|
|
132
|
+
`@Capabilities.NavigationPropertyRestriction` in containment mode.
|
|
133
|
+
|
|
10
134
|
## Version 2.13.8 - 2022-03-29
|
|
11
135
|
|
|
12
136
|
### Fixed
|
|
13
137
|
|
|
14
138
|
- to.hdbcds/hdi/sql: Correctly handle `localized` in conjunction with `@cds.persistence.exists` and `@cds.persistence.skip`
|
|
15
139
|
|
|
16
|
-
## Version 2.13.6 - 2022-25
|
|
140
|
+
## Version 2.13.6 - 2022-03-25
|
|
17
141
|
|
|
18
142
|
### Fixed
|
|
19
143
|
|
|
20
144
|
- to.hdbcds/hdi/sql: Correctly handle `localized` in conjunction with `@cds.persistence.exists`
|
|
21
145
|
|
|
22
|
-
## Version 2.13.4 - 2022-22
|
|
146
|
+
## Version 2.13.4 - 2022-03-22
|
|
23
147
|
|
|
24
148
|
No changes compared to Version 2.13.0; fixes latest NPM tag
|
|
25
149
|
|
|
26
|
-
## Version 2.13.2 - 2022-22
|
|
150
|
+
## Version 2.13.2 - 2022-03-22
|
|
27
151
|
|
|
28
152
|
No changes compared to Version 2.13.0; fixes latest NPM tag
|
|
29
153
|
|
|
30
|
-
## Version 2.13.0 - 2022-22
|
|
154
|
+
## Version 2.13.0 - 2022-03-22
|
|
31
155
|
|
|
32
156
|
### Added
|
|
33
157
|
|
package/bin/cdsc.js
CHANGED
|
@@ -44,6 +44,18 @@ class ProcessExitError extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
try {
|
|
48
|
+
cdsc_main();
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// This whole try/catch is only here because process.exit does not work in combination with
|
|
52
|
+
// stdout/err - see comment at ProcessExitError
|
|
53
|
+
if (err instanceof ProcessExitError)
|
|
54
|
+
process.exitCode = err.exitCode;
|
|
55
|
+
else
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
function remapCmdOptions(options, cmdOptions) {
|
|
48
60
|
if (!cmdOptions)
|
|
49
61
|
return options;
|
|
@@ -73,6 +85,9 @@ function remapCmdOptions(options, cmdOptions) {
|
|
|
73
85
|
options.variableReplacements.$user = {};
|
|
74
86
|
options.variableReplacements.$user.locale = value;
|
|
75
87
|
break;
|
|
88
|
+
case 'serviceNames':
|
|
89
|
+
options.serviceNames = value.split(',');
|
|
90
|
+
break;
|
|
76
91
|
default:
|
|
77
92
|
options[key] = value;
|
|
78
93
|
}
|
|
@@ -80,8 +95,9 @@ function remapCmdOptions(options, cmdOptions) {
|
|
|
80
95
|
return options;
|
|
81
96
|
}
|
|
82
97
|
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
function cdsc_main() {
|
|
99
|
+
// Parse the command line and translate it into options
|
|
100
|
+
|
|
85
101
|
const cmdLine = optionProcessor.processCmdLine(process.argv);
|
|
86
102
|
// Deal with '--version' explicitly
|
|
87
103
|
if (cmdLine.options.version) {
|
|
@@ -115,6 +131,11 @@ try {
|
|
|
115
131
|
displayUsage(cmdLine.errors, optionProcessor.helpText, 2);
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
if (cmdLine.options.options) {
|
|
135
|
+
if (!loadOptionsFromJson(cmdLine))
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
118
139
|
// Default warning level is 2 (info)
|
|
119
140
|
// FIXME: Is that not set anywhere in the API?
|
|
120
141
|
if (!cmdLine.options.warning)
|
|
@@ -125,11 +146,7 @@ try {
|
|
|
125
146
|
cmdLine.options.out = '-';
|
|
126
147
|
|
|
127
148
|
// --cds-home <dir>: modules starting with '@sap/cds/' are searched in <dir>
|
|
128
|
-
|
|
129
|
-
if (!global.cds)
|
|
130
|
-
global.cds = {};
|
|
131
|
-
global.cds.home = cmdLine.options.cdsHome;
|
|
132
|
-
}
|
|
149
|
+
// -> cmdLine.options.cdsHome is passed down to moduleResolve
|
|
133
150
|
|
|
134
151
|
// Set default command if required
|
|
135
152
|
cmdLine.command = cmdLine.command || 'toCsn';
|
|
@@ -147,8 +164,8 @@ try {
|
|
|
147
164
|
if (cmdLine.options.directBackend)
|
|
148
165
|
validateDirectBackendOption(cmdLine.command, cmdLine.options, cmdLine.args);
|
|
149
166
|
|
|
150
|
-
|
|
151
|
-
if (cmdLine.options.beta) {
|
|
167
|
+
// If set through CLI (and not options file), `beta` is a string and needs processing.
|
|
168
|
+
if (cmdLine.options.beta && typeof cmdLine.options.beta === 'string') {
|
|
152
169
|
const features = cmdLine.options.beta.split(',');
|
|
153
170
|
cmdLine.options.beta = {};
|
|
154
171
|
features.forEach((val) => {
|
|
@@ -156,35 +173,32 @@ try {
|
|
|
156
173
|
});
|
|
157
174
|
}
|
|
158
175
|
|
|
176
|
+
const to = cmdLine.options.toSql ? 'toSql' : 'toHana';
|
|
159
177
|
// remap string values for `assertIntegrity` option to boolean
|
|
160
|
-
if (cmdLine.options.assertIntegrity &&
|
|
161
|
-
|
|
162
|
-
cmdLine.options.assertIntegrity === 'false'
|
|
178
|
+
if (cmdLine.options[to] && cmdLine.options[to].assertIntegrity &&
|
|
179
|
+
(cmdLine.options[to].assertIntegrity === 'true' ||
|
|
180
|
+
cmdLine.options[to].assertIntegrity === 'false')
|
|
163
181
|
)
|
|
164
|
-
cmdLine.options.assertIntegrity = cmdLine.options.assertIntegrity === 'true';
|
|
182
|
+
cmdLine.options[to].assertIntegrity = cmdLine.options[to].assertIntegrity === 'true';
|
|
165
183
|
|
|
166
184
|
// Enable all beta-flags if betaMode is set to true
|
|
167
185
|
if (cmdLine.options.betaMode)
|
|
168
186
|
cmdLine.options.beta = availableBetaFlags;
|
|
169
187
|
|
|
170
|
-
|
|
188
|
+
// If set through CLI (and not options file), `deprecated` is a string and needs processing.
|
|
189
|
+
if (cmdLine.options.deprecated && typeof cmdLine.options.deprecated === 'string') {
|
|
171
190
|
const features = cmdLine.options.deprecated.split(',');
|
|
172
191
|
cmdLine.options.deprecated = {};
|
|
173
192
|
features.forEach((val) => {
|
|
174
193
|
cmdLine.options.deprecated[val] = true;
|
|
175
194
|
});
|
|
176
195
|
}
|
|
196
|
+
|
|
197
|
+
parseSeverityOptions(cmdLine);
|
|
198
|
+
|
|
177
199
|
// Do the work for the selected command
|
|
178
200
|
executeCommandLine(cmdLine.command, cmdLine.options, cmdLine.args);
|
|
179
201
|
}
|
|
180
|
-
catch (err) {
|
|
181
|
-
// This whole try/catch is only here because process.exit does not work in combination with
|
|
182
|
-
// stdout/err - see comment at ProcessExitError
|
|
183
|
-
if (err instanceof ProcessExitError)
|
|
184
|
-
process.exitCode = err.exitCode;
|
|
185
|
-
else
|
|
186
|
-
throw err;
|
|
187
|
-
}
|
|
188
202
|
|
|
189
203
|
/**
|
|
190
204
|
* `--direct-backend` can only be used with certain backends and with certain files.
|
|
@@ -230,10 +244,10 @@ function displayUsage(error, helpText, code) {
|
|
|
230
244
|
function executeCommandLine(command, options, args) {
|
|
231
245
|
const normalizeFilename = options.testMode && process.platform === 'win32';
|
|
232
246
|
const messageLevels = {
|
|
233
|
-
Error: 0, Warning: 1, Info: 2,
|
|
247
|
+
Error: 0, Warning: 1, Info: 2, Debug: 3,
|
|
234
248
|
};
|
|
235
249
|
// All messages are put into the message array, even those which should not
|
|
236
|
-
// been displayed (severity '
|
|
250
|
+
// been displayed (severity 'Debug')
|
|
237
251
|
|
|
238
252
|
// Create output directory if necessary
|
|
239
253
|
if (options.out && options.out !== '-' && !fs.existsSync(options.out))
|
|
@@ -329,17 +343,18 @@ function executeCommandLine(command, options, args) {
|
|
|
329
343
|
options.toOdata.odataContainment = true;
|
|
330
344
|
}
|
|
331
345
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
332
|
-
const
|
|
346
|
+
const odataOptions = remapCmdOptions(options, options.toOdata);
|
|
333
347
|
if (options.toOdata && options.toOdata.csn) {
|
|
348
|
+
const odataCsn = main.for.odata(csn, odataOptions);
|
|
334
349
|
displayNamedCsn(odataCsn, 'odata_csn');
|
|
335
350
|
}
|
|
336
351
|
else if (options.toOdata && options.toOdata.json) {
|
|
337
|
-
const result = main.to.edm.all(
|
|
352
|
+
const result = main.to.edm.all(csn, odataOptions);
|
|
338
353
|
for (const serviceName in result)
|
|
339
354
|
writeToFileOrDisplay(options.out, `${serviceName}.json`, result[serviceName]);
|
|
340
355
|
}
|
|
341
356
|
else {
|
|
342
|
-
const result = main.to.edmx.all(
|
|
357
|
+
const result = main.to.edmx.all(csn, odataOptions);
|
|
343
358
|
for (const serviceName in result)
|
|
344
359
|
writeToFileOrDisplay(options.out, `${serviceName}.xml`, result[serviceName]);
|
|
345
360
|
}
|
|
@@ -447,7 +462,7 @@ function executeCommandLine(command, options, args) {
|
|
|
447
462
|
* @param {CompileMessage[]} messages
|
|
448
463
|
*/
|
|
449
464
|
function displayMessages( model, messages = options.messages ) {
|
|
450
|
-
if (!Array.isArray(messages))
|
|
465
|
+
if (!Array.isArray(messages) || options.quiet)
|
|
451
466
|
return model;
|
|
452
467
|
|
|
453
468
|
const log = console.error;
|
|
@@ -552,6 +567,8 @@ function executeCommandLine(command, options, args) {
|
|
|
552
567
|
content = JSON.stringify(content, null, 2);
|
|
553
568
|
|
|
554
569
|
if (dir === '-') {
|
|
570
|
+
if (options.quiet)
|
|
571
|
+
return;
|
|
555
572
|
if (!omitHeadline) {
|
|
556
573
|
const sqlTypes = {
|
|
557
574
|
sql: true, hdbconstraint: true, hdbtable: true, hdbview: true,
|
|
@@ -569,15 +586,73 @@ function executeCommandLine(command, options, args) {
|
|
|
569
586
|
fs.writeFileSync(path.join(dir, fileName), content);
|
|
570
587
|
}
|
|
571
588
|
}
|
|
589
|
+
}
|
|
572
590
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
591
|
+
function loadOptionsFromJson(cmdLine) {
|
|
592
|
+
try {
|
|
593
|
+
let opt = JSON.parse(fs.readFileSync(cmdLine.options.options, 'utf-8'));
|
|
594
|
+
if (opt.cds)
|
|
595
|
+
opt = opt.cds;
|
|
596
|
+
if (opt.cdsc)
|
|
597
|
+
opt = opt.cdsc;
|
|
598
|
+
Object.assign(cmdLine.options, opt);
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
catch (e) {
|
|
602
|
+
catchErrors(e);
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function catchErrors(err) {
|
|
608
|
+
// @ts-ignore
|
|
609
|
+
if (err instanceof Error && err.hasBeenReported)
|
|
610
|
+
return;
|
|
611
|
+
console.error( '' );
|
|
612
|
+
console.error( 'INTERNAL ERROR:' );
|
|
613
|
+
console.error( util.inspect(err, false, null) );
|
|
614
|
+
console.error( '' );
|
|
615
|
+
process.exitCode = 70;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Parses the options `--error` and similar.
|
|
620
|
+
* Sets the dictionary `severities` on the given options.
|
|
621
|
+
*
|
|
622
|
+
* @param {object} options
|
|
623
|
+
*/
|
|
624
|
+
function parseSeverityOptions({ options }) {
|
|
625
|
+
if (!options.severities)
|
|
626
|
+
options.severities = Object.create(null);
|
|
627
|
+
|
|
628
|
+
const severityMap = {
|
|
629
|
+
error: 'Error',
|
|
630
|
+
warn: 'Warning',
|
|
631
|
+
info: 'Info',
|
|
632
|
+
debug: 'Debug',
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
// Note: We use a for loop to ensure that the order of the options on the command line is respected, i.e.
|
|
636
|
+
// `--warn id --error id` would lead to `id` being reclassified as an error and not a warning.
|
|
637
|
+
for (const key in options) {
|
|
638
|
+
switch (key) {
|
|
639
|
+
case 'error':
|
|
640
|
+
case 'warn':
|
|
641
|
+
case 'info':
|
|
642
|
+
case 'debug':
|
|
643
|
+
parseSeverityOption(options[key], severityMap[key]);
|
|
644
|
+
break;
|
|
645
|
+
default:
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function parseSeverityOption(list, severity) {
|
|
651
|
+
const ids = list.split(',');
|
|
652
|
+
for (let id of ids) {
|
|
653
|
+
id = id.trim();
|
|
654
|
+
if (id)
|
|
655
|
+
options.severities[id] = severity;
|
|
656
|
+
}
|
|
582
657
|
}
|
|
583
658
|
}
|
package/lib/api/main.js
CHANGED
|
@@ -33,9 +33,10 @@ const propertyToCheck = {
|
|
|
33
33
|
odata: 'toOdata',
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const {
|
|
36
|
+
const { cloneCsnNonDict } = require('../model/csnUtils');
|
|
37
37
|
const { toHdbcdsSource } = require('../render/toHdbcds');
|
|
38
38
|
const { ModelError } = require('../base/error');
|
|
39
|
+
const { forEach, forEachKey } = require('../utils/objectUtils');
|
|
39
40
|
|
|
40
41
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
41
42
|
const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
|
|
@@ -91,7 +92,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
91
92
|
// Not able to check
|
|
92
93
|
return;
|
|
93
94
|
}
|
|
94
|
-
const { error, warning,
|
|
95
|
+
const { error, warning, throwWithAnyError } = makeMessageFunction(csn, options, module);
|
|
95
96
|
|
|
96
97
|
for (const name of relevantOptionNames ) {
|
|
97
98
|
if (options[name] !== csn.meta.options[name])
|
|
@@ -103,7 +104,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
103
104
|
warning('options-mismatch-pretransformed-csn', null, `Expected pre-processed CSN to have option "${ name }" set to "${ options[name] }". Found: "${ csn.meta.options[name] }"`);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
throwWithAnyError();
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/**
|
|
@@ -153,7 +154,7 @@ function odata(csn, options = {}) {
|
|
|
153
154
|
*/
|
|
154
155
|
function cdl(csn, externalOptions = {}) {
|
|
155
156
|
const internalOptions = prepareOptions.to.cdl(externalOptions);
|
|
156
|
-
const { result } = backends.toCdlWithCsn(
|
|
157
|
+
const { result } = backends.toCdlWithCsn(cloneCsnNonDict(csn, internalOptions), internalOptions);
|
|
157
158
|
return result;
|
|
158
159
|
}
|
|
159
160
|
/**
|
|
@@ -251,7 +252,7 @@ function hdi(csn, options = {}) {
|
|
|
251
252
|
const sqlArtifactsWithCSNNamesToSort = Object.create(null);
|
|
252
253
|
const sqlArtifactsNotToSort = Object.create(null);
|
|
253
254
|
|
|
254
|
-
|
|
255
|
+
forEach(flat, (key) => {
|
|
255
256
|
const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');
|
|
256
257
|
nameMapping[artifactNameLikeInCsn] = key;
|
|
257
258
|
if (key.endsWith('.hdbtable') || key.endsWith('.hdbview'))
|
|
@@ -269,7 +270,7 @@ function hdi(csn, options = {}) {
|
|
|
269
270
|
}, Object.create(null));
|
|
270
271
|
|
|
271
272
|
// now add the not-sorted stuff, like indices
|
|
272
|
-
|
|
273
|
+
forEach(sqlArtifactsNotToSort, (key) => {
|
|
273
274
|
sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];
|
|
274
275
|
});
|
|
275
276
|
|
|
@@ -291,10 +292,10 @@ function hdi(csn, options = {}) {
|
|
|
291
292
|
function remapNames(dict, csn, filter) {
|
|
292
293
|
const result = Object.create(null);
|
|
293
294
|
|
|
294
|
-
|
|
295
|
+
forEach(dict, (key, value) => {
|
|
295
296
|
const name = remapName(key, csn, filter);
|
|
296
297
|
result[name] = value;
|
|
297
|
-
}
|
|
298
|
+
});
|
|
298
299
|
|
|
299
300
|
return result;
|
|
300
301
|
}
|
|
@@ -396,15 +397,15 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
396
397
|
*/
|
|
397
398
|
function createDefinitions() {
|
|
398
399
|
const result = [];
|
|
399
|
-
|
|
400
|
+
forEach(hdbkinds, (kind, artifacts) => {
|
|
400
401
|
const suffix = `.${ kind }`;
|
|
401
|
-
|
|
402
|
+
forEach(artifacts, (name, sqlStatement) => {
|
|
402
403
|
if ( kind !== 'hdbindex' )
|
|
403
404
|
result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
|
|
404
405
|
else
|
|
405
406
|
result.push({ name, suffix, sql: sqlStatement });
|
|
406
|
-
}
|
|
407
|
-
}
|
|
407
|
+
});
|
|
408
|
+
});
|
|
408
409
|
return result;
|
|
409
410
|
}
|
|
410
411
|
/**
|
|
@@ -414,9 +415,7 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
414
415
|
*/
|
|
415
416
|
function createDeletions() {
|
|
416
417
|
const result = [];
|
|
417
|
-
|
|
418
|
-
result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' });
|
|
419
|
-
|
|
418
|
+
forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
|
|
420
419
|
return result;
|
|
421
420
|
}
|
|
422
421
|
/**
|
|
@@ -426,9 +425,7 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
426
425
|
*/
|
|
427
426
|
function createMigrations() {
|
|
428
427
|
const result = [];
|
|
429
|
-
|
|
430
|
-
result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset });
|
|
431
|
-
|
|
428
|
+
forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
|
|
432
429
|
return result;
|
|
433
430
|
}
|
|
434
431
|
}
|
|
@@ -586,12 +583,13 @@ function edmxall(csn, options = {}) {
|
|
|
586
583
|
*/
|
|
587
584
|
function flattenResultStructure(toProcess) {
|
|
588
585
|
const result = {};
|
|
589
|
-
|
|
586
|
+
forEach(toProcess, (fileType, artifacts) => {
|
|
590
587
|
if (fileType === 'messages')
|
|
591
|
-
|
|
592
|
-
|
|
588
|
+
return;
|
|
589
|
+
forEach(artifacts, (filename) => {
|
|
593
590
|
result[`${ filename }.${ fileType }`] = artifacts[filename];
|
|
594
|
-
|
|
591
|
+
});
|
|
592
|
+
});
|
|
595
593
|
|
|
596
594
|
return result;
|
|
597
595
|
}
|
|
@@ -641,6 +639,7 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
641
639
|
*/
|
|
642
640
|
function api( csn, options = {}, ...args ) {
|
|
643
641
|
try {
|
|
642
|
+
checkOutdatedOptions( options );
|
|
644
643
|
return processor( csn, options, ...args );
|
|
645
644
|
}
|
|
646
645
|
catch (err) {
|
|
@@ -664,6 +663,48 @@ function publishCsnProcessor( processor, _name ) {
|
|
|
664
663
|
}
|
|
665
664
|
}
|
|
666
665
|
|
|
666
|
+
// Note: No toCsn, because @sap/cds may still use it (2022-06-15)
|
|
667
|
+
const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
|
|
668
|
+
/**
|
|
669
|
+
* Checks if outdated options are used and if so, throw a compiler error.
|
|
670
|
+
* These include:
|
|
671
|
+
* - magicVars (now variableReplacements)
|
|
672
|
+
* - toOdata/toSql/toHana/forHana -> now flat options
|
|
673
|
+
*
|
|
674
|
+
* @param {CSN.Options} options Backend options
|
|
675
|
+
*/
|
|
676
|
+
function checkOutdatedOptions(options) {
|
|
677
|
+
if (!options)
|
|
678
|
+
return;
|
|
679
|
+
const { warning } = makeMessageFunction(null, options, 'api');
|
|
680
|
+
|
|
681
|
+
// This warning has been emitted once, we don't need to emit it again.
|
|
682
|
+
if (options.messages && options.messages.some(m => m.messageId === 'api-invalid-option'))
|
|
683
|
+
return;
|
|
684
|
+
|
|
685
|
+
for (const name of oldBackendOptionNames) {
|
|
686
|
+
if (typeof options[name] === 'object') // may be a boolean due to internal options
|
|
687
|
+
warning('api-invalid-option', null, { '#': 'std', name });
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (options.magicVars)
|
|
691
|
+
warning('api-invalid-option', null, { '#': 'magicVars' });
|
|
692
|
+
|
|
693
|
+
// Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
|
|
694
|
+
// forgot about user -> $user and locale -> $user.locale
|
|
695
|
+
if (options.variableReplacements) {
|
|
696
|
+
if (options.variableReplacements.user)
|
|
697
|
+
warning('api-invalid-option', null, { '#': 'user' });
|
|
698
|
+
if (options.variableReplacements.locale)
|
|
699
|
+
warning('api-invalid-option', null, { '#': 'locale' });
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
forEachKey(options.variableReplacements || {}, (name) => {
|
|
703
|
+
if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
|
|
704
|
+
warning('api-invalid-option', null, { '#': 'noDollar', name });
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
667
708
|
|
|
668
709
|
/**
|
|
669
710
|
* Option format used by the old API, where they are grouped thematically.
|
package/lib/api/options.js
CHANGED
|
@@ -80,13 +80,12 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
|
|
|
80
80
|
function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
81
81
|
customValidators = {}, combinationValidators = [], moduleName = '') {
|
|
82
82
|
const options = Object.assign({}, defaults);
|
|
83
|
-
const inputOptionNames = Object.keys(input);
|
|
84
83
|
for (const name of overallOptions) {
|
|
85
84
|
// Ensure that arrays are not passed as a reference!
|
|
86
85
|
// This caused issues with the way messages are handled in processMessages
|
|
87
|
-
if (Array.isArray(input[name])
|
|
86
|
+
if (Array.isArray(input[name]))
|
|
88
87
|
options[name] = [ ...input[name] ];
|
|
89
|
-
else if (
|
|
88
|
+
else if (Object.hasOwnProperty.call(input, name))
|
|
90
89
|
options[name] = input[name];
|
|
91
90
|
}
|
|
92
91
|
|
package/lib/api/validate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
|
+
const { forEach } = require('../utils/objectUtils');
|
|
4
5
|
|
|
5
6
|
/* eslint-disable arrow-body-style */
|
|
6
7
|
const booleanValidator = {
|
|
@@ -159,16 +160,15 @@ function validate(options, moduleName, customValidators = {}, combinationValidat
|
|
|
159
160
|
// TODO: issuing messages in this function looks very strange...
|
|
160
161
|
{
|
|
161
162
|
const messageCollector = { messages: [] };
|
|
162
|
-
const { error,
|
|
163
|
+
const { error, throwWithAnyError } = makeMessageFunction(null, messageCollector, moduleName);
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
const optionValue = options[optionName];
|
|
165
|
+
forEach(options, (optionName, optionValue) => {
|
|
166
166
|
const validator = customValidators[optionName] || validators[optionName] || booleanValidator;
|
|
167
167
|
|
|
168
168
|
if (!validator.validate(optionValue))
|
|
169
169
|
error('invalid-option', null, {}, `Expected option "${ optionName }" to have "${ validator.expected(optionValue) }". Found: "${ validator.found(optionValue) }"`);
|
|
170
|
-
}
|
|
171
|
-
|
|
170
|
+
});
|
|
171
|
+
throwWithAnyError();
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
const message = makeMessageFunction(null, options, moduleName);
|
|
@@ -179,7 +179,7 @@ function validate(options, moduleName, customValidators = {}, combinationValidat
|
|
|
179
179
|
message[combinationValidator.severity]('invalid-option-combination', null, {}, combinationValidator.getMessage(options));
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
message.
|
|
182
|
+
message.throwWithAnyError();
|
|
183
183
|
}
|
|
184
184
|
/* eslint-enable jsdoc/no-undefined-types */
|
|
185
185
|
|