@sap/cds-dk 9.3.1 → 9.4.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 (77) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/bin/bind.js +1 -1
  3. package/bin/cds.js +3 -0
  4. package/bin/compile/index.js +53 -3
  5. package/bin/data-products/get-csn-via-dp-cli.js +41 -0
  6. package/bin/data-products/get-csn.js +76 -0
  7. package/bin/data-products/import.js +114 -0
  8. package/bin/data-products/index.js +30 -0
  9. package/bin/data-products/merge-csns.js +46 -0
  10. package/bin/data-products/prepare-csn.js +282 -0
  11. package/bin/data-products/prepare-files.js +69 -0
  12. package/bin/export.js +240 -0
  13. package/bin/help/export.md +37 -0
  14. package/bin/help/import.md +11 -0
  15. package/bin/help/minify.md +23 -0
  16. package/bin/import.js +13 -3
  17. package/bin/inspect.js +226 -0
  18. package/bin/minify.js +123 -0
  19. package/bin/up.js +3 -24
  20. package/bin/util/cli.js +132 -0
  21. package/bin/util/log.js +2 -0
  22. package/bin/version.js +2 -1
  23. package/bin/watch.js +21 -3
  24. package/lib/bind/shared.js +3 -1
  25. package/lib/build/buildTaskEngine.js +2 -1
  26. package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
  27. package/lib/build/plugins/helm/index.js +1 -1
  28. package/lib/build/provider/internalBuildTaskProvider.js +3 -3
  29. package/lib/build/provider/nodejs/index.js +62 -3
  30. package/lib/deploy/index.js +1 -1
  31. package/lib/deploy/to-hana/hana.js +18 -16
  32. package/lib/import/odata/v2parser.js +6 -2
  33. package/lib/import/odata/v4parser.js +21 -2
  34. package/lib/import/odata.package.json +4 -4
  35. package/lib/init/constants.js +2 -1
  36. package/lib/init/index.js +2 -0
  37. package/lib/init/mvn.js +3 -2
  38. package/lib/init/projectReader.js +1 -0
  39. package/lib/init/registries/mta.js +6 -0
  40. package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
  41. package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
  42. package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
  43. package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
  44. package/lib/init/template/app-frontend/files/manifest.json +13 -0
  45. package/lib/init/template/app-frontend/files/mta.yaml.hbs +71 -0
  46. package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
  47. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
  48. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
  49. package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
  50. package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
  51. package/lib/init/template/app-frontend/index.js +98 -0
  52. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  53. package/lib/init/template/attachments/index.js +4 -10
  54. package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
  55. package/lib/init/template/github-actions/index.js +0 -2
  56. package/lib/init/template/handler/index.js +1 -0
  57. package/lib/init/template/helm/index.js +5 -1
  58. package/lib/init/template/html5-repo/files/mta.yaml.hbs +9 -2
  59. package/lib/init/template/html5-repo/index.js +0 -3
  60. package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
  61. package/lib/init/template/ias/files/mta.yaml.hbs +6 -5
  62. package/lib/init/template/ias/files/values.yaml.hbs +10 -1
  63. package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
  64. package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
  65. package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
  66. package/lib/init/template/portal/files/mta.yaml.hbs +12 -2
  67. package/lib/init/template/sample/files/db/schema.cds +5 -8
  68. package/lib/init/template/typer/files/xsconfig.json +2 -1
  69. package/lib/init/template/typescript/files/package.json +1 -1
  70. package/lib/init/template/typescript/files/tsconfig.json +4 -1
  71. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
  72. package/lib/init/template/workzone-standard/index.js +3 -2
  73. package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
  74. package/lib/lint/eslintHelp.json +1 -1
  75. package/lib/util/term.js +2 -4
  76. package/npm-shrinkwrap.json +29 -29
  77. package/package.json +1 -1
