@sap/cds-dk 4.5.2 → 4.6.2

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 (42) hide show
  1. package/CHANGELOG.md +55 -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 +38 -18
  9. package/bin/repl.js +6 -2
  10. package/bin/watch.js +58 -13
  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/auth_manager.js +4 -4
  15. package/lib/client/settings_manager.js +6 -5
  16. package/lib/import/common.js +10 -1
  17. package/lib/import/importapi.js +4 -3
  18. package/lib/import/index.js +34 -5
  19. package/lib/import/message.js +2 -1
  20. package/lib/import/utils/MetadataConverter.js +25 -4
  21. package/lib/import/utils/MetadataConverterFactory.js +1 -2
  22. package/lib/import/utils/MultiMap.js +26 -0
  23. package/lib/import/v2parser.js +131 -12
  24. package/lib/import/v4parser.js +214 -168
  25. package/lib/init/constants.js +1 -1
  26. package/lib/init/index.js +2 -0
  27. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +36 -52
  28. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +15 -0
  29. package/lib/init/template/lint/index.js +19 -17
  30. package/lib/init/template/mta/files/mta.yaml.hbs +0 -7
  31. package/lib/init/template/mtx/files/xs-security.json.hbs +3 -1
  32. package/lib/init/template/mtx/package/dependencies.package.json +1 -1
  33. package/lib/init/template/nodejs/files/.gitignore.hbs +1 -0
  34. package/lib/init/util/commandUtil.js +5 -0
  35. package/lib/lint/Linter.js +17 -20
  36. package/lib/lint/generators.js +5 -4
  37. package/lib/lint/io.js +62 -36
  38. package/lib/util/term.js +2 -0
  39. package/lib/watch/node-watch.js +38 -19
  40. package/npm-shrinkwrap.json +1485 -1959
  41. package/package.json +3 -3
  42. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/test.js +0 -10
package/CHANGELOG.md CHANGED
@@ -6,6 +6,60 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
8
8
 
