@sap/cds-dk 8.7.1 → 8.7.3
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 +25 -1
- package/bin/compile/index.js +10 -2
- package/bin/import.js +7 -7
- package/bin/lint.js +10 -4
- package/lib/build/util.js +9 -10
- package/lib/deploy/to-hana/hana.js +3 -3
- package/lib/import/common.js +6 -8
- package/lib/import/importapi.js +1 -1
- package/lib/import/odata/ParserContext.js +1 -0
- package/lib/import/odata/v4parser.js +3 -2
- package/lib/init/constants.js +1 -1
- package/lib/init/merge.js +1 -1
- package/lib/init/registries/mta.js +2 -2
- package/lib/init/template/approuter/files/mta.yaml.hbs +2 -2
- package/lib/init/template/approuter/index.js +2 -2
- package/lib/init/template/ias/files/mta.yaml.hbs +1 -1
- package/lib/init/template/ias/files/sidecar.package.json.hbs +12 -0
- package/lib/init/template/ias/index.js +1 -3
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +8 -8
- package/lib/init/template/multitenancy/index.js +0 -5
- package/lib/init/template/workzone-standard/index.js +0 -7
- package/lib/lint/Linter.js +242 -201
- package/lib/lint/io.js +4 -7
- package/lib/util/cf.js +19 -8
- package/lib/util/write.js +1 -1
- package/npm-shrinkwrap.json +44 -17
- package/package.json +1 -1
package/lib/lint/Linter.js
CHANGED
|
@@ -1,42 +1,185 @@
|
|
|
1
1
|
const term = require("../util/term");
|
|
2
|
-
|
|
3
2
|
const io = require("./io");
|
|
4
3
|
const checks = require("./checks");
|
|
5
4
|
const generators = require("./generators");
|
|
6
|
-
|
|
7
5
|
const cds = require("../../lib/cds");
|
|
8
6
|
const { path, exists, isdir, isfile, readdir } = cds.utils
|
|
9
7
|
const { exit } = require("process");
|
|
8
|
+
|
|
10
9
|
const LOG = cds.debug("lint");
|
|
11
10
|
const LOG_CONFIG = cds.debug("lint:config");
|
|
12
|
-
|
|
13
11
|
const GENERAL_ERROR = 1;
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Safely require a module, return empty object if module not found
|
|
15
|
+
* @param {string} module - module to require
|
|
16
|
+
* @param {boolean} esm - whether to use dynamic import to accomodate ESM
|
|
17
|
+
* @returns {Promise<ReturnType<NodeRequire> | {}>} module or empty object
|
|
18
|
+
*/
|
|
19
|
+
async function tryRequire(module, esm = false) {
|
|
20
|
+
try {
|
|
21
|
+
return esm ? import(module) : require(module)
|
|
22
|
+
} catch (e) {
|
|
23
|
+
if (e.code === 'MODULE_NOT_FOUND' && e.message.includes(module)) return {}
|
|
24
|
+
// target module is either .mjs (etc) or user project is ESM
|
|
25
|
+
if (['ERR_REQUIRE_ASYNC_MODULE', 'ERR_REQUIRE_ESM'].includes(e.code)) return tryRequire(module, true)
|
|
26
|
+
throw e
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load ESLint module
|
|
32
|
+
* @param {string} currentDir - current directory to start search
|
|
33
|
+
* @returns {Promise<ReturnType<NodeRequire>>} - ESLint module
|
|
34
|
+
*/
|
|
35
|
+
async function _loadEslint(currentDir) {
|
|
36
|
+
const { loadESLint, ESLint } = require('eslint')
|
|
37
|
+
const DefaultESLint = loadESLint
|
|
38
|
+
? await loadESLint({ cwd: currentDir }) // ESLint 9
|
|
39
|
+
: new ESLint({ cwd: currentDir }) // ESLint 8
|
|
40
|
+
return DefaultESLint;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {string[]} configFiles - possible config file paths
|
|
45
|
+
* @param {string} currentDir - current directory to start search
|
|
46
|
+
* @returns {string} - path to config file
|
|
47
|
+
*/
|
|
48
|
+
async function findConfigPath (configFiles, currentDir = '.') {
|
|
49
|
+
let configDir = path.resolve(currentDir)
|
|
50
|
+
while (configDir && configDir !== path.dirname(configDir)) {
|
|
51
|
+
for (const configFile of configFiles) {
|
|
52
|
+
const configPath = path.join(configDir, configFile)
|
|
53
|
+
const config = await tryRequire(configPath)
|
|
54
|
+
if (configFile !== 'package.json' || config?.eslintConfig) {
|
|
55
|
+
if (exists(configPath) && isfile(configPath)) { // TODO exists neccessary ?
|
|
56
|
+
return configPath
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
configDir = path.dirname(configDir);
|
|
61
|
+
}
|
|
62
|
+
return ''
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* This class and its subclasses are an attempt to smoothen out some of the
|
|
67
|
+
* workflow in the Linter class. It gets rid of some of the isESLintLegacy ? ... : ...
|
|
68
|
+
* assignments, although not all. The remaining branching is too closely coupled
|
|
69
|
+
* to some properties of the Linter class to be easily abstracted away.
|
|
70
|
+
* @abstract
|
|
71
|
+
*/
|
|
72
|
+
class ESLintLoader {
|
|
73
|
+
/**
|
|
74
|
+
* Searches for ESLint config file types (in order or precedence)
|
|
75
|
+
* and returns corresponding directory (usually project's root dir)
|
|
76
|
+
* https://eslint.org/docs/user-guide/configuring#configuration-file-formats
|
|
77
|
+
* @param {string} currentDir start here and search until root dir
|
|
78
|
+
* @returns {string} dir containing ESLint config file (empty if not exists)
|
|
79
|
+
* @abstract
|
|
80
|
+
*/
|
|
81
|
+
// eslint-disable-next-line no-unused-vars
|
|
82
|
+
async getConfigPath (currentDir = '.') { throw new Error("Not implemented") }
|
|
83
|
+
/** @type {import('./io').sanitizeEslintConfig} */
|
|
84
|
+
// eslint-disable-next-line no-unused-vars
|
|
85
|
+
sanitizeEslintConfig (configIn, customRuleExample, logger) { throw new Error("Not implemented") }
|
|
86
|
+
/** @type {import('./io').readEslintConfig} */
|
|
87
|
+
// eslint-disable-next-line no-unused-vars
|
|
88
|
+
readEslintConfig(configPath) { throw new Error("Not implemented") }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class ESLint8Loader extends ESLintLoader {
|
|
92
|
+
async getConfigPath(currentDir = '.') {
|
|
93
|
+
return findConfigPath([
|
|
94
|
+
".eslintrc.js",
|
|
95
|
+
".eslintrc.cjs",
|
|
96
|
+
".eslintrc.yaml",
|
|
97
|
+
".eslintrc.yml",
|
|
98
|
+
".eslintrc.json",
|
|
99
|
+
".eslintrc",
|
|
100
|
+
"package.json",
|
|
101
|
+
], currentDir)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
sanitizeEslintConfig (configIn, customRuleExample, logger) {
|
|
105
|
+
return io.sanitizeEslintConfigLegacy(configIn, customRuleExample, logger)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
readEslintConfig(configPath) {
|
|
109
|
+
return io.readEslintConfigLegacy(configPath)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class ESLint9Loader extends ESLintLoader {
|
|
114
|
+
async getConfigPath(currentDir = '.') {
|
|
115
|
+
return findConfigPath([
|
|
116
|
+
"eslint.config.js",
|
|
117
|
+
"eslint.config.cjs",
|
|
118
|
+
"eslint.config.mjs",
|
|
119
|
+
// the following 3 formats require additional setup by the user as of today,
|
|
120
|
+
// see https://eslint.org/docs/latest/use/configure/configuration-files#typescript-configuration-files
|
|
121
|
+
"eslint.config.ts",
|
|
122
|
+
"eslint.config.mts",
|
|
123
|
+
"eslint.config.cts",
|
|
124
|
+
], currentDir)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
sanitizeEslintConfig (configIn, customRuleExample, logger) {
|
|
128
|
+
return io.sanitizeEslintConfig(configIn, customRuleExample, logger)
|
|
38
129
|
}
|
|
39
130
|
|
|
131
|
+
readEslintConfig(configPath) {
|
|
132
|
+
return io.readEslintConfig(configPath)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class Linter {
|
|
137
|
+
/** @type {string | undefined} */
|
|
138
|
+
eslintCmd = "";
|
|
139
|
+
/** @type {string} */
|
|
140
|
+
eslintCmdShort = "eslint";
|
|
141
|
+
/** @type {string | string[]} */
|
|
142
|
+
eslintCmdFileExpr = "";
|
|
143
|
+
/** @type {boolean} */
|
|
144
|
+
isFile = false;
|
|
145
|
+
/** @type {string} */
|
|
146
|
+
help = "";
|
|
147
|
+
/** @type {string} */
|
|
148
|
+
debug = "";
|
|
149
|
+
/** @type {string[]} */
|
|
150
|
+
flags = [];
|
|
151
|
+
/** @type {string} */
|
|
152
|
+
configPath = "";
|
|
153
|
+
/** @type {object} */
|
|
154
|
+
configContents = {};
|
|
155
|
+
/** @type {import("fs").PathLike} */
|
|
156
|
+
cdsdkPath = path.join(__dirname, "../..");
|
|
157
|
+
/** @type {import("fs").PathLike} */
|
|
158
|
+
globalPath = path.join(this.cdsdkPath, "../..");
|
|
159
|
+
/** @type {boolean} */
|
|
160
|
+
extendsPlugin = false;
|
|
161
|
+
/** @type {string[]} */
|
|
162
|
+
fileExtensions = [];
|
|
163
|
+
/** @type {string} */
|
|
164
|
+
pluginPath = "";
|
|
165
|
+
/** @type {object} */
|
|
166
|
+
pluginApi = {};
|
|
167
|
+
/** @type {'global' | 'local'} */
|
|
168
|
+
lintType = "global";
|
|
169
|
+
/** @type {object} */
|
|
170
|
+
ruleOpts = {};
|
|
171
|
+
/** @type {array} */
|
|
172
|
+
customRulesOpts = [];
|
|
173
|
+
/** @type {array} */
|
|
174
|
+
pluginRules = [];
|
|
175
|
+
/** @type {ESLintLoader | undefined} */
|
|
176
|
+
#loader = undefined;
|
|
177
|
+
get loader () {
|
|
178
|
+
return this.#loader ??= this.isESLintLegacy
|
|
179
|
+
? new ESLint8Loader()
|
|
180
|
+
: new ESLint9Loader()
|
|
181
|
+
};
|
|
182
|
+
|
|
40
183
|
/**
|
|
41
184
|
* Initializes `cds lint` call and generates the required content
|
|
42
185
|
* object for `cds` executable
|
|
@@ -59,146 +202,69 @@ class Linter {
|
|
|
59
202
|
/**
|
|
60
203
|
* Runner for 'cds lint' which is a wrapper for eslint cmd calls
|
|
61
204
|
* that detects and adds the required cmd line arguments
|
|
62
|
-
* @param {
|
|
63
|
-
* @param {
|
|
205
|
+
* @param {string[]} args files/globs for eslint to lint
|
|
206
|
+
* @param {{[key: string]: unknown}} options options/flag passed by user
|
|
64
207
|
*/
|
|
65
208
|
async lint(args, options) {
|
|
66
209
|
// Check for ESLint (legacy)
|
|
67
210
|
const currentDir = process.cwd();
|
|
68
|
-
const DefaultESLint = await
|
|
211
|
+
const DefaultESLint = await _loadEslint(currentDir)
|
|
69
212
|
this.isESLintLegacy = DefaultESLint.configType === "eslintrc";
|
|
70
|
-
|
|
71
|
-
this.eslintCmdOpts = this._sanitizeUserOpts(options);
|
|
213
|
+
this.eslintCmdOpts = options; // these should probably be passed on in #runEsLint!
|
|
72
214
|
this.eslintCmdFileExpr = args.length ? args : ["."];
|
|
73
|
-
if (this.eslintCmdOpts.help) {
|
|
74
|
-
this._printHelp();
|
|
75
|
-
} else {
|
|
76
|
-
// Determine if case "local" or "global" and resolve plugin accordingly
|
|
77
|
-
await this._getConfigFileAndSetProjectPath(args);
|
|
78
|
-
|
|
79
|
-
if (this.pluginPath) {
|
|
80
|
-
const overwriteRules = checks.hasEslintConfigContent(this.configContents, "rules");
|
|
81
|
-
// Overwrite rules severities
|
|
82
|
-
if (overwriteRules) {
|
|
83
|
-
await this._overwriteRuleSeverities();
|
|
84
|
-
}
|
|
85
215
|
|
|
86
|
-
|
|
87
|
-
|
|
216
|
+
if (this.eslintCmdOpts.help) {
|
|
217
|
+
this.#printHelp();
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
// Determine if case "local" or "global" and resolve plugin accordingly
|
|
221
|
+
await this.#getConfigFileAndSetProjectPath(args);
|
|
222
|
+
|
|
223
|
+
if (this.pluginPath) {
|
|
224
|
+
const overwriteRules = checks.hasEslintConfigContent(this.configContents, "rules");
|
|
225
|
+
// Overwrite rules severities
|
|
226
|
+
if (overwriteRules) {
|
|
227
|
+
await this.#overwriteRuleSeverities();
|
|
88
228
|
}
|
|
89
229
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
await this._runEslint(currentDir, DefaultESLint);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
term.error(err);
|
|
95
|
-
}
|
|
230
|
+
// Limit to CDS file extensions
|
|
231
|
+
this.#addExtensions();
|
|
96
232
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
_printHelp() {
|
|
100
|
-
console.log(this.help.replace(/ \*([^*]+)\*/g, ` ${term.codes.bold}$1${term.codes.reset}`));
|
|
101
|
-
}
|
|
102
233
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
234
|
+
// Run ESLint with collected options
|
|
235
|
+
try {
|
|
236
|
+
await this.#runEslint(currentDir, DefaultESLint);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
term.error(err);
|
|
109
239
|
}
|
|
110
|
-
return options;
|
|
111
240
|
}
|
|
112
241
|
|
|
113
242
|
/**
|
|
114
|
-
*
|
|
115
|
-
* and returns corresponding directory (usually project's root dir)
|
|
116
|
-
* https://eslint.org/docs/user-guide/configuring#configuration-file-formats
|
|
117
|
-
* @param {string} currentDir start here and search until root dir
|
|
118
|
-
* @returns {string} dir containing ESLint config file (empty if not exists)
|
|
243
|
+
* Prints help message
|
|
119
244
|
*/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
".eslintrc.js",
|
|
123
|
-
".eslintrc.cjs",
|
|
124
|
-
".eslintrc.yaml",
|
|
125
|
-
".eslintrc.yml",
|
|
126
|
-
".eslintrc.json",
|
|
127
|
-
".eslintrc",
|
|
128
|
-
"package.json",
|
|
129
|
-
];
|
|
130
|
-
let configDir = path.resolve(currentDir);
|
|
131
|
-
while (configDir && configDir !== path.dirname(configDir)) {
|
|
132
|
-
for (let i = 0; i < configFiles.length; i++) {
|
|
133
|
-
const configPath = path.join(configDir, configFiles[i]);
|
|
134
|
-
const config = this.safeRequire(configPath)
|
|
135
|
-
|
|
136
|
-
if (configFiles[i] !== "package.json" || config?.eslintConfig) {
|
|
137
|
-
if (exists(configPath) && await isfile(configPath)) {
|
|
138
|
-
return configPath;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
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);
|
|
173
|
-
}
|
|
174
|
-
return "";
|
|
245
|
+
#printHelp() {
|
|
246
|
+
console.log(this.help.replace(/ \*([^*]+)\*/g, ` ${term.codes.bold}$1${term.codes.reset}`));
|
|
175
247
|
}
|
|
176
248
|
|
|
177
|
-
|
|
249
|
+
#addExtensions() {
|
|
178
250
|
// Add CDS file extensions to lint
|
|
179
|
-
this.fileExtensions = this.pluginApi.getFileExtensions()
|
|
251
|
+
this.fileExtensions = this.pluginApi.getFileExtensions()
|
|
252
|
+
.map((ext) => path.extname(ext));
|
|
180
253
|
// Only lint file extensions prescribed by plugin
|
|
181
|
-
this.ignorePatterns =
|
|
182
|
-
|
|
183
|
-
this.ignorePatterns.push(`!${ext}`);
|
|
184
|
-
});
|
|
254
|
+
this.ignorePatterns = this.fileExtensions
|
|
255
|
+
.map(ext => `!${ext}`)
|
|
185
256
|
}
|
|
186
257
|
|
|
187
|
-
async
|
|
258
|
+
async #runEslint(currentDir, DefaultESLint) {
|
|
188
259
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
260
|
+
const eslintOpts = this.lintType === "global"
|
|
261
|
+
? {
|
|
262
|
+
cwd: process.cwd(),
|
|
263
|
+
overrideConfig: {},
|
|
264
|
+
}
|
|
265
|
+
: {
|
|
191
266
|
cwd: currentDir,
|
|
192
|
-
overrideConfig: {
|
|
193
|
-
...this.configContents,
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
if (this.lintType === "global") {
|
|
198
|
-
eslintOpts = {
|
|
199
|
-
cwd: process.cwd(),
|
|
200
|
-
overrideConfig: {},
|
|
201
|
-
};
|
|
267
|
+
overrideConfig: {...this.configContents},
|
|
202
268
|
}
|
|
203
269
|
|
|
204
270
|
if (this.isESLintLegacy) {
|
|
@@ -213,7 +279,7 @@ class Linter {
|
|
|
213
279
|
} else {
|
|
214
280
|
LOG?.("Using flat ESLint configuration");
|
|
215
281
|
const cdsPlugin = require(this.pluginPath)
|
|
216
|
-
//
|
|
282
|
+
// Exclude our own apis from plugin to avoid wrapper errors
|
|
217
283
|
delete cdsPlugin.configs.recommended?.plugins?.['@sap/cds']
|
|
218
284
|
eslintOpts.overrideConfig = [cdsPlugin.configs.recommended]
|
|
219
285
|
}
|
|
@@ -224,17 +290,22 @@ class Linter {
|
|
|
224
290
|
|
|
225
291
|
LOG_CONFIG?.(eslintOpts);
|
|
226
292
|
|
|
227
|
-
|
|
228
|
-
|
|
293
|
+
if (LOG) {
|
|
294
|
+
let lintString = `eslint`;
|
|
229
295
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (this.customRulesOpts && this.customRulesOpts.length > 0) {
|
|
234
|
-
lintString += ` --rulesdir "${this.customRulesOpts}"`
|
|
235
|
-
}
|
|
296
|
+
if (this.fileExtensions) {
|
|
297
|
+
lintString += ` --ext "${this.fileExtensions.join(",")}"`
|
|
298
|
+
}
|
|
236
299
|
|
|
237
|
-
|
|
300
|
+
for (const [name, rule] of Object.entries(this.ruleOpts)) {
|
|
301
|
+
lintString += ` --rule ${name}:${rule}`
|
|
302
|
+
}
|
|
303
|
+
if (this.customRulesOpts && this.customRulesOpts.length > 0) {
|
|
304
|
+
lintString += ` --rulesdir "${this.customRulesOpts}"`
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
LOG(lintString);
|
|
308
|
+
}
|
|
238
309
|
|
|
239
310
|
if (!process.env.isTest) {
|
|
240
311
|
const eslint = new DefaultESLint(eslintOpts);
|
|
@@ -252,7 +323,7 @@ class Linter {
|
|
|
252
323
|
results = results.concat(resultsModel);
|
|
253
324
|
}
|
|
254
325
|
|
|
255
|
-
if (results
|
|
326
|
+
if (results?.length > 0) {
|
|
256
327
|
console.log(formatter.format(results));
|
|
257
328
|
}
|
|
258
329
|
}
|
|
@@ -272,31 +343,17 @@ class Linter {
|
|
|
272
343
|
}
|
|
273
344
|
}
|
|
274
345
|
|
|
275
|
-
async
|
|
276
|
-
let input;
|
|
346
|
+
async #getConfigFileAndSetProjectPath(args) {
|
|
277
347
|
// Get config path
|
|
278
348
|
if (args.length > 0) {
|
|
279
349
|
const firstArg = args[0];
|
|
280
|
-
|
|
281
|
-
this.isFile = true;
|
|
282
|
-
}
|
|
283
|
-
input = args.join(" ");
|
|
284
|
-
if (!input) {
|
|
285
|
-
input = ".";
|
|
286
|
-
}
|
|
350
|
+
this.isFile = firstArg === "." || isfile(firstArg) || isdir(firstArg)
|
|
287
351
|
}
|
|
288
|
-
this.configPath = this.
|
|
289
|
-
? await this._getConfigPathLegacy(process.cwd())
|
|
290
|
-
: await this._getConfigPath(process.cwd());
|
|
352
|
+
this.configPath = await this.loader.getConfigPath(process.cwd())
|
|
291
353
|
if (!this.configPath) {
|
|
292
|
-
this.
|
|
293
|
-
this.configContents = this.isESLintLegacy
|
|
294
|
-
? await io.sanitizeEslintConfigLegacy({}, false, LOG)
|
|
295
|
-
: await io.sanitizeEslintConfig({}, false, LOG);
|
|
354
|
+
this.configContents = this.loader.sanitizeEslintConfig({}, false, LOG)
|
|
296
355
|
} else {
|
|
297
|
-
const configContents = this.
|
|
298
|
-
? await io.readEslintConfigLegacy(this.configPath)
|
|
299
|
-
: await io.readEslintConfig(this.configPath);
|
|
356
|
+
const configContents = this.loader.readEslintConfig(this.configPath)
|
|
300
357
|
this.configContents = configContents;
|
|
301
358
|
if (this.isESLintLegacy) {
|
|
302
359
|
this.configContents = await io.sanitizeEslintConfig(configContents, false, LOG);
|
|
@@ -313,14 +370,16 @@ class Linter {
|
|
|
313
370
|
paths: [path.dirname(this.configPath)],
|
|
314
371
|
});
|
|
315
372
|
this.pluginApi = require(this.pluginPath.replace("index.js", "api"));
|
|
316
|
-
} catch
|
|
373
|
+
} catch {
|
|
317
374
|
// CLI will report (no locally installed plugin)
|
|
318
375
|
}
|
|
319
376
|
}
|
|
320
377
|
}
|
|
321
378
|
}
|
|
322
379
|
} else {
|
|
323
|
-
const cdsConfig =
|
|
380
|
+
const cdsConfig = Array.isArray(configContents)
|
|
381
|
+
? configContents?.find(c => c.files?.includes("*.cds")) ?? {}
|
|
382
|
+
: configContents
|
|
324
383
|
this.configContents = cdsConfig.files
|
|
325
384
|
? {
|
|
326
385
|
...cdsConfig.plugins.configs?.recommended,
|
|
@@ -337,7 +396,7 @@ class Linter {
|
|
|
337
396
|
paths: [this.cdsdkPath],
|
|
338
397
|
});
|
|
339
398
|
this.pluginApi = require(this.pluginPath.replace("index.js", "api"));
|
|
340
|
-
} catch
|
|
399
|
+
} catch {
|
|
341
400
|
// CLI will report (no globally installed plugin)
|
|
342
401
|
}
|
|
343
402
|
}
|
|
@@ -345,46 +404,28 @@ class Linter {
|
|
|
345
404
|
LOG?.(`ESLint CDS plugin resolved: ${this.pluginPath}`);
|
|
346
405
|
}
|
|
347
406
|
|
|
348
|
-
async
|
|
407
|
+
async #overwriteRuleSeverities() {
|
|
349
408
|
let configContents = {};
|
|
350
409
|
if (this.configPath) {
|
|
351
410
|
configContents = await io.readEslintConfigLegacy(this.configPath);
|
|
352
411
|
} else {
|
|
353
|
-
configContents = this.
|
|
354
|
-
? await io.readEslintConfigLegacy(await this._getConfigPath(this.cdsdkPath))
|
|
355
|
-
: await io.readEslintConfig(await this._getConfigPath(this.cdsdkPath));
|
|
412
|
+
configContents = this.loader.readEslintConfig(await this.loader.getConfigPath(this.cdsdkPath))
|
|
356
413
|
}
|
|
357
|
-
|
|
414
|
+
const rules = configContents?.rules ?? {};
|
|
358
415
|
// Allow recommended plugin rules in cds-dk to be overwritten
|
|
359
416
|
// by user by adding rule to cmd line (because of precedence)
|
|
360
417
|
const pluginRules = require(this.pluginPath).configs.recommended.rules;
|
|
361
418
|
if (rules && pluginRules) {
|
|
362
|
-
for (const rule of Object.
|
|
363
|
-
if (typeof
|
|
364
|
-
isfile(path.relative(".", ".eslint", "rules",
|
|
365
|
-
pluginRules[
|
|
366
|
-
this.ruleOpts[
|
|
419
|
+
for (const [name, rule] of Object.entries(rules)) {
|
|
420
|
+
if (typeof rule !== "undefined" && rule != pluginRules[name] ||
|
|
421
|
+
isfile(path.relative(".", ".eslint", "rules", name))) {
|
|
422
|
+
pluginRules[name] = rule;
|
|
423
|
+
this.ruleOpts[name] = rule;
|
|
367
424
|
this.pluginRules = pluginRules;
|
|
368
425
|
}
|
|
369
426
|
}
|
|
370
427
|
}
|
|
371
428
|
}
|
|
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
|
-
}
|
|
387
|
-
}
|
|
388
429
|
}
|
|
389
430
|
|
|
390
431
|
module.exports = Linter;
|
package/lib/lint/io.js
CHANGED
|
@@ -8,7 +8,7 @@ module.exports = {
|
|
|
8
8
|
/**
|
|
9
9
|
* Reads ESLint config contents from file
|
|
10
10
|
* @param {*} configPath config file path to read from
|
|
11
|
-
* @returns config file contents
|
|
11
|
+
* @returns {object[] | object} config file contents
|
|
12
12
|
*/
|
|
13
13
|
async readEslintConfigLegacy(configPath) {
|
|
14
14
|
let config;
|
|
@@ -19,7 +19,7 @@ module.exports = {
|
|
|
19
19
|
case ".eslintrc.cjs":
|
|
20
20
|
try {
|
|
21
21
|
config = JSONC.parse((configString.replace(/^module\.exports =/, "")));
|
|
22
|
-
} catch
|
|
22
|
+
} catch {
|
|
23
23
|
config = {};
|
|
24
24
|
}
|
|
25
25
|
break;
|
|
@@ -102,7 +102,6 @@ module.exports = {
|
|
|
102
102
|
* (2) Option "root": false to stop further config merging
|
|
103
103
|
* (3) Add's custom rule example 'no-entity-moo' if requested
|
|
104
104
|
* @param {*} configPath ESLint config file
|
|
105
|
-
* @returns {void}
|
|
106
105
|
*/
|
|
107
106
|
async sanitizeEslintConfigLegacy(configIn, customRuleExample = false, logger = console) {
|
|
108
107
|
let configContents = {};
|
|
@@ -121,10 +120,8 @@ module.exports = {
|
|
|
121
120
|
if (!configContents["extends"].includes(extendsValue)) {
|
|
122
121
|
configContents["extends"].push(extendsValue);
|
|
123
122
|
}
|
|
124
|
-
} else {
|
|
125
|
-
|
|
126
|
-
configContents["extends"] = [configContents["extends"], extendsValue];
|
|
127
|
-
}
|
|
123
|
+
} else if (configContents["extends"] !== extendsValue) {
|
|
124
|
+
configContents["extends"] = [configContents["extends"], extendsValue];
|
|
128
125
|
}
|
|
129
126
|
} else {
|
|
130
127
|
configContents["extends"] = extendsValue;
|
package/lib/util/cf.js
CHANGED
|
@@ -44,7 +44,7 @@ module.exports = new class CfUtil {
|
|
|
44
44
|
args = args.map(arg => arg.replace(/"/g, '\\"'));
|
|
45
45
|
const cmdLine = `${CF_COMMAND} "${args.join('" "')}"`;
|
|
46
46
|
DEBUG?.time(cmdLine);
|
|
47
|
-
|
|
47
|
+
DEBUG?.('>>>', cmdLine);
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
50
|
const result = await execAsync(cmdLine, {
|
|
@@ -146,13 +146,21 @@ module.exports = new class CfUtil {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
#token
|
|
149
150
|
async _getCfAuthorization() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
if (this.#token) return this.#token
|
|
152
|
+
if (!this._tokenPromise) {
|
|
153
|
+
this._tokenPromise = this.cfRun('oauth-token')
|
|
154
|
+
.then(result => {
|
|
155
|
+
this.#token = result.stdout
|
|
156
|
+
return this.#token
|
|
157
|
+
})
|
|
158
|
+
.catch(err => {
|
|
159
|
+
this._tokenPromise = null
|
|
160
|
+
throw err.message
|
|
161
|
+
})
|
|
155
162
|
}
|
|
163
|
+
return this._tokenPromise
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
async getCfTarget() {
|
|
@@ -163,7 +171,10 @@ module.exports = new class CfUtil {
|
|
|
163
171
|
return target;
|
|
164
172
|
}
|
|
165
173
|
|
|
174
|
+
#checkedVersion = false
|
|
166
175
|
async _checkCliVersion() {
|
|
176
|
+
if (this.#checkedVersion) return
|
|
177
|
+
this.#checkedVersion = true
|
|
167
178
|
const result = await this.cfRun('-v');
|
|
168
179
|
const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i)
|
|
169
180
|
if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
|
|
@@ -358,7 +369,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
|
|
|
358
369
|
async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false, create = true } = {}) {
|
|
359
370
|
const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
|
|
360
371
|
if (serviceKey || !create) {
|
|
361
|
-
if (serviceKey && !silent)
|
|
372
|
+
if (serviceKey && !silent) DEBUG?.(`getting service key ${bold(serviceKeyName)}`);
|
|
362
373
|
return serviceKey;
|
|
363
374
|
}
|
|
364
375
|
|
|
@@ -389,4 +400,4 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
|
|
|
389
400
|
|
|
390
401
|
throw `Could not create service key ${bold(serviceKeyName)}`;
|
|
391
402
|
}
|
|
392
|
-
}
|
|
403
|
+
}
|
package/lib/util/write.js
CHANGED
|
@@ -88,7 +88,7 @@ function _write (content, /*to:*/ dest, _many) { // NOSONAR
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
file = path.resolve (cds.root,file)
|
|
91
|
-
// write to the target file, creating parent folders if
|
|
91
|
+
// write to the target file, creating parent folders if necessary
|
|
92
92
|
return mkdirp (dirname(file))
|
|
93
93
|
.then (()=> fs.writeFile (file, typeof content === 'string' ? content : JSON.stringify (content, null, 2)))
|
|
94
94
|
.then (()=> dest.foreach && dest.foreach(file))
|