package/bin/import.js CHANGED
@@ -1,8 +1,8 @@
1
1
  module.exports = Object.assign(_import, {
2
2
  _name: 'import',
3
- options: ['--for', '--out', '--as', '--name', '--include-namespaces', '--dependencies', '--from', '--config', '--destination', '--group'],
4
- shortcuts: ['-4', '-o', '-as', '-n', '-ns', '-d', undefined, '-c', undefined, undefined, '-', '-f'],
5
- flags: ['--dry', '--force', '--no-copy', '--no-save', '--keep-namespace'],
3
+ options: [ '--services', '--for', '--out', '--as', '--name', '--include-namespaces', '--dependencies', '--from', '--config', '--destination', '--group' ],
4
+ shortcuts: [ '-s', '-4', '-o', '-as', '-n', '-ns', '-d', undefined, '-c', undefined, undefined, '-', '-f'],
5
+ flags: [ '--dry', '--force', '--no-copy', '--no-save', '--keep-namespace', '--data-product', '--edmx', '--rfc', '--odata', '--openapi', '--asyncapi' ],
6
6
  help: `
7
7
  # SYNOPSIS
8
8
 
@@ -121,6 +121,11 @@ async function _import(args, options = {}) {
121
121
  await cds.plugins
122
122
  const cwd = process.cwd();
123
123
 
124
+ if (options['data-product']) {
125
+ const import_ = require ('./data-products/import.js')
126
+ return import_.exec (this)
127
+ }
128
+
124
129
  // process options
125
130
  const apiOptions = {};
126
131
  await common.processOptions(options, apiOptions);
@@ -140,6 +145,11 @@ async function _import(args, options = {}) {
140
145
  }
141
146
 
142
147
  let csn
148
+ if (options.edmx) options.from = 'edmx'
149
+ else if (options.odata) options.from = 'edmx'
150
+ else if (options.openapi) options.from = 'openapi'
151
+ else if (options.asyncapi) options.from = 'asyncapi'
152
+ else if (options.rfc) options.from = 'rfc'
143
153
  if (options.from) {
144
154
  if (!cds.import.from[options.from]) {
145
155
  // the required plugin might not be installed, e.g. rfc importer
package/bin/inspect.js ADDED
@@ -0,0 +1,226 @@
1
+ #! /usr/bin/env node
2
+ /* USAGE:
3
+ cds inspect model.Req
4
+ cds inspect model.Requisition --ids
5
+ */
6
+
7
+ module.exports = Object.assign (inspect, {
8
+ options: [ '--stats', '--list', '--kind' ],
9
+ shortcuts: [ '-s', '-l', '-k' ],
10
+ help: `
11
+ # SYNOPSIS
12
+
13
+ *cds inspect* [ <options> ]
14
+
15
+ Inspects the cds models of a project, or a specific definition.
16
+ Without arguments shows --stats a summary of your models.
17
+
18
+ You can list all definitions, and thereby filter the listed definitions
19
+ by kind, and/or a name pattern, using wildcards like * and ?.
20
+
21
+ For a specific definition you can list its elements, keys, associations,
22
+ compositions, or IDs using --list. It also shows where the definition
23
+ is inherited from, and counts of its elements.
24
+
25
+ See examples below.
26
+
27
+ # OPTIONS
28
+
29
+ *-s | --stats*
30
+
31
+ Shows a summary of the kinds of definitions found.
32
+
33
+ *-l | --list* <names|elements|keys|associations|compositions|ids>
34
+
35
+ Lists the specified kind of elements of a specific definition.
36
+
37
+ *-k | --kind* <entity|type|service|...>
38
+
39
+ Filters the listed definitions by kind.
40
+
41
+ # EXAMPLES
42
+
43
+ *cds inspect*
44
+ *cds inspect* --stats
45
+ *cds inspect* --kind aspect
46
+ *cds inspect* --list all
47
+ *cds inspect* --list names --kind entity
48
+ *cds inspect* --list names model.Req*
49
+ *cds inspect* requisition.model.Requisition
50
+ *cds inspect* requisition.model.Requisition --list elements
51
+ *cds inspect* requisition.model.Requisition --list ids
52
+ *cds inspect* requisition.model.Requisition --list keys
53
+ *cds inspect* requisition.model.Requisition --list associations
54
+ *cds inspect* requisition.model.Requisition --list compositions
55
+ `})
56
+
57
+ async function inspect(){
58
+
59
+ const cds = require ('../lib/cds')
60
+ const path = require ('node:path')
61
+
62
+ const { YELLOW, GREEN, CYAN, RED, GRAY, RESET } = cds.utils.colors
63
+ const i2 = ' '.repeat(2)
64
+ const i4 = ' '.repeat(4)
65
+ const args = process.argv.slice(3)
66
+
67
+ const option = (...oo) => {
68
+ for (let o of oo) {
69
+ const i = args.indexOf(o)
70
+ if (i>=0) return args.splice(i,2)[1] || ''
71
+ }
72
+ }
73
+
74
+ const fetch = { __proto__: {
75
+ get 'id elements' () { return this.ids },
76
+ },
77
+ names: () => true,
78
+ elements: () => true,
79
+ ids: e => e.name.match(/(ID|Id|id$)/),
80
+ keys: e => e.key,
81
+ associations: a => a.isAssociation && !a.isComposition,
82
+ compositions: a => a.isComposition,
83
+ 'to-many': a => a.is2many,
84
+ 'to-one': a => a.is2one,
85
+ unmanaged: a => a.is2one && a.on,
86
+ virtuals: e => e.virtual,
87
+ calculated: e => e.value,
88
+ }
89
+
90
+
91
+ const $location = d => {
92
+ if (Object.hasOwn(d,'$location')) return d.$location.file+':'+d.$location.line
93
+ let includes = d.parent?.includes; if (!includes) return ''
94
+ for (let inc of includes) {
95
+ let e = csn.definitions[inc]?.elements?.[d.name]
96
+ if (e) return $location(e)
97
+ }
98
+ }
99
+
100
+ const csn = await cds.load('*').then(cds.linked)
101
+ let defs = Object.values (csn.definitions)
102
+
103
+ const kinds = {
104
+ entities: 'entity',
105
+ aspects: 'aspect',
106
+ types: 'type',
107
+ services: 'service',
108
+ events: 'event',
109
+ actions: 'action',
110
+ functions: 'function',
111
+ contexts: 'context',
112
+ all: undefined,
113
+ }
114
+
115
+ let kind = option ('-k','--kind')
116
+ let list = option ('-l','--list')
117
+ let stats = option ('-s','--stats')
118
+ if (list in kinds) [kind,list] = [kinds[list],'names']
119
+ if (kind) defs = defs.filter (d => d.kind === kind)
120
+ if (process.argv.length < 4) stats = true
121
+
122
+ const regexp4 = pattern => RegExp (pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'))
123
+ for (let pattern of args) {
124
+ const regexp = regexp4(pattern || '.*')
125
+ defs = defs.filter (d => regexp.test(d.name))
126
+ }
127
+
128
+ console.log()
129
+ if (stats !== undefined) {
130
+
131
+ const count = (kind,f) => {
132
+ let n = defs.filter(f).length; if (!n) return
133
+ if (kind === 'entities') for (let each of ['.texts','.drafts']) {
134
+ let n = defs.filter(d => d.name.endsWith(each)).length
135
+ if (n) kind += ` - ${YELLOW}${n}${GRAY} ${each}`
136
+ }
137
+ if (n) console.log (YELLOW, n.toString().padStart(4,' '), GRAY + kind)
138
+ }
139
+ for (let k in kinds) count (k, d => d.kind === kinds[k])
140
+ console.log (GRAY, '-'.repeat(14), RESET)
141
+ console.log (YELLOW, defs.length.toString().padStart(4,' '), GRAY + 'in total')
142
+
143
+ } else {
144
+ defs = defs.filter (d => !d.name.match(/\.(texts|drafts)$/))
145
+ switch (list) {
146
+
147
+ case 'names':
148
+ const pad = kind?.length || 7 // eslint-disable-line no-case-declarations
149
+ defs.forEach (d => console.log (i2, RED + d.kind.padEnd(pad), GREEN + d.name))
150
+ console.log(GRAY+i2, '-'.repeat(27), RESET)
151
+ console.log(GRAY+i2, 'Total:', YELLOW + defs.length, GRAY + 'definitions')
152
+ break;
153
+
154
+ case 'sources':
155
+ csn.$sources.forEach(f => console.log (GRAY+i2, path.relative(cds.root,f)))
156
+ break;
157
+
158
+ default:
159
+ defs.forEach (inspect)
160
+
161
+ }
162
+ }
163
+ console.log(RESET)
164
+
165
+
166
+ function inspect (d) {
167
+
168
+ console.log(GRAY+i2, '-'.repeat(79), RESET)
169
+ console.log(GRAY+i2, 'Inspecting', RED + d.kind, GREEN + d.name, RESET)
170
+ console.log(GRAY+i2, '( declared at'+ CYAN, $location(d), GRAY+')', RESET)
171
+ console.log()
172
+
173
+ if (list !== undefined) {
174
+ const filter = fetch [list]
175
+ if (!filter) return console.error (RED, 'ERROR:'+ RESET, 'Use --list with one of:\n\n ', Object.keys(fetch).join('\n '), RESET)
176
+ const elements = Object.values(d.elements??{}).filter(filter)
177
+ const pad = elements.reduce((p, e) => Math.max(p, e.name.length), 0)
178
+ for (let e of elements) {
179
+ console.log (i4, GRAY + '-', YELLOW + e.name.padEnd(pad), GRAY, $location(e))
180
+ }
181
+ return console.log()
182
+ }
183
+
184
+ count(d.elements, 'keys')
185
+ count(d.elements, 'id elements')
186
+ count(d.elements, 'elements')
187
+ count(d.elements, 'associations', 'to-many', 'unmanaged')
188
+ count(d.elements, 'compositions', 'to-many')
189
+ count(d.elements, 'virtuals')
190
+ count(d.elements, 'calculated')
191
+
192
+ if (d.includes) {
193
+ console.log(RESET + '\n', i2, 'inherits: \n')
194
+ inheritance(d)
195
+ }
196
+ console.log()
197
+
198
+ }
199
+
200
+ function inheritance (d, indent = '') {
201
+ if (d.includes) for (let each of d.includes) {
202
+ let d = csn.definitions[each]
203
+ console.log(i4, ' -', count(d.elements), GRAY + 'elements from' + indent, RED + d.kind, GREEN + d.name, RESET)
204
+ inheritance(d, indent + i2)
205
+ }
206
+ }
207
+
208
+ function count (defs, kind, ...more) {
209
+ if (!defs) return
210
+ if (!Array.isArray(defs)) defs = Object.values(defs)
211
+ if (kind) {
212
+ const filter = fetch[kind]
213
+ if (filter) defs = defs.filter(filter)
214
+ if (!defs.length) return
215
+ }
216
+ let info = YELLOW+ defs.length.toString().padStart(3, ' ')
217
+ if (kind) info += GRAY + ' ' + kind; else return info
218
+ for (let kind of more) {
219
+ let filter = fetch[kind] || fetch[kind.replace(/-/g,'_')]
220
+ let n = defs.filter(filter).length
221
+ if (n) info += ', '+ YELLOW + defs.filter(filter).length + GRAY+' ' + kind
222
+ }
223
+ console.log (i4, info, GRAY)
224
+ }
225
+
226
+ }
package/bin/minify.js ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ const cds = require ('../lib/cds.js')
3
+ const { Minifier } = cds.minify; if (!Minifier) throw new Error ('@sap/cds >= 9.4.0 required for this command')
4
+ const { Command } = require ('./util/cli.js')
5
+ const { inspect } = require ('node:util')
6
+
7
+ class Minify extends Command {
8
+
9
+ options = Minify.options // for IntelliSense
10
+ static options = {
11
+ cleanse: { _excluding: 'keep' },
12
+ keep: { _excluding: 'skip',
13
+ aspect: false,
14
+ entity: true,
15
+ event: true,
16
+ type: false,
17
+ action: true,
18
+ function: true,
19
+ },
20
+ skip: {},
21
+ dry: false,
22
+ debug: false,
23
+ inspect: false,
24
+ }
25
+ static shortcuts = { skip:'x', dry:'y' }
26
+ static filename = __filename
27
+
28
+ parseArgs (conf) {
29
+ let [sources,o] = super.parseArgs (conf)
30
+ this.sources = sources.length ? o.from = sources : sources = o.from || '*'
31
+ return [sources,o]
32
+ }
33
+
34
+ async exec ( args, options ) {
35
+ let [sources,o] = this.parseArgs ({ args, options })
36
+ try {
37
+ let csn = await this.load (sources,o); this.csn = csn
38
+ let min = await this.minify (csn,o); this.min = min
39
+ if (o.dry) return
40
+ if (o.inspect) return this.inspect (min,o)
41
+ if (this.print) return this.print (min)
42
+ else await this.export (min,o)
43
+ console.log()
44
+ } catch (e) {
45
+ if (typeof e === 'string') return console.error (e)
46
+ if (e.code) return console.error (e.message)
47
+ else throw e
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Subclasses override this method to provide custom loading behavior.
53
+ */
54
+ async load (sources,o) { // eslint-disable-line no-unused-vars
55
+ return cds.load (sources)
56
+ }
57
+
58
+ /**
59
+ * Subclasses override this method to provide custom cleansing behavior.
60
+ */
61
+ async cleanse (csn,o) { // eslint-disable-line no-unused-vars
62
+ return csn
63
+ }
64
+
65
+ /**
66
+ * Subclasses override this method to provide custom minification behavior.
67
+ */
68
+ async minify (csn,o) {
69
+ let m = this.minifier ??= o.dry ? new Dryifier(csn) : new Minifier()
70
+ csn = await this.cleanse (csn,o)
71
+ return m.minify (csn,o)
72
+ }
73
+
74
+ /**
75
+ * Subclasses override this method to generate exported artifacts.
76
+ */
77
+ export (csn,o) {} // eslint-disable-line no-unused-vars
78
+
79
+ /**
80
+ * Subclasses override this method to provide custom print behavior.
81
+ */
82
+ print (csn,o) { // eslint-disable-line no-unused-vars
83
+ console.log (cds.compile.to.json (csn))
84
+ }
85
+
86
+ inspect (csn, {inspect:depth} = this.options) {
87
+ const colors = cds.utils.colors.enabled
88
+ console.log (inspect (cds.linked(csn), { depth, colors }))
89
+ }
90
+ }
91
+
92
+
93
+ /**
94
+ * Custom minifier to capture kept definitions in dry run mode
95
+ */
96
+ class Dryifier extends Minifier {
97
+
98
+ constructor(csn) { super(); this.all = Object.keys (csn.definitions) }
99
+ capture = {}
100
+ level = 0
101
+
102
+ keep (each,d) {
103
+ if (each in this.kept) return; else this.capture[each] = ++this.level
104
+ super.keep (each, d)
105
+ this.level--
106
+ }
107
+
108
+ minify (csn,o) {
109
+ const { GREEN, GRAY, DIMMED, RESET } = cds.utils.colors
110
+ const min = super.minify (csn, o)
111
+ const kept = Object.keys(this.capture)
112
+ console.log (RESET+'\n', 'Keep:', kept.length, '\n')
113
+ for (let [ each, level ] of Object.entries(this.capture))
114
+ console.log (' '+GRAY,'•'.repeat(level)+GREEN, each, RESET)
115
+ console.log (RESET+'\n', 'Skip:', this.all.length - kept.length, '\n')
116
+ for (let each of this.all)
117
+ each in this.capture || console.log (DIMMED, ' -', each, RESET)
118
+ console.log (RESET+'\n', 'Total:', this.all.length, '\n')
119
+ return min
120
+ }
121
+ }
122
+
123
+ module.exports = Object.assign (Minify._for_cds_dk(), { Minify })
package/bin/up.js CHANGED
@@ -27,9 +27,9 @@ module.exports = Object.assign(up, {
27
27
  *cds up --to k8s --namespace e2e-tests*
28
28
  `})
