@sap/cds-dk 4.6.0 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/bin/add.js +2 -1
  3. package/bin/bind.js +121 -48
  4. package/bin/cds.js +8 -0
  5. package/bin/compile/index.js +2 -1
  6. package/bin/env.js +0 -2
  7. package/bin/repl.js +11 -4
  8. package/bin/watch.js +25 -13
  9. package/lib/bind/bindingResolver.js +26 -14
  10. package/lib/bind/bindingUtil.js +144 -0
  11. package/lib/bind/cfKeyProvider.js +2 -80
  12. package/lib/bind/kubernetesCredentialsProvider.js +157 -0
  13. package/lib/bind/userCdsrcJsonStorageProvider.js +7 -7
  14. package/lib/client/activate.js +9 -17
  15. package/lib/client/auth_manager.js +1 -1
  16. package/lib/client/extend.js +4 -9
  17. package/lib/client/settings_manager.js +5 -4
  18. package/lib/import/index.js +3 -2
  19. package/lib/import/internalDoc/edm2cdsTypeMapping.md +38 -0
  20. package/lib/import/utils/MultiMap.js +1 -0
  21. package/lib/import/v2parser.js +102 -37
  22. package/lib/import/v4parser.js +59 -20
  23. package/lib/index.js +6 -5
  24. package/lib/init/constants.js +2 -2
  25. package/lib/init/index.js +1 -0
  26. package/lib/init/template/data/index.js +29 -7
  27. package/lib/init/template/hana/files/common/undeploy.json +5 -0
  28. package/lib/init/template/hana/files/java/package.json +3 -0
  29. package/lib/init/template/hana/index.js +19 -23
  30. package/lib/init/template/lint/index.js +28 -18
  31. package/lib/init/template/mta/files/mta.yaml.hbs +12 -5
  32. package/lib/init/template/mta/projectReader.js +1 -3
  33. package/lib/init/template/nodejs/files/.gitignore.hbs +0 -2
  34. package/lib/init/template/samples/index.js +1 -1
  35. package/lib/init/template/templateBase.js +10 -41
  36. package/lib/init/util/fsUtil.js +1 -1
  37. package/lib/lint/Linter.js +13 -33
  38. package/lib/lint/checks.js +1 -5
  39. package/lib/lint/generators.js +5 -4
  40. package/lib/lint/io.js +23 -31
  41. package/lib/watch/node-watch.js +46 -20
  42. package/npm-shrinkwrap.json +11524 -5622
  43. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -6,6 +6,76 @@ 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.7.1 - 2021-12-06
