@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +41 -3
  2. package/bin/cds.js +49 -28
  3. package/bin/debug.js +90 -0
  4. package/bin/repl.js +50 -4
  5. package/bin/watch.js +13 -3
  6. package/lib/bind/userCdsrcJsonStorageProvider.js +0 -19
  7. package/lib/build/buildTaskEngine.js +4 -4
  8. package/lib/build/provider/hana/2migration.js +34 -20
  9. package/lib/build/provider/hana/index.js +74 -41
  10. package/lib/build/provider/internalBuildTaskProvider.js +1 -1
  11. package/lib/build/provider/mtx-extension/index.js +4 -4
  12. package/lib/build/provider/mtx-sidecar/index.js +2 -2
  13. package/lib/build/provider/nodejs/index.js +3 -19
  14. package/lib/build/util.js +41 -17
  15. package/lib/cds.js +10 -2
  16. package/lib/deploy/to-hana/hdiDeployUtil.js +14 -10
  17. package/lib/import/message.js +2 -1
  18. package/lib/import/odata/metadataconverter/MetadataConverter.js +5 -0
  19. package/lib/import/odata/v4parser.js +32 -43
  20. package/lib/init/add.js +26 -0
  21. package/lib/init/constants.js +5 -6
  22. package/lib/init/index.js +17 -25
  23. package/lib/init/merge.js +2 -5
  24. package/lib/init/projectReader.js +76 -9
  25. package/lib/init/registries/mta.js +0 -1
  26. package/lib/init/template/approuter/files/package.json +1 -1
  27. package/lib/init/template/cloud-logging/files/package.json.hbs +1 -12
  28. package/lib/init/template/cloud-logging/files/values.yaml.hbs +16 -0
  29. package/lib/init/template/cloud-logging/index.js +14 -10
  30. package/lib/init/template/data/as-json.js +1 -1
  31. package/lib/init/template/data/index.js +1 -26
  32. package/lib/init/template/esm/files/package.json.hbs +3 -0
  33. package/lib/init/template/esm/index.js +16 -0
  34. package/lib/init/template/handler/files/handler.xs.hbs +56 -0
  35. package/lib/init/template/handler/index.js +84 -9
  36. package/lib/init/template/helm/index.js +4 -1
  37. package/lib/init/template/html5-repo/files/xs-app.json.hbs +1 -0
  38. package/lib/init/template/html5-repo/index.js +2 -0
  39. package/lib/init/template/http/index.js +102 -35
  40. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  41. package/lib/init/template/sample/files/nodejs/{admin-service.js → admin-service.js.hbs} +8 -1
  42. package/lib/init/template/sample/files/nodejs/{cat-service.js → cat-service.js.hbs} +8 -3
  43. package/lib/init/template/sample/index.js +4 -2
  44. package/lib/init/template/telemetry/files/package.json.hbs +18 -0
  45. package/lib/init/template/telemetry/index.js +8 -2
  46. package/lib/init/template/typescript/files/package.json +2 -2
  47. package/lib/init/template/workzone-standard/index.js +1 -1
  48. package/lib/init/types.d.ts +0 -6
  49. package/lib/watch/node-watch.js +25 -9
  50. package/npm-shrinkwrap.json +63 -48
  51. 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](http://semver.org/).
6
+ This project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
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
- // warn users if they are using cds instead of cds-ts in a TypeScript project
90
- const tsconfig = path.join(cds.root ?? '.', 'tsconfig.json')
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 in the given CAP project folder, which can be a module name like @capire/bookshop.',
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
- console.log ('Following are made avaliable as global variables:')
50
- console.log ('- cds.services:', Object.keys(cds.services)); Object.assign (repl.context, cds.services)
51
- console.log ('- cds.entities:', Object.keys(cds.entities)); Object.assign (repl.context, cds.entities)
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: [ '--debug', '--in-memory', '--in-memory?', '--with-mocks' ],
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. Use the 'npm install' command to install up-to-date versions of the missing packages.`)
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 error, including BuildErrors
191
- // for BuildErrors we do not merge other build messages that might exist in order to keep the original error intact
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
- module.exports = async (model, beforeImage, srcPath, options = {}) => {
11
- const { definitions, migrations, afterImage, journalFileNames } = _compileToHana(model, options, beforeImage)
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 (journalFileNames.has(name)) {
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 journalFileNames = new Set()
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
- journalFileNames = _getJournalFileNames(afterImage, options)
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, journalFileNames)
87
+ _debug(definitions, migrations, migrationTableNames)
72
88
 
73
89
  return {
74
- definitions, migrations, afterImage, journalFileNames
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 _getJournalFileNames(model, options) {
162
- const journalNames = new Set(cds.reflect(model).all(item => {
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 ${journalNames.size} model entities annotated with '@cds.persistence.journal`)
173
- DEBUG?.(`[hdbmigrationtable] ${[...journalNames].join(', ')}\n`)
180
+ DEBUG?.(`\n[hdbmigrationtable] found ${migrationTableNames.length} model entities annotated with '@cds.persistence.journal`)
181
+ DEBUG?.(`[hdbmigrationtable] ${migrationTableNames.join(', ')}\n`)
174
182
 
175
- return journalNames
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
+ }