29
29
 
30
- const { execSync, spawn } = require('node:child_process')
30
+ const { execSync } = require('node:child_process')
31
31
  const cds = require('../lib/cds')
32
- const { highlight, bold } = require('../lib/util/term')
32
+ const { highlight } = require('../lib/util/term')
33
33
  const { exists, fs, path, path: { join } } = cds.utils
34
34
  const { readProject } = require('../lib/init/projectReader')
35
35
 
@@ -45,27 +45,6 @@ const run = cmd => {
45
45
  }
46
46
  }
47
47
 
48
- const runDetectingError = async (cmd, errorRegex) => {
49
- console.log(highlight(cmd))
50
- return new Promise((resolve, reject) => {
51
- const child = spawn(cmd, { shell: true, stdio: 'pipe' })
52
- let out = '';
53
- ['stdout', 'stderr'].forEach(key => {
54
- child[key].on('data', data => {
55
- process[key].write(data.toString())
56
- out += data.toString()
57
- })
58
- })
59
- child.on('close', () => {
60
- const error = out.match(errorRegex)
61
- if (error) {
62
- return reject('Error in command: ' + bold(cmd) + ' → exiting')
63
- }
64
- resolve(out)
65
- })
66
- })
67
- }
68
-
69
48
  async function up() {
70
49
  const project = readProject()
71
50
  const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice } = project
