@sap/cds-dk 4.5.3 → 4.6.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/bin/bind.js +124 -8
  3. package/bin/cds-ts.js +1 -1
  4. package/bin/cds.js +1 -0
  5. package/bin/compile/index.js +2 -1
  6. package/bin/env.js +50 -9
  7. package/bin/help.js +1 -0
  8. package/bin/import.js +34 -14
  9. package/bin/repl.js +6 -2
  10. package/bin/watch.js +59 -14
  11. package/lib/bind/bindingResolver.js +138 -0
  12. package/lib/bind/cfKeyProvider.js +353 -0
  13. package/lib/bind/userCdsrcJsonStorageProvider.js +144 -0
  14. package/lib/client/activate.js +3 -9
  15. package/lib/client/auth_manager.js +4 -4
  16. package/lib/client/extend.js +4 -9
  17. package/lib/client/settings_manager.js +6 -5
  18. package/lib/import/common.js +10 -1
  19. package/lib/import/importapi.js +4 -3
  20. package/lib/import/index.js +34 -5
  21. package/lib/import/message.js +1 -0
  22. package/lib/import/utils/MetadataConverter.js +25 -4
  23. package/lib/import/utils/MetadataConverterFactory.js +1 -2
  24. package/lib/import/utils/MultiMap.js +26 -0
  25. package/lib/import/v2parser.js +137 -16
  26. package/lib/import/v4parser.js +212 -172
  27. package/lib/init/constants.js +1 -1
  28. package/lib/init/template/data/index.js +11 -1
  29. package/lib/init/template/hana/index.js +16 -20
  30. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +36 -52
  31. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +15 -0
  32. package/lib/init/template/lint/index.js +19 -17
  33. package/lib/init/template/mta/files/mta.yaml.hbs +0 -7
  34. package/lib/init/template/mta/projectReader.js +1 -3
  35. package/lib/init/template/mtx/files/xs-security.json.hbs +3 -1
  36. package/lib/init/template/mtx/package/dependencies.package.json +1 -1
  37. package/lib/init/template/nodejs/files/.gitignore.hbs +1 -0
  38. package/lib/init/template/samples/index.js +1 -1
  39. package/lib/init/template/templateBase.js +10 -41
  40. package/lib/init/util/commandUtil.js +5 -0
  41. package/lib/lint/Linter.js +19 -28
  42. package/lib/lint/generators.js +5 -4
  43. package/lib/lint/io.js +76 -51
  44. package/lib/util/term.js +2 -0
  45. package/lib/watch/node-watch.js +38 -19
  46. package/npm-shrinkwrap.json +1435 -1946
  47. package/package.json +3 -3
  48. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/test.js +0 -10
package/lib/lint/io.js CHANGED
@@ -1,7 +1,8 @@
1
1
  const os = require("os");
2
- const fs = require("fs");
2
+ const fsp = require("fs").promises;
3
3
  const path = require("path");
4
4
  const yaml = require("@sap/cds-foss")("yaml");
5
+ const term = require("../util/term");
5
6
  const FsUtil = require("../init/util/fsUtil");
6
7
  const fsUtil = new FsUtil();
7
8
 
