@sap/cds-dk 4.5.4 → 4.6.4

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 +61 -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 +40 -10
  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 +37 -7
  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 +138 -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 +1053 -1279
  47. package/package.json +3 -3
  48. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/test.js +0 -10
package/CHANGELOG.md CHANGED
@@ -6,6 +6,66 @@ 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.4 - 2021-11-24
10
+
11
+ ### Fixed
12
+
13
+ - `cds import` fix for TypeError issue during OData V2 EDMX conversion to CSN.
14
+ - `cds import <file_name>` fix now updates the service name in the `package.json` with only the file name. Earlier, the service name sometimes used to be of the form `A.B`, with `A` being file name and `B` being a part of the schema namespace value in the EDMX file
15
+
16
+ ### Changed
17
+
18
+ - `cds init` uses latest `Maven Java archetype` version `1.20.3` for creating Java projects.
19
+
20
+ ## Version 4.6.3 - 2021-11-16
21
+
22
+ ### Fixed
23
+
24
+ - `cds watch` recovers again from compilation errors and properly prints these
25
+ - `cds import` fix for omitting empty `doc` components.
26
+ - `cds add lint` no longer duplicates initial contents in `package.json`
27
+ - `cds add data` no longer ignores imported services for which credentials are stored in the project
28
+
29
+ ## Version 4.6.2 - 2021-11-09
30
+
31
+ ### Fixed
32
+
33
+ - `cds add lint` now correctly detects missing local `eslint`
34
+ - `cds add lint` no longer removes parts of `package.json`
35
+ - `cds add lint` no longer creates duplicate configuration entries
36
+
37
+ ## Version 4.6.1 - 2021-11-08
38
+
39
+ ### Changed
40
+
41
+ - `cds init` uses latest `Maven Java archetype` version `1.20.0` for creating Java projects.
42
+ - `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.
43
+
44
+ ### Fixed
45
+
46
+ - `cds repl` now finds modules like `hdb` that are installed locally in the app folder
47
+ - `cds repl` now enables the `await` statement also on Windows
48
+ - `cds lint` now limits all file extensions to those allowed by the @sap/eslint-plugin-cds ESLint configuration
49
+
50
+ ## Version 4.6.0 - 2021-11-02
51
+ ### Added
52
+
53
+ - `cds bind` binds the given service to a hana instance by storing the credentials in `.cdsrc.json` in your user home directory [beta].
54
+ - `cds import` introduced option `--include-namespaces` which imports the custom defined namespaces.
55
+ - `cds import` preserve documentation in CSN file for actions and functions from OData V4 EDMX file.
56
+ - `cds import` now supports reading values of the `as` option from cds.env
57
+
58
+ ### Fixed
59
+
60
+ - `cds import` fix for reflecting only supported datatypes in csn for actions and functions for OData V4.
61
+ - `cds import` fix for TypeError issue (#9868) during OData V4 EDMX conversion to CSN.
62
+ - `cds watch` no longer omits starting some services every other time it is restarting the process
63
+ - `cds-ts watch` now honors a local `tsconfig.json` file
64
+ - `cds import` fix for TypeError issue (#9950) during OData V2 EDMX conversion to CSN.
65
+ - `cds import` fix to generate the csn (#9950) during OData V2 EDMX conversion to CSN.
66
+ - `cds import` fix to replace the aliases with original schema namespace value.
67
+ - `cds import` adds `@cds.persistence.skip` back to imported models. Its accidental removal in 4.5.0 caused wrong DB deployments of imported entities.
68
+
9
69
  ## Version 4.5.4 - 2021-10-07
10
70
 
11
71
  ### Fixed
@@ -64,6 +124,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
64
124
  + no longer skips over entities marked with `@cds.persistence.skip`, which is true for 'external' entities created by `cds import`.
65
125
  - `cds init --add` handles comma list correctly.
66
126
  - `cds import` fix for multi-line documentation text in OData V4 EDMX file.
127
+ - `cds import` fix preserves the missing entities in csn for OData V2 EDMX file.
67
128
 
68
129
 
69
130
  ## 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'],
4
+ options: ['--out', '--into', '--as', '--include-namespaces'],
5
5
  flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
6
- shortcuts: ['-o', '-in', '-as', '-', '-f'],
6
+ shortcuts: ['-o', '-in', '-as', '-ns', '-', '-f'],
7
7
  help: `
8
8
  # SYNOPSIS
9
9
 
