@sap/cds-dk 8.4.2 → 8.5.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.
- package/CHANGELOG.md +41 -3
- package/bin/cds.js +49 -28
- package/bin/debug.js +90 -0
- package/bin/repl.js +50 -4
- package/bin/watch.js +13 -3
- package/lib/bind/userCdsrcJsonStorageProvider.js +0 -19
- package/lib/build/buildTaskEngine.js +4 -4
- package/lib/build/provider/hana/2migration.js +34 -20
- package/lib/build/provider/hana/index.js +74 -41
- package/lib/build/provider/internalBuildTaskProvider.js +1 -1
- package/lib/build/provider/mtx-extension/index.js +4 -4
- package/lib/build/provider/mtx-sidecar/index.js +2 -2
- package/lib/build/provider/nodejs/index.js +3 -19
- package/lib/build/util.js +41 -17
- package/lib/cds.js +10 -2
- package/lib/deploy/to-hana/hdiDeployUtil.js +14 -10
- package/lib/import/message.js +2 -1
- package/lib/import/odata/metadataconverter/MetadataConverter.js +5 -0
- package/lib/import/odata/v4parser.js +32 -43
- package/lib/init/add.js +26 -0
- package/lib/init/constants.js +5 -6
- package/lib/init/index.js +17 -25
- package/lib/init/merge.js +2 -5
- package/lib/init/projectReader.js +76 -9
- package/lib/init/registries/mta.js +0 -1
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/cloud-logging/files/package.json.hbs +1 -12
- package/lib/init/template/cloud-logging/files/values.yaml.hbs +16 -0
- package/lib/init/template/cloud-logging/index.js +14 -10
- package/lib/init/template/data/as-json.js +1 -1
- package/lib/init/template/data/index.js +1 -26
- package/lib/init/template/esm/files/package.json.hbs +3 -0
- package/lib/init/template/esm/index.js +16 -0
- package/lib/init/template/handler/files/handler.xs.hbs +56 -0
- package/lib/init/template/handler/index.js +84 -9
- package/lib/init/template/helm/index.js +4 -1
- package/lib/init/template/html5-repo/files/xs-app.json.hbs +1 -0
- package/lib/init/template/html5-repo/index.js +2 -0
- package/lib/init/template/http/index.js +102 -35
- package/lib/init/template/nodejs/files/package.json.hbs +1 -1
- package/lib/init/template/sample/files/nodejs/{admin-service.js → admin-service.js.hbs} +8 -1
- package/lib/init/template/sample/files/nodejs/{cat-service.js → cat-service.js.hbs} +8 -3
- package/lib/init/template/sample/index.js +4 -2
- package/lib/init/template/telemetry/files/package.json.hbs +18 -0
- package/lib/init/template/telemetry/index.js +8 -2
- package/lib/init/template/typescript/files/package.json +2 -2
- package/lib/init/template/workzone-standard/index.js +1 -1
- package/lib/init/types.d.ts +0 -6
- package/lib/watch/node-watch.js +25 -9
- package/npm-shrinkwrap.json +63 -48
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,48 @@
|
|
|
3
3
|
|
|
4
4
|
All notable changes to this project will be documented in this file.
|
|
5
5
|
|
|
6
|
-
This project adheres to [Semantic Versioning](
|
|
6
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
-
The format is based on [Keep a Changelog](
|
|
8
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
9
|
+
|
|
10
|
+
## Version 8.5.1 - 2024-12-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `cds watch` now detects a TypeScript project and tries to run with `tsx` if this is installed locally or globally. Otherwise a warning message is emitted. `CDS_TYPESCRIPT=false` can be used to opt out of this behavior.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `cds add html5-repo` adds an `index.html` as `welcomePage` to the `xs-app.json`, if available.
|
|
19
|
+
- `cds build` now logs existing plugin build messages if a BuildError is thrown.
|
|
20
|
+
|
|
21
|
+
## Version 8.5.0 - 2024-11-26
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `cds import` now generates generic action/function in case of bound action/function collision and unbound function collision with @cds.validate = false.
|
|
26
|
+
- `cds add typescript` creates projects with `cds-tsx` to run apps instead of former `cds-ts`.
|
|
27
|
+
- `cds deploy` writes error output to `stderr`.
|
|
28
|
+
- `cds add http` now evaluates mocks users when creating `Authorization` headers
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `cds debug` lets you debug Node.js applications running locally or in Cloud Foundry.
|
|
33
|
+
- `cds watch --inspect` and `cds watch --inspect-brk` activate the debugger in the same way as the standard Node.js CLI options, i.e. they accept a `host:port` combination and `0` as values.
|
|
34
|
+
- The existing `cds watch --debug` is now an alias to `cds watch --inspect`.
|
|
35
|
+
- `cds add cloud-logging` adds cloud-logging as an alternative for application logging in Kyma.
|
|
36
|
+
- `cds add cloud-logging --with-telemetry` or `cds add telemetry` adds cloud-logging as well as support for `cap-js/telemetry` plugin in Kyma.
|
|
37
|
+
- `cds add handler` now also works for Node.js projects. It creates an implementation file for each service with event handlers for all entites and actions.
|
|
38
|
+
- `cds add esm` creates ESM-compatible Node.js projects. Sample code added by `add sample` and `add handler` will respect this setting if added aftwards. Existing code will not be adjusted by `cds add esm`, though.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- `cds import` throws proper error message if Annotation element doesn't have Term attribute.
|
|
43
|
+
- `cds add approuter` no longer enforces Node.js 22, which is not supporter by current `@sap/approuter` version 17. The previous Node.js version 20 is used again.
|
|
44
|
+
- `cds add -p` with custom options now works if the entry was not in the `package.json` beforehand.
|
|
45
|
+
- `cds add http` works for actions without parameters.
|
|
46
|
+
- `cds add workzone` includes the `updateManifestJson` task on initial generation.
|
|
47
|
+
- `cds build --ws` no longer creates migration tables in shared database or wrong workspace.
|
|
9
48
|
|
|
10
49
|
## Version 8.4.2 - 2024-11-19
|
|
11
50
|
|
|
@@ -23,7 +62,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
23
62
|
- `cds add hana` adds the `requires.db` entry to `.cdsrc.json` for Java projects.
|
|
24
63
|
- `cds add data` creates decimal values with correct scale `0` if only precision is given, like in `Decimal(15)`.
|
|
25
64
|
|
|
26
|
-
|
|
27
65
|
## Version 8.4.1 - 2024-11-08
|
|
28
66
|
|
|
29
67
|
### Added
|
package/bin/cds.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const path = require('node:path')
|
|
4
|
+
const { sep } = { path }
|
|
5
|
+
const fs = require('node:fs')
|
|
3
6
|
|
|
4
7
|
const cli = {
|
|
5
8
|
|
|
@@ -25,9 +28,6 @@ const cli = {
|
|
|
25
28
|
},
|
|
26
29
|
|
|
27
30
|
async exec (cmd = process.argv[2], ...argv) {
|
|
28
|
-
const path = require('node:path')
|
|
29
|
-
const fs = require('node:fs')
|
|
30
|
-
|
|
31
31
|
if (!argv.length) argv = process.argv.slice(3)
|
|
32
32
|
if (!cmd) cmd = process.stdin.isTTY ? 'help' : 'compile'
|
|
33
33
|
if (cmd in this.Shortcuts) cmd = process.argv[2] = this.Shortcuts[cmd]
|
|
@@ -66,7 +66,10 @@ const cli = {
|
|
|
66
66
|
const p = argv[argv.indexOf('-p') + 1] || argv[argv.indexOf('--package') + 1]
|
|
67
67
|
console.log(`running with --package, adding dependency ${p}`)
|
|
68
68
|
await require('../lib/util/command').spawnCommand(`npm`, ['add', p], { cwd: cds.root })
|
|
69
|
+
delete require.cache[require.resolve(path.join(cds.root,'./package.json'))]
|
|
69
70
|
}
|
|
71
|
+
cds.env.plugins = cds.env.for('cds').plugins // reload plugin env
|
|
72
|
+
cds.add = require('../lib/init/add')
|
|
70
73
|
await cds.plugins
|
|
71
74
|
for (const [, Plugin] of cds.add.registered) {
|
|
72
75
|
const all = Object.entries((new Plugin).options())
|
|
@@ -86,13 +89,8 @@ const cli = {
|
|
|
86
89
|
|
|
87
90
|
if (args[1]?.['resolve-bindings']) await _resolveBindings({ silent: cmd === 'env' })
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (!process.env.CDS_TYPESCRIPT && cds.cli?.command === 'serve' && fs.existsSync(tsconfig)) {
|
|
92
|
-
// cds.root may for some subcommands not point to the correct directory (e.g. cds w where users can pass a project directory)
|
|
93
|
-
// showing the full detected path should give the user an idea whether this info is relevant for them
|
|
94
|
-
console.error(`Detected ${tsconfig}. If this is a TypeScript project, consider running commands using 'cds-ts' instead of 'cds'.\n`)
|
|
95
|
-
}
|
|
92
|
+
_prepareTsIfNeeded(cds)
|
|
93
|
+
|
|
96
94
|
return await task.apply(this, args)
|
|
97
95
|
}
|
|
98
96
|
} catch (e) {
|
|
@@ -231,24 +229,6 @@ function _dieForUnknownCommand(cmd) {
|
|
|
231
229
|
throw msg;
|
|
232
230
|
}
|
|
233
231
|
|
|
234
|
-
function _require (id,o) {
|
|
235
|
-
try { var resolved = require.resolve(id,o) } catch(e){ DEBUG?.(`[cds] - Command not found: ${id}`); return }
|
|
236
|
-
DEBUG?.(`[cds] - Command resolved: ${resolved}`)
|
|
237
|
-
return require (resolved)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const { sep } = require ('path')
|
|
241
|
-
if (process[Symbol.for('ts-node.register.instance')])
|
|
242
|
-
process.env.CDS_TYPESCRIPT = 'ts-node'
|
|
243
|
-
else if (process._preload_modules?.some(m => m?.includes(`${sep}tsx${sep}`)))
|
|
244
|
-
process.env.CDS_TYPESCRIPT = 'tsx'
|
|
245
|
-
|
|
246
|
-
module.exports = Object.assign ((..._) => cli.exec(..._), cli)
|
|
247
|
-
|
|
248
|
-
if (!module.parent) module.exports()
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
232
|
function getCommands() {
|
|
253
233
|
const fs = require('node:fs');
|
|
254
234
|
const path = require('node:path');
|
|
@@ -267,4 +247,45 @@ function getCommands() {
|
|
|
267
247
|
return Array.from(result).sort()
|
|
268
248
|
}
|
|
269
249
|
|
|
250
|
+
function _require (id,o) {
|
|
251
|
+
try { var resolved = require.resolve(id,o) } catch(e){ DEBUG?.(`[cds] - Command not found: ${id}`); return }
|
|
252
|
+
DEBUG?.(`[cds] - Command resolved: ${resolved}`)
|
|
253
|
+
return require (resolved)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = Object.assign ((..._) => cli.exec(..._), cli)
|
|
257
|
+
if (!module.parent) module.exports()
|
|
258
|
+
|
|
259
|
+
// check for an already loaded TS runner
|
|
260
|
+
if (process[Symbol.for('ts-node.register.instance')])
|
|
261
|
+
process.env.CDS_TYPESCRIPT = 'ts-node'
|
|
262
|
+
else if (process._preload_modules?.some(m => m?.includes(`${sep}tsx${sep}`)))
|
|
263
|
+
process.env.CDS_TYPESCRIPT = 'tsx'
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Load tsx if needed in a TS project. Warn users if this fails.
|
|
267
|
+
*/
|
|
268
|
+
function _prepareTsIfNeeded(cds) {
|
|
269
|
+
const tsconfig = path.join(cds.root ?? '.', 'tsconfig.json')
|
|
270
|
+
if (cds.cli?.command === 'serve' && fs.existsSync(tsconfig)) { // for `cds serve` started directly or through watch
|
|
271
|
+
if (/\b(0|no|false)\b/.test(process.env.CDS_TYPESCRIPT)) // opt-out switch to run JS code in a TS project
|
|
272
|
+
return delete process.env.CDS_TYPESCRIPT
|
|
273
|
+
|
|
274
|
+
if (!process.env.CDS_TYPESCRIPT ) {
|
|
275
|
+
try {
|
|
276
|
+
cds._localOrGlobal('tsx/cjs') // cjs is needed as we are in CJS code
|
|
277
|
+
console.log(`Detected ${path.relative(cds.root, tsconfig)}. Running with tsx.\n`)
|
|
278
|
+
process.env.CDS_TYPESCRIPT = 'tsx'
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e
|
|
282
|
+
// cds.root may for some subcommands not point to the correct directory (e.g. cds w where users can pass a project directory)
|
|
283
|
+
// showing the full detected path should give the user an idea whether this info is relevant for them
|
|
284
|
+
console.error(`Detected ${tsconfig}. If this is a TypeScript project, consider running commands using 'cds-ts' or 'cds-tsx' instead of 'cds'.\n`)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
|
|
270
291
|
/* eslint no-console:off */
|
package/bin/debug.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module.exports = Object.assign(debug, {
|
|
2
|
+
options: ['--port', '--host'],
|
|
3
|
+
shortcuts: [],
|
|
4
|
+
flags: [/*'--vscode', '--k8s'*/],
|
|
5
|
+
help: `
|
|
6
|
+
# SYNOPSIS
|
|
7
|
+
|
|
8
|
+
*cds debug* [<app>]
|
|
9
|
+
|
|
10
|
+
Debugs a CAP-based application in the Chrome Developer Tools.
|
|
11
|
+
|
|
12
|
+
If <app> is given, it's assumed to be running on the currently logged-in Cloud Foundry space.
|
|
13
|
+
|
|
14
|
+
If no <app> is given, the app in the current working is started with 'cds watch --debug'.
|
|
15
|
+
|
|
16
|
+
# OPTIONS
|
|
17
|
+
|
|
18
|
+
*--host* the debug host (default: '127.0.0.1')
|
|
19
|
+
|
|
20
|
+
*--port* the debug port (default: 9229)
|
|
21
|
+
|
|
22
|
+
# EXAMPLES
|
|
23
|
+
|
|
24
|
+
*cds debug*
|
|
25
|
+
*cds debug* bookshop-srv
|
|
26
|
+
`})
|
|
27
|
+
|
|
28
|
+
async function debug() {
|
|
29
|
+
const cds = require('../lib/cds')
|
|
30
|
+
const { fork, spawn, execSync, exec } = require('node:child_process')
|
|
31
|
+
const os = require('node:os').platform()
|
|
32
|
+
|
|
33
|
+
const [appName] = cds.cli.argv
|
|
34
|
+
const { port = 9229, host = '127.0.0.1', vscode } = cds.cli.options
|
|
35
|
+
const ide = vscode ? 'vscode' : 'chrome'
|
|
36
|
+
|
|
37
|
+
if (!appName) { // local app
|
|
38
|
+
const script = process.argv[1] // `.../cds` or `.../cds-tsx`
|
|
39
|
+
console.log(`Starting '${cds.utils.path.basename(script)} watch --debug'`)
|
|
40
|
+
fork(script, ['watch', '--debug']).on('error', e => { throw 'Failed to start debug process:' + e.message })
|
|
41
|
+
} else if (cds.cli.options.k8s) { // Kyma/Kubernetes
|
|
42
|
+
// to be implemented
|
|
43
|
+
} else { // Cloud Foundry
|
|
44
|
+
const script = `ps aux | grep '[n]ode' | awk '{print \\$2}' | head -n 1`
|
|
45
|
+
const pid = execSync(`cf ssh ${appName} -c "${script}"`, { encoding: 'utf-8', shell: true }).trim()
|
|
46
|
+
if (!pid) throw `Node.js process for app '${appName}' not found`
|
|
47
|
+
execSync(`cf ssh ${appName} -c "kill -usr1 ${pid}"`, { stdio: 'inherit' })
|
|
48
|
+
console.log(`Opening SSH tunnel for CF app '${appName}'`)
|
|
49
|
+
spawn('cf', ['ssh', '-N', '-L', `${port}:${host}:${port}`, appName], { stdio: 'inherit' })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const maxAttempts = 15
|
|
53
|
+
let attempts = 0
|
|
54
|
+
|
|
55
|
+
const openDevTools = async () => {
|
|
56
|
+
attempts++
|
|
57
|
+
try {
|
|
58
|
+
switch (ide) {
|
|
59
|
+
case 'chrome': {
|
|
60
|
+
const res = await fetch(`http://localhost:${port}/json/list`)
|
|
61
|
+
const data = await res.json()
|
|
62
|
+
if (!data?.length) throw 'No debugger found on port ' + port
|
|
63
|
+
const wsUrl = data[0].webSocketDebuggerUrl
|
|
64
|
+
let command
|
|
65
|
+
const url = `devtools://devtools/bundled/inspector.html?ws=${wsUrl.split('ws://')[1]}`
|
|
66
|
+
console.log(`Opening Chrome DevTools at ${url}`)
|
|
67
|
+
if (os === 'darwin') {
|
|
68
|
+
command = `open -a "Google Chrome" '${url}'`
|
|
69
|
+
} else if (os === 'win32') {
|
|
70
|
+
command = `start chrome "${url}"`
|
|
71
|
+
} else {
|
|
72
|
+
command = `google-chrome '${url}' || chromium-browser '${url}' || chromium '${url}'`
|
|
73
|
+
}
|
|
74
|
+
exec(command, (error) => { if (error) throw 'Failed to open DevTools: ' + error.message })
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
case 'vscode': {
|
|
78
|
+
// to be implemented
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (attempts < maxAttempts) {
|
|
83
|
+
setTimeout(openDevTools, 1000) // connection fails sometimes, retry
|
|
84
|
+
} else {
|
|
85
|
+
throw `Failed to connect to debugger after ${maxAttempts} attempts: ${err}`
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
openDevTools()
|
|
90
|
+
}
|
package/bin/repl.js
CHANGED
|
@@ -42,13 +42,59 @@ async function repl (_, options={}) {
|
|
|
42
42
|
const ctx={}
|
|
43
43
|
|
|
44
44
|
repl.defineCommand ('run', {
|
|
45
|
-
help: 'Runs a cds server
|
|
45
|
+
help: 'Runs a cds server from a given CAP project folder, or module name like @capire/bookshop.',
|
|
46
46
|
async action (project) {
|
|
47
47
|
// await cds.exec ('--project', project, '--port', '0')
|
|
48
48
|
await cds.test (project)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log ('
|
|
49
|
+
|
|
50
|
+
const GREEN = '\x1b[32m', RESET = '\x1b[0m'
|
|
51
|
+
console.log ('------------------------------------------------------------')
|
|
52
|
+
console.log ('Following are made available as repl globals:')
|
|
53
|
+
console.log()
|
|
54
|
+
let ntt = _export ('entities')
|
|
55
|
+
let srv = _export ('services')
|
|
56
|
+
console.log()
|
|
57
|
+
console.log('Simply type e.g.', GREEN+ntt+RESET, 'or', GREEN+srv+RESET, 'in the prompt to use the respective objects.')
|
|
58
|
+
console.log()
|
|
59
|
+
this.displayPrompt()
|
|
60
|
+
|
|
61
|
+
function _export (cds_property) {
|
|
62
|
+
let exports = cds[cds_property], first
|
|
63
|
+
console.log (`from cds.${cds_property}: {`)
|
|
64
|
+
for (let each in exports) {
|
|
65
|
+
if (each.includes('.')) continue
|
|
66
|
+
repl.context[each] = exports [each]
|
|
67
|
+
console.log (` ${GREEN}${each}${RESET},`)
|
|
68
|
+
first ??= each
|
|
69
|
+
}
|
|
70
|
+
console.log (`}`)
|
|
71
|
+
return first
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
repl.defineCommand ('inspect', {
|
|
77
|
+
help: 'Sets options for util.inspect, e.g. `.inspect .depth=1`.',
|
|
78
|
+
async action (_args) {
|
|
79
|
+
const args = _args.split(' '), subjects={}, options = {}, defaults = inspect.defaultOptions
|
|
80
|
+
for (let each of args) {
|
|
81
|
+
// args of shape .<option>=<value> set inspect options
|
|
82
|
+
if (each[0] === '.') each = eval(`options${each}`)
|
|
83
|
+
// others are subjects to inpsect with given options
|
|
84
|
+
else subjects[each ||= 'defaults'] = each === 'defaults' ? defaults : function _recurse (o,slug) {
|
|
85
|
+
const [ head,...tail ] = slug, x = o[head]
|
|
86
|
+
if (tail.length && x && typeof x === 'object') return _recurse (x,tail)
|
|
87
|
+
else return x
|
|
88
|
+
} (repl.context, each.split('.'))
|
|
89
|
+
}
|
|
90
|
+
// if no subjects were given, just set the options globally
|
|
91
|
+
if (!Object.keys(subjects).length) {
|
|
92
|
+
Object.assign (defaults, options)
|
|
93
|
+
console.log('\n', 'updated node:util.inspect.defaultOptions with:', options)
|
|
94
|
+
} else for (let [each,subject] of Object.entries(subjects))
|
|
95
|
+
console.log ('\n'+each+':', inspect (subject, options))
|
|
96
|
+
console.log()
|
|
97
|
+
this.displayPrompt()
|
|
52
98
|
}
|
|
53
99
|
})
|
|
54
100
|
|
package/bin/watch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const watchOnlyOptions = ['--ext', '--livereload', '--open']
|
|
1
|
+
const watchOnlyOptions = ['--ext', '--livereload', '--open', '--debug', '--inspect', '--inspect-brk']
|
|
2
2
|
module.exports = Object.assign ( watch, {
|
|
3
|
-
flags: [ '--
|
|
3
|
+
flags: [ '--in-memory', '--in-memory?', '--with-mocks' ],
|
|
4
4
|
options: [ '--port', ...watchOnlyOptions ],
|
|
5
5
|
help: `
|
|
6
6
|
# SYNOPSIS
|
|
@@ -42,6 +42,16 @@ module.exports = Object.assign ( watch, {
|
|
|
42
42
|
Specify from which profile(s) the binding information is taken.
|
|
43
43
|
*Example:* cds w --profile hybrid,production
|
|
44
44
|
|
|
45
|
+
*--debug* / *--inspect* <host:port | 127.0.0.1:9229>
|
|
46
|
+
|
|
47
|
+
Activate debugger on the given host:port.
|
|
48
|
+
If port 0 is specified, a random available port will be used.
|
|
49
|
+
|
|
50
|
+
*--inspect-brk* <host:port | 127.0.0.1:9229>
|
|
51
|
+
|
|
52
|
+
Activate debugger on the given host:port and break at start of user script.
|
|
53
|
+
If port 0 is specified, a random available port will be used.
|
|
54
|
+
|
|
45
55
|
# SEE ALSO
|
|
46
56
|
|
|
47
57
|
*cds serve --help* for the different start options.
|
|
@@ -72,7 +82,7 @@ async function watch ([cwd], {
|
|
|
72
82
|
...options
|
|
73
83
|
}={}) {
|
|
74
84
|
if (cwd && !cds.utils.exists(cwd)) try { // try module name like `@capire/bookshop`
|
|
75
|
-
cwd = require.resolve(cwd+'/package.json').slice(0,-13)
|
|
85
|
+
cwd = require.resolve(cwd+'/package.json', {paths:[ process.cwd() ]}).slice(0,-13)
|
|
76
86
|
} catch (err) {
|
|
77
87
|
throw `Error: No such folder or package: '${cwd}'`
|
|
78
88
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const os = require('os');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const readline = require('readline');
|
|
4
3
|
|
|
5
4
|
const fs = require('fs').promises;
|
|
6
5
|
const { bold } = require('../util/term');
|
|
@@ -11,24 +10,6 @@ const gitUtil = require('../util/git');
|
|
|
11
10
|
const CDSRC_FILENAME = '.cdsrc-private.json';
|
|
12
11
|
|
|
13
12
|
class UserCdsrcJsonStorageProvider {
|
|
14
|
-
async askBooleanQuestion(question) {
|
|
15
|
-
const rl = readline.createInterface({
|
|
16
|
-
input: process.stdin,
|
|
17
|
-
output: process.stdout
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
question = question.trim() + ' ';
|
|
21
|
-
return new Promise(resolve => {
|
|
22
|
-
rl.question(question, answer => {
|
|
23
|
-
if (answer === 'y' || answer === 'Y') {
|
|
24
|
-
resolve(true);
|
|
25
|
-
} else {
|
|
26
|
-
resolve(false);
|
|
27
|
-
}
|
|
28
|
-
rl.close();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
13
|
|
|
33
14
|
async storeServices(services, options = {}) {
|
|
34
15
|
let jsonPath = options.out || CDSRC_FILENAME;
|
|
@@ -2,7 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('../cds'), { _log: log } = cds
|
|
4
4
|
const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
|
|
5
|
-
const { relativePaths, BuildError, resolveRequiredSapModels, redactCredentials } = require('./util')
|
|
5
|
+
const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels, redactCredentials } = require('./util')
|
|
6
6
|
const { SEVERITIES, LOG_LEVELS } = require('./constants')
|
|
7
7
|
const BuildTaskFactory = require('./buildTaskFactory')
|
|
8
8
|
const InternalBuildPlugin = require('./provider/internalBuildPlugin')
|
|
@@ -39,7 +39,7 @@ class BuildTaskEngine {
|
|
|
39
39
|
// validate required @sap namespace models - log only
|
|
40
40
|
const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
|
|
41
41
|
if (unresolved.length > 0) {
|
|
42
|
-
throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved.
|
|
42
|
+
throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Run the 'npm install' command to install up-to-date versions of the missing packages.`)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// create build plugins
|
|
@@ -187,8 +187,8 @@ class BuildTaskEngine {
|
|
|
187
187
|
if (errors.length > 0) {
|
|
188
188
|
const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
|
|
189
189
|
if (error) {
|
|
190
|
-
// throw original
|
|
191
|
-
|
|
190
|
+
// throw original BuildError including existing BuildMessages
|
|
191
|
+
error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
|
|
192
192
|
throw error
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -7,15 +7,15 @@ const cdscVersion = `-- generated by cds-compiler version ${cdsc.version()}`
|
|
|
7
7
|
const { getArtifactCdsPersistenceName } = cdsc
|
|
8
8
|
const LOG = cds.log('cli|build'), DEBUG = cds.debug('cli|build')
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const { definitions, migrations, afterImage,
|
|
10
|
+
async function compileToHdbMigration(model, beforeImage, srcPath, options = {}) {
|
|
11
|
+
const { definitions, migrations, afterImage, migrationTableNames } = _compileToHana(model, options, beforeImage)
|
|
12
12
|
const definitionResult = []
|
|
13
13
|
|
|
14
14
|
for (const { name, suffix, sql } of definitions) {
|
|
15
15
|
let definitionEntry = { name, suffix, content: sql }
|
|
16
16
|
|
|
17
17
|
if (suffix === '.hdbtable') {
|
|
18
|
-
if (
|
|
18
|
+
if (migrationTableNames.has(name)) {
|
|
19
19
|
const migration = migrations.find(migration => migration.name === name)
|
|
20
20
|
definitionEntry = await _2migrationtable(srcPath, migration || _emptyMigration(name), sql)
|
|
21
21
|
}
|
|
@@ -27,6 +27,22 @@ module.exports = async (model, beforeImage, srcPath, options = {}) => {
|
|
|
27
27
|
return { definitions: definitionResult, afterImage }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function getJournalEntities(model, options = {}) {
|
|
31
|
+
return cds.reflect(model).all(item => {
|
|
32
|
+
if (item.kind === 'entity' && item['@cds.persistence.journal'] === true) {
|
|
33
|
+
if (item['@cds.persistence.skip'] === true || item['@cds.persistence.exists'] === true) {
|
|
34
|
+
options.messages?.push(new BuildMessage(`[hdbmigrationtable] annotation @cds.persistence.journal skipped for entity '${item.name}' as persistence exists`, SEVERITY_WARNING))
|
|
35
|
+
}
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
return false
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getMigrationTableName(entity, model) {
|
|
43
|
+
return getArtifactCdsPersistenceName(entity.name, 'quoted', model, 'hana')
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
function _compileToHana(model, options, beforeImage) {
|
|
31
47
|
let definitions = []
|
|
32
48
|
let migrations = []
|
|
@@ -58,20 +74,20 @@ function _compileToHana(model, options, beforeImage) {
|
|
|
58
74
|
migrations = result.migrations
|
|
59
75
|
afterImage = result.afterImage
|
|
60
76
|
}
|
|
61
|
-
let
|
|
77
|
+
let migrationTableNames = new Set()
|
|
62
78
|
if (afterImage) {
|
|
63
79
|
// 1. determine journal file names before _filterJournalArtifacts is called as getArtifactCdsPersistenceName
|
|
64
80
|
// might no longer return the proper file name for the reduced CSN, e.g. for draft entities
|
|
65
|
-
|
|
81
|
+
migrationTableNames = _getMigrationTableNames(afterImage, options)
|
|
66
82
|
|
|
67
83
|
// 2. leave only persisted journal-related entities in afterImage
|
|
68
84
|
afterImage = _filterJournalArtifacts(afterImage)
|
|
69
85
|
}
|
|
70
86
|
|
|
71
|
-
_debug(definitions, migrations,
|
|
87
|
+
_debug(definitions, migrations, migrationTableNames)
|
|
72
88
|
|
|
73
89
|
return {
|
|
74
|
-
definitions, migrations, afterImage,
|
|
90
|
+
definitions, migrations, afterImage, migrationTableNames
|
|
75
91
|
}
|
|
76
92
|
}
|
|
77
93
|
|
|
@@ -158,21 +174,13 @@ function _emptyMigration(name) {
|
|
|
158
174
|
return { name, suffix: ".hdbmigrationtable", changeset: [] }
|
|
159
175
|
}
|
|
160
176
|
|
|
161
|
-
function
|
|
162
|
-
const
|
|
163
|
-
if (item.kind === 'entity' && item['@cds.persistence.journal'] === true) {
|
|
164
|
-
if (item['@cds.persistence.skip'] === true || item['@cds.persistence.exists'] === true) {
|
|
165
|
-
options.messages?.push(new BuildMessage(`[hdbmigrationtable] annotation @cds.persistence.journal skipped for entity '${item.name}' as persistence exists`, SEVERITY_WARNING))
|
|
166
|
-
}
|
|
167
|
-
return true
|
|
168
|
-
}
|
|
169
|
-
return false
|
|
170
|
-
}).map(entity => getArtifactCdsPersistenceName(entity.name, 'quoted', model, 'hana')))
|
|
177
|
+
function _getMigrationTableNames(model, options) {
|
|
178
|
+
const migrationTableNames = getJournalEntities(model, options).map(entity => getMigrationTableName(entity, model))
|
|
171
179
|
|
|
172
|
-
DEBUG?.(`\n[hdbmigrationtable] found ${
|
|
173
|
-
DEBUG?.(`[hdbmigrationtable] ${
|
|
180
|
+
DEBUG?.(`\n[hdbmigrationtable] found ${migrationTableNames.length} model entities annotated with '@cds.persistence.journal`)
|
|
181
|
+
DEBUG?.(`[hdbmigrationtable] ${migrationTableNames.join(', ')}\n`)
|
|
174
182
|
|
|
175
|
-
return
|
|
183
|
+
return new Set(migrationTableNames)
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
// only keep journal related entities
|
|
@@ -217,3 +225,9 @@ ${migration ? migration.changeset.map(change => change.sql).join('\n') : 'Empty
|
|
|
217
225
|
}
|
|
218
226
|
}
|
|
219
227
|
}
|
|
228
|
+
|
|
229
|
+
module.exports = {
|
|
230
|
+
compileToHdbMigration,
|
|
231
|
+
getJournalEntities,
|
|
232
|
+
getMigrationTableName
|
|
233
|
+
}
|