9
+ ## Version 4.6.2 - 2021-11-09
10
+
11
+ ### Fixed
12
+
13
+ - `cds add lint` now correctly detects missing local `eslint`
14
+ - `cds add lint` no longer removes parts of `package.json`
15
+ - `cds add lint` no longer creates duplicate configuration entries
16
+
17
+ ## Version 4.6.1 - 2021-11-08
18
+
19
+ ### Changed
20
+
21
+ - `cds init` uses latest `Maven Java archetype` version `1.20.0` for creating Java projects.
22
+ - `cds watch` now resolves local node modules like `hdb` even though `@sap/cds` is not installed locally. This is useful during development where `@sap/cds` shall only be installed in a 'central' location, while extra modules shall still be found in the app's dir.
23
+
24
+ ### Fixed
25
+
26
+ - `cds repl` now finds modules like `hdb` that are installed locally in the app folder
27
+ - `cds repl` now enables the `await` statement also on Windows
28
+ - `cds lint` now limits all file extensions to those allowed by the @sap/eslint-plugin-cds ESLint configuration
29
+
30
+ ## Version 4.6.0 - 2021-11-02
31
+ ### Added
32
+
33
+ - `cds bind` binds the given service to a hana instance by storing the credentials in `.cdsrc.json` in your user home directory [beta].
34
+ - `cds import` introduced option `--include-namespaces` which imports the custom defined namespaces.
35
+ - `cds import` preserve documentation in CSN file for actions and functions from OData V4 EDMX file.
36
+ - `cds import` now supports reading values of the `as` option from cds.env
37
+
38
+ ### Fixed
39
+
40
+ - `cds import` fix for reflecting only supported datatypes in csn for actions and functions for OData V4.
41
+ - `cds import` fix for TypeError issue (#9868) during OData V4 EDMX conversion to CSN.
42
+ - `cds watch` no longer omits starting some services every other time it is restarting the process
43
+ - `cds-ts watch` now honors a local `tsconfig.json` file
44
+ - `cds import` fix for TypeError issue (#9950) during OData V2 EDMX conversion to CSN.
45
+ - `cds import` fix to generate the csn (#9950) during OData V2 EDMX conversion to CSN.
46
+ - `cds import` fix to replace the aliases with original schema namespace value.
47
+ - `cds import` adds `@cds.persistence.skip` back to imported models. Its accidental removal in 4.5.0 caused wrong DB deployments of imported entities.
48
+
49
+ ## Version 4.5.4 - 2021-10-07
50
+
51
+ ### Fixed
52
+
53
+ - `cds watch` no longer fails if started with `--ext`, `--livereload`, or `--open` arguments
54
+
55
+ ## Version 4.5.3 - 2021-10-05
56
+
57
+ ### Fixed
58
+
59
+ - `cds import` fix for `--dry` shortcut `-`
60
+ - `cds import` fix for missing type properties in csn for unbounded action and function in OData V4.
61
+ - A bug with `cds init` when called in BAS project wizard
62
+
9
63
  ## Version 4.5.2 - 2021-09-30
10
64
 
11
65
  ### Changed
@@ -50,6 +104,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
50
104
  + no longer skips over entities marked with `@cds.persistence.skip`, which is true for 'external' entities created by `cds import`.
51
105
  - `cds init --add` handles comma list correctly.
52
106
  - `cds import` fix for multi-line documentation text in OData V4 EDMX file.
107
+ - `cds import` fix preserves the missing entities in csn for OData V2 EDMX file.
53
108
 
54
109
 
55
110
  ## Version 4.4.2 - 2021-09-03
package/bin/bind.js CHANGED
@@ -1,17 +1,133 @@
1
- module.exports = Object.assign (
2
- cds_bind, {help: `
1
+
2
+
3
+ module.exports = Object.assign(cds_bind, {
4
+ options: ['--to', '--profile', '--kind', '--output-file'],
5
+ flags: [],
6
+ shortcuts: ['-2', '-p', '-k', '-o'],
7
+ help: `
3
8
  # SYNOPSIS
4
9
 
5
- *cds bind* <service> [<options>]
10
+ *cds bind* <service> [<options>] -- beta feature
6
11
 
7
- Binds the given service to
12
+ Binds the given service to a service instance by storing connection information in *.cdsrc-private.json*
13
+ in your project directory. Credentials are not stored in the file but rather retrieved dynamically during *cds watch*.
14
+ Use option *--output-file* to specify a different path or file.
15
+
16
+ Currently *cds bind* only supports Node.js projects.
8
17
 
9
18
  # OPTIONS
10
19
 
11
- *-2|--to* <credentials>
20
+ *-2 | --to* <instance>[:<key>]
21
+ bind to a given instance
22
+
23
+ *-p | --profile* <profile>
24
+ add the given profile, defaults to *hybrid*
25
+
26
+ *-k | --kind* <kind>
27
+ the kind of service
28
+
29
+ *-o | --output-file <path>*
30
+ save bindings to the given *.cdsrc.json* or *package.json* file. Default is *.cdsrc-private.json*.
31
+ If *path* is a directory, then it will save it to the *.cdsrc.json* file in that directory.
32
+
33
+ # EXAMPLE
34
+
35
+ cds bind --to my-hdi-container
36
+ cds bind --to my-hdi-container,my-xsuaa
37
+ cds bind uaa --to my-xsuaa:my-xsuaa-key --kind xsuaa --profile myprofile
38
+
39
+ cds bind --to my-hdi-container --output-file ~
40
+ cds bind --to my-hdi-container --output-file package.json
41
+
42
+ `});
43
+
44
+ const { constants, promises: fsp } = require('fs');
45
+ const path = require('path');
46
+
47
+ const cds = require('@sap/cds/lib');
48
+ const LOG = cds.log('bind');
49
+ // required for spinner
50
+ LOG.isDebug = LOG._debug;
51
+ LOG.isTTY = process.stdout.isTTY;
52
+
53
+ const { bold, info } = require('../lib/util/term');
54
+
55
+ const Spinner = require('../lib/init/util/spinner');
56
+
12
57
 
13
- `})
58
+ async function cds_bind(args, options) { // eslint-disable-line
59
+
60
+ options.profile = options.profile || 'hybrid';
61
+
62
+ if (await isJavaProject('.')) {
63
+ throw new Error(`Found a 'pom.xml' file in your project root folder indicating a Java project. Currently, 'cds bind' only supports Node.js projects.`);
64
+ }
65
+
66
+ if (!options.to) {
67
+ throw new Error(`use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`);
68
+ }
69
+
70
+ if (args.length > 1) {
71
+ throw new Error(`Too many arguments: Please specify only one or no service.`);
72
+ }
73
+
74
+ let serviceArg = args[0]
75
+ const targets = options.to.split(/,/g);
76
+
77
+ if (targets.length >= 2 && serviceArg) {
78
+ throw new Error(`Service argument cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the service argument.`);
79
+ }
80
+
81
+ if (targets.length >= 2 && options.kind) {
82
+ throw new Error(`The option '--kind' cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the '--kind' option.`);
83
+ }
84
+
85
+ // prepare for different providers
86
+ const keyProvider = require('../lib/bind/cfKeyProvider');
87
+ keyProvider.setLogger(LOG);
88
+ const spinner = new Spinner(LOG);
89
+ const resolvedServices = await spinner.run(`Retrieving data from Cloud Foundry...`, async () => { // NOSONAR
90
+ const resolvedServices = [];
91
+ await Promise.all( targets.map( async target => {
92
+ const [instance, key] = target.split(/:/);
93
+ const resolvedService = await keyProvider.resolve(undefined, { instance, key });
94
+ // if (!options["save-credentials"]) {
95
+ delete resolvedService.credentials;
96
+ resolvedService.binding.resolved = false;
97
+ // }
98
+ resolvedServices.push(resolvedService);
99
+ }));
100
+ return resolvedServices;
101
+ });
102
+
103
+ if (!options.kind) {
104
+ keyProvider.determineKinds(resolvedServices);
105
+ }
106
+
107
+ for (const resolvedService of resolvedServices) {
108
+ resolvedService.kind = options.kind || resolvedService.kind;
109
+ resolvedService.name = serviceArg || keyProvider.getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
110
+ }
111
+
112
+ // prepare for different providers
113
+ const userCdsrcJsonStorageProvider = require('../lib/bind/userCdsrcJsonStorageProvider');
114
+
115
+ await userCdsrcJsonStorageProvider.storeServices(resolvedServices, {
116
+ ...options,
117
+ logger: LOG
118
+ });
119
+
120
+ LOG.log();
121
+ LOG.log(`${info("TIP:")} Run with cloud bindings: ${bold(`cds watch --profile ${options.profile}`)}`);
122
+ // LOG.log();
123
+ }
14
124
 
15
- function cds_bind (projects, options) { // eslint-disable-line
16
- console.log (`cds bind is not yet released`)
125
+ async function isJavaProject(projectPath) {
126
+ const pomXmlPath = path.join(projectPath, 'pom.xml');
127
+ try {
128
+ await fsp.access(pomXmlPath, constants.F_OK);
129
+ return true;
130
+ } catch (err) {
131
+ return false;
132
+ }
17
133
  }
package/bin/cds-ts.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env ts-node
1
+ #!/usr/bin/env ts-node-cwd
2
2
  require('./cds').apply(process.argv)
package/bin/cds.js CHANGED
@@ -5,6 +5,7 @@ const cli = { //NOSONAR
5
5
  Shortcuts: {
6
6
  i: 'init',
7
7
  a: 'add',
8
+ y: 'bind',
8
9
  m: 'import',
9
10
  c: 'compile',
10
11
  s: 'serve',
@@ -38,6 +38,7 @@ module.exports = Object.assign ( compile, {
38
38
  - json, yml
39
39
  - edm, edmx, edmx-v2, edmx-v4, edmx-w4, edmx-x4
40
40
  - sql, hdbcds, hdbtable
41
+ - cdl [beta]
41
42
  - xsuaa
42
43
  - openapi
43
44
 
@@ -261,7 +262,7 @@ function compile (models, options={}) {
261
262
 
262
263
  function suffix4 (x) { return x && ({
263
264
  edmx: '.xml',
264
- "edmx-v2": '.xml',
265
+ "edmx-v2": '.xml',
265
266
  "edmx-v4": '.xml',
266
267
  openapi: '.openapi3.json', //'.yml',
267
268
  cdl: '.cds',
package/bin/env.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = Object.assign (env, {
2
- options: ['--for', '--mocked'], flags: [], shortcuts: ['-4'],
2
+ options: ['--for', '--mocked'], flags: ['--process-env', '--resolve-bindings'], shortcuts: ['-4', undefined, '-P', '-b'],
3
3
  help: `
4
4
  # SYNOPSIS
5
5
 
@@ -11,13 +11,29 @@ module.exports = Object.assign (env, {
11
11
 
12
12
  # EXPLANATION
13
13
 
14
- Without any arguments, this will display the effective configuration in
15
- the current environment in JavaScript object format. <ls | list> displays
16
- it in _.properties_ format.
14
+ Lists the effective configuration of the current environment in
15
+ _.properties_ format if no command, *ls*, or *list* is given.
17
16
 
18
- The other variants either get or set a single option or display the sources
19
- from which the effective configuration has been loaded.
17
+ The other commands either get or set a single property or display
18
+ the sources from which the effective configuration has been loaded.
20
19
 
20
+ # OPTIONS
21
+
22
+ *-4* | *--for* <path>
23
+
24
+ Load configuration from path instead of the current working directory.
25
+
26
+ *--profile* <profile,...>
27
+
28
+ Load configuration for the specified profile(s).
29
+
30
+ *-P* | *--process-env*
31
+
32
+ Show properties from Node.js _process.env_.
33
+
34
+ *-b* | *--resolve-bindings*
35
+
36
+ Resolve _cds bind_ bindings.
21
37
  `})
22
38
 
23
39
  const path = require('path')
@@ -29,15 +45,20 @@ const info = (s) => process.stdout.isTTY ? term.info (s) : s
29
45
  async function env ([cmd, key, value], options={}) {
30
46
 
31
47
  const cds = require('@sap/cds/lib')
32
- const fn = commands[cmd] || (()=>{
48
+ const fn = (options["process-env"] ? processEnvCommands[cmd] : commands[cmd]) || (()=>{
33
49
  this.help ('env')
34
50
  throw new Error (`Don't understand '${cmd}' here...`)
35
51
  })
36
52
 
53
+ if (options.profile) process.env.CDS_ENV = options.profile
54
+
37
55
  const env = options.for ? cds.env.for (options.for) : cds.env
38
56
  env['_home_cds-dk'] = path.resolve(__dirname, '..') // to help tools find the DK
39
57
 
58
+ if (options["resolve-bindings"]) await require('../lib/bind/bindingResolver').applyTo({env, output: "debug"})
59
+
40
60
  if (!options.for && options.mocked) await cds.service.bindings
61
+
41
62
  fn (env, key, value)
42
63
  }
43
64
 
@@ -69,19 +90,39 @@ const commands = {
69
90
  if (!process.stdout.isTTY) return commands.json (conf, key, val)
70
91
  // tty: display formatted object
71
92
  const obj = (key ? conf.get(key) : conf)
72
- console.log (inspect (obj, {depth:22,colors:true}))
93
+ console.log (inspect (obj, {depth:22,colors:true}).replace(/\s+use:.*\[Getter\].*/g,''))
73
94
  },
74
95
 
75
96
  // eslint-disable-next-line no-unused-vars
76
97
  json (conf, key, val) {
77
98
  const obj = (key ? conf.get(key) : conf)
78
- return console.log (JSON.stringify(obj, null, 2))
99
+ const str = JSON.stringify(obj, null, 2)
100
+ return console.log (str ? str.replace(/\s+use:.*\[Getter\].*/g,'') : str)
79
101
  },
80
102
 
81
103
  set () {
82
104
  throw new Error ('cds config set is not yet implemented, sorry')
105
+ }
106
+ }
107
+
108
+ const processEnvCommands = {
109
+ get [undefined]() { return processEnvCommands.list },
110
+ get ''() { return processEnvCommands.list },
111
+ get ls() { return process.list },
112
+
113
+ list (_, key) {
114
+ if (key) {
115
+ processEnvCommands.get(undefined, key)
116
+ } else {
117
+ for (const name in process.env) {
118
+ console.log(`${name}=${process.env[name]}`)
119
+ }
120
+ }
83
121
  },
84
122
 
123
+ get (_, key) {
124
+ console.log (process.env[key] || "")
125
+ }
85
126
  }
86
127
 
87
128
  /* eslint no-console:0 */
package/bin/help.js CHANGED
@@ -10,6 +10,7 @@ module.exports = exports = Object.assign (help, {help:`
10
10
 
11
11
  *i* | *init* jump-start cds-based projects
12
12
  *a* | *add* add a feature to an existing project
13
+ *y* | *bind* [beta] bind a service to a hana instance
13
14
  *m* | *import* add models from external sources
14
15
  *c* | *compile* compile cds models to different outputs
15
16
  *s* | *serve* run your services in local server
package/bin/import.js CHANGED
@@ -1,9 +1,9 @@
1
1
 
2
2
  module.exports = Object.assign(_import, {
3
3
  _name: 'import',
4
- options: ['--out', '--into', '--as'],
5
- flags: ['--dry', '--no-copy', '--no-save', '--keep-namespace', '--f'],
6
- shortcuts: ['-o', '-in', '-'],
4
+ options: ['--out', '--into', '--as', '--include-namespaces'],
5
+ flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
6
+ shortcuts: ['-o', '-in', '-as', '-ns', '-', '-f'],
7
7
  help: `
8
8
  # SYNOPSIS
9
9
 
@@ -38,11 +38,14 @@ module.exports = Object.assign(_import, {
38
38
  *- | --dry*
39
39
  Writes the converted csn to stdout only.
40
40
 
41
- *--as <output_file_format>*
41
+ *-as | --as <output_file_format>*
42
42
  Converts the input edmx or xml file into one of these supported formats "cds", "csn", "json"
43
43
 
44
- *--f*
45
- Forcefully overwrites the contents in the CDS file and updates the checksum
44
+ *-f | --force*
45
+ Forcefully overwrites the contents in the CDS file and updates the checksum
46
+
47
+ *-ns | --include-namespaces <string_of_namespaces> or* *
48
+ Imports only entities matching to the given list of namespaces.
46
49
 
47
50
 
48
51
 
@@ -52,6 +55,8 @@ module.exports = Object.assign(_import, {
52
55
  cds import ~/Downloads/API_BUSINESS_PARTNER.edmx
53
56
  cds import - ~/Downloads/API_BUSINESS_PARTNER.edmx
54
57
  cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --as cds
58
+ cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --include-namespaces "sap,c4c"
59
+ cds import ~/Downloads/API_BUSINESS_PARTNER.edmx --include-namespaces "*"
55
60
 
56
61
  `})
57
62
  const cds = require('@sap/cds/lib')
@@ -62,12 +67,19 @@ const common = require('../lib/import/common');
62
67
  const cdsCompiler = require('../lib/compile').compile.to;
63
68
  const md5 = require('md5');
64
69
  let cwd;
65
- async function _import(file, { out, dry, into, as,
70
+ async function _import(file, { out, dry, into, as,
71
+ 'include-namespaces': include_namespaces,
66
72
  'no-save': no_save,
67
73
  'no-copy': no_copy,
68
74
  'keep-namespace': keep_namespace,
69
- 'f': force
75
+ force
70
76
  } = {}) {
77
+ if (!as) {
78
+ if (cds.env.import) as = cds.env.import.as;
79
+ }
80
+ if (!include_namespaces) {
81
+ if(cds.env.import) include_namespaces = cds.env.import.include_namespaces;
82
+ }
71
83
  cwd = into || process.cwd();
72
84
  if (as && !common.supportedFormats().includes(as))
73
85
  throw new Error(messages.INVALID_AS_OPTION);
@@ -78,6 +90,14 @@ async function _import(file, { out, dry, into, as,
78
90
  const dst = path.join(external, path.basename(file))
79
91
  if (!dry && !no_copy) file = await _copy_to_srv_external(file, dst)
80
92
  const src = await edm2csn.getMetadataFromFile(file)
93
+ this.include_all_namespaces = false;
94
+ let namespaces = [];
95
+ if(include_namespaces) {
96
+ if(include_namespaces === "*") this.include_all_namespaces = true;
97
+ else if(typeof include_namespaces === 'string' && !include_namespaces.startsWith('-')) namespaces = include_namespaces.replace(/ /g, "").split(',');
98
+ else throw new Error(messages.INCORRECT_INPUT_NAMESPACES);
99
+ }
100
+ this.namespaces = namespaces;
81
101
  let csn = await edm2csn.edmx2csn(src, !keep_namespace && file, this)
82
102
  const service = _service_name_from(csn);
83
103
  if (out) out = edm2csn.validateAndComputeOutputFilePath(out, as);
@@ -96,23 +116,23 @@ async function _generateChecksumValidate(dest, output, force) {
96
116
  return output;
97
117
  }
98
118
  else {
99
- let fileContent = await read(dest, "utf-8");
100
- fileContent = String(fileContent);
101
- if (fileContent.indexOf("checksum") === -1){
102
- let checksum = md5(fileContent);
119
+ let existingFileContent = await read(dest, "utf-8");
120
+ existingFileContent = String(existingFileContent);
121
+ if (existingFileContent.indexOf("checksum") === -1){
122
+ let checksum = md5(existingFileContent);
103
123
  output = "/* checksum : " + checksum + " */\n" + output;
104
124
  return output;
105
125
  }
106
- let fileChecksum = fileContent.substring(fileContent.indexOf("checksum") + 11, fileContent.indexOf(" */"));
107
- let newFile = fileContent.substring(fileContent.indexOf('*/') + 3);
108
- let checksum = md5(newFile);
126
+ let fileChecksum = existingFileContent.substring(existingFileContent.indexOf("checksum") + 11, existingFileContent.indexOf(" */"));
127
+ existingFileContent = existingFileContent.substring(existingFileContent.indexOf('*/') + 3);
128
+ let checksum = md5(existingFileContent);
109
129
  if (fileChecksum === checksum) {
110
- output = "/* checksum : " + checksum + " */\n" + output;
130
+ output = "/* checksum : " + checksum + " */\n" + existingFileContent;
111
131
  return output;
112
132
  }
113
133
  else {
114
134
  if (force) {
115
- output = "/* checksum : " + checksum + " */\n" + newFile;
135
+ output = "/* checksum : " + md5(output) + " */\n" + output;
116
136
  return output;
117
137
  }
118
138
  else {
@@ -195,4 +215,4 @@ async function _update_package_json(dest, service, version) {
195
215
  await write(package_json, JSON.stringify(conf, null, ' '))
196
216
  console.log(`[cds] - updated ./package.json`)
197
217
  }
198
- }
218
+ }
package/bin/repl.js CHANGED
@@ -14,8 +14,12 @@ module.exports = Object.assign (repl, { options: [], help: `
14
14
 
15
15
  function repl (_, options={}) {
16
16
 
17
- if (module.parent && module.parent.filename.endsWith('/bin/cds.js')) {
18
- return require('child_process').spawn('node',['--experimental-repl-await', __filename ],{stdio:'inherit'})
17
+ // enable `await` statement for Node.js < 16
18
+ if (!process.env['CDS_REPL_SPAWNED'] && process.version.split('.')[0].slice(1) < '16') {
19
+ process.env['CDS_REPL_SPAWNED'] = true
20
+ return require('child_process').spawn('node',['--experimental-repl-await',
21
+ ...process.argv.slice(1) ], {stdio:'inherit'}
22
+ )
19
23
  }
20
24
 
21
25
  const cds = require('@sap/cds/lib')
package/bin/watch.js CHANGED
@@ -1,6 +1,7 @@
1
+ const watchOnlyOptions = ['--ext', '--livereload', '--open']
1
2
  module.exports = Object.assign ( watch, {
2
3
  flags: [ '--debug', '--in-memory', '--in-memory?' ],
3
- options: [ '--port', '--ext', '--livereload', '--open' ],
4
+ options: [ '--port', ...watchOnlyOptions ],
4
5
  help: `
5
6
  # SYNOPSIS
6
7
 
@@ -42,7 +43,7 @@ module.exports = Object.assign ( watch, {
42
43
  `})
43
44
 
44
45
  const DEBUG = /\bwatch\b/.test(process.env.DEBUG) && console.debug
45
- const path = require ('path')
46
+ const {delimiter,dirname,relative,resolve,sep} = require ('path')
46
47
  const fs = require ('fs')
47
48
  const t = module.exports.codes = {
48
49
  reset: '\x1b[0m', // Default
@@ -61,16 +62,20 @@ async function watch ([cwd], {
61
62
  ext = extDefaults,
62
63
  ...options
63
64
  }={}) {
64
-
65
65
  args.push (`--${Object.keys(options).find(a => /^in-memory\??$/.test(a)) || 'in-memory?'}`)
66
- args.push (...process.argv.slice(3).filter(a => a !== cwd))
67
- const env = Object.assign (process.env, {
66
+ args.push (...process.argv.slice(3)
67
+ .filter(removeArgsIn(watchOnlyOptions)) // remove all watch-only options, as `cds serve` would not accept them
68
+ .filter(a => a !== cwd))
69
+
70
+ let env = Object.assign (process.env, {
68
71
  _args: JSON.stringify(args),
69
72
  _options: JSON.stringify(options)
70
73
  })
74
+
75
+ // allow running w/o or partial local node_modules, also add cds-dk for its sqlite
76
+ env.NODE_PATH = [...paths(cwd||process.cwd()), ...paths(__dirname)].join(delimiter)
77
+
71
78
  if (typeof ext !== 'string') ext = extDefaults
72
- if ('port' in options) env.PORT = options.port
73
- env.NODE_PATH = path.resolve (__dirname,'../node_modules') // allow global sqlite to be resolved
74
79
 
75
80
  let liveReload //= ...
76
81
  let firstTime = true
@@ -81,7 +86,7 @@ async function watch ([cwd], {
81
86
  env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'])
82
87
  .catch(err => { console.log(`Error starting live reload: ${err.message}`); liveReload = null })
83
88
  } else {
84
- ignore = RegExp(`app${path.sep}.*\\.(js|ts)|`+ignore.source) // exclude more frontend stuff
89
+ ignore = RegExp(`app${sep}.*\\.(js|ts)|`+ignore.source) // exclude more frontend stuff
85
90
  }
86
91
 
87
92
  const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
@@ -89,8 +94,8 @@ async function watch ([cwd], {
89
94
  const ocwd = process.env._original_cwd = process.cwd()
90
95
  if (fs.existsSync(cwd)) log (`cd ${cwd}`)
91
96
  else try {
92
- const resolved = path.dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
93
- log (`cd ${path.relative(ocwd,cwd = resolved)}`)
97
+ const resolved = dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
98
+ log (`cd ${relative(ocwd,cwd = resolved)}`)
94
99
  } catch(_){ throw new Error(`No such folder or package: '${ocwd}' -> '${cwd}'`) }
95
100
  }
96
101
  log ()
@@ -98,8 +103,12 @@ async function watch ([cwd], {
98
103
  log (`watching: ${ext}...`)
99
104
  if (liveReload) log (`live reload enabled for browsers`)
100
105
 
106
+ const bindingManager = new require('../lib/bind/bindingResolver').bindingManager({cwd})
107
+ const bindings = await bindingManager.resolve()
108
+ if (bindings) env = {...env, CDS_CONFIG: JSON.stringify(bindings)}
109
+
101
110
  const nodemon = require('../lib/watch/node-watch')
102
- return nodemon ({
111
+ const watcher = nodemon ({
103
112
  watch: ['*','.env','.cdsrc.json'],
104
113
  script:__filename, cwd, env, ext, ignore,
105
114
  delay: parseInt(process.env.CDS_WATCH_DELAY) || 200,
@@ -109,12 +118,18 @@ async function watch ([cwd], {
109
118
  if (liveReload) liveReload.markPending(files)
110
119
  if (this.delayed) clearTimeout (this.delayed)
111
120
  this.delayed = setTimeout(()=>{
121
+ bindingManager.update({
122
+ onBeforeUpdate: () => watcher.stop(),
123
+ onAfterUpdate: bindings => {
124
+ watcher.setEnv('CDS_CONFIG', bindings && JSON.stringify(bindings))
125
+ watcher.restart()
126
+ }
127
+ })
112
128
  log (`${t.bold} _______________________\n`)
113
129
  FileHandlers.changed (files,cwd)
114
130
  }, 111)
115
131
  }).on('quit', ()=>{
116
132
  log (`${t.bold+t.green}\n[cds] - my watch has ended.\n`)
117
- process.exit()
118
133
  }).on('message', async (msg) => { switch (msg.code) { // messages from child process
119
134
  case 'listening':
120
135
  env.PORT = (msg.address && msg.address.port) || msg.port
@@ -125,6 +140,8 @@ async function watch ([cwd], {
125
140
  case 'EADDRINUSE': env.PORT = 0; _addr_in_use(msg); break
126
141
  default: console.error (msg)
127
142
  }})
143
+
144
+ return watcher
128
145
  }
129
146
 
130
147
 
@@ -143,7 +160,26 @@ const FileHandlers = {
143
160
  },
144
161
  }
145
162
 
163
+ function removeArgsIn (toRemove) {
164
+ let argRemoved = false
165
+ return arg => { // filter function
166
+ if (toRemove.includes(arg)) {
167
+ argRemoved = true
168
+ return false
169
+ }
170
+ else if (argRemoved) { // if next arg is a value, skip it as well
171
+ argRemoved = false
172
+ if (!arg.startsWith('-')) return false
173
+ }
174
+ return true
175
+ }
176
+ }
146
177
 
178
+ // builds paths from dir + its parent node_modules
179
+ function paths (dir) {
180
+ const parts = resolve(dir).split(sep), n = parts.length, nm = sep+'node_modules'
181
+ return parts.map ((_,i,a)=> a.slice(0,n-i).join(sep)+nm).filter(p => fs.existsSync(p))
182
+ }
147
183
 
148
184
  //
149
185
  // Child process impl launched by watch
@@ -153,8 +189,17 @@ if (!module.parent) (async()=>{ // launched by nodemon
153
189
  // run through cli in order to use its error handling
154
190
  const bootstrap = require('./cds')(...JSON.parse(process.env._args))
155
191
  const {cds} = global
192
+
193
+ cds.once('listening', ({server, url}) => {
194
+ process.send ({ code:'listening', port: server.address().port })
195
+ // Need to give bindings an explicit call to purge,
196
+ // as its .exit handler has no chance to execute on Windows.
197
+ process.on ('message', (msg)=> { if (msg && msg.exit) { // see node-watch
198
+ (new cds.service.bindings(url)).purge()
199
+ }})
200
+ })
201
+
156
202
  try {
157
- cds.once('listening', ({server}) => process.send ({ code:'listening', port: server.address().port }))
158
203
  await bootstrap
159
204
  if (!cds.service.providers.length) return _no_services_found()
160
205
  } catch (e) { switch (e.code) {