@@ -42,7 +42,10 @@ module.exports = Object.assign(_import, {
42
42
  Converts the input edmx or xml file into one of these supported formats "cds", "csn", "json"
43
43
 
44
44
  *-f | --force*
45
- Forcefully overwrites the contents in the CDS file and updates the checksum
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
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
@@ -43,7 +43,7 @@ module.exports = Object.assign ( watch, {
43
43
  `})
44
44
 
45
45
  const DEBUG = /\bwatch\b/.test(process.env.DEBUG) && console.debug
46
- const path = require ('path')
46
+ const {delimiter,dirname,relative,resolve,sep} = require ('path')
47
47
  const fs = require ('fs')
48
48
  const t = module.exports.codes = {
49
49
  reset: '\x1b[0m', // Default
@@ -67,12 +67,15 @@ async function watch ([cwd], {
67
67
  .filter(removeArgsIn(watchOnlyOptions)) // remove all watch-only options, as `cds serve` would not accept them
68
68
  .filter(a => a !== cwd))
69
69
 
70
- const env = Object.assign (process.env, {
70
+ let env = Object.assign (process.env, {
71
71
  _args: JSON.stringify(args),
72
72
  _options: JSON.stringify(options)
73
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
+
74
78
  if (typeof ext !== 'string') ext = extDefaults
75
- env.NODE_PATH = path.resolve (__dirname,'../node_modules') // allow global sqlite to be resolved
76
79
 
77
80
  let liveReload //= ...
78
81
  let firstTime = true
@@ -83,7 +86,7 @@ async function watch ([cwd], {
83
86
  env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'])
84
87
  .catch(err => { console.log(`Error starting live reload: ${err.message}`); liveReload = null })
85
88
  } else {
86
- 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
87
90
  }
88
91
 
89
92
  const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
@@ -91,8 +94,8 @@ async function watch ([cwd], {
91
94
  const ocwd = process.env._original_cwd = process.cwd()
92
95
  if (fs.existsSync(cwd)) log (`cd ${cwd}`)
93
96
  else try {
94
- const resolved = path.dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
95
- 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)}`)
96
99
  } catch(_){ throw new Error(`No such folder or package: '${ocwd}' -> '${cwd}'`) }
97
100
  }
98
101
  log ()
@@ -100,8 +103,12 @@ async function watch ([cwd], {
100
103
  log (`watching: ${ext}...`)
101
104
  if (liveReload) log (`live reload enabled for browsers`)
102
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
+
103
110
  const nodemon = require('../lib/watch/node-watch')
104
- return nodemon ({
111
+ const watcher = nodemon ({
105
112
  watch: ['*','.env','.cdsrc.json'],
106
113
  script:__filename, cwd, env, ext, ignore,
107
114
  delay: parseInt(process.env.CDS_WATCH_DELAY) || 200,
@@ -111,12 +118,18 @@ async function watch ([cwd], {
111
118
  if (liveReload) liveReload.markPending(files)
112
119
  if (this.delayed) clearTimeout (this.delayed)
113
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
+ })
114
128
  log (`${t.bold} _______________________\n`)
115
129
  FileHandlers.changed (files,cwd)
116
130
  }, 111)
117
131
  }).on('quit', ()=>{
118
132
  log (`${t.bold+t.green}\n[cds] - my watch has ended.\n`)
119
- process.exit()
120
133
  }).on('message', async (msg) => { switch (msg.code) { // messages from child process
121
134
  case 'listening':
122
135
  env.PORT = (msg.address && msg.address.port) || msg.port
@@ -127,6 +140,8 @@ async function watch ([cwd], {
127
140
  case 'EADDRINUSE': env.PORT = 0; _addr_in_use(msg); break
128
141
  default: console.error (msg)
129
142
  }})
143
+
144
+ return watcher
130
145
  }
131
146
 
132
147
 
@@ -160,6 +175,12 @@ function removeArgsIn (toRemove) {
160
175
  }
161
176
  }
162
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
+ }
183
+
163
184
  //
164
185
  // Child process impl launched by watch
165
186
  //
@@ -168,15 +189,24 @@ if (!module.parent) (async()=>{ // launched by nodemon
168
189
  // run through cli in order to use its error handling
169
190
  const bootstrap = require('./cds')(...JSON.parse(process.env._args))
170
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
+
171
202
  try {
172
- cds.once('listening', ({server}) => process.send ({ code:'listening', port: server.address().port }))
173
203
  await bootstrap
174
204
  if (!cds.service.providers.length) return _no_services_found()
175
205
  } catch (e) { switch (e.code) {
176
206
  case 'NO_SERVICES_FOUND': return _no_services_found(e)
177
207
  case 'MODEL_NOT_FOUND': return _no_models_found(e)
178
208
  case 'EADDRINUSE': return process.send(e) //> tell cds watch
179
- default: console.error(e)
209
+ default: { throw e } // cli error handling will catch and format
180
210
  }}
181
211
  function _no_models_found(e) { console.log (`
182
212
  No models found in ${cds.resolve(e.model||'*',false)}.