@sap/cds-dk 9.3.2 → 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 (74) hide show
  1. package/CHANGELOG.md +41 -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/util/cli.js +132 -0
  20. package/bin/util/log.js +2 -0
  21. package/bin/version.js +2 -1
  22. package/bin/watch.js +21 -3
  23. package/lib/bind/shared.js +3 -1
  24. package/lib/build/buildTaskEngine.js +2 -1
  25. package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
  26. package/lib/build/plugins/helm/index.js +1 -1
  27. package/lib/build/provider/internalBuildTaskProvider.js +3 -3
  28. package/lib/build/provider/nodejs/index.js +62 -3
  29. package/lib/deploy/index.js +1 -1
  30. package/lib/deploy/to-hana/hana.js +18 -16
  31. package/lib/import/odata/v2parser.js +2 -2
  32. package/lib/import/odata/v4parser.js +21 -2
  33. package/lib/import/odata.package.json +4 -4
  34. package/lib/init/constants.js +2 -1
  35. package/lib/init/index.js +2 -0
  36. package/lib/init/mvn.js +3 -2
  37. package/lib/init/projectReader.js +1 -0
  38. package/lib/init/registries/mta.js +6 -0
  39. package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
  40. package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
  41. package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
  42. package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
  43. package/lib/init/template/app-frontend/files/manifest.json +13 -0
  44. package/lib/init/template/app-frontend/files/mta.yaml.hbs +71 -0
  45. package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
  46. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
  47. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
  48. package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
  49. package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
  50. package/lib/init/template/app-frontend/index.js +98 -0
  51. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  52. package/lib/init/template/attachments/index.js +4 -10
  53. package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
  54. package/lib/init/template/handler/index.js +1 -0
  55. package/lib/init/template/helm/index.js +5 -1
  56. package/lib/init/template/html5-repo/files/mta.yaml.hbs +7 -2
  57. package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
  58. package/lib/init/template/ias/files/mta.yaml.hbs +6 -5
  59. package/lib/init/template/ias/files/values.yaml.hbs +10 -1
  60. package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
  61. package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
  62. package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
  63. package/lib/init/template/portal/files/mta.yaml.hbs +12 -2
  64. package/lib/init/template/sample/files/db/schema.cds +5 -8
  65. package/lib/init/template/typer/files/xsconfig.json +2 -1
  66. package/lib/init/template/typescript/files/package.json +1 -1
  67. package/lib/init/template/typescript/files/tsconfig.json +4 -1
  68. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
  69. package/lib/init/template/workzone-standard/index.js +3 -2
  70. package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
  71. package/lib/lint/eslintHelp.json +1 -1
  72. package/lib/util/term.js +2 -4
  73. package/npm-shrinkwrap.json +25 -25
  74. 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 })
@@ -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
  }
@@ -8,7 +8,7 @@ Your services are available at:
8
8
  {{- $deployment := (get $root.Values $name) }}
9
9
  {{- if $deployment.expose.enabled }}
10
10
  {{- $ := merge (dict "name" $name "deployment" $deployment) $root }}
11
- {{- $url := include "cap.deploymentHostFull" $ }}
11
+ {{- $url := tpl (include "cap.deploymentHostFull" $) $ }}
12
12
  {{ printf "%s - https://%s" $name $url }}
13
13
  {{- end -}}
14
14
  {{- end }}
@@ -39,7 +39,7 @@ module.exports = class HelmBuildPlugin extends Plugin {
39
39
  const isInternal = chartYaml.dependencies?.find(dep => dep.name === 'web-application')?.repository !== undefined
40
40
 
41
41
  if (!isInternal) {
42
- // copy web appplication helm chart
42
+ // copy web application helm chart
43
43
  await this.copy(join(__dirname, 'files', 'subcharts', 'web-application')).to('charts/web-application')
44
44
 
45
45
  // copy service instance helm chart
@@ -143,7 +143,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
143
143
  sidecarEnv = cds.env.for("cds", sidecarPath)
144
144
  }
145
145
  if (
146
- cds.env.profiles?.includes('with-mtx-sidecar') || // new presets
146
+ cds.env.profiles?.includes('with-mtx-sidecar') || // new presets // FIXME: BAD hard-coded dependency to profile
147
147
  cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
148
148
  ) {
149
149
  DEBUG?.("MTX app with sidecar")
@@ -161,7 +161,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
161
161
  }
162
162
 
163
163
  if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
164
- throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
164
+ throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`) // FIXME: BAD hard-coded dependency to profile
165
165
  }
166
166
  if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
167
167
  // cds build is executed in sidecar folder
@@ -222,7 +222,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
222
222
  */
223
223
  static _isMtxs() {
224
224
  return cds.env.requires.toggles
225
- || cds.env.profiles.includes('with-mtx-sidecar')
225
+ || cds.env.profiles.includes('with-mtx-sidecar') // FIXME: BAD hard-coded dependency to profile
226
226
  || cds.env.requires['cds.xt.ModelProviderService']
227
227
  || (typeof cds.env.requires.multitenancy === "object")
228
228
  }