@@ -121,7 +100,7 @@ async function up() {
121
100
  const nsKubectl = ns ? `-n ${ns}` : ''
122
101
 
123
102
  run('npm i --package-lock')
124
- await runDetectingError('ctz containerize.yaml --log --push', /^\[?ERROR\]?.*/m)
103
+ run('ctz containerize.yaml --log --push')
125
104
  let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs --timeout=10m`
126
105
  if (hasXsuaa) helmUpgrade += ' --set-file xsuaa.jsonParameters=xs-security.json'
127
106
  run(helmUpgrade)
@@ -0,0 +1,132 @@
1
+ const { parseArgs } = require ('node:util')
2
+ const path = require ('node:path')
3
+ const fs = require ('node:fs')
4
+
5
+ class Command { static filename = __filename // to be overridden in subclasses
6
+
7
+ get class(){ return this.constructor }
8
+ get name(){ return this.class.name }
9
+
10
+ /** @type <T>(defaults: T) => [ string[], T ] */
11
+ parseArgs (conf) {
12
+
13
+ const args = conf.args || process.argv.slice(2)
14
+ const shorts = { ...this.class.shortcuts, ...conf.shortcuts }
15
+ const options = { ...this.class.options, ...conf.options, help:false }
16
+ for (let k in options) options[k] = option4 (options[k]) .short (shorts[k]||k[0])
17
+
18
+ // parse the CLI args with node:util.parseArgs
19
+ const { positionals:p, values:o } = parseArgs ({
20
+ allowPositionals: true, strict: false, ...conf,
21
+ args, options,
22
+ })
23
+ this.positionals = p
24
+ this.options = o
25
+
26
+ // convert the parsed values according to the options
27
+ for (let k in o) {
28
+ const v = options[k]; if (!v) continue
29
+ if (v.convert) o[k] = v.convert (o[k],o)
30
+ }
31
+
32
+ // help / debug
33
+ if (o.help || args == '?' || !args.length) throw this.help()
34
+ if (o.debug) throw this.debug()
35
+
36
+ // parsed
37
+ return [ this.positionals, this.options ]
38
+ }
39
+
40
+ usage() {
41
+ const source = this.class.filename; if (!source) return super.usage = undefined
42
+ const { dir, name } = path.parse (source)
43
+ const md = path.join(dir,'help',name+'.md')
44
+ return super.usage = fs.readFileSync (md,'utf8')
45
+ }
46
+
47
+ help (usage = this.usage()) {
48
+ console.log (usage
49
+ .replace(/\n# ([^\n]*)\n/g, `\n\x1b[1m$1\x1b[0m\n`)
50
+ .replace(/ \*([^*]+)\*/g, ` \x1b[1m$1\x1b[0m`)
51
+ .replace(/ _([^_]+)_/g, ` \x1b[4m$1\x1b[0m`)
52
+ )
53
+ process.exit(0)
54
+ }
55
+
56
+ debug (positionals = this.positionals, o = this.options) {
57
+ console.log('Debugging information:')
58
+ const shortcuts = {}, options = {...o}; delete options.o
59
+ for (let o in options) shortcuts[this.class.shortcuts[o] || o[0]] = o
60
+ console.log ({ positionals, options, shortcuts })
61
+ process.exit(0)
62
+ }
63
+
64
+ static _for_cds_dk (cmd = this) {
65
+ return { _is_new_cmd: true,
66
+ options: Object.keys(this.options).map(o => '--'+o), // for shell completion
67
+ exec() { return (new cmd) .exec (process.argv.slice(3)) },
68
+ get help() { return (new cmd).usage() },
69
+ }
70
+ }
71
+ }
72
+
73
+
74
+ class Option {
75
+ constructor (spec) { Object.assign (this,spec) }
76
+ convert (x) { return x }
77
+ to(fn) { this.convert = fn; return this }
78
+ short(sc) { this.short = sc; return this }
79
+ get array() { this.multiple = true; return this }
80
+ }
81
+
82
+ class Flags extends Option {
83
+ constructor (defaults) {
84
+ super({
85
+ type: 'string',
86
+ multiple: true,
87
+ default: [],
88
+ defaults,
89
+ })
90
+ if (defaults._excluding) {
91
+ this.skip = defaults._excluding
92
+ delete defaults._excluding
93
+ }
94
+ }
95
+ convert (strings,o) {
96
+ const flags = { ...this.defaults }
97
+ const keep = strings4(strings).reduce?.((o,x)=>(o[x]=true,o),{}) || strings
98
+ if (keep.all) for (let x in this.defaults) flags[x] = true
99
+ if (this.skip) {
100
+ const skip = o[this.skip].reduce?.((o,x)=>(o[x]=true,o),{}) || o[this.skip]
101
+ if (skip.all) for (let x in this.defaults) delete flags[x]
102
+ else for (let x in skip) delete flags[x]
103
+ }
104
+ for (let x in keep) flags[x] = keep[x]
105
+ return flags
106
+ }
107
+ }
108
+
109
+ const option4 = exports.option4 = (v) => {
110
+ if (v instanceof Option) return v
111
+ if (v instanceof RegExp) return pattern(v)
112
+ if (v instanceof Array) return strings(v)
113
+ if (v == null) return string('')
114
+ if (typeof v === 'string') return string(v)
115
+ if (typeof v === 'object') return flags(v)
116
+ if (typeof v === 'boolean') return flag(v)
117
+ if (typeof v === 'number') return number(v)
118
+ }
119
+ const option = exports.option = (x) => new Option (x)
120
+ const number = exports.number = d => string (String(d)).to(Number)
121
+ const pattern = exports.pattern = d => string (d.source !== '.' ? [d.source] : []) .array.to (regex4)
122
+ const strings = exports.strings = d => string (d.length ? d : undefined) .array.to (strings4)
123
+ const string = exports.string = d => option ({ type:'string', default:d })
124
+ const flag = exports.flag = d => option ({ type:'boolean', default:d })
125
+ const flags = exports.flags = (d,x) => new Flags (d,x)
126
+ const regex4 = exports.regex4 = raw => RegExp (strings4(raw).join('|')
127
+ .replaceAll ('.','\\.')
128
+ .replaceAll ('*','.*'),
129
+ 'i')
130
+ const strings4 = exports.strings4 = raw => raw.length === 1 ? raw[0].split?.(',')||'' : raw
131
+
132
+ exports.Command = Command
package/bin/util/log.js CHANGED
@@ -15,6 +15,8 @@ module.exports = (messages, options={}) => {
15
15
  error: { Error:4, undefined:4 },
16
16
  } [logLevel.toLowerCase()] || { Error:4, undefined:4, Warning:3 }
17
17
  const log = options.log || console.error
18
+ // cds10: Remove cds.utils.colors.for check
19
+ if (log === console.error && cds.utils.colors.for) format.colors = cds.utils.colors.for(process.stderr).enabled
18
20
 
19
21
  if (!Array.isArray (messages)) messages = [messages]
20
22
  for (let m of messages.filter (m => m.severity in levels)) {
package/bin/version.js CHANGED
@@ -145,7 +145,8 @@ function _pkg_path(id, ...paths) {
145
145
  }
146
146
 
147
147
  function capJavaVersion(cwd) {
148
- const cmd = 'mvn com.sap.cds:cds-maven-plugin:version -B -Dcds.version.excludeCds -Dcds.version.json'
148
+ const { MAVEN_ARCHETYPE_VERSION } = require('../lib/init/constants')
149
+ const cmd = `mvn com.sap.cds:cds-maven-plugin:${MAVEN_ARCHETYPE_VERSION}:version -B -ntp -Dcds.version.excludeCds -Dcds.version.json`
149
150
 
150
151
  let out = ''
151
152
  try {
package/bin/watch.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const watchOnlyOptions = ['--ext', '--livereload', '--open', '--debug', '--inspect', '--inspect-brk', '--include', '--exclude']
2
2
 
3
3
  module.exports = Object.assign ( watch, {
4
- flags: [ '--in-memory', '--in-memory?', '--with-mocks' ],
4
+ flags: [ '--in-memory', '--in-memory?', '--with-mocks', '--with-mtx' ],
5
5
  options: [ '--port', ...watchOnlyOptions ],
6
6
  help: `
7
7
  # SYNOPSIS
@@ -51,6 +51,11 @@ module.exports = Object.assign ( watch, {
51
51
  Specify from which profile(s) the binding information is taken.
52
52
  *Example:* cds w --profile hybrid,production
53
53
 
54
+ *--with-mtx*
55
+
56
+ A shortcut for _--profile with-mtx_ to enable mtx features also in
57
+ development, while by default, these are only enabled in production.
58
+
54
59
  *--debug* / *--inspect* <host:port | 127.0.0.1:9229>
55
60
 
56
61
  Activate debugger on the given host:port.
@@ -67,11 +72,11 @@ module.exports = Object.assign ( watch, {
67
72
 
68
73
  `})
69
74
 
75
+ const fs = require ('node:fs')
70
76
  const cds = require('../lib/cds')
71
77
  const DEBUG = /\b(y|all|cli|watch)\b/.test (process.env.DEBUG) ? ((..._) => console.debug ('[cli] -', ..._)) : undefined
72
78
  const {delimiter,relative,resolve,sep} = require ('path')
73
79
  const {codes} = require('../lib/util/term'), t = codes
74
- const fs = require ('fs')
75
80
 
76
81
  const escapedSep = sep === '\\' ? '\\\\' : sep // escape backslash for windows
77
82
  const ignoreDefaults = [
@@ -99,6 +104,7 @@ async function watch ([cwd], {
99
104
  const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
100
105
  const paths4 = p => typeof p === 'string' ? p.split(',').map(p => p.trim()) : (p || [])
101
106
  const included = paths4(include), excluded = paths4(exclude)
107
+ const isJava = fs.existsSync('pom.xml') && process.env.CDS_ENV !== 'node'
102
108
 
103
109
  if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
104
110
  if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
@@ -118,7 +124,8 @@ async function watch ([cwd], {
118
124
  }
119
125
  else cwd = cds.root
120
126
 
121
- log (`\n${t.bold}cds ${args.join(' ')}`)
127
+ if (isJava) options.livereload = false
128
+ if (!isJava) log (`\n${t.bold}cds ${args.join(' ')}`)
122
129
 
123
130
  const env = Object.assign (process.env, {
124
131
  _args: JSON.stringify(args),
@@ -164,6 +171,17 @@ async function watch ([cwd], {
164
171
  Object.assign(env, await bind.env({cwd}))
165
172
  }
166
173
 
174
+ // if (isJava) {
175
+ // const { spawn } = require('node:child_process')
176
+ // const path = require('node:path')
177
+ // const cwd = path.join(cds.root, cds.env.folders.srv)
178
+ // const child = spawn('mvn', ['cds:watch'], { cwd, stdio: 'inherit' })
179
+ // child.on('close', (code) => {
180
+ // if (code !== 0) console.error(`mvn process exited with code ${code}`)
181
+ // })
182
+ // return
183
+ // }
184
+
167
185
  const nodemon = require('../lib/watch/node-watch')
168
186
  const watcher = nodemon ({
169
187
  script: resolve(__dirname,'../lib/watch/watched'), cwd, env, ext, ignore, watched,
@@ -82,7 +82,9 @@ async function bind(options = { on: 'cf' }) {
82
82
  if (!requiresName) throw `unknown CDS service name for service ${bold(instance)} of kind ${bold(kind)} – please specify as argument for ${bold('cds bind')}`
83
83
  resolved.name = requiresName
84
84
  }
85
- resolved.name ??= `custom-service:${instance || secret}`
85
+ if (exists('pom.xml')) resolved.name ??= `custom-service:${instance || secret}`
86
+ else resolved.name ??= `${instance || secret}`
87
+
86
88
  const kindText = kind ? ` with kind ${bold(kind)}` : ''
87
89
  console.log(`binding ${bold(resolved.name)} to ${plugin.displayName()} ${_bindingText(binding)}${kindText}`)
88
90
  }
@@ -6,7 +6,7 @@ const { inspect } = require('node:util')
6
6
  const cds = require('../cds'), { _log: log } = cds
7
7
  const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
8
8
  const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
9
- const { SEVERITIES, LOG_LEVELS } = require('./constants')
9
+ const { SEVERITIES, SEVERITY_ERROR, LOG_LEVELS } = require('./constants')
10
10
  const BuildTaskFactory = require('./buildTaskFactory')
11
11
  const InternalBuildPlugin = require('./provider/internalBuildPlugin')
12
12
  const COMPILATION_ERROR = 'CompilationError'
@@ -195,6 +195,7 @@ class BuildTaskEngine {
195
195
  const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
196
196
  if (error) {
197
197
  // throw original BuildError including existing BuildMessages
198
+ error.messages = error.messages.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
198
199
  error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
199
200
  throw error
200
201
  }