@@ -13,28 +14,33 @@ module.exports = {
13
14
  * @param {*} configPath config file path to read from
14
15
  * @returns config file contents
15
16
  */
16
- readEslintConfig(configPath) {
17
+ async readEslintConfig(configPath) {
17
18
  let config;
18
19
  let configFile = path.basename(configPath);
20
+ let configString = await fsp.readFile(configPath, "utf8");
19
21
  switch (configFile) {
20
22
  case ".eslintrc.js":
21
23
  case ".eslintrc.cjs":
22
- config = require(configPath);
24
+ try {
25
+ config = JSON.parse(configString.replace(/^module\.exports =/, ""));
26
+ } catch (err) {
27
+ config = {};
28
+ }
23
29
  break;
24
30
  case ".eslintrc.yaml":
25
31
  case ".eslintrc.yml":
26
- config = yaml.parse(fs.readFileSync(configPath, "utf8"));
32
+ config = yaml.parse(await fsp.readFile(configPath, "utf8"));
27
33
  break;
28
34
  case ".eslintrc.json":
29
35
  case ".eslintrc":
36
+ config = JSON.parse(await fsp.readFile(configPath, "utf8"));
37
+ break;
30
38
  case "package.json":
31
- try {
32
- config = JSON.parse(fs.readFileSync(configPath, "utf8"));
33
- if (configFile === "package.json" && "eslintConfig" in config) {
34
- config = config["eslintConfig"];
35
- }
36
- } catch (err) {
37
- // Do nothing
39
+ config = JSON.parse(await fsp.readFile(configPath, "utf8"));
40
+ if ("eslintConfig" in config) {
41
+ config = config["eslintConfig"];
42
+ } else {
43
+ config = {}
38
44
  }
39
45
  break;
40
46
  default:
@@ -52,8 +58,11 @@ module.exports = {
52
58
  * @param {*} configPath ESLint config file
53
59
  * @returns {void}
54
60
  */
55
- async sanitizeEslintConfig(configPath, customRuleExample=false) {
56
- let configContents = this.readEslintConfig(configPath) || {};
61
+ async sanitizeEslintConfig(configPath, customRuleExample = false, logger = console) {
62
+ let configContents = {};
63
+ if (await fsUtil.pathExists(configPath)) {
64
+ configContents = (await this.readEslintConfig(configPath)) || {};
65
+ }
57
66
  const configType = "recommended";
58
67
  const extendsValue = `plugin:@sap/cds/${configType}`;
59
68
  if ("extends" in configContents) {
@@ -62,7 +71,9 @@ module.exports = {
62
71
  configContents["extends"].push(extendsValue);
63
72
  }
64
73
  } else {
65
- configContents["extends"] = [configContents["extends"], extendsValue];
74
+ if (configContents["extends"] !== extendsValue) {
75
+ configContents["extends"] = [configContents["extends"], extendsValue];
76
+ }
66
77
  }
67
78
  } else {
68
79
  configContents["extends"] = extendsValue;
@@ -79,7 +90,7 @@ module.exports = {
79
90
  configContents["rules"] = { [ruleName]: ruleValue };
80
91
  }
81
92
  }
82
- await this.writeEslintConfig(configContents, configPath);
93
+ await this.writeEslintConfig(configPath, configContents, logger);
83
94
  return;
84
95
  },
85
96
 
@@ -89,16 +100,37 @@ module.exports = {
89
100
  * @param {*} configPath config file path to write to
90
101
  * @returns {void}
91
102
  */
92
- async writeEslintConfig(configContents, configPath) {
103
+ async writeEslintConfig(configPath, configContents, logger=console) {
104
+ const configType = "recommended";
105
+ const extendsValue = `plugin:@sap/cds/${configType}`;
93
106
  let contents = {};
94
107
  let configFile = path.basename(configPath);
108
+ let configToAdd = {};
95
109
  switch (configFile) {
96
110
  case ".eslintrc.js":
97
111
  case ".eslintrc.cjs":
98
- await fsUtil.writeFile(
99
- configPath,
100
- `module.exports = ${JSON.stringify(configContents, null, 2)}`
101
- );
112
+ try {
113
+ configContents = await this.readEslintConfig(configPath);
114
+ } catch (err) {
115
+ configContents = {};
116
+ }
117
+ if (configContents && configContents["extends"]) {
118
+ if (
119
+ !configContents["extends"] === extendsValue ||
120
+ !configContents["extends"].includes(extendsValue)
121
+ ) {
122
+ configToAdd["extends"] = extendsValue;
123
+ }
124
+ } else {
125
+ configToAdd["extends"] = extendsValue;
126
+ }
127
+ if (configToAdd["extends"]) {
128
+ logger.log(
129
+ `${term.warn(
130
+ `\n\nPlease add the following to your "${configFile}":\n\nmodule.exports = ${JSON.stringify(configToAdd, null, 2)}`
131
+ )}\n`
132
+ );
133
+ }
102
134
  break;
103
135
  case ".eslintrc.yaml":
104
136
  case ".eslintrc.yml":
@@ -106,27 +138,20 @@ module.exports = {
106
138
  break;
107
139
  case ".eslintrc.json":
108
140
  case ".eslintrc":
141
+ await fsUtil.writeFile(configPath, `${JSON.stringify(configContents, null, 2)}`);
142
+ break;
109
143
  case "package.json":
110
- if (configFile === "package.json") {
111
- if (fs.existsSync(configPath)) {
112
- contents = JSON.parse(fs.readFileSync(configPath, "utf8"));
113
- } else {
114
- contents = {};
115
- }
116
- if (!("eslintConfig" in configContents)) {
117
- contents["eslintConfig"] = configContents;
118
- } else {
119
- contents["eslintConfig"] = [
120
- ...configContents,
121
- ...contents["eslintConfig"],
122
- ];
123
- }
124
- configContents = contents;
144
+ if (await fsUtil.pathExists(configPath)) {
145
+ contents = JSON.parse(await fsp.readFile(configPath, "utf8"));
146
+ } else {
147
+ contents = {};
125
148
  }
126
- await fsUtil.writeFile(
127
- configPath,
128
- `${JSON.stringify(configContents, null, 2)}`
129
- );
149
+ if (!("eslintConfig" in configContents)) {
150
+ contents["eslintConfig"] = configContents;
151
+ } else {
152
+ contents["eslintConfig"] = [...configContents, ...contents["eslintConfig"]];
153
+ }
154
+ await fsUtil.writeFile(configPath, `${JSON.stringify(contents, null, 2)}`);
130
155
  break;
131
156
  default:
132
157
  break;
@@ -141,8 +166,12 @@ module.exports = {
141
166
  */
142
167
  async readVscodeSettings(settingsPath) {
143
168
  let settings;
144
- if (fs.existsSync(settingsPath)) {
145
- settings = await fsUtil.readJSONC(settingsPath);
169
+ if (fsUtil.pathExists(settingsPath)) {
170
+ try {
171
+ settings = await fsUtil.readJSONC(settingsPath);
172
+ } catch (err) {
173
+ settings = {};
174
+ }
146
175
  }
147
176
  return settings;
148
177
  },
@@ -186,10 +215,7 @@ module.exports = {
186
215
  }
187
216
 
188
217
  if (settings["eslint.validate"]) {
189
- settings["eslint.validate"] = this.mergeArrays(
190
- settings["eslint.validate"],
191
- lintFileTypes
192
- );
218
+ settings["eslint.validate"] = this.mergeArrays(settings["eslint.validate"], lintFileTypes);
193
219
  } else {
194
220
  settings["eslint.validate"] = lintFileTypes;
195
221
  }
@@ -208,7 +234,7 @@ module.exports = {
208
234
  }
209
235
  }
210
236
 
211
- await this.writeVscodeSettings(settings, settingsPath);
237
+ await this.writeVscodeSettings(settingsPath, settings);
212
238
  },
213
239
 
214
240
  /**
@@ -217,7 +243,7 @@ module.exports = {
217
243
  * @param {*} settingsPath settings file path to write to
218
244
  * @returns {void}
219
245
  */
220
- async writeVscodeSettings(settings, settingsPath) {
246
+ async writeVscodeSettings(settingsPath, settings) {
221
247
  if (!settings) {
222
248
  await fsUtil.mkdirp(path.dirname(settingsPath));
223
249
  }
@@ -236,10 +262,9 @@ module.exports = {
236
262
  if (propsToAdd) {
237
263
  if (Object.keys(userOpts).includes(propKey)) {
238
264
  if (!["config", "format"].includes(propKey)) {
239
- propsToAdd = this.mergeArrays(
240
- userOpts[propKey].split(","),
241
- propsToAdd.split(",")
242
- ).join(",");
265
+ propsToAdd = this.mergeArrays(userOpts[propKey].split(","), propsToAdd.split(",")).join(
266
+ ","
267
+ );
243
268
  }
244
269
  }
245
270
  userOpts[propKey] = propsToAdd;
package/lib/util/term.js CHANGED
@@ -8,6 +8,7 @@ const t = module.exports.codes = {
8
8
  link: '\x1b[4m', // underline
9
9
  red: '\x1b[91m', // Bright Foreground Red
10
10
  green: '\x1b[32m', // Foreground Green
11
+ blue: '\x1b[34m', // Foreground Blue
11
12
  orange: '\x1b[38;2;255;140;0m' // darker orange, works with bright and dark background
12
13
  }
13
14
 
@@ -24,6 +25,7 @@ module.exports.warn = o => as(t.orange, o)
24
25
  module.exports.info = o => as(t.green, o)
25
26
  module.exports.link = o => as(t.link, o)
26
27
  module.exports.bold = o => as(t.bold, o)
28
+ module.exports.highlight = o => as(t.blue,as(t.bold, o))
27
29
 
28
30
  const format = module.exports.format = (o, severity='Error', asInternalError=false, withStack=false) => {
29
31
  switch (severity) {
@@ -5,28 +5,26 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
5
5
  // Phoning home...
6
6
  const EventEmitter = require('events')
7
7
  const emitter = new EventEmitter
8
- process.on('exit', () => {
9
- if (child) child.kill() // propagate exit to child
10
- emitter.emit('quit') // listener might exit(), so expect this to be the last statement here
11
- })
8
+ process.on('exit', () => { gracefullyKill (child); emitter.emit('quit') })
12
9
 
13
10
  // Re-starting child process...
14
11
  const { fork } = require('child_process')
15
12
  const restart = (evt, name)=>{
16
- const execArgv = [...process.execArgv]
17
- if (child) child.kill()
18
- if (evt && evt.startsWith('--')) execArgv.push(evt)
19
- if (name && name.startsWith('--')) execArgv.push(name)
20
- if (options.debug) execArgv.push('--inspect')
21
- if (options['debug-brk']) execArgv.push('--inspect-brk')
22
- const files = evt === "update" ? [ name ] : undefined
23
- emitter.emit ('restart', files)
24
- child = fork (script, { cwd, env, stdio:'inherit', execArgv }, (err)=>{
25
- if (err) console.error (err)
26
- })
27
- child.on('message', msg => {
28
- if (msg === 'restart') return restart()
29
- emitter.emit('message', msg)
13
+ gracefullyKill (child, () => {
14
+ const execArgv = [...process.execArgv]
15
+ if (evt && evt.startsWith('--')) execArgv.push(evt)
16
+ if (name && name.startsWith('--')) execArgv.push(name)
17
+ if (options.debug) execArgv.push('--inspect')
18
+ if (options['debug-brk']) execArgv.push('--inspect-brk')
19
+ const files = evt === "update" ? [ name ] : undefined
20
+ emitter.emit ('restart', files)
21
+ child = fork (script, { cwd, env, stdio:'inherit', execArgv }, (err)=>{
22
+ if (err) console.error (err)
23
+ })
24
+ child.on('message', msg => {
25
+ if (msg === 'restart') return restart()
26
+ emitter.emit('message', msg)
27
+ })
30
28
  })
31
29
  }
32
30
  restart()
@@ -46,10 +44,19 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
46
44
  else if (input === 'break' || input === 'brk') restart('--inspect-brk')
47
45
  else if (input === 'debug-brk') restart('--inspect-brk')
48
46
  else if (input === 'ps') ps(child,env)
49
- else if (input === 'bye' || input.match(/^n$/i)) { child.kill(); process.exit() }
47
+ else if (input === 'bye' || input.match(/^n$/i)) { gracefullyKill (child, () => process.exit()) }
50
48
  else console.log ('?\n')
51
49
  })
52
50
 
51
+ emitter.restart = restart
52
+ emitter.stop = () => { gracefullyKill(child, () => false); child = undefined; }
53
+ emitter.setEnv = (name, value) => {
54
+ if (typeof value === "undefined") {
55
+ delete env[name]
56
+ } else {
57
+ env[name]
58
+ }
59
+ }
53
60
  return emitter
54
61
  }
55
62
 
@@ -59,3 +66,15 @@ const ps = (child,env) => console.log (`\x1b[32m
59
66
  ${ child.pid} child cds ${JSON.parse(env._args).join(' ')}
60
67
  \x1b[0m`
61
68
  )
69
+
70
+ // On Windows, kill() abruptly kills the child process (https://nodejs.org/api/child_process.html#subprocesskillsignal)
71
+ // Send a custom message an wait a bit to give cleanup code a chance to execute.
72
+ const isWindows = (process.platform === 'win32')
73
+ const waitTime = parseInt(process.env.CDS_WATCH_KILL_DELAY) || (isWindows ? 500 : 0)
74
+ function gracefullyKill (proc, fn=()=>{}) {
75
+ if (proc) {
76
+ proc.send({ exit: true }, () => { /* ignore */ })
77
+ setTimeout(()=> { proc.kill(); fn()}, waitTime)
78
+ }
79
+ else fn()
80
+ }