@sap/cds-compiler 2.11.4 → 2.12.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 +58 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +9 -10
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +12 -0
- package/lib/api/main.js +2 -0
- package/lib/api/options.js +2 -2
- package/lib/base/message-registry.js +31 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +97 -69
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/checks.js +32 -9
- package/lib/compiler/definer.js +25 -4
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolver.js +97 -6
- package/lib/compiler/shared.js +12 -1
- package/lib/compiler/utils.js +7 -0
- package/lib/edm/annotations/genericTranslation.js +34 -17
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +1 -1
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +30 -23
- package/lib/edm/edmUtils.js +11 -12
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +15 -14
- package/lib/gen/languageLexer.interp +9 -1
- package/lib/gen/languageLexer.js +830 -779
- package/lib/gen/languageLexer.tokens +7 -6
- package/lib/gen/languageParser.js +2401 -2282
- package/lib/json/from-csn.js +47 -16
- package/lib/json/to-csn.js +17 -5
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/genericAntlrParser.js +68 -51
- package/lib/language/language.g4 +128 -74
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +5 -3
- package/lib/main.js +3 -2
- package/lib/model/csnRefs.js +116 -68
- package/lib/model/csnUtils.js +40 -48
- package/lib/model/enrichCsn.js +30 -14
- package/lib/optionProcessor.js +3 -3
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +193 -79
- package/lib/render/toHdbcds.js +179 -95
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +57 -40
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +6 -4
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +4 -5
- package/lib/transform/db/flattening.js +5 -6
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +36 -23
- package/lib/transform/forHanaNew.js +35 -626
- package/lib/transform/forOdataNew.js +5 -4
- package/lib/transform/localized.js +3 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +13 -13
- package/lib/transform/translateAssocsToJoins.js +8 -8
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +2 -1
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,12 +7,69 @@
|
|
|
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.12.0 - 2022-01-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- CDL parser: You can now use multiline string literals and text blocks.
|
|
15
|
+
Use backticks (\`) for string literals that can span multiple lines and can use JavaScript-like escape
|
|
16
|
+
sequences such as `\u{0020}`. You can also use three backticks (\`\`\`) for strings (a.k.a. text blocks)
|
|
17
|
+
which are automatically indentation-stripped and can have an optional language identifier that is used
|
|
18
|
+
for syntax highlighting, similar to markdown. In difference to the former, text blocks require the
|
|
19
|
+
opening and closing backticks to be on separate lines.
|
|
20
|
+
Example:
|
|
21
|
+
|
|
22
|
+
@annotation: `Multi
|
|
23
|
+
line\u{0020}strings`
|
|
24
|
+
|
|
25
|
+
@textblock: ```xml
|
|
26
|
+
<summary>
|
|
27
|
+
<detail>The root tag has no indentation in this example</detail>
|
|
28
|
+
</summary>
|
|
29
|
+
```
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
- Enhance the ellipsis operator `...` for array annotations by an `up to ‹val›`:
|
|
33
|
+
only values in the array of the base annotation up to (including) the first match
|
|
34
|
+
of the specified `‹val›` are included at the specified place in the final array value.
|
|
35
|
+
An array annotation can have more than on `... up to ‹val›` items and must also
|
|
36
|
+
have a pure `...` item after them.
|
|
37
|
+
A structured `‹val›` matches if the array item is also a structure and all property
|
|
38
|
+
values in `‹val›` are equal to the corresponding property value in the array value;
|
|
39
|
+
it is not necessary to specify all properties of the array value items in `‹val›`.
|
|
40
|
+
Example
|
|
41
|
+
|
|
42
|
+
@Anno: [{name: one, val: 1}, {name: two, val: 2}, {name: four, val: 4}]
|
|
43
|
+
type T: Integer;
|
|
44
|
+
@Anno: [{name: zero, val: 0}, ... up to {name: two}, {name: three, val: 3}, ...]
|
|
45
|
+
annotate T;
|
|
46
|
+
|
|
47
|
+
- for.odata: Support `@cds.on {update|insert}` as replacement for deprecated `@odata.on { update|insert }` to
|
|
48
|
+
set `@Core.Computed`.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- Update OData Vocabularies 'Aggregation', 'Capabilities', 'Common', 'Core', PersonalData, 'Session', 'UI'
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- to.sql/hdi/hdbcds: With `exists`, ensure that the precedence of the existing association-on-conditions and where-conditions is kept by adding braces.
|
|
57
|
+
- to.sql/hdi: Window function suffixes are now properly rendered.
|
|
58
|
+
- to.sql: `$self` comparisons inside aspects are not checked and won't result in an error anymore.
|
|
59
|
+
- to.hdbcds:
|
|
60
|
+
+ Correctly apply the "."-to-"_"-translation algorithm to artifacts that are marked with `@cds.persistence.exists`.
|
|
61
|
+
+ Message with ID `anno-hidden-exists` (former `anno-unstable-hdbcds`) is now
|
|
62
|
+
only issued if the compiler generates a SAP HANA CDS artifact which would hide
|
|
63
|
+
a native database object from being resolved in a SAP HANA CDS `using … as …`.
|
|
64
|
+
- to.cdl: Annotation paths containing special characters such as spaces or `@` are now quoted, e.g. `@![some@annotation]`.
|
|
65
|
+
- compiler: A warning is emitted for elements of views with localized keys as the localized property is ignored for them.
|
|
66
|
+
|
|
10
67
|
## Version 2.11.4 - 2021-12-21
|
|
11
68
|
|
|
12
69
|
### Fixed
|
|
13
70
|
|
|
14
71
|
- CDL parser: in many situations, improve message when people use reserved keywords as identifier
|
|
15
|
-
- Improve error text and error location for
|
|
72
|
+
- Improve error text and error location for ambiguous auto-redirection target
|
|
16
73
|
- to.sql/hdi/hdbcds:
|
|
17
74
|
+ Correctly detect `exists` in projections
|
|
18
75
|
+ Correctly handle elements starting with `$` in the on-condition of associations
|
|
@@ -30,22 +30,22 @@ const fs = require('fs');
|
|
|
30
30
|
const path = require('path');
|
|
31
31
|
|
|
32
32
|
const cliArgs = process.argv.slice(2);
|
|
33
|
-
const
|
|
33
|
+
const filepath = cliArgs[0];
|
|
34
34
|
|
|
35
35
|
if (cliArgs.length !== 1)
|
|
36
36
|
exitError(`Expected exactly one argument, ${cliArgs.length} given`);
|
|
37
37
|
|
|
38
|
-
if (!
|
|
39
|
-
exitError('Expected non-empty
|
|
38
|
+
if (!filepath)
|
|
39
|
+
exitError('Expected non-empty filepath as argument!');
|
|
40
40
|
|
|
41
41
|
// Do not use allow-list approach.
|
|
42
42
|
// There may be CDS files with other extensions than `.cds`.
|
|
43
|
-
if (
|
|
43
|
+
if (filepath.endsWith('.csn') || filepath.endsWith('.json'))
|
|
44
44
|
exitError('Only CDS files can be passed! Found CSN file!');
|
|
45
45
|
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
fs.writeFileSync(
|
|
46
|
+
let sourceStr = fs.readFileSync(filepath, 'utf-8');
|
|
47
|
+
sourceStr = modernizeIdentifierStyle(sourceStr, filepath);
|
|
48
|
+
fs.writeFileSync(filepath, sourceStr);
|
|
49
49
|
process.exit(0); // success
|
|
50
50
|
|
|
51
51
|
// --------------------------------------------------------
|
package/bin/cdsc.js
CHANGED
|
@@ -227,7 +227,7 @@ function displayUsage(error, helpText, code) {
|
|
|
227
227
|
out.write(`${helpText}\n`);
|
|
228
228
|
if (error) {
|
|
229
229
|
if (error instanceof Array)
|
|
230
|
-
out.write(`${error.map(
|
|
230
|
+
out.write(`${error.map(err => `cdsc: ERROR: ${err}`).join('\n')}\n`);
|
|
231
231
|
else
|
|
232
232
|
out.write(`cdsc: ERROR: ${error}\n`);
|
|
233
233
|
}
|
|
@@ -301,11 +301,11 @@ function executeCommandLine(command, options, args) {
|
|
|
301
301
|
// Return the original model (for chaining)
|
|
302
302
|
function toCsn( model ) {
|
|
303
303
|
if (options.directBackend) {
|
|
304
|
-
displayNamedCsn(model, 'csn'
|
|
304
|
+
displayNamedCsn(model, 'csn');
|
|
305
305
|
}
|
|
306
306
|
else {
|
|
307
307
|
// Result already provided by caller
|
|
308
|
-
displayNamedXsn(model, 'csn'
|
|
308
|
+
displayNamedXsn(model, 'csn');
|
|
309
309
|
}
|
|
310
310
|
return model;
|
|
311
311
|
}
|
|
@@ -316,7 +316,7 @@ function executeCommandLine(command, options, args) {
|
|
|
316
316
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
317
317
|
|
|
318
318
|
if (options.toHana && options.toHana.csn) {
|
|
319
|
-
displayNamedCsn(for_hdbcds(csn, remapCmdOptions(options, options.toHana)), 'hana_csn'
|
|
319
|
+
displayNamedCsn(for_hdbcds(csn, remapCmdOptions(options, options.toHana)), 'hana_csn');
|
|
320
320
|
}
|
|
321
321
|
else {
|
|
322
322
|
const hanaResult = main.to.hdbcds(csn, remapCmdOptions(options, options.toHana));
|
|
@@ -339,7 +339,7 @@ function executeCommandLine(command, options, args) {
|
|
|
339
339
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
340
340
|
const odataCsn = main.for.odata(csn, remapCmdOptions(options, options.toOdata));
|
|
341
341
|
if (options.toOdata && options.toOdata.csn) {
|
|
342
|
-
displayNamedCsn(odataCsn, 'odata_csn'
|
|
342
|
+
displayNamedCsn(odataCsn, 'odata_csn');
|
|
343
343
|
}
|
|
344
344
|
else if (options.toOdata && options.toOdata.json) {
|
|
345
345
|
const result = main.to.edm.all(odataCsn, options);
|
|
@@ -392,7 +392,7 @@ function executeCommandLine(command, options, args) {
|
|
|
392
392
|
const csn = options.directBackend ? model : compactModel(model, options);
|
|
393
393
|
if (options.toSql && options.toSql.src === 'hdi') {
|
|
394
394
|
if (options.toSql.csn) {
|
|
395
|
-
displayNamedCsn(for_hdi(csn, remapCmdOptions(options, options.toSql)), 'hdi_csn'
|
|
395
|
+
displayNamedCsn(for_hdi(csn, remapCmdOptions(options, options.toSql)), 'hdi_csn');
|
|
396
396
|
}
|
|
397
397
|
else {
|
|
398
398
|
const hdiResult = main.to.hdi(csn, remapCmdOptions(options, options.toSql));
|
|
@@ -401,7 +401,7 @@ function executeCommandLine(command, options, args) {
|
|
|
401
401
|
}
|
|
402
402
|
}
|
|
403
403
|
else if (options.toSql && options.toSql.csn) {
|
|
404
|
-
displayNamedCsn(for_sql(csn, remapCmdOptions(options, options.toSql)), 'sql_csn'
|
|
404
|
+
displayNamedCsn(for_sql(csn, remapCmdOptions(options, options.toSql)), 'sql_csn');
|
|
405
405
|
}
|
|
406
406
|
else {
|
|
407
407
|
const sqlResult = main.to.sql(csn, remapCmdOptions(options, options.toSql));
|
|
@@ -505,7 +505,7 @@ function executeCommandLine(command, options, args) {
|
|
|
505
505
|
// or display it to stdout if 'options.out' is '-'.
|
|
506
506
|
// Depending on 'options.rawOutput', the model is either compacted to 'name.json' or
|
|
507
507
|
// written in raw form to '<name>_raw.txt'.
|
|
508
|
-
function displayNamedXsn(xsn, name
|
|
508
|
+
function displayNamedXsn(xsn, name) {
|
|
509
509
|
if (options.rawOutput) {
|
|
510
510
|
writeToFileOrDisplay(options.out, `${name}_raw.txt`, util.inspect(reveal(xsn, options.rawOutput), false, null), true);
|
|
511
511
|
}
|
|
@@ -525,9 +525,8 @@ function executeCommandLine(command, options, args) {
|
|
|
525
525
|
/**
|
|
526
526
|
* @param {CSN.Model} csn
|
|
527
527
|
* @param {string} name
|
|
528
|
-
* @param {CSN.Options} options
|
|
529
528
|
*/
|
|
530
|
-
function displayNamedCsn(csn, name
|
|
529
|
+
function displayNamedCsn(csn, name) {
|
|
531
530
|
if (!csn) // only print CSN if it is set.
|
|
532
531
|
return;
|
|
533
532
|
if (options.internalMsg) {
|
package/doc/CHANGELOG_ARCHIVE.md
CHANGED
|
@@ -1516,7 +1516,7 @@ Changes
|
|
|
1516
1516
|
* Preserve the `key` properties of elements selected in a view (like we do in projections).
|
|
1517
1517
|
* Improve the CSN representation for views.
|
|
1518
1518
|
Represent the `where` and `on` condition of `select`s like other conditions.
|
|
1519
|
-
* Project name in github is now `
|
|
1519
|
+
* Project name in github is now `cap/cds-compiler`.
|
|
1520
1520
|
|
|
1521
1521
|
Features
|
|
1522
1522
|
* Support `select *` in views.
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,12 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 2.12.0 - 2022-01-25
|
|
12
|
+
|
|
13
|
+
### Added `sqlSnippets`
|
|
14
|
+
|
|
15
|
+
- to.sql/hdi/hdbcds: Introduce the annotations `@sql.prepend` and `@sql.append` that allow inserting user-written SQL snippets into the compiler generated content.
|
|
16
|
+
|
|
11
17
|
## Version 2.11.0
|
|
12
18
|
|
|
13
19
|
### Removed `foreignKeyConstraints`
|
|
@@ -163,6 +169,12 @@ The association to join transformation treats foreign key accesses with priority
|
|
|
163
169
|
|
|
164
170
|
Unique constraints are now generally available.
|
|
165
171
|
|
|
172
|
+
## Version 1.33.0 - 2020-08-24
|
|
173
|
+
|
|
174
|
+
### Added `hanaAssocRealCardinality`
|
|
175
|
+
|
|
176
|
+
Render JOIN cardinality in native HANA association if provided. If no cardinality has been specified.
|
|
177
|
+
|
|
166
178
|
## Version 1.32.0 - 2020-07-10
|
|
167
179
|
|
|
168
180
|
### Removed `aspectCompositions`
|
package/lib/api/main.js
CHANGED
|
@@ -165,6 +165,7 @@ function cdl(csn, externalOptions = {}) {
|
|
|
165
165
|
*/
|
|
166
166
|
function forSql(csn, options = {}) {
|
|
167
167
|
const internalOptions = prepareOptions.to.sql(options);
|
|
168
|
+
internalOptions.transformation = 'sql';
|
|
168
169
|
internalOptions.toSql.csn = true;
|
|
169
170
|
return backends.toSqlWithCsn(csn, internalOptions).csn;
|
|
170
171
|
}
|
|
@@ -207,6 +208,7 @@ function forHdbcds(csn, options = {}) {
|
|
|
207
208
|
*/
|
|
208
209
|
function sql(csn, options = {}) {
|
|
209
210
|
const internalOptions = prepareOptions.to.sql(options);
|
|
211
|
+
internalOptions.transformation = 'sql';
|
|
210
212
|
|
|
211
213
|
// we need the CSN for view sorting
|
|
212
214
|
internalOptions.toSql.csn = true;
|
package/lib/api/options.js
CHANGED
|
@@ -84,9 +84,9 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
|
|
|
84
84
|
for (const name of overallOptions) {
|
|
85
85
|
// Ensure that arrays are not passed as a reference!
|
|
86
86
|
// This caused issues with the way messages are handled in processMessages
|
|
87
|
-
if (Array.isArray(input[name]) && inputOptionNames.
|
|
87
|
+
if (Array.isArray(input[name]) && inputOptionNames.includes(name))
|
|
88
88
|
options[name] = [ ...input[name] ];
|
|
89
|
-
else if (inputOptionNames.
|
|
89
|
+
else if (inputOptionNames.includes(name))
|
|
90
90
|
options[name] = input[name];
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -42,12 +42,15 @@ const centralMessages = {
|
|
|
42
42
|
'anno-definition': { severity: 'Warning' },
|
|
43
43
|
'anno-duplicate': { severity: 'Error', configurableFor: true }, // does not hurt us
|
|
44
44
|
'anno-duplicate-unrelated-layer': { severity: 'Error', configurableFor: true }, // does not hurt us
|
|
45
|
+
'anno-invalid-sql-element': { severity: 'Error'}, // @sql.prepend/append
|
|
46
|
+
'anno-invalid-sql-struct': { severity: 'Error'}, // @sql.prepend/append
|
|
47
|
+
'anno-invalid-sql-view': { severity: 'Error' }, // @sql.prepend/append
|
|
48
|
+
'anno-invalid-sql-view-element': { severity: 'Error'}, // @sql.prepend/append
|
|
45
49
|
'anno-undefined-action': { severity: 'Info' },
|
|
46
50
|
'anno-undefined-art': { severity: 'Info' }, // for annotate statement (for CDL path root)
|
|
47
51
|
'anno-undefined-def': { severity: 'Info' }, // for annotate statement (for CSN or CDL path cont)
|
|
48
52
|
'anno-undefined-element': { severity: 'Info' },
|
|
49
53
|
'anno-undefined-param': { severity: 'Info' },
|
|
50
|
-
'anno-unstable-hdbcds': { severity: 'Warning' },
|
|
51
54
|
|
|
52
55
|
'args-expected-named': { severity: 'Error', configurableFor: 'deprecated' }, // future --sloppy
|
|
53
56
|
'args-no-params': { severity: 'Error', configurableFor: 'deprecated' }, // future --sloppy
|
|
@@ -128,6 +131,11 @@ const centralMessages = {
|
|
|
128
131
|
'syntax-fragile-alias': { severity: 'Error', configurableFor: true },
|
|
129
132
|
'syntax-fragile-ident': { severity: 'Error', configurableFor: true },
|
|
130
133
|
|
|
134
|
+
'syntax-invalid-text-block' : { severity: 'Error' },
|
|
135
|
+
'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
|
|
136
|
+
'syntax-invalid-escape': { severity: 'Error' },
|
|
137
|
+
'syntax-missing-escape': { severity: 'Error' },
|
|
138
|
+
|
|
131
139
|
'type-managed-composition': { severity: 'Error', configurableFor: 'deprecated' }, // TODO: non-config
|
|
132
140
|
|
|
133
141
|
'def-missing-element': { severity: 'Error' },
|
|
@@ -149,7 +157,7 @@ const centralMessages = {
|
|
|
149
157
|
// For messageIds, where no text has been provided via code (central def)
|
|
150
158
|
const centralMessageTexts = {
|
|
151
159
|
'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
|
|
152
|
-
'anno-unexpected-ellipsis': '
|
|
160
|
+
'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
|
|
153
161
|
'missing-type-parameter': 'Missing value for type parameter $(NAME) in reference to type $(ID)',
|
|
154
162
|
'syntax-csn-expected-object': 'Expected object for property $(PROP)',
|
|
155
163
|
'syntax-csn-expected-column': 'Expected object or string \'*\' for property $(PROP)',
|
|
@@ -171,6 +179,22 @@ const centralMessageTexts = {
|
|
|
171
179
|
one: 'Expected array in $(PROP) to have at least one item',
|
|
172
180
|
suffix: 'With sibling property $(OTHERPROP), expected array in $(PROP) to have at least one item',
|
|
173
181
|
},
|
|
182
|
+
'syntax-invalid-text-block': 'Missing newline in text block',
|
|
183
|
+
'syntax-unknown-escape': 'Unknown escape sequence $(CODE)',
|
|
184
|
+
'syntax-invalid-escape': {
|
|
185
|
+
std: 'Invalid escape sequence $(CODE)',
|
|
186
|
+
octal: 'Octal escape sequences are not supported. Use unicode escapes instead',
|
|
187
|
+
whitespace: 'Unknown escape sequence: Can\'t escape whitespace',
|
|
188
|
+
codepoint: 'Undefined code-point for $(CODE)',
|
|
189
|
+
'unicode-hex': 'Expected hexadecimal numbers for unicode escape but found $(CODE)',
|
|
190
|
+
'hex-count': 'Expected $(NUMBER) hexadecimal numbers for escape sequence but found $(CODE)',
|
|
191
|
+
'unicode-brace': 'Missing closing brace for unicode escape sequence',
|
|
192
|
+
'language-identifier': 'Escape sequences in text-block\'s language identifier are not allowed',
|
|
193
|
+
},
|
|
194
|
+
'syntax-missing-escape': {
|
|
195
|
+
std: 'Missing escape. Replace $(CODE) with $(NEWCODE)',
|
|
196
|
+
placeholder: 'Placeholders are not supported. Replace $(CODE) with $(NEWCODE)',
|
|
197
|
+
},
|
|
174
198
|
'ref-undefined-def': {
|
|
175
199
|
std: 'Artifact $(ART) has not been found',
|
|
176
200
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -289,6 +313,11 @@ const centralMessageTexts = {
|
|
|
289
313
|
'odata-spec-violation-type': 'Expected element to have a type',
|
|
290
314
|
'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
|
|
291
315
|
'odata-spec-violation-namespace': 'Expected service name not to be one of the reserved names $(NAMES)',
|
|
316
|
+
// Other odata/edm errors
|
|
317
|
+
'odata-definition-exists': {
|
|
318
|
+
std: 'Entity can\'t be created due to name collision with existing definition $(NAME)',
|
|
319
|
+
proxy: 'No proxy entity created due to name collision with existing definition $(NAME) of kind $(KIND)'
|
|
320
|
+
}
|
|
292
321
|
}
|
|
293
322
|
|
|
294
323
|
/**
|
package/lib/base/model.js
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Create a command line option processor and define valid commands, options and parameters.
|
|
5
|
+
* In order to understand a command line like this:
|
|
6
|
+
* $ node cdsc.js -x 1 --foo toXyz -y --bar-wiz bla arg1 arg2
|
|
7
|
+
*
|
|
8
|
+
* The following definitions should be made:
|
|
9
|
+
*
|
|
10
|
+
* ```js
|
|
11
|
+
* const optionProcessor = createOptionProcessor();
|
|
12
|
+
* optionProcessor
|
|
13
|
+
* .help(`General help text`);
|
|
14
|
+
* .option('-x, --long-form <i>')
|
|
15
|
+
* .option(' --foo')
|
|
16
|
+
* optionProcessor.command('toXyz')
|
|
17
|
+
* .help(`Help text for command "toXyz")
|
|
18
|
+
* .option('-y --y-in-long-form')
|
|
19
|
+
* .option(' --bar-wiz <w>', ['bla', 'foo'])
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Options *must* have a long form, can have at most one <param>, and optionally
|
|
23
|
+
* an array of valid param values as strings. Commands and param values must not
|
|
24
|
+
* start with '-'. The whole processor and each command may carry a help text.
|
|
25
|
+
* To actually parse a command line, use
|
|
26
|
+
* const cli = optionProcessor.processCmdLine(process.argv);
|
|
27
|
+
* (see below)
|
|
28
|
+
*/
|
|
25
29
|
function createOptionProcessor() {
|
|
26
30
|
const optionProcessor = {
|
|
27
31
|
commands: {},
|
|
@@ -32,13 +36,14 @@ function createOptionProcessor() {
|
|
|
32
36
|
command,
|
|
33
37
|
positionalArgument: (argumentDefinition) => {
|
|
34
38
|
// Default positional arguments; may be overwritten by commands.
|
|
35
|
-
|
|
39
|
+
_setPositionalArguments(argumentDefinition);
|
|
36
40
|
return optionProcessor;
|
|
37
41
|
},
|
|
38
42
|
help,
|
|
39
43
|
processCmdLine,
|
|
40
44
|
verifyOptions,
|
|
41
45
|
camelOptionsForCommand,
|
|
46
|
+
// TODO: Why exported?
|
|
42
47
|
_parseCommandString,
|
|
43
48
|
_parseOptionString,
|
|
44
49
|
}
|
|
@@ -48,9 +53,10 @@ function createOptionProcessor() {
|
|
|
48
53
|
* API: Define a general option.
|
|
49
54
|
* @param {string} optString Option string describing the command line option.
|
|
50
55
|
* @param {string[]} [validValues] Array of valid values for the options.
|
|
56
|
+
* @param {object} [options] Further options such as `ignoreCase: true`
|
|
51
57
|
*/
|
|
52
|
-
function option(optString, validValues) {
|
|
53
|
-
return _addOption(optionProcessor, optString, validValues);
|
|
58
|
+
function option(optString, validValues, options) {
|
|
59
|
+
return _addOption(optionProcessor, optString, validValues, options);
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
/**
|
|
@@ -64,7 +70,7 @@ function createOptionProcessor() {
|
|
|
64
70
|
|
|
65
71
|
/**
|
|
66
72
|
* API: Define a command
|
|
67
|
-
* @param {string} cmdString Command name, e.g. 'S, toSql'
|
|
73
|
+
* @param {string} cmdString Command name, short and long form, e.g. 'S, toSql'
|
|
68
74
|
*/
|
|
69
75
|
function command(cmdString) {
|
|
70
76
|
/** @type {object} */
|
|
@@ -73,7 +79,7 @@ function createOptionProcessor() {
|
|
|
73
79
|
positionalArguments: [],
|
|
74
80
|
option,
|
|
75
81
|
positionalArgument: (argumentDefinition) => {
|
|
76
|
-
|
|
82
|
+
_setPositionalArguments(argumentDefinition, command.positionalArguments);
|
|
77
83
|
return command;
|
|
78
84
|
},
|
|
79
85
|
help,
|
|
@@ -92,12 +98,12 @@ function createOptionProcessor() {
|
|
|
92
98
|
}
|
|
93
99
|
return command;
|
|
94
100
|
|
|
95
|
-
// API: Define a command option
|
|
96
|
-
function option(optString, validValues) {
|
|
97
|
-
return _addOption(command, optString, validValues);
|
|
101
|
+
// Command API: Define a command option
|
|
102
|
+
function option(optString, validValues, options) {
|
|
103
|
+
return _addOption(command, optString, validValues, options);
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
// API: Define the command help text
|
|
106
|
+
// Command API: Define the command help text
|
|
101
107
|
function help(text) {
|
|
102
108
|
command.helpText = text;
|
|
103
109
|
return command;
|
|
@@ -112,8 +118,9 @@ function createOptionProcessor() {
|
|
|
112
118
|
*
|
|
113
119
|
* @param {string} argumentDefinition Positional arguments, e.g. '<input> <output>' or '<files...>'
|
|
114
120
|
* @param {object[]} argList Array, to which the parsed arguments will be added. Default is global scope.
|
|
121
|
+
* @private
|
|
115
122
|
*/
|
|
116
|
-
function
|
|
123
|
+
function _setPositionalArguments(argumentDefinition, argList = optionProcessor.positionalArguments) {
|
|
117
124
|
if (argList.find((arg) => arg.isDynamic)) {
|
|
118
125
|
throw new Error(`Can't add positional arguments after a dynamic one`);
|
|
119
126
|
}
|
|
@@ -149,8 +156,10 @@ function createOptionProcessor() {
|
|
|
149
156
|
* @private
|
|
150
157
|
* @see option()
|
|
151
158
|
*/
|
|
152
|
-
function _addOption(command, optString, validValues) {
|
|
159
|
+
function _addOption(command, optString, validValues, options) {
|
|
153
160
|
const opt = _parseOptionString(optString, validValues);
|
|
161
|
+
Object.assign(opt, options);
|
|
162
|
+
|
|
154
163
|
if (command.options[opt.longName]) {
|
|
155
164
|
throw new Error(`Duplicate assignment for long option ${opt.longName}`);
|
|
156
165
|
} else if (optionProcessor.options[opt.longName]) {
|
|
@@ -217,7 +226,6 @@ function createOptionProcessor() {
|
|
|
217
226
|
let longName;
|
|
218
227
|
let shortName;
|
|
219
228
|
let param;
|
|
220
|
-
let camelName;
|
|
221
229
|
|
|
222
230
|
// split at spaces (with optional preceding comma)
|
|
223
231
|
const tokens = optString.trim().split(/,? +/);
|
|
@@ -261,26 +269,16 @@ function createOptionProcessor() {
|
|
|
261
269
|
throw new Error(`Valid values must be of type string: ${optString}`);
|
|
262
270
|
});
|
|
263
271
|
}
|
|
264
|
-
|
|
272
|
+
|
|
265
273
|
return {
|
|
266
274
|
longName,
|
|
267
275
|
shortName,
|
|
268
|
-
camelName,
|
|
276
|
+
camelName: camelifyLongOption(longName),
|
|
269
277
|
param,
|
|
270
278
|
validValues
|
|
271
279
|
}
|
|
272
280
|
}
|
|
273
281
|
|
|
274
|
-
// Return a camelCase name "fooBar" for a long option "--foo-bar"
|
|
275
|
-
function _camelify(opt) {
|
|
276
|
-
return opt.substring(2).replace(/-./g, s => s.substring(1).toUpperCase());
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Return a long option name like "--foo-bar" for a camel-case name "fooBar"
|
|
280
|
-
function _unCamelify(opt) {
|
|
281
|
-
return `--${opt.replace(/[A-Z]/g, s => '-' + s.toLowerCase())}`;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
282
|
// API: Let the option processor digest a command line 'argv'
|
|
285
283
|
// The expectation is to get a commandline like this:
|
|
286
284
|
// $ node cdsc.js -x 1 --foo toXyz -y --bar-wiz bla arg1 arg2
|
|
@@ -336,7 +334,12 @@ function createOptionProcessor() {
|
|
|
336
334
|
argv = [ ...argv.slice(0, i), ...arg.split('='), ...argv.slice(i + 1)];
|
|
337
335
|
arg = argv[i];
|
|
338
336
|
}
|
|
339
|
-
|
|
337
|
+
|
|
338
|
+
if (arg === '--') {
|
|
339
|
+
// No more options after '--'
|
|
340
|
+
seenDashDash = true;
|
|
341
|
+
}
|
|
342
|
+
else if (!seenDashDash && arg.startsWith('-')) {
|
|
340
343
|
if (result.command) {
|
|
341
344
|
// We already have a command
|
|
342
345
|
const opt = optionProcessor.commands[result.command].options[arg];
|
|
@@ -378,10 +381,7 @@ function createOptionProcessor() {
|
|
|
378
381
|
}
|
|
379
382
|
}
|
|
380
383
|
}
|
|
381
|
-
else
|
|
382
|
-
// No more options after '--'
|
|
383
|
-
seenDashDash = true;
|
|
384
|
-
} else {
|
|
384
|
+
else {
|
|
385
385
|
// Command or arg
|
|
386
386
|
if (result.command === undefined) {
|
|
387
387
|
if (optionProcessor.commands[arg]) {
|
|
@@ -420,8 +420,7 @@ function createOptionProcessor() {
|
|
|
420
420
|
*/
|
|
421
421
|
function getCurrentPositionArguments() {
|
|
422
422
|
const cmd = optionProcessor.commands[result.command];
|
|
423
|
-
|
|
424
|
-
return args;
|
|
423
|
+
return ( cmd && cmd.positionalArguments && cmd.positionalArguments.length ) ? cmd.positionalArguments : optionProcessor.positionalArguments;
|
|
425
424
|
}
|
|
426
425
|
|
|
427
426
|
function processPositionalArgument(argumentValue) {
|
|
@@ -478,13 +477,13 @@ function createOptionProcessor() {
|
|
|
478
477
|
result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
|
|
479
478
|
} else {
|
|
480
479
|
result.options[command][opt.camelName] = value;
|
|
481
|
-
if (opt
|
|
480
|
+
if (!isValidOptionValue(opt, value)) {
|
|
482
481
|
result.cmdErrors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
483
482
|
}
|
|
484
483
|
}
|
|
485
484
|
} else {
|
|
486
485
|
result.options[opt.camelName] = value;
|
|
487
|
-
if (
|
|
486
|
+
if (!isValidOptionValue(opt, value)) {
|
|
488
487
|
result.errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
489
488
|
}
|
|
490
489
|
}
|
|
@@ -545,7 +544,7 @@ function createOptionProcessor() {
|
|
|
545
544
|
}
|
|
546
545
|
// Look at each supplied option
|
|
547
546
|
for (const camelName in options) {
|
|
548
|
-
const opt = opts[
|
|
547
|
+
const opt = opts[uncamelifyLongOption(camelName)];
|
|
549
548
|
let error;
|
|
550
549
|
if (!opt) {
|
|
551
550
|
// Don't report commands in top-level options
|
|
@@ -570,7 +569,7 @@ function createOptionProcessor() {
|
|
|
570
569
|
// Parameter is required for this option
|
|
571
570
|
if (typeof param === 'boolean') {
|
|
572
571
|
return `Missing value for option "${prefix}${opt.camelName}"`;
|
|
573
|
-
} else if (opt
|
|
572
|
+
} else if (!isValidOptionValue(opt, param)) {
|
|
574
573
|
return `Invalid value "${param}" for option "${prefix}${opt.camelName}" - use one of [${opt.validValues}]`;
|
|
575
574
|
}
|
|
576
575
|
return false;
|
|
@@ -585,36 +584,65 @@ function createOptionProcessor() {
|
|
|
585
584
|
}
|
|
586
585
|
}
|
|
587
586
|
|
|
587
|
+
function isValidOptionValue(opt, value) {
|
|
588
|
+
// Explicitly convert to string, input 'value' may be boolean
|
|
589
|
+
value = String(value);
|
|
590
|
+
if (!opt.validValues || !opt.validValues.length)
|
|
591
|
+
return true;
|
|
592
|
+
if (opt.ignoreCase)
|
|
593
|
+
return opt.validValues.some( valid => valid.toLowerCase() === value.toLowerCase() );
|
|
594
|
+
return opt.validValues.includes(value);
|
|
595
|
+
}
|
|
596
|
+
|
|
588
597
|
// Return an array of unique camelNames of the options for the specified command
|
|
589
598
|
// If invalid command -> an empty array
|
|
590
599
|
function camelOptionsForCommand(command) {
|
|
591
|
-
if (command
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
} else {
|
|
597
|
-
return [];
|
|
598
|
-
}
|
|
600
|
+
if (!command || !optionProcessor.commands[command])
|
|
601
|
+
return []
|
|
602
|
+
const cmd = optionProcessor.commands[command];
|
|
603
|
+
const names = Object.keys(cmd.options).map(name => cmd.options[name].camelName);
|
|
604
|
+
return [...new Set(names)];
|
|
599
605
|
}
|
|
600
606
|
}
|
|
601
607
|
|
|
602
|
-
|
|
608
|
+
/**
|
|
609
|
+
* Return a camelCase name "fooBar" for a long option "--foo-bar"
|
|
610
|
+
*/
|
|
611
|
+
function camelifyLongOption(opt) {
|
|
612
|
+
return opt.substring(2).replace(/-./g, s => s.substring(1).toUpperCase());
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Return a long option name like "--foo-bar" for a camel-case name "fooBar"
|
|
617
|
+
*/
|
|
618
|
+
function uncamelifyLongOption(opt) {
|
|
619
|
+
return `--${opt.replace(/[A-Z]/g, s => '-' + s.toLowerCase())}`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Check if 'opt' looks like a "-f" short option
|
|
624
|
+
*/
|
|
603
625
|
function isShortOption(opt) {
|
|
604
626
|
return /^-[a-zA-Z?]$/.test(opt);
|
|
605
627
|
}
|
|
606
628
|
|
|
607
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Check if 'opt' looks like a "--foo-bar" long option
|
|
631
|
+
*/
|
|
608
632
|
function isLongOption(opt) {
|
|
609
633
|
return /^--[a-zA-Z0-9-]+$/.test(opt);
|
|
610
634
|
}
|
|
611
635
|
|
|
612
|
-
|
|
636
|
+
/**
|
|
637
|
+
* Check if 'opt' looks like a "<foobar>" parameter
|
|
638
|
+
*/
|
|
613
639
|
function isParam(opt) {
|
|
614
640
|
return /^<[a-zA-Z-]+>$/.test(opt);
|
|
615
641
|
}
|
|
616
642
|
|
|
617
|
-
|
|
643
|
+
/**
|
|
644
|
+
* Check if 'arg' looks like "<foobar...>"
|
|
645
|
+
*/
|
|
618
646
|
function isDynamicPositionalArgument(arg) {
|
|
619
647
|
return /^<[a-zA-Z-]+[.]{3}>$/.test(arg);
|
|
620
648
|
}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"jsdoc/no-undefined-types": 0,
|
|
13
13
|
// eslint-plugin-jsdoc warning
|
|
14
14
|
"jsdoc/require-property": 0,
|
|
15
|
+
// most of the main functions have the normal forEachArtifact/Member signature anyway
|
|
16
|
+
"jsdoc/require-param-description": 0,
|
|
15
17
|
// =airbnb, >eslint:
|
|
16
18
|
"max-len": [ "error", {
|
|
17
19
|
"code": 110,
|