@sap/cds-dk 7.8.2 → 7.9.1
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/bin/add.js +28 -7
- package/bin/bind.js +64 -21
- package/bin/build.js +35 -3
- package/bin/cds.js +31 -13
- package/bin/compile/index.js +44 -3
- package/bin/completion/completionFs.js +61 -0
- package/bin/completion/index.js +212 -0
- package/bin/completion/scripts/cds-bash.sh +17 -0
- package/bin/completion/scripts/cds-ps.ps1 +18 -0
- package/bin/completion/scripts/cds-zsh.sh +12 -0
- package/bin/help.js +8 -2
- package/bin/init.js +34 -4
- package/bin/subscribe.js +5 -1
- package/bin/watch.js +4 -2
- package/lib/bind/KubernetesApiAccessor.js +3 -2
- package/lib/bind/bindingResolver.js +3 -2
- package/lib/bind/bindingUtil.js +10 -8
- package/lib/bind/cfKeyProvider.js +58 -153
- package/lib/bind/index.js +46 -36
- package/lib/bind/kubernetesCredentialsProvider.js +6 -5
- package/lib/bind/userCdsrcJsonStorageProvider.js +25 -3
- package/lib/build/buildTaskEngine.js +1 -1
- package/lib/build/provider/hana/index.js +1 -1
- package/lib/build/provider/hana/template/undeploy.json +3 -1
- package/lib/build/provider/nodejs/index.js +1 -1
- package/lib/compile/openapi/index.js +5 -0
- package/lib/deploy/to-hana/hana.js +25 -7
- package/lib/env/schemas/cds-rc.js +29 -2
- package/lib/import/odata/v4parser.js +78 -25
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +18 -13
- package/lib/init/merge.js +1 -0
- package/lib/init/mvn.js +8 -47
- package/lib/init/plugin.js +0 -4
- package/lib/init/projectReader.js +17 -10
- package/lib/init/template/approuter/files/values.yaml.hbs +0 -12
- package/lib/init/template/completion/completionSetup.js +184 -0
- package/lib/init/template/completion/index.js +22 -0
- package/lib/init/template/data/as-csv.js +25 -17
- package/lib/init/template/data/as-json.js +115 -30
- package/lib/init/template/data/index.js +28 -15
- package/lib/init/template/hana/files/undeploy.json +2 -1
- package/lib/init/template/helm/files/chart/values.schema.json +1 -1
- package/lib/init/template/helm/files/content-deployment/Chart.yaml +0 -2
- package/lib/init/template/helm/files/content-deployment/values.schema.json +0 -1
- package/lib/init/template/helm/files/service-instance/Chart.yaml +0 -2
- package/lib/init/template/helm/files/service-instance/values.schema.json +0 -1
- package/lib/init/template/helm/files/web-application/Chart.yaml +0 -2
- package/lib/init/template/helm/files/web-application/values.schema.json +0 -1
- package/lib/init/template/helm-unified-runtime/files/values.schema.json +1 -1
- package/lib/init/template/http/index.js +150 -104
- package/lib/init/template/kafka/index.js +6 -0
- package/lib/init/template/lint/index.js +12 -13
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/files/values.yaml.hbs +0 -5
- package/lib/init/template/multitenancy/index.js +13 -5
- package/lib/init/template/nodejs/files/.vscode/settings.json +11 -0
- package/lib/init/template/nodejs/files/eslint.config.js +13 -0
- package/lib/init/template/nodejs/files/package.json.hbs +3 -1
- package/lib/init/template/sample/index.js +18 -20
- package/lib/init/template/tiny-sample/index.js +1 -2
- package/lib/init/template/typer/index.js +2 -2
- package/lib/lint/Linter.js +133 -55
- package/lib/lint/checks.js +5 -6
- package/lib/lint/io.js +119 -10
- package/lib/mtx/auth_manager.js +5 -5
- package/lib/mtx/params.js +4 -2
- package/lib/mtx/settings_manager.js +1 -1
- package/lib/mtx/tenant_action.js +12 -2
- package/lib/mtx/util/logging.js +10 -4
- package/lib/util/axios.js +2 -1
- package/lib/util/cf.js +32 -37
- package/lib/util/git.js +1 -1
- package/lib/util/term.js +1 -1
- package/{bin → lib/watch}/watched.js +2 -2
- package/npm-shrinkwrap.json +34 -34
- package/package.json +2 -2
- package/lib/init/template/nodejs/files/.eslintrc +0 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { join } = require('path')
|
|
2
2
|
const { readProject } = require('../../projectReader')
|
|
3
3
|
const { merge, mergeGitignore } = require('../../merge')
|
|
4
|
-
const { warn,
|
|
4
|
+
const { warn, dim } = require('../../../util/term')
|
|
5
5
|
const { TYPER } = require('../../constants').OPTIONS
|
|
6
6
|
const { exists, read } = require('../../../cds').utils
|
|
7
7
|
|
|
@@ -25,7 +25,7 @@ module.exports = class TyperTemplate extends require('../../plugin') {
|
|
|
25
25
|
}
|
|
26
26
|
await merge(template('package.json')).into('package.json')
|
|
27
27
|
await mergeGitignore('.gitignore', template('gitignore'))
|
|
28
|
-
console.log(
|
|
28
|
+
console.log(dim(` Adding '@cds-models' to .gitignore to ignore generated types`)) // REVISIT: other facets are silent wrt. what they change
|
|
29
29
|
try {
|
|
30
30
|
await merge(source).into(target)
|
|
31
31
|
} catch (e) {
|
package/lib/lint/Linter.js
CHANGED
|
@@ -10,6 +10,8 @@ const { exit } = require("process");
|
|
|
10
10
|
const LOG = cds.debug("lint");
|
|
11
11
|
const LOG_CONFIG = cds.debug("lint:config");
|
|
12
12
|
|
|
13
|
+
const GENERAL_ERROR = 1;
|
|
14
|
+
|
|
13
15
|
class Linter {
|
|
14
16
|
constructor() {
|
|
15
17
|
this.eslintCmd = "";
|
|
@@ -61,6 +63,11 @@ class Linter {
|
|
|
61
63
|
* @param {*} options options/flag passed by user
|
|
62
64
|
*/
|
|
63
65
|
async lint(args, options) {
|
|
66
|
+
// Check for ESLint (legacy)
|
|
67
|
+
const currentDir = process.cwd();
|
|
68
|
+
const DefaultESLint = await this._loadEslint(currentDir);
|
|
69
|
+
this.isESLintLegacy = DefaultESLint.configType === "eslintrc";
|
|
70
|
+
|
|
64
71
|
this.eslintCmdOpts = this._sanitizeUserOpts(options);
|
|
65
72
|
this.eslintCmdFileExpr = args.length ? args : ["."];
|
|
66
73
|
if (this.eslintCmdOpts.help) {
|
|
@@ -82,7 +89,7 @@ class Linter {
|
|
|
82
89
|
|
|
83
90
|
// Run ESLint with collected options
|
|
84
91
|
try {
|
|
85
|
-
await this._runEslint();
|
|
92
|
+
await this._runEslint(currentDir, DefaultESLint);
|
|
86
93
|
} catch (err) {
|
|
87
94
|
term.error(err);
|
|
88
95
|
}
|
|
@@ -91,7 +98,6 @@ class Linter {
|
|
|
91
98
|
|
|
92
99
|
_printHelp() {
|
|
93
100
|
console.log(this.help.replace(/ \*([^*]+)\*/g, ` ${term.codes.bold}$1${term.codes.reset}`));
|
|
94
|
-
return;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
_sanitizeUserOpts(options) {
|
|
@@ -111,7 +117,7 @@ class Linter {
|
|
|
111
117
|
* @param {string} currentDir start here and search until root dir
|
|
112
118
|
* @returns {string} dir containing ESLint config file (empty if not exists)
|
|
113
119
|
*/
|
|
114
|
-
async
|
|
120
|
+
async _getConfigPathLegacy(currentDir = ".") {
|
|
115
121
|
const configFiles = [
|
|
116
122
|
".eslintrc.js",
|
|
117
123
|
".eslintrc.cjs",
|
|
@@ -122,18 +128,48 @@ class Linter {
|
|
|
122
128
|
"package.json",
|
|
123
129
|
];
|
|
124
130
|
let configDir = path.resolve(currentDir);
|
|
125
|
-
while (configDir !== path.
|
|
131
|
+
while (configDir && configDir !== path.dirname(configDir)) {
|
|
126
132
|
for (let i = 0; i < configFiles.length; i++) {
|
|
127
133
|
const configPath = path.join(configDir, configFiles[i]);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
134
|
+
const config = this.safeRequire(configPath)
|
|
135
|
+
|
|
136
|
+
if (configFiles[i] !== "package.json" || config?.eslintConfig) {
|
|
131
137
|
if (exists(configPath) && await isfile(configPath)) {
|
|
132
138
|
return configPath;
|
|
133
139
|
}
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
|
-
|
|
142
|
+
|
|
143
|
+
configDir = path.dirname(configDir);
|
|
144
|
+
}
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Searches for ESLint config file types (in order or precedence)
|
|
150
|
+
* and returns corresponding directory (usually project's root dir)
|
|
151
|
+
* https://eslint.org/docs/user-guide/configuring#configuration-file-formats
|
|
152
|
+
* @param {string} currentDir start here and search until root dir
|
|
153
|
+
* @returns {string} dir containing ESLint config file (empty if not exists)
|
|
154
|
+
*/
|
|
155
|
+
async _getConfigPath(currentDir = ".") {
|
|
156
|
+
const configFiles = [
|
|
157
|
+
"eslint.config.js",
|
|
158
|
+
"eslint.config.cjs",
|
|
159
|
+
"eslint.config.mjs"
|
|
160
|
+
];
|
|
161
|
+
let configDir = path.resolve(currentDir);
|
|
162
|
+
while (configDir && configDir !== path.dirname(configDir)) {
|
|
163
|
+
for (let i = 0; i < configFiles.length; i++) {
|
|
164
|
+
const configPath = path.join(configDir, configFiles[i]);
|
|
165
|
+
const config = this.safeRequire(configPath)
|
|
166
|
+
if (configFiles[i] !== "package.json" || config?.eslintConfig) {
|
|
167
|
+
if (exists(configPath) && await isfile(configPath)) { // TODO exists neccessary ?
|
|
168
|
+
return configPath;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
configDir = path.dirname(configDir);
|
|
137
173
|
}
|
|
138
174
|
return "";
|
|
139
175
|
}
|
|
@@ -146,45 +182,51 @@ class Linter {
|
|
|
146
182
|
this.fileExtensions.forEach((ext) => {
|
|
147
183
|
this.ignorePatterns.push(`!${ext}`);
|
|
148
184
|
});
|
|
149
|
-
return;
|
|
150
185
|
}
|
|
151
186
|
|
|
152
|
-
async _runEslint() {
|
|
187
|
+
async _runEslint(currentDir, DefaultESLint) {
|
|
153
188
|
try {
|
|
154
|
-
const currentDir = process.cwd();
|
|
155
|
-
|
|
156
|
-
const { ESLint } = require("eslint");
|
|
157
189
|
|
|
158
190
|
let eslintOpts = {
|
|
159
191
|
cwd: currentDir,
|
|
160
|
-
extensions: this.fileExtensions,
|
|
161
192
|
overrideConfig: {
|
|
162
193
|
...this.configContents,
|
|
163
194
|
},
|
|
164
|
-
useEslintrc: false,
|
|
165
195
|
};
|
|
166
196
|
|
|
167
197
|
if (this.lintType === "global") {
|
|
168
198
|
eslintOpts = {
|
|
169
199
|
cwd: process.cwd(),
|
|
170
|
-
|
|
171
|
-
overrideConfig: {
|
|
172
|
-
plugins: ["@sap/eslint-plugin-cds"],
|
|
173
|
-
extends: "plugin:@sap/cds/recommended"
|
|
174
|
-
},
|
|
175
|
-
useEslintrc: false,
|
|
176
|
-
resolvePluginsRelativeTo: this.cdsdkPath
|
|
200
|
+
overrideConfig: {},
|
|
177
201
|
};
|
|
178
202
|
}
|
|
179
203
|
|
|
204
|
+
if (this.isESLintLegacy) {
|
|
205
|
+
LOG?.("Using legacy ESLint configuration");
|
|
206
|
+
eslintOpts.extensions = this.fileExtensions;
|
|
207
|
+
eslintOpts.overrideConfig.extends = "plugin:@sap/cds/recommended-legacy"
|
|
208
|
+
eslintOpts.overrideConfig.plugins = ["@sap/eslint-plugin-cds"]
|
|
209
|
+
eslintOpts.useEslintrc = false
|
|
210
|
+
if (this.lintType === "global") {
|
|
211
|
+
eslintOpts.resolvePluginsRelativeTo = this.cdsdkPath
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
LOG?.("Using flat ESLint configuration");
|
|
215
|
+
const cdsPlugin = require(this.pluginPath)
|
|
216
|
+
// Exvlude our own apis from plugin to avoid wrapper errors
|
|
217
|
+
delete cdsPlugin.configs.recommended?.plugins?.['@sap/cds']
|
|
218
|
+
eslintOpts.overrideConfig = [cdsPlugin.configs.recommended]
|
|
219
|
+
}
|
|
220
|
+
|
|
180
221
|
if (this.customRulesOpts && this.customRulesOpts.length > 0) {
|
|
181
222
|
eslintOpts.rulePaths = [this.customRulesOpts]
|
|
182
223
|
}
|
|
183
224
|
|
|
184
|
-
LOG_CONFIG
|
|
225
|
+
LOG_CONFIG?.(eslintOpts);
|
|
185
226
|
|
|
186
227
|
let lintString = `eslint`;
|
|
187
|
-
if (this.fileExtensions) { lintString += ` --ext "${this.fileExtensions.join(",")}"`}
|
|
228
|
+
if (this.fileExtensions) { lintString += ` --ext "${this.fileExtensions.join(",")}"` }
|
|
229
|
+
|
|
188
230
|
for (const ruleOpt of Object.entries(this.ruleOpts)) {
|
|
189
231
|
lintString += ` --rule ${ruleOpt[0]}:${ruleOpt[1]}`;
|
|
190
232
|
}
|
|
@@ -192,10 +234,10 @@ class Linter {
|
|
|
192
234
|
lintString += ` --rulesdir "${this.customRulesOpts}"`
|
|
193
235
|
}
|
|
194
236
|
|
|
195
|
-
LOG
|
|
237
|
+
LOG?.(lintString);
|
|
196
238
|
|
|
197
239
|
if (!process.env.isTest) {
|
|
198
|
-
const eslint = new
|
|
240
|
+
const eslint = new DefaultESLint(eslintOpts);
|
|
199
241
|
const formatter = await eslint.loadFormatter("stylish");
|
|
200
242
|
|
|
201
243
|
let results = (await eslint.lintText("")).map((result) => {
|
|
@@ -221,13 +263,12 @@ class Linter {
|
|
|
221
263
|
const eslintBase = `${require.resolve('eslint').split('eslint')[0]}/eslint`;
|
|
222
264
|
const template = require(path.join(eslintBase, `messages/${err.messageTemplate}.js`));
|
|
223
265
|
console.log(template(err.messageData || {}));
|
|
224
|
-
exit(1)
|
|
225
266
|
} catch {
|
|
226
|
-
|
|
267
|
+
// Ignore template error then fallback to use `error.stack`.
|
|
268
|
+
console.log(err.stack);
|
|
227
269
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
exit(1)
|
|
270
|
+
}
|
|
271
|
+
exit(GENERAL_ERROR); // TODO use error constant
|
|
231
272
|
}
|
|
232
273
|
}
|
|
233
274
|
|
|
@@ -244,31 +285,50 @@ class Linter {
|
|
|
244
285
|
input = ".";
|
|
245
286
|
}
|
|
246
287
|
}
|
|
247
|
-
this.configPath =
|
|
288
|
+
this.configPath = this.isESLintLegacy
|
|
289
|
+
? await this._getConfigPathLegacy(process.cwd())
|
|
290
|
+
: await this._getConfigPath(process.cwd());
|
|
248
291
|
if (!this.configPath) {
|
|
249
292
|
this.lintType === "global";
|
|
250
|
-
this.configContents =
|
|
293
|
+
this.configContents = this.isESLintLegacy
|
|
294
|
+
? await io.sanitizeEslintConfigLegacy({}, false, LOG)
|
|
295
|
+
: await io.sanitizeEslintConfig({}, false, LOG);
|
|
251
296
|
} else {
|
|
252
|
-
const configContents =
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
297
|
+
const configContents = this.isESLintLegacy
|
|
298
|
+
? await io.readEslintConfigLegacy(this.configPath)
|
|
299
|
+
: await io.readEslintConfig(this.configPath);
|
|
300
|
+
this.configContents = configContents;
|
|
301
|
+
if (this.isESLintLegacy) {
|
|
302
|
+
this.configContents = await io.sanitizeEslintConfig(configContents, false, LOG);
|
|
303
|
+
if (this.configContents) {
|
|
304
|
+
if ("extends" in this.configContents) {
|
|
305
|
+
this.extendsPlugin = checks.hasEslintConfigContent(
|
|
306
|
+
this.configContents,
|
|
307
|
+
"extends"
|
|
308
|
+
);
|
|
309
|
+
if (this.extendsPlugin) {
|
|
310
|
+
this.lintType = "local";
|
|
311
|
+
try {
|
|
312
|
+
this.pluginPath = require.resolve("@sap/eslint-plugin-cds", {
|
|
313
|
+
paths: [path.dirname(this.configPath)],
|
|
314
|
+
});
|
|
315
|
+
this.pluginApi = require(this.pluginPath.replace("index.js", "api"));
|
|
316
|
+
} catch (err) {
|
|
317
|
+
// CLI will report (no locally installed plugin)
|
|
318
|
+
}
|
|
269
319
|
}
|
|
270
320
|
}
|
|
271
321
|
}
|
|
322
|
+
} else {
|
|
323
|
+
const cdsConfig = configContents?.filter(c => c.files?.includes("*.cds"))[0] ?? {}
|
|
324
|
+
this.configContents = cdsConfig.files
|
|
325
|
+
? {
|
|
326
|
+
...cdsConfig.plugins.configs?.recommended,
|
|
327
|
+
languageOptions: cdsConfig.languageOptions,
|
|
328
|
+
files: cdsConfig.files,
|
|
329
|
+
rules: cdsConfig.rules,
|
|
330
|
+
}
|
|
331
|
+
: {};
|
|
272
332
|
}
|
|
273
333
|
}
|
|
274
334
|
if (!this.pluginPath) {
|
|
@@ -281,17 +341,20 @@ class Linter {
|
|
|
281
341
|
// CLI will report (no globally installed plugin)
|
|
282
342
|
}
|
|
283
343
|
}
|
|
284
|
-
|
|
344
|
+
LOG?.(`Lint type: ${this.lintType}`);
|
|
345
|
+
LOG?.(`ESLint CDS plugin resolved: ${this.pluginPath}`);
|
|
285
346
|
}
|
|
286
347
|
|
|
287
348
|
async _overwriteRuleSeverities() {
|
|
288
349
|
let configContents = {};
|
|
289
350
|
if (this.configPath) {
|
|
290
|
-
configContents = await io.
|
|
351
|
+
configContents = await io.readEslintConfigLegacy(this.configPath);
|
|
291
352
|
} else {
|
|
292
|
-
configContents =
|
|
353
|
+
configContents = this.isESLintLegacy
|
|
354
|
+
? await io.readEslintConfigLegacy(await this._getConfigPath(this.cdsdkPath))
|
|
355
|
+
: await io.readEslintConfig(await this._getConfigPath(this.cdsdkPath));
|
|
293
356
|
}
|
|
294
|
-
let rules = configContents
|
|
357
|
+
let rules = configContents?.rules || {};
|
|
295
358
|
// Allow recommended plugin rules in cds-dk to be overwritten
|
|
296
359
|
// by user by adding rule to cmd line (because of precedence)
|
|
297
360
|
const pluginRules = require(this.pluginPath).configs.recommended.rules;
|
|
@@ -305,7 +368,22 @@ class Linter {
|
|
|
305
368
|
}
|
|
306
369
|
}
|
|
307
370
|
}
|
|
308
|
-
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async _loadEslint(currentDir) {
|
|
374
|
+
const { loadESLint, ESLint } = require("eslint");
|
|
375
|
+
const DefaultESLint = loadESLint
|
|
376
|
+
? await loadESLint({ cwd: currentDir }) // ESLint 9
|
|
377
|
+
: new ESLint({ cwd: currentDir }) // ESLint 8
|
|
378
|
+
return DefaultESLint;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
safeRequire(module) {
|
|
382
|
+
try {
|
|
383
|
+
return require(module)
|
|
384
|
+
} catch (e) {
|
|
385
|
+
return {}
|
|
386
|
+
}
|
|
309
387
|
}
|
|
310
388
|
}
|
|
311
389
|
|
package/lib/lint/checks.js
CHANGED
|
@@ -7,7 +7,7 @@ const IS_WIN = os.platform() === "win32";
|
|
|
7
7
|
|
|
8
8
|
module.exports = {
|
|
9
9
|
/**
|
|
10
|
-
* Checks whether ESLint v>=
|
|
10
|
+
* Checks whether ESLint v>=8 is installed by trying to resolve
|
|
11
11
|
* 'eslint' module from (1) <inputPath> or (2) global npm path
|
|
12
12
|
* @param {*} inputPath where to resolve 'eslint' module from
|
|
13
13
|
* @param {*} globalPath resolves 'eslint' module from cds-dk
|
|
@@ -23,14 +23,14 @@ module.exports = {
|
|
|
23
23
|
paths: [inputPath, globalPath],
|
|
24
24
|
});
|
|
25
25
|
const packageJson = require(eslintPath);
|
|
26
|
-
if (packageJson.version
|
|
26
|
+
if (packageJson.version >= "8") {
|
|
27
27
|
version = packageJson.version;
|
|
28
28
|
} else {
|
|
29
29
|
errMessage =
|
|
30
|
-
`ESLint v>=
|
|
30
|
+
`ESLint v>=8.0.0 required!\n\n` +
|
|
31
31
|
` Make sure to update and check for any breaking changes:\n` +
|
|
32
32
|
` ${term.warn(
|
|
33
|
-
"https://eslint.org/docs/user-guide/migrating-to-
|
|
33
|
+
"https://eslint.org/docs/user-guide/migrating-to-8.0.0"
|
|
34
34
|
)}\n`;
|
|
35
35
|
}
|
|
36
36
|
const bin = packageJson.bin.eslint;
|
|
@@ -53,7 +53,6 @@ module.exports = {
|
|
|
53
53
|
if (logger) {
|
|
54
54
|
logger.error(errMessage);
|
|
55
55
|
}
|
|
56
|
-
return;
|
|
57
56
|
},
|
|
58
57
|
|
|
59
58
|
/**
|
|
@@ -132,5 +131,5 @@ module.exports = {
|
|
|
132
131
|
// Do nothing
|
|
133
132
|
}
|
|
134
133
|
return false;
|
|
135
|
-
}
|
|
134
|
+
}
|
|
136
135
|
};
|
package/lib/lint/io.js
CHANGED
|
@@ -11,7 +11,7 @@ module.exports = {
|
|
|
11
11
|
* @param {*} configPath config file path to read from
|
|
12
12
|
* @returns config file contents
|
|
13
13
|
*/
|
|
14
|
-
async
|
|
14
|
+
async readEslintConfigLegacy(configPath) {
|
|
15
15
|
let config;
|
|
16
16
|
const configFile = path.basename(configPath);
|
|
17
17
|
const configString = await read(configPath, "utf8");
|
|
@@ -33,7 +33,7 @@ module.exports = {
|
|
|
33
33
|
config = await readJSONC(configPath);
|
|
34
34
|
break;
|
|
35
35
|
case "package.json":
|
|
36
|
-
config =
|
|
36
|
+
config = await read(configPath, "json");
|
|
37
37
|
if ("eslintConfig" in config) {
|
|
38
38
|
config = config["eslintConfig"];
|
|
39
39
|
} else {
|
|
@@ -46,16 +46,68 @@ module.exports = {
|
|
|
46
46
|
return config;
|
|
47
47
|
},
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Reads ESLint config contents from file
|
|
51
|
+
* @param {*} configPath config file path to read from
|
|
52
|
+
* @returns config file contents
|
|
53
|
+
*/
|
|
54
|
+
async readEslintConfig(configPath) {
|
|
55
|
+
let config;
|
|
56
|
+
const configFile = path.basename(configPath);
|
|
57
|
+
switch (configFile) {
|
|
58
|
+
case "eslint.config.js":
|
|
59
|
+
case "eslint.config.cjs":
|
|
60
|
+
case "eslint.config.mjs":
|
|
61
|
+
config = this.safeRequire(configPath)
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
return config;
|
|
67
|
+
},
|
|
68
|
+
|
|
49
69
|
/**
|
|
50
70
|
* Checks ESLint config file contents for locally installed
|
|
51
71
|
* plugin with:
|
|
52
72
|
* (1) All CDS recommended rules "on"
|
|
53
|
-
* (2)
|
|
54
|
-
* (3) Add's custom rule example 'no-entity-moo' if requested
|
|
73
|
+
* (2) Add's custom rule example 'no-entity-moo' if requested
|
|
55
74
|
* @param {*} configPath ESLint config file
|
|
56
75
|
* @returns {void}
|
|
57
76
|
*/
|
|
58
77
|
async sanitizeEslintConfig(configIn, customRuleExample = false, logger = console) {
|
|
78
|
+
let configContents;
|
|
79
|
+
const isFilePath = typeof configIn === "string";
|
|
80
|
+
if (isFilePath) {
|
|
81
|
+
configContents = this.safeRequire(configIn); // TODO error handling invalid json
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
configContents ??= { ...configIn };
|
|
85
|
+
|
|
86
|
+
if (customRuleExample) {
|
|
87
|
+
const ruleName = "no-entity-moo";
|
|
88
|
+
const ruleValue = 2;
|
|
89
|
+
if ("rules" in configContents) {
|
|
90
|
+
configContents["rules"][ruleName] = ruleValue;
|
|
91
|
+
} else {
|
|
92
|
+
configContents["rules"] = { [ruleName]: ruleValue };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (isFilePath) {
|
|
96
|
+
await this.writeEslintConfig(configIn, logger);
|
|
97
|
+
}
|
|
98
|
+
return configContents;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Checks ESLint config file contents for locally installed
|
|
103
|
+
* plugin with:
|
|
104
|
+
* (1) All CDS recommended rules "on"
|
|
105
|
+
* (2) Option "root": false to stop further config merging
|
|
106
|
+
* (3) Add's custom rule example 'no-entity-moo' if requested
|
|
107
|
+
* @param {*} configPath ESLint config file
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
async sanitizeEslintConfigLegacy(configIn, customRuleExample = false, logger = console) {
|
|
59
111
|
let configContents = {};
|
|
60
112
|
const isFilePath = typeof configIn === "string";
|
|
61
113
|
if (isFilePath) {
|
|
@@ -93,19 +145,20 @@ module.exports = {
|
|
|
93
145
|
}
|
|
94
146
|
}
|
|
95
147
|
if (isFilePath) {
|
|
96
|
-
await this.
|
|
148
|
+
await this.writeEslintConfigLegacy(configIn, configContents, logger);
|
|
97
149
|
}
|
|
98
150
|
return configContents;
|
|
99
151
|
},
|
|
100
152
|
|
|
153
|
+
|
|
101
154
|
/**
|
|
102
155
|
* Writes ESLint config contents to file
|
|
103
156
|
* @param {*} configContents config file contents
|
|
104
157
|
* @param {*} configPath config file path to write to
|
|
105
158
|
* @returns {Promise<void>}
|
|
106
159
|
*/
|
|
107
|
-
async
|
|
108
|
-
const configType = "recommended";
|
|
160
|
+
async writeEslintConfigLegacy(configPath, configContents, logger = console) {
|
|
161
|
+
const configType = "recommended-legacy";
|
|
109
162
|
const extendsValue = `plugin:@sap/cds/${configType}`;
|
|
110
163
|
let contents = {};
|
|
111
164
|
let configFile = path.basename(configPath);
|
|
@@ -114,7 +167,7 @@ module.exports = {
|
|
|
114
167
|
case ".eslintrc.js":
|
|
115
168
|
case ".eslintrc.cjs":
|
|
116
169
|
try {
|
|
117
|
-
configContents = await this.
|
|
170
|
+
configContents = await this.readEslintConfigLegacy(configPath);
|
|
118
171
|
} catch (err) {
|
|
119
172
|
configContents = {};
|
|
120
173
|
}
|
|
@@ -146,7 +199,7 @@ module.exports = {
|
|
|
146
199
|
break;
|
|
147
200
|
case "package.json":
|
|
148
201
|
if (exists(configPath)) {
|
|
149
|
-
contents =
|
|
202
|
+
contents = await read(configPath, "json");
|
|
150
203
|
} else {
|
|
151
204
|
contents = {};
|
|
152
205
|
}
|
|
@@ -162,6 +215,54 @@ module.exports = {
|
|
|
162
215
|
}
|
|
163
216
|
},
|
|
164
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Suggest ESLint config contents to file
|
|
220
|
+
* @param {*} configContents config file contents
|
|
221
|
+
* @param {*} configPath config file path to write to
|
|
222
|
+
* @returns {Promise<void>}
|
|
223
|
+
*/
|
|
224
|
+
async writeEslintConfig(configPath, logger = console) {
|
|
225
|
+
let configFile = path.basename(configPath);
|
|
226
|
+
switch (configFile) {
|
|
227
|
+
case "eslint.config.js":
|
|
228
|
+
case "eslint.config.cjs":
|
|
229
|
+
case "eslint.config.mjs":
|
|
230
|
+
if (exists(configPath)) {
|
|
231
|
+
logger.log(
|
|
232
|
+
`${term.warn(
|
|
233
|
+
`\n\nPlease add the following to your "${configFile}":` +
|
|
234
|
+
'\n\n 1. At the top of your file, import the following:')}` +
|
|
235
|
+
`\n\n ${term.bold('const cds = require(\'@sap/eslint-plugin-cds\')')}` +
|
|
236
|
+
`${term.warn(
|
|
237
|
+
'\n\n 2. In your export, add the following to the top of the array:')}` +
|
|
238
|
+
`\n\n ${term.bold('cds.configs.recommended')}`
|
|
239
|
+
);
|
|
240
|
+
} else {
|
|
241
|
+
await write(configPath, `
|
|
242
|
+
const cds = require('@sap/eslint-plugin-cds')
|
|
243
|
+
|
|
244
|
+
module.exports = [
|
|
245
|
+
cds.configs.recommended,
|
|
246
|
+
{
|
|
247
|
+
plugins: {
|
|
248
|
+
"@sap/cds": cds
|
|
249
|
+
},
|
|
250
|
+
"files": [
|
|
251
|
+
...cds.configs.recommended.files
|
|
252
|
+
],
|
|
253
|
+
"rules": {
|
|
254
|
+
...cds.configs.recommended.rules
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
`)
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
165
266
|
/**
|
|
166
267
|
* Read VS Code settings from file
|
|
167
268
|
* @param {*} settingsPath settings file path to read from
|
|
@@ -204,7 +305,7 @@ module.exports = {
|
|
|
204
305
|
* (3) "Global" case adds configFile and removes rulePaths
|
|
205
306
|
* @param {*} settingsPath VS Code settings file
|
|
206
307
|
*/
|
|
207
|
-
async sanitizeVscodeSettings(
|
|
308
|
+
async sanitizeVscodeSettings(settingsPath, lintType, lintFileTypes, customRuleExample = false) {
|
|
208
309
|
let settings = (await this.readVscodeSettings(settingsPath)) || {};
|
|
209
310
|
const rulePaths = [path.join(".eslint/rules")];
|
|
210
311
|
|
|
@@ -253,4 +354,12 @@ module.exports = {
|
|
|
253
354
|
}
|
|
254
355
|
return userOpts;
|
|
255
356
|
},
|
|
357
|
+
|
|
358
|
+
safeRequire(module) {
|
|
359
|
+
try {
|
|
360
|
+
return require(module)
|
|
361
|
+
} catch (e) {
|
|
362
|
+
return null
|
|
363
|
+
}
|
|
364
|
+
}
|
|
256
365
|
};
|
package/lib/mtx/auth_manager.js
CHANGED
|
@@ -65,26 +65,26 @@ async function retrieveTokenOrPasscodeUrl(params) {
|
|
|
65
65
|
DEBUG?.(`Getting authentication token or passcode URL from ${method.toUpperCase()} ${params.obfuscateQueryParams(url)}`);
|
|
66
66
|
response = await axios[method](url, data);
|
|
67
67
|
} catch (e) {
|
|
68
|
-
|
|
69
|
-
if (error.status === 404) { // may represent 405 in case of wrong method
|
|
68
|
+
if (e.status === 404) { // may represent 405 in case of wrong method
|
|
70
69
|
continue reqParams;
|
|
71
70
|
} else if (params.has('refreshToken')) {
|
|
72
71
|
DEBUG?.('Discarding invalid refresh token');
|
|
73
72
|
params.delete('refreshToken');
|
|
74
73
|
} else {
|
|
74
|
+
error = e;
|
|
75
75
|
break reqParams;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
} while (!
|
|
78
|
+
} while (!response);
|
|
79
79
|
}
|
|
80
80
|
const data = response?.data ?? error?.auth;
|
|
81
81
|
assign(params, data);
|
|
82
|
-
if (
|
|
82
|
+
if (error) {
|
|
83
83
|
throw new Error(REASONS.invalid_scope.test(error.message)
|
|
84
84
|
? 'Token has invalid scope. Check if your user has the required roles'
|
|
85
85
|
: `No valid passcode or token provided. Get a passcode${
|
|
86
86
|
params.get('passcodeUrl') ? ' from ' + params.get('passcodeUrl') : ''
|
|
87
|
-
} and re-run the command with '-p <passcode>'
|
|
87
|
+
} and re-run the command with '-p <passcode>'`, { cause: error });
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
package/lib/mtx/params.js
CHANGED
|
@@ -213,6 +213,7 @@ class AllParams extends BasicParams {
|
|
|
213
213
|
// `extend` PARAMS
|
|
214
214
|
this.force = new Param('force', false);
|
|
215
215
|
this.templates = new Param('templates', false);
|
|
216
|
+
this["download-migrated-projects"] = new Param('download-migrated-projects', false)
|
|
216
217
|
|
|
217
218
|
// `activate` PARAMS
|
|
218
219
|
this.undeploy = new Param('undeploy', false);
|
|
@@ -220,6 +221,9 @@ class AllParams extends BasicParams {
|
|
|
220
221
|
this.sync = new Param('sync', false);
|
|
221
222
|
this.async = new Param('async', false);
|
|
222
223
|
|
|
224
|
+
// `subscribe` PARAMS
|
|
225
|
+
this.body = new Param('body', '', { persist: Persistence.none });
|
|
226
|
+
|
|
223
227
|
// `login` PARAMS
|
|
224
228
|
this.skipToken = new Param('skipToken', false, { internal: true });
|
|
225
229
|
|
|
@@ -229,8 +233,6 @@ class AllParams extends BasicParams {
|
|
|
229
233
|
this.clearInvalid = new Param('clearInvalid', false);
|
|
230
234
|
this["clear-invalid"] = this.clearInvalid.newAlias('clear-invalid');
|
|
231
235
|
|
|
232
|
-
// download for mxts
|
|
233
|
-
this["download-migrated-projects"] = new Param('download-migrated-projects', false)
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
238
|
|
|
@@ -252,7 +252,7 @@ class SettingsManager {
|
|
|
252
252
|
async function updateTokenUrl() {
|
|
253
253
|
const tokenUrl = params.get('tokenUrl');
|
|
254
254
|
const renewUrl = !logout && !params.get('skipToken')
|
|
255
|
-
&& (!tokenUrl || tokenUrl.includes('//-/cds') || params.get('appUrl') !== loadedSettings.get('appUrl'));
|
|
255
|
+
&& (!tokenUrl || tokenUrl.includes('//-/cds') || tokenUrl.includes(OAUTH_PATH_OLD_MTX) || params.get('appUrl') !== loadedSettings.get('appUrl'));
|
|
256
256
|
if (renewUrl) {
|
|
257
257
|
const tokenUrl = await getTokenBaseUrl(params, renewUrl);
|
|
258
258
|
params.set('tokenUrl', tokenUrl);
|
package/lib/mtx/tenant_action.js
CHANGED
|
@@ -48,8 +48,17 @@ module.exports = class TenantAction {
|
|
|
48
48
|
console.log(capitalize(this.gerund), { tenant }, this.preposition, { url: params.get('appUrl') });
|
|
49
49
|
const url = `${params.get('appUrl')}/-/cds/deployment/${this.command}`;
|
|
50
50
|
DEBUG?.(`Sending request to ${url}`);
|
|
51
|
+
const body = params.get('body');
|
|
52
|
+
let parsedBody;
|
|
53
|
+
if (body) {
|
|
54
|
+
try {
|
|
55
|
+
parsedBody = JSON.parse(body)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw getMessage('Invalid subscription body', { error, command: this.command });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
51
60
|
try {
|
|
52
|
-
await require('axios').post(url, { tenant }, params.get('reqAuth'));
|
|
61
|
+
await require('axios').post(url, { tenant, metadata: parsedBody, options: parsedBody }, params.get('reqAuth'));
|
|
53
62
|
} catch (error) {
|
|
54
63
|
handleHttpError(error, params, { url });
|
|
55
64
|
}
|
|
@@ -74,6 +83,7 @@ module.exports = class TenantAction {
|
|
|
74
83
|
if (!cds_mtx) {
|
|
75
84
|
throw '@sap/cds-mtxs outdated. Please upgrade to @sap/cds-mtxs@>=1.7.0 and re-run this command.';
|
|
76
85
|
}
|
|
77
|
-
|
|
86
|
+
const body = params.get('body')
|
|
87
|
+
return cds_mtx(this.command, tenant, body);
|
|
78
88
|
}
|
|
79
89
|
}
|