10
+
11
+ ### Fixed
12
+
13
+ - `cds activate` authentication no longer fails
14
+ - `cds.compile.to.openapi` does no longer crash if `cds.compile` was already initialized
15
+
16
+ ## Version 4.7.0 - 2021-12-06
17
+
18
+ ### Added
19
+
20
+ - `cds add data --for` can be passed a regular expression, allowing more flexible name filters. For example `--for '^Supplier$'` would only match entity `Supplier`, but not `SupplierAddress`. Note that as before, `--for Supplier` is interpreted as `--for '^Supplier.*'`, i.e. matches both entities.
21
+ - `cds init --add hana` and `cds add hana` now add a `undeploy.json` file containing wildcards. This ensures that SAP HANA artefacts are automatically cleaned-up in case views, unique keys or constraint definitions of associations have been changed in CDS model.
22
+
23
+ ### Changed
24
+
25
+ - `cds import` now adds annotations for missing ON conditions in associations instead of appending it in `doc`.
26
+ - `cds import` has updated mapping for OData V2 and V4
27
+ - `cds add lint` now configures `csv` files in `.vscode/settings.json`
28
+ - `cds add hana` for Java projects now adds an `engines.node` version of `^16` to the generated `db/package.json`, to pin the Node.js version. This will help in the future when runtime environments change their default to some version higher than the one supported by `@sap/hdi-deploy`.
29
+ - `cds add mta` for Node.js projects now adds `npm ci` commands instead of `npm install`. This makes use of `package-lock.json` to enforce reproducible builds.
30
+ - `cds watch` now ignores folders named `target`, to avoid restarts when Maven's build output changes
31
+
32
+ ### Fixed
33
+
34
+ - `cds import` throws warning messages for unsupported data types.
35
+ - `cds watch` allows the explicit `--with-mocks` option, although this is anyways included by default
36
+ - `cds watch` no longer runs into multiple restarts if many files are changed at once, like in git branch changes
37
+
38
+ ## Version 4.6.4 - 2021-11-24
39
+
40
+ ### Fixed
41
+
42
+ - `cds import` fix for TypeError issue during OData V2 EDMX conversion to CSN.
43
+ - `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
44
+
45
+ ### Changed
46
+
47
+ - `cds init` uses latest Maven Java archetype version `1.20.3` for creating Java projects.
48
+
49
+ ## Version 4.6.3 - 2021-11-16
50
+
51
+ ### Fixed
52
+
53
+ - `cds watch` recovers again from compilation errors and properly prints these
54
+ - `cds import` fix for omitting empty `doc` components.
55
+ - `cds add lint` no longer duplicates initial contents in `package.json`
56
+ - `cds add data` no longer ignores imported services for which credentials are stored in the project
57
+
58
+ ## Version 4.6.2 - 2021-11-09
59
+
60
+ ### Fixed
61
+
62
+ - `cds add lint` now correctly detects missing local `eslint`
63
+ - `cds add lint` no longer removes parts of `package.json`
64
+ - `cds add lint` no longer creates duplicate configuration entries
65
+
66
+ ## Version 4.6.1 - 2021-11-08
67
+
68
+ ### Changed
69
+
70
+ - `cds init` uses latest `Maven Java archetype` version `1.20.0` for creating Java projects.
71
+ - `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.
72
+
73
+ ### Fixed
74
+
75
+ - `cds repl` now finds modules like `hdb` that are installed locally in the app folder
76
+ - `cds repl` now enables the `await` statement also on Windows
77
+ - `cds lint` now limits all file extensions to those allowed by the @sap/eslint-plugin-cds ESLint configuration
78
+
9
79
  ## Version 4.6.0 - 2021-11-02
10
80
  ### Added
11
81
 
package/bin/add.js CHANGED
@@ -36,7 +36,8 @@ module.exports = Object.assign(add, {
36
36
 
37
37
  *-4* | *--for* <entity name or prefix>
38
38
 
39
- Filters the CSN model with the given name or prefix
39
+ Filters entity names with the given string. If it contains meta characters like ^ or *
40
+ it is treated like a regular expression, otherwise as a prefix.
40
41
 
41
42
  *-o* | *--out* <target folder>
42
43
 
package/bin/bind.js CHANGED
@@ -1,24 +1,27 @@
1
-
2
-
3
1
  module.exports = Object.assign(cds_bind, {
4
- options: ['--to', '--profile', '--kind', '--output-file'],
5
- flags: [],
6
- shortcuts: ['-2', '-p', '-k', '-o'],
2
+ options: ['--to', '--on', '--profile', '--kind', '--output-file'],
3
+ flags: ['--exec'],
4
+ shortcuts: ['-2', '-n', '-p', '-k', '-o'],
7
5
  help: `
8
6
  # SYNOPSIS
9
7
 
10
- *cds bind* <service> [<options>] -- beta feature
8
+ *cds bind* <service> [<options>]
9
+ *cds bind --exec* [--profile <profile>] [--] <command> <args ...>
11
10
 
12
11
  Binds the given service to a service instance by storing connection information in *.cdsrc-private.json*
13
12
  in your project directory. Credentials are not stored in the file but rather retrieved dynamically during *cds watch*.
14
13
  Use option *--output-file* to specify a different path or file.
15
14
 
16
- Currently *cds bind* only supports Node.js projects.
15
+ With *--exec* you can execute arbitrary commands with your service bindings. The service bindings are provided
16
+ in the *VCAP_SERVICES* env variable to the command.
17
17
 
18
18
  # OPTIONS
19
19
 
20
- *-2 | --to* <instance>[:<key>]
21
- bind to a given instance
20
+ *-2 | --to* <instance>[:<key>] | <service-binding> | <secret>
21
+ bind to a given Cloud Foundry instance, Kubernetes service binding or Kubernetes secret.
22
+
23
+ *-n | --on* cf | k8s
24
+ bind to service on Cloud Foundry or Kubernetes, defaults to Cloud Foundry
22
25
 
23
26
  *-p | --profile* <profile>
24
27
  add the given profile, defaults to *hybrid*
@@ -26,7 +29,7 @@ module.exports = Object.assign(cds_bind, {
26
29
  *-k | --kind* <kind>
27
30
  the kind of service
28
31
 
29
- *-o | --output-file <path>*
32
+ *-o | --output-file* <path>
30
33
  save bindings to the given *.cdsrc.json* or *package.json* file. Default is *.cdsrc-private.json*.
31
34
  If *path* is a directory, then it will save it to the *.cdsrc.json* file in that directory.
32
35
 
@@ -36,13 +39,12 @@ module.exports = Object.assign(cds_bind, {
36
39
  cds bind --to my-hdi-container,my-xsuaa
37
40
  cds bind uaa --to my-xsuaa:my-xsuaa-key --kind xsuaa --profile myprofile
38
41
 
39
- cds bind --to my-hdi-container --output-file ~
42
+ cds bind --to my-hdi-container --output-file .
40
43
  cds bind --to my-hdi-container --output-file package.json
41
44
 
42
- `});
43
-
44
- const { constants, promises: fsp } = require('fs');
45
- const path = require('path');
45
+ cds bind --to my-hdi-container --on k8s
46
+ `
47
+ });
46
48
 
47
49
  const cds = require('@sap/cds/lib');
48
50
  const LOG = cds.log('bind');
@@ -50,19 +52,25 @@ const LOG = cds.log('bind');
50
52
  LOG.isDebug = LOG._debug;
51
53
  LOG.isTTY = process.stdout.isTTY;
52
54
 
53
- const { bold, info } = require('../lib/util/term');
55
+ const { highlight, bold, info } = require('../lib/util/term');
54
56
 
55
57
  const Spinner = require('../lib/init/util/spinner');
58
+ const resolveBinding = require('../lib/bind/bindingResolver').resolver(LOG);
59
+ const { determineKinds, getRequiresNameFromKind, execAndExit, checkIsJavaProject, throwError, platformText, bindingText } = require('../lib/bind/bindingUtil');
56
60
 
61
+ const VCAP_LABEL_PLACEHOLDER = '<replace with service label>';
57
62
 
58
- async function cds_bind(args, options) { // eslint-disable-line
59
-
60
- options.profile = options.profile || 'hybrid';
63
+ async function cds_bind(args, options) {
64
+ options.profile = parseProfile(options.profile);
65
+ setProfile(options.profile);
61
66
 
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.`);
67
+ if (options.exec) {
68
+ cds_bind_exec(args, options)
69
+ return;
64
70
  }
65
71
 
72
+ const env = cds.env.for('cds', process.cwd());
73
+
66
74
  if (!options.to) {
67
75
  throw new Error(`use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`);
68
76
  }
@@ -71,6 +79,8 @@ async function cds_bind(args, options) { // eslint-disable-line
71
79
  throw new Error(`Too many arguments: Please specify only one or no service.`);
72
80
  }
73
81
 
82
+ const isJavaProject = await checkIsJavaProject(process.cwd());
83
+
74
84
  let serviceArg = args[0]
75
85
  const targets = options.to.split(/,/g);
76
86
 
@@ -82,31 +92,72 @@ async function cds_bind(args, options) { // eslint-disable-line
82
92
  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
93
  }
84
94
 
85
- // prepare for different providers
86
- const keyProvider = require('../lib/bind/cfKeyProvider');
87
- keyProvider.setLogger(LOG);
88
95
  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);
96
+
97
+ let on;
98
+ if (options.on === 'k8s' || options.on === 'kubernetes') {
99
+ on = 'k8s';
100
+ } else if (!options.on || options.on === 'cf' || options.on === 'cloudfoundry') {
101
+ on = 'cf';
102
+ } else {
103
+ throw new Error(`Invalid value '${options.on}' for option --on.`);
104
+ }
105
+
106
+ const onText = { 'cf': 'Cloud Foundry', 'k8s': 'Kubernetes' }[on];
107
+ const resolvedServices = await spinner.run(`Retrieving data from ${onText}...`, async () => { // NOSONAR
108
+ return await Promise.all( targets.map( async target => {
109
+
110
+ let binding;
111
+ if (on === 'k8s') {
112
+ binding = { type: on, name: target };
113
+ } else if (on === 'cf') {
114
+ const [instance, key] = target.split(/:/);
115
+ binding = { type: on, instance, key };
116
+ } else {
117
+ throwError();
118
+ }
119
+
120
+ const resolvedService = await resolveBinding(undefined, binding);
121
+ resolvedService._target = target;
122
+ delete resolvedService.credentials;
123
+ resolvedService.binding.resolved = false;
124
+ return resolvedService;
99
125
  }));
100
- return resolvedServices;
101
126
  });
102
127
 
103
- if (!options.kind) {
104
- keyProvider.determineKinds(resolvedServices);
128
+ if (options.kind) {
129
+ resolvedServices.forEach( service => service.kind = options.kind );
130
+ } else {
131
+ determineKinds(resolvedServices);
105
132
  }
106
133
 
107
134
  for (const resolvedService of resolvedServices) {
108
- resolvedService.kind = options.kind || resolvedService.kind;
109
- resolvedService.name = serviceArg || keyProvider.getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
135
+ if (serviceArg) {
136
+ resolvedService.name = serviceArg;
137
+ } else if (resolvedService.kind) {
138
+ resolvedService.name = getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
139
+ }
140
+
141
+ if (!resolvedService.name) {
142
+ if (isJavaProject) {
143
+ const { binding } = resolvedService;
144
+ // Generate a custom service entry for Java projects w/o the need to specify a name manually
145
+ resolvedService.name = `custom-service:${binding.instance || binding.secret}`
146
+ } else {
147
+ throw new Error(`Unknown CDS service name for service "${resolvedService._target}". Please specify as argument for "cds bind".`)
148
+ }
149
+ }
150
+
151
+ const { kind, name, binding } = resolvedService;
152
+ const kindText = kind && `with kind ${highlight(kind)}`;
153
+ const nameText = !isJavaProject && highlight(name);
154
+
155
+ LOG.log(['Binding', nameText, 'to', platformText(binding), bindingText(binding, highlight), kindText].filter( t => t ).join(' '));
156
+
157
+ if (isJavaProject && !hasVcapLabel(binding) && !hasVcapLabel(env.requires[resolvedService.name]) && !(kind && hasVcapLabel(env.requires[kind]))) {
158
+ resolvedService.vcap = { label: VCAP_LABEL_PLACEHOLDER };
159
+ LOG.log(`${info('INFO:')} Please add a "vcap.label" property to the binding for consumption in CAP Java projects`);
160
+ }
110
161
  }
111
162
 
112
163
  // prepare for different providers
@@ -118,16 +169,38 @@ async function cds_bind(args, options) { // eslint-disable-line
118
169
  });
119
170
 
120
171
  LOG.log();
121
- LOG.log(`${info("TIP:")} Run with cloud bindings: ${bold(`cds watch --profile ${options.profile}`)}`);
122
- // LOG.log();
172
+ LOG.log(`${info('TIP:')} Run with cloud bindings: ${bold(runCmd(options, isJavaProject))}`);
173
+ }
174
+
175
+ async function cds_bind_exec(command) { // eslint-disable-lint
176
+ const cds = require('@sap/cds/lib');
177
+ const env = cds.env.for('cds', process.cwd());
178
+ env._emulate_vcap_services();
179
+ await require('../lib/bind/bindingResolver').applyTo({env, output: 'info'});
180
+ execAndExit(...command);
181
+ }
182
+
183
+ function hasVcapLabel(service) {
184
+ return service && service.vcap && service.vcap.label && service.vcap.label != VCAP_LABEL_PLACEHOLDER;
123
185
  }
124
186
 
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;
187
+ function runCmd({profile}, isJavaProject) {
188
+ if (isJavaProject) {
189
+ const p = profile === 'hybrid' ? '' : ` --profile ${profile}`;
190
+ return `cds bind exec${p} mvn spring-boot:run`;
191
+ } else {
192
+ const p = profile === 'development' ? '' : ` --profile ${profile}`;
193
+ return `cds watch${p}`;
132
194
  }
133
195
  }
196
+
197
+ function setProfile(profile) {
198
+ profile = parseProfile(profile);
199
+ if (profile) process.env.CDS_ENV = profile;
200
+ }
201
+
202
+ function parseProfile(profile) {
203
+ if (!profile) return 'hybrid';
204
+ else if (profile === '-') return 'development';
205
+ else return profile;
206
+ }
package/bin/cds.js CHANGED
@@ -39,7 +39,15 @@ const cli = { //NOSONAR
39
39
 
40
40
  const task = this.load (cmd, /*else*/ (f)=> argv.unshift(cmd) && _defaultCompile (argv,f))
41
41
  if (task) {
42
+ // REVISIT: '--' handling for cds bind --exec. Should be moved to cds_cli
43
+ const endOfOptions = argv.indexOf('--')
44
+ let appendArgs = []
45
+ if (endOfOptions>=0) {
46
+ appendArgs = argv.slice(endOfOptions+1)
47
+ argv = argv.slice(0, endOfOptions)
48
+ }
42
49
  const args = cds_cli.args(task,argv)
50
+ args[0].push(...appendArgs)
43
51
  return task.apply (this, args)
44
52
  }
45
53
  },
@@ -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
@@ -50,8 +50,6 @@ async function env ([cmd, key, value], options={}) {
50
50
  throw new Error (`Don't understand '${cmd}' here...`)
51
51
  })
52
52
 
53
- if (options.profile) process.env.CDS_ENV = options.profile
54
-
55
53
  const env = options.for ? cds.env.for (options.for) : cds.env
56
54
  env['_home_cds-dk'] = path.resolve(__dirname, '..') // to help tools find the DK
57
55
 
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')
@@ -27,7 +31,8 @@ function repl (_, options={}) {
27
31
  const info = colors ? require('../lib/util/term').info : s => s
28
32
  log(info('Welcome to cds repl v'+ cds.version))
29
33
 
30
- const cdsr = cds.repl = require('repl').start ({...options, writer:_writer, ignoreUndefined:true })
34
+ const cdsr = require('repl').start ({...options, writer:_writer, ignoreUndefined:true })
35
+ Object.defineProperty (cds,'repl',{value:cdsr})
31
36
 
32
37
  cds.lazify (Object.assign (cdsr.context,{ cds,
33
38
  Foo: lazy => new cds.entity ({name:'Foo'}),
@@ -35,7 +40,9 @@ function repl (_, options={}) {
35
40
  }))
36
41
 
37
42
  // cosmetics: don't show cds.env when inspecting cds
38
- cds.env && Reflect.defineProperty (cds,'env',{ value:cds.env, enumerable:false })
43
+ if (Reflect.getOwnPropertyDescriptor(cds,'env')) {
44
+ Reflect.defineProperty (cds,'env',{ value:cds.env, enumerable:false })
45
+ }
39
46
 
40
47
  inspect.defaultOptions.depth = 11
41
48
  function _writer (o) {
package/bin/watch.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const watchOnlyOptions = ['--ext', '--livereload', '--open']
2
2
  module.exports = Object.assign ( watch, {
3
- flags: [ '--debug', '--in-memory', '--in-memory?' ],
3
+ flags: [ '--debug', '--in-memory', '--in-memory?', '--with-mocks' ],
4
4
  options: [ '--port', ...watchOnlyOptions ],
5
5
  help: `
6
6
  # SYNOPSIS
@@ -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
@@ -55,24 +55,30 @@ const t = module.exports.codes = {
55
55
  orange: '\x1b[38;2;255;140;0m' // darker orange, works with bright and dark background
56
56
  }
57
57
  const extDefaults = 'cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml,env'
58
- let ignore = /node_modules|\.cds-services\.json$/
58
+ let ignore = /node_modules|target|\.cds-services\.json$/
59
59
 
60
60
  async function watch ([cwd], {
61
- args = ['serve', 'all', '--with-mocks'],
61
+ args = ['serve', 'all'],
62
62
  ext = extDefaults,
63
63
  ...options
64
64
  }={}) {
65
- args.push (`--${Object.keys(options).find(a => /^in-memory\??$/.test(a)) || 'in-memory?'}`)
66
- args.push (...process.argv.slice(3)
65
+ if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
66
+ if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
67
+ args
68
+ .push (...(process.argv.slice(3).filter(a => !args.includes(a))) // add deduped command line args
67
69
  .filter(removeArgsIn(watchOnlyOptions)) // remove all watch-only options, as `cds serve` would not accept them
70
+ .filter(a => a !== '--watch') // from `cds serve --watch`
68
71
  .filter(a => a !== cwd))
69
72
 
70
73
  let env = Object.assign (process.env, {
71
74
  _args: JSON.stringify(args),
72
75
  _options: JSON.stringify(options)
73
76
  })
77
+
78
+ // allow running w/o or partial local node_modules, also add cds-dk for its sqlite
79
+ env.NODE_PATH = [...paths(cwd||process.cwd()), ...paths(__dirname)].join(delimiter)
80
+
74
81
  if (typeof ext !== 'string') ext = extDefaults
75
- env.NODE_PATH = path.resolve (__dirname,'../node_modules') // allow global sqlite to be resolved
76
82
 
77
83
  let liveReload //= ...
78
84
  let firstTime = true
@@ -83,7 +89,7 @@ async function watch ([cwd], {
83
89
  env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'])
84
90
  .catch(err => { console.log(`Error starting live reload: ${err.message}`); liveReload = null })
85
91
  } else {
86
- ignore = RegExp(`app${path.sep}.*\\.(js|ts)|`+ignore.source) // exclude more frontend stuff
92
+ ignore = RegExp(`app${sep}.*\\.(js|ts)|`+ignore.source) // exclude more frontend stuff
87
93
  }
88
94
 
89
95
  const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
@@ -91,8 +97,8 @@ async function watch ([cwd], {
91
97
  const ocwd = process.env._original_cwd = process.cwd()
92
98
  if (fs.existsSync(cwd)) log (`cd ${cwd}`)
93
99
  else try {
94
- const resolved = path.dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
95
- log (`cd ${path.relative(ocwd,cwd = resolved)}`)
100
+ const resolved = dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
101
+ log (`cd ${relative(ocwd,cwd = resolved)}`)
96
102
  } catch(_){ throw new Error(`No such folder or package: '${ocwd}' -> '${cwd}'`) }
97
103
  }
98
104
  log ()
@@ -102,7 +108,7 @@ async function watch ([cwd], {
102
108
 
103
109
  const bindingManager = new require('../lib/bind/bindingResolver').bindingManager({cwd})
104
110
  const bindings = await bindingManager.resolve()
105
- if (bindings) env = {...env, CDS_CONFIG: JSON.stringify(bindings)}
111
+ if (bindings) env = {...env, 'CDS_REQUIRES': JSON.stringify(bindings.requires)}
106
112
 
107
113
  const nodemon = require('../lib/watch/node-watch')
108
114
  const watcher = nodemon ({
@@ -118,7 +124,7 @@ async function watch ([cwd], {
118
124
  bindingManager.update({
119
125
  onBeforeUpdate: () => watcher.stop(),
120
126
  onAfterUpdate: bindings => {
121
- watcher.setEnv('CDS_CONFIG', bindings && JSON.stringify(bindings))
127
+ watcher.setEnv('CDS_REQUIRES', bindings && JSON.stringify(bindings.requires))
122
128
  watcher.restart()
123
129
  }
124
130
  })
@@ -172,6 +178,12 @@ function removeArgsIn (toRemove) {
172
178
  }
173
179
  }
174
180
 
181
+ // builds paths from dir + its parent node_modules
182
+ function paths (dir) {
183
+ const parts = resolve(dir).split(sep), n = parts.length, nm = sep+'node_modules'
184
+ return parts.map ((_,i,a)=> a.slice(0,n-i).join(sep)+nm).filter(p => fs.existsSync(p))
185
+ }
186
+
175
187
  //
176
188
  // Child process impl launched by watch
177
189
  //
@@ -197,7 +209,7 @@ if (!module.parent) (async()=>{ // launched by nodemon
197
209
  case 'NO_SERVICES_FOUND': return _no_services_found(e)
198
210
  case 'MODEL_NOT_FOUND': return _no_models_found(e)
199
211
  case 'EADDRINUSE': return process.send(e) //> tell cds watch
200
- default: console.error(e)
212
+ default: { throw e } // cli error handling will catch and format
201
213
  }}
202
214
  function _no_models_found(e) { console.log (`
203
215
  No models found in ${cds.resolve(e.model||'*',false)}.
@@ -1,6 +1,8 @@
1
1
  const Config = require('@sap/cds/lib/env')
2
2
  const cds = require('@sap/cds/lib');
3
3
  const LOG = cds.log('bind');
4
+ const resolveBinding = resolver(LOG);
5
+ const { platformText, bindingText } = require('./bindingUtil');
4
6
 
5
7
  function getUnresolvedBindings({cwd = process.cwd(), env}) {
6
8
  if (!env) env = Config.for('cds', cwd)
@@ -28,7 +30,7 @@ async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'in
28
30
  const cacheKey = JSON.stringify(binding)
29
31
  if (!cache[cacheKey]) {
30
32
  cache[cacheKey] = config.requires[name] = await resolveBinding(name, binding)
31
- LOG[output](`bound ${name} to Cloud Foundry service instance ${binding.instance}`)
33
+ LOG[output](`bound ${name} to ${platformText(binding)} ${bindingText(binding)}`)
32
34
  } else {
33
35
  config.requires[name] = cache[cacheKey]
34
36
  }
@@ -43,17 +45,26 @@ async function resolveBindings({bindings, cwd = process.cwd(), env, output = 'in
43
45
  }
44
46
 
45
47
  let cfKeyProvider
46
-
47
- async function resolveBinding(name, binding) {
48
- switch (binding.type) {
49
- case 'cf':
50
- if (!cfKeyProvider) {
51
- cfKeyProvider = require('./cfKeyProvider');
52
- cfKeyProvider.setLogger(LOG);
53
- }
54
- return cfKeyProvider.resolve(name, binding);
55
- default:
56
- throw new Error(`Unsupported binding type "${binding.type}" for service "${name}".`)
48
+ let kubernetesCredentialsProvider
49
+
50
+ function resolver(log) {
51
+ return function resolveBinding(name, binding) {
52
+ switch (binding.type) {
53
+ case 'cf':
54
+ if (!cfKeyProvider) {
55
+ cfKeyProvider = require('./cfKeyProvider');
56
+ cfKeyProvider.setLogger(log);
57
+ }
58
+ return cfKeyProvider.resolve(name, binding)
59
+ case 'k8s':
60
+ if (!kubernetesCredentialsProvider) {
61
+ kubernetesCredentialsProvider = require('./kubernetesCredentialsProvider')
62
+ kubernetesCredentialsProvider.setLogger(log)
63
+ }
64
+ return kubernetesCredentialsProvider.resolve(name, binding)
65
+ default:
66
+ throw new Error(`Unsupported binding type "${binding.type}" for service "${name}".`)
67
+ }
57
68
  }
58
69
  }
59
70
 
@@ -67,8 +78,8 @@ async function applyTo(options) {
67
78
  Object.assign(env.requires[name], { binding, credentials })
68
79
  }
69
80
 
70
- if (Config._emulate_vcap_services) {
71
- Config._emulate_vcap_services()
81
+ if (env._emulate_vcap_services) {
82
+ env._emulate_vcap_services()
72
83
  }
73
84
  }
74
85
 
@@ -131,6 +142,7 @@ function feature(env) {
131
142
  }
132
143
 
133
144
  module.exports = {
145
+ resolver,
134
146
  applyTo,
135
147
  bindingManager: function(options) {
136
148
  return new BindingManager(options)