@sap/cds-compiler 2.11.4 → 2.13.8
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 +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +30 -63
- package/lib/api/options.js +5 -5
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +52 -2
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +33 -14
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +7 -6
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +94 -801
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +10 -27
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +2 -1
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2340
- package/lib/compiler/resolver.js +0 -2988
- package/lib/transform/universalCsnEnricher.js +0 -67
|
@@ -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,43 +70,43 @@ 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} */
|
|
71
|
-
const
|
|
77
|
+
const cmd = {
|
|
72
78
|
options: {},
|
|
73
79
|
positionalArguments: [],
|
|
74
|
-
option,
|
|
80
|
+
option: commandOption,
|
|
75
81
|
positionalArgument: (argumentDefinition) => {
|
|
76
|
-
|
|
77
|
-
return
|
|
82
|
+
_setPositionalArguments(argumentDefinition, cmd.positionalArguments);
|
|
83
|
+
return cmd;
|
|
78
84
|
},
|
|
79
|
-
help,
|
|
85
|
+
help: commandHelp,
|
|
80
86
|
..._parseCommandString(cmdString)
|
|
81
87
|
};
|
|
82
|
-
if (optionProcessor.commands[
|
|
83
|
-
throw new Error(`Duplicate assignment for long command ${
|
|
88
|
+
if (optionProcessor.commands[cmd.longName]) {
|
|
89
|
+
throw new Error(`Duplicate assignment for long command ${cmd.longName}`);
|
|
84
90
|
}
|
|
85
|
-
optionProcessor.commands[
|
|
91
|
+
optionProcessor.commands[cmd.longName] = cmd;
|
|
86
92
|
|
|
87
|
-
if (
|
|
88
|
-
if (optionProcessor.commands[
|
|
89
|
-
throw new Error(`Duplicate assignment for short command ${
|
|
93
|
+
if (cmd.shortName) {
|
|
94
|
+
if (optionProcessor.commands[cmd.shortName]) {
|
|
95
|
+
throw new Error(`Duplicate assignment for short command ${cmd.shortName}`);
|
|
90
96
|
}
|
|
91
|
-
optionProcessor.commands[
|
|
97
|
+
optionProcessor.commands[cmd.shortName] = cmd;
|
|
92
98
|
}
|
|
93
|
-
return
|
|
99
|
+
return cmd;
|
|
94
100
|
|
|
95
|
-
// API: Define a command option
|
|
96
|
-
function
|
|
97
|
-
return _addOption(
|
|
101
|
+
// Command API: Define a command option
|
|
102
|
+
function commandOption(optString, validValues, options) {
|
|
103
|
+
return _addOption(cmd, optString, validValues, options);
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
// API: Define the command help text
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
return
|
|
106
|
+
// Command API: Define the command help text
|
|
107
|
+
function commandHelp(text) {
|
|
108
|
+
cmd.helpText = text;
|
|
109
|
+
return cmd;
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
|
|
@@ -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,31 +156,33 @@ function createOptionProcessor() {
|
|
|
149
156
|
* @private
|
|
150
157
|
* @see option()
|
|
151
158
|
*/
|
|
152
|
-
function _addOption(
|
|
159
|
+
function _addOption(cmd, optString, validValues, options) {
|
|
153
160
|
const opt = _parseOptionString(optString, validValues);
|
|
154
|
-
|
|
161
|
+
Object.assign(opt, options);
|
|
162
|
+
|
|
163
|
+
if (cmd.options[opt.longName]) {
|
|
155
164
|
throw new Error(`Duplicate assignment for long option ${opt.longName}`);
|
|
156
165
|
} else if (optionProcessor.options[opt.longName]) {
|
|
157
166
|
// This path is only taken if optString is for commands
|
|
158
167
|
optionProcessor.optionClashes.push({
|
|
159
168
|
option: opt.longName,
|
|
160
|
-
description: `Command '${
|
|
169
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.longName}`
|
|
161
170
|
});
|
|
162
171
|
}
|
|
163
|
-
|
|
172
|
+
cmd.options[opt.longName] = opt;
|
|
164
173
|
if (opt.shortName) {
|
|
165
|
-
if (
|
|
174
|
+
if (cmd.options[opt.shortName]) {
|
|
166
175
|
throw new Error(`Duplicate assignment for short option ${opt.shortName}`);
|
|
167
176
|
} else if (optionProcessor.options[opt.shortName]) {
|
|
168
177
|
// This path is only taken if optString is for commands
|
|
169
178
|
optionProcessor.optionClashes.push({
|
|
170
179
|
option: opt.shortName,
|
|
171
|
-
description: `Command '${
|
|
180
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.shortName}`
|
|
172
181
|
});
|
|
173
182
|
}
|
|
174
|
-
|
|
183
|
+
cmd.options[opt.shortName] = opt;
|
|
175
184
|
}
|
|
176
|
-
return
|
|
185
|
+
return cmd;
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
// Internal: Parse one command string like "F, toFoo". Return an object like this
|
|
@@ -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,52 +334,15 @@ function createOptionProcessor() {
|
|
|
336
334
|
argv = [ ...argv.slice(0, i), ...arg.split('='), ...argv.slice(i + 1)];
|
|
337
335
|
arg = argv[i];
|
|
338
336
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// We already have a command
|
|
342
|
-
const opt = optionProcessor.commands[result.command].options[arg];
|
|
343
|
-
if (opt) {
|
|
344
|
-
// Found as a command option
|
|
345
|
-
i += processOption(i, opt, result.command);
|
|
346
|
-
} else {
|
|
347
|
-
// No command option, try general options as fallback
|
|
348
|
-
const opt = optionProcessor.options[arg];
|
|
349
|
-
if (opt) {
|
|
350
|
-
i += processOption(i, opt, false);
|
|
351
|
-
} else {
|
|
352
|
-
// Not found at all, put into unknownOptions if it is an option
|
|
353
|
-
// for another cdsc command.
|
|
354
|
-
// We dig into the other cdsc commands in order to check if
|
|
355
|
-
// the option expects a parameter and if so to take the next argument as a value
|
|
356
|
-
if (Object.keys(optionProcessor.commands).some(cmd => optionProcessor.commands[cmd].options[arg])) {
|
|
357
|
-
const cmd = optionProcessor.commands[
|
|
358
|
-
Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg])
|
|
359
|
-
];
|
|
360
|
-
i += processOption(i, cmd.options[arg], optionProcessor.commands[result.command], true);
|
|
361
|
-
} else { // still add it to the unknownOptions
|
|
362
|
-
result.unknownOptions.push(`Unknown option "${arg}" for the command "${result.command}"`);
|
|
363
|
-
// if the next argument looks like an argument for this unknown option => skip it
|
|
364
|
-
if ((i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cds|json)$'))
|
|
365
|
-
i++;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
} else {
|
|
370
|
-
// We don't have a command
|
|
371
|
-
const opt = optionProcessor.options[arg];
|
|
372
|
-
if (opt) {
|
|
373
|
-
// Found as a general option
|
|
374
|
-
i += processOption(i, opt, false);
|
|
375
|
-
} else {
|
|
376
|
-
// Not found, complain
|
|
377
|
-
result.unknownOptions.push(`Unknown option "${arg}"`);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
else if (arg === '--') {
|
|
337
|
+
|
|
338
|
+
if (arg === '--') {
|
|
382
339
|
// No more options after '--'
|
|
383
340
|
seenDashDash = true;
|
|
384
|
-
}
|
|
341
|
+
}
|
|
342
|
+
else if (!seenDashDash && arg.startsWith('-')) {
|
|
343
|
+
i += processOption(i);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
385
346
|
// Command or arg
|
|
386
347
|
if (result.command === undefined) {
|
|
387
348
|
if (optionProcessor.commands[arg]) {
|
|
@@ -407,7 +368,11 @@ function createOptionProcessor() {
|
|
|
407
368
|
const missingArg = getCurrentPositionArguments().find((arg) => arg.required && !result.args[arg.name]);
|
|
408
369
|
if (missingArg) {
|
|
409
370
|
const forCommand = result.command ? ` for '${ result.command }'` : '';
|
|
410
|
-
|
|
371
|
+
const errorMsg = `Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`;
|
|
372
|
+
if (forCommand)
|
|
373
|
+
result.cmdErrors.push(errorMsg)
|
|
374
|
+
else
|
|
375
|
+
result.errors.push(errorMsg)
|
|
411
376
|
}
|
|
412
377
|
|
|
413
378
|
return result;
|
|
@@ -420,8 +385,7 @@ function createOptionProcessor() {
|
|
|
420
385
|
*/
|
|
421
386
|
function getCurrentPositionArguments() {
|
|
422
387
|
const cmd = optionProcessor.commands[result.command];
|
|
423
|
-
|
|
424
|
-
return args;
|
|
388
|
+
return ( cmd && cmd.positionalArguments && cmd.positionalArguments.length ) ? cmd.positionalArguments : optionProcessor.positionalArguments;
|
|
425
389
|
}
|
|
426
390
|
|
|
427
391
|
function processPositionalArgument(argumentValue) {
|
|
@@ -449,57 +413,127 @@ function createOptionProcessor() {
|
|
|
449
413
|
|
|
450
414
|
// (Note that this works on 'argv' and 'result' from above).
|
|
451
415
|
// Process 'argv[i]' as an option.
|
|
452
|
-
// Check the option definition
|
|
416
|
+
// Check the option definition to see if a parameter is expected.
|
|
453
417
|
// If so, take it (complain if one is found in 'argv').
|
|
454
418
|
// Populate 'result.options' with the result. Return the number params found (0 or 1).
|
|
455
|
-
function processOption(i
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
419
|
+
function processOption(i) {
|
|
420
|
+
const arg = argv[i];
|
|
421
|
+
let currentCommand = result.command;
|
|
422
|
+
|
|
423
|
+
// First check top-level options
|
|
424
|
+
let currentOption = optionProcessor.options[arg];
|
|
425
|
+
if (currentCommand) {
|
|
426
|
+
// If there is a command and it has an option that overrides it, use it instead.
|
|
427
|
+
const cmdOpt = optionProcessor.commands[currentCommand].options[arg];
|
|
428
|
+
if (cmdOpt)
|
|
429
|
+
currentOption = cmdOpt;
|
|
430
|
+
else if (currentOption)
|
|
431
|
+
// Otherwise, if there exist a top-level option, set 'command' to null.
|
|
432
|
+
currentCommand = null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (!currentOption)
|
|
436
|
+
return reportUnknown();
|
|
437
|
+
|
|
438
|
+
if (!currentOption.param) {
|
|
439
|
+
setCurrentOption(true);
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const param = paramForOption(currentOption);
|
|
444
|
+
if (param === null)
|
|
445
|
+
return 0;
|
|
446
|
+
setCurrentOption(param);
|
|
447
|
+
return 1;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Report that an option is unknown. If the option exists for other
|
|
451
|
+
* commands or if the next argument looks like a param, return 1,
|
|
452
|
+
* otherwise 0, indicating how many argv fields have been consumed.
|
|
453
|
+
*
|
|
454
|
+
* @returns {number}
|
|
455
|
+
*/
|
|
456
|
+
function reportUnknown() {
|
|
457
|
+
if (currentCommand)
|
|
458
|
+
result.unknownOptions.push(`Unknown option "${arg}" for the command "${currentCommand}"`);
|
|
459
|
+
else
|
|
460
|
+
result.unknownOptions.push(`Unknown option "${arg}"`);
|
|
461
|
+
|
|
462
|
+
if (currentCommand) {
|
|
463
|
+
// Not found at all. We dig into the other cdsc commands in order to check if
|
|
464
|
+
// the option expects a parameter and if so to take the next argument as a value
|
|
465
|
+
const otherCmd = Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg]);
|
|
466
|
+
const otherCmdOpt = otherCmd && optionProcessor.commands[otherCmd].options[arg];
|
|
467
|
+
if (otherCmdOpt && hasParamForUnknown(otherCmdOpt)) {
|
|
468
|
+
return 1
|
|
468
469
|
}
|
|
469
|
-
return 0;
|
|
470
470
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const value = argv[i + 1];
|
|
474
|
-
const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
|
|
475
|
-
if (command) {
|
|
476
|
-
// if an unknown option for a command => add it to the array and warn about
|
|
477
|
-
if (unknownOption) {
|
|
478
|
-
result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
|
|
479
|
-
} else {
|
|
480
|
-
result.options[command][opt.camelName] = value;
|
|
481
|
-
if (opt.validValues && !opt.validValues.includes(value)) {
|
|
482
|
-
result.cmdErrors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
} else {
|
|
486
|
-
result.options[opt.camelName] = value;
|
|
487
|
-
if (opt.validValues && !opt.validValues.some( validValue => validValue.toLowerCase() === value.toLowerCase() ) ) {
|
|
488
|
-
result.errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
471
|
+
|
|
472
|
+
if (hasParamForUnknown(null))
|
|
491
473
|
return 1;
|
|
474
|
+
|
|
475
|
+
return 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function setCurrentOption(val) {
|
|
479
|
+
if (currentCommand) {
|
|
480
|
+
if (!result.options[currentCommand])
|
|
481
|
+
result.options[currentCommand] = {};
|
|
482
|
+
result.options[currentCommand][currentOption.camelName] = val;
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
result.options[currentOption.camelName] = val;
|
|
492
486
|
}
|
|
493
487
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
488
|
+
|
|
489
|
+
function reportMissingParam(opt) {
|
|
490
|
+
let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
|
|
491
|
+
if (currentCommand) {
|
|
492
|
+
error = `${error} of command "${currentCommand}"`;
|
|
493
|
+
result.cmdErrors.push(error);
|
|
494
|
+
} else {
|
|
495
|
+
result.errors.push(error);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function reportInvalidValue(opt, value) {
|
|
500
|
+
const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
|
|
501
|
+
const errors = currentCommand ? result.cmdErrors : result.errors;
|
|
502
|
+
errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Get the value for the option's parameter. If the option does not require one,
|
|
507
|
+
* returns `null`. Reports missing parameters and invalid values.
|
|
508
|
+
*
|
|
509
|
+
* @returns {null|*}
|
|
510
|
+
*/
|
|
511
|
+
function paramForOption(opt, reportMissing = true) {
|
|
512
|
+
if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
|
|
513
|
+
if (reportMissing)
|
|
514
|
+
reportMissingParam(opt)
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const value = argv[i + 1];
|
|
519
|
+
if (!isValidOptionValue(opt, value) && reportMissing) {
|
|
520
|
+
reportInvalidValue(opt, value);
|
|
521
|
+
}
|
|
522
|
+
return value;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Returns true if:
|
|
527
|
+
* - we didn't find an option (opt === null) _or_
|
|
528
|
+
* - we found an option and it requires a param
|
|
529
|
+
* _and_ if the next arg looks like an argument.
|
|
530
|
+
*
|
|
531
|
+
* @param {object|null} opt
|
|
532
|
+
* @returns {boolean}
|
|
533
|
+
*/
|
|
534
|
+
function hasParamForUnknown(opt) {
|
|
535
|
+
return ((!opt || opt.param) && (i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cdl|cds|json)$'));
|
|
501
536
|
}
|
|
502
|
-
return 0;
|
|
503
537
|
}
|
|
504
538
|
}
|
|
505
539
|
|
|
@@ -507,16 +541,16 @@ function createOptionProcessor() {
|
|
|
507
541
|
// If 'command' is supplied, check only 'options.command', otherwise check
|
|
508
542
|
// only top-level options
|
|
509
543
|
// Return an array of complaints (possibly empty)
|
|
510
|
-
function verifyOptions(options,
|
|
544
|
+
function verifyOptions(options, commandName = undefined, silent = false) {
|
|
511
545
|
const result = [];
|
|
512
546
|
let opts;
|
|
513
547
|
|
|
514
|
-
if((options.betaMode || options.beta) && !options.testMode && !silent) {
|
|
548
|
+
if ((options.betaMode || options.beta) && !options.testMode && !silent) {
|
|
515
549
|
const mode = options.beta ? 'beta' : 'beta-mode';
|
|
516
550
|
result.push(`Option --${mode} was used. This option should not be used in productive scenarios!`)
|
|
517
551
|
}
|
|
518
552
|
|
|
519
|
-
if(options) {
|
|
553
|
+
if (options) {
|
|
520
554
|
[
|
|
521
555
|
'defaultBinaryLength', 'defaultStringLength',
|
|
522
556
|
/*'length', 'precision', 'scale'*/
|
|
@@ -529,13 +563,13 @@ function createOptionProcessor() {
|
|
|
529
563
|
});
|
|
530
564
|
}
|
|
531
565
|
|
|
532
|
-
if (
|
|
533
|
-
const cmd = optionProcessor.commands[
|
|
566
|
+
if (commandName) {
|
|
567
|
+
const cmd = optionProcessor.commands[commandName];
|
|
534
568
|
if (!cmd) {
|
|
535
|
-
throw new Error(`Expected existing command: "${
|
|
569
|
+
throw new Error(`Expected existing command: "${cmd}"`);
|
|
536
570
|
}
|
|
537
571
|
opts = cmd.options;
|
|
538
|
-
options = options[
|
|
572
|
+
options = options[cmd] || {};
|
|
539
573
|
if (typeof options === 'boolean') {
|
|
540
574
|
// Special case: command without options
|
|
541
575
|
options = {};
|
|
@@ -545,16 +579,16 @@ function createOptionProcessor() {
|
|
|
545
579
|
}
|
|
546
580
|
// Look at each supplied option
|
|
547
581
|
for (const camelName in options) {
|
|
548
|
-
const opt = opts[
|
|
582
|
+
const opt = opts[uncamelifyLongOption(camelName)];
|
|
549
583
|
let error;
|
|
550
584
|
if (!opt) {
|
|
551
585
|
// Don't report commands in top-level options
|
|
552
|
-
if ((
|
|
553
|
-
error = `Unknown option "${
|
|
586
|
+
if ((commandName || !optionProcessor.commands[camelName]) && !silent) {
|
|
587
|
+
error = `Unknown option "${commandName ? commandName + '.' : ''}${camelName}"`;
|
|
554
588
|
}
|
|
555
589
|
} else {
|
|
556
590
|
const param = options[camelName];
|
|
557
|
-
error = verifyOptionParam(param, opt,
|
|
591
|
+
error = verifyOptionParam(param, opt, commandName ? commandName + '.' : '');
|
|
558
592
|
}
|
|
559
593
|
if (error) {
|
|
560
594
|
result.push(error);
|
|
@@ -570,7 +604,7 @@ function createOptionProcessor() {
|
|
|
570
604
|
// Parameter is required for this option
|
|
571
605
|
if (typeof param === 'boolean') {
|
|
572
606
|
return `Missing value for option "${prefix}${opt.camelName}"`;
|
|
573
|
-
} else if (opt
|
|
607
|
+
} else if (!isValidOptionValue(opt, param)) {
|
|
574
608
|
return `Invalid value "${param}" for option "${prefix}${opt.camelName}" - use one of [${opt.validValues}]`;
|
|
575
609
|
}
|
|
576
610
|
return false;
|
|
@@ -585,36 +619,65 @@ function createOptionProcessor() {
|
|
|
585
619
|
}
|
|
586
620
|
}
|
|
587
621
|
|
|
622
|
+
function isValidOptionValue(opt, value) {
|
|
623
|
+
// Explicitly convert to string, input 'value' may be boolean
|
|
624
|
+
value = String(value);
|
|
625
|
+
if (!opt.validValues || !opt.validValues.length)
|
|
626
|
+
return true;
|
|
627
|
+
if (opt.ignoreCase)
|
|
628
|
+
return opt.validValues.some( valid => valid.toLowerCase() === value.toLowerCase() );
|
|
629
|
+
return opt.validValues.includes(value);
|
|
630
|
+
}
|
|
631
|
+
|
|
588
632
|
// Return an array of unique camelNames of the options for the specified command
|
|
589
633
|
// If invalid command -> an empty array
|
|
590
|
-
function camelOptionsForCommand(
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
} else {
|
|
597
|
-
return [];
|
|
598
|
-
}
|
|
634
|
+
function camelOptionsForCommand(cmd) {
|
|
635
|
+
if (!cmd || !optionProcessor.commands[cmd])
|
|
636
|
+
return []
|
|
637
|
+
cmd = optionProcessor.commands[cmd];
|
|
638
|
+
const names = Object.keys(cmd.options).map(name => cmd.options[name].camelName);
|
|
639
|
+
return [...new Set(names)];
|
|
599
640
|
}
|
|
600
641
|
}
|
|
601
642
|
|
|
602
|
-
|
|
643
|
+
/**
|
|
644
|
+
* Return a camelCase name "fooBar" for a long option "--foo-bar"
|
|
645
|
+
*/
|
|
646
|
+
function camelifyLongOption(opt) {
|
|
647
|
+
return opt.substring(2).replace(/-./g, s => s.substring(1).toUpperCase());
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Return a long option name like "--foo-bar" for a camel-case name "fooBar"
|
|
652
|
+
*/
|
|
653
|
+
function uncamelifyLongOption(opt) {
|
|
654
|
+
return `--${opt.replace(/[A-Z]/g, s => '-' + s.toLowerCase())}`;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Check if 'opt' looks like a "-f" short option
|
|
659
|
+
*/
|
|
603
660
|
function isShortOption(opt) {
|
|
604
661
|
return /^-[a-zA-Z?]$/.test(opt);
|
|
605
662
|
}
|
|
606
663
|
|
|
607
|
-
|
|
664
|
+
/**
|
|
665
|
+
* Check if 'opt' looks like a "--foo-bar" long option
|
|
666
|
+
*/
|
|
608
667
|
function isLongOption(opt) {
|
|
609
668
|
return /^--[a-zA-Z0-9-]+$/.test(opt);
|
|
610
669
|
}
|
|
611
670
|
|
|
612
|
-
|
|
671
|
+
/**
|
|
672
|
+
* Check if 'opt' looks like a "<foobar>" parameter
|
|
673
|
+
*/
|
|
613
674
|
function isParam(opt) {
|
|
614
675
|
return /^<[a-zA-Z-]+>$/.test(opt);
|
|
615
676
|
}
|
|
616
677
|
|
|
617
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Check if 'arg' looks like "<foobar...>"
|
|
680
|
+
*/
|
|
618
681
|
function isDynamicPositionalArgument(arg) {
|
|
619
682
|
return /^<[a-zA-Z-]+[.]{3}>$/.test(arg);
|
|
620
683
|
}
|
|
@@ -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,
|
|
@@ -110,7 +110,8 @@ function checkActionOrFunction(art, artName, prop, path) {
|
|
|
110
110
|
* @param {CSN.Path} currPath The current path
|
|
111
111
|
*/
|
|
112
112
|
function checkUserDefinedType(type, typeName, currPath) {
|
|
113
|
-
|
|
113
|
+
// TODO: isBuiltinType does not resolve any type-chains.
|
|
114
|
+
if (!isBuiltinType(type.type) && type.kind && type.kind !== 'type') {
|
|
114
115
|
const serviceOfType = this.csnUtils.getServiceName(typeName);
|
|
115
116
|
if (serviceName && serviceName !== serviceOfType) {
|
|
116
117
|
// if (!(isMultiSchema && serviceOfType)) {
|
|
@@ -82,7 +82,7 @@ function checkAtSapAnnotations(node) {
|
|
|
82
82
|
function checkReadOnlyAndInsertOnly(artifact, artifactName) {
|
|
83
83
|
if (!this.csnUtils.getServiceName(artifactName))
|
|
84
84
|
return;
|
|
85
|
-
if (artifact.kind
|
|
85
|
+
if (artifact.kind === 'entity' && artifact['@readonly'] && artifact['@insertonly'])
|
|
86
86
|
this.warning(null, artifact.$path, 'Annotations “@readonly” and “@insertonly” can\'t be assigned in combination');
|
|
87
87
|
}
|
|
88
88
|
|