@sap/cds-dk 9.6.0 → 9.7.0

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 (87) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/bin/add.js +5 -2
  3. package/bin/cds.js +11 -26
  4. package/bin/compile/index.js +3 -2
  5. package/bin/data-products/import.js +3 -0
  6. package/bin/debug.js +76 -16
  7. package/bin/env.js +2 -1
  8. package/bin/export.js +18 -13
  9. package/bin/help/export.md +2 -1
  10. package/bin/import.js +21 -18
  11. package/bin/init.js +8 -1
  12. package/bin/minify.js +11 -12
  13. package/bin/mock.js +17 -9
  14. package/bin/repl.js +2 -2
  15. package/bin/up.js +34 -4
  16. package/bin/version.js +202 -131
  17. package/lib/build/buildTaskEngine.js +4 -0
  18. package/lib/build/buildTaskFactory.js +5 -35
  19. package/lib/build/index.js +10 -10
  20. package/lib/build/plugin.js +6 -3
  21. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +3 -1
  22. package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/_helpers.tpl +1 -1
  23. package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +8 -0
  24. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +18 -0
  25. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +11 -1
  26. package/lib/build/plugins.js +8 -16
  27. package/lib/build/provider/hana/index.js +16 -19
  28. package/lib/build/provider/internalBuildPlugin.js +28 -4
  29. package/lib/build/provider/internalBuildTaskProvider.js +26 -88
  30. package/lib/build/provider/mtx/index.js +0 -8
  31. package/lib/build/provider/mtx-sidecar/index.js +88 -80
  32. package/lib/build/provider/nodejs/index.js +3 -8
  33. package/lib/build/util.js +52 -87
  34. package/lib/deploy/to-hana/hana.js +117 -109
  35. package/lib/env/schemas/cds-rc.js +0 -4
  36. package/lib/import/odata/v4parser.js +80 -56
  37. package/lib/init/constants.js +5 -2
  38. package/lib/init/index.js +44 -41
  39. package/lib/init/merge.js +7 -2
  40. package/lib/init/projectReader.js +4 -10
  41. package/lib/init/template/ams/index.js +1 -4
  42. package/lib/init/template/attachments/files/package.json.hbs +1 -1
  43. package/lib/init/template/data/index.js +1 -1
  44. package/lib/init/template/extension/index.js +4 -4
  45. package/lib/init/template/hana/files/package.json.hbs +3 -3
  46. package/lib/init/template/handler/index.js +1 -1
  47. package/lib/init/template/helm/index.js +3 -3
  48. package/lib/init/template/helm-unified-runtime/index.js +5 -1
  49. package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -1
  50. package/lib/init/template/ias/files/mta.yaml.hbs +3 -3
  51. package/lib/init/template/initial/files/.vscode/tasks.json +10 -0
  52. package/lib/init/template/{nodejs/files/README.md → initial/files/readme.md} +2 -3
  53. package/lib/init/template/initial/index.js +15 -0
  54. package/lib/init/template/java/index.js +4 -11
  55. package/lib/init/template/kyma/index.js +21 -20
  56. package/lib/init/template/lint/index.js +3 -1
  57. package/lib/init/template/mta/index.js +0 -5
  58. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -2
  59. package/lib/init/template/multitenancy/files/values.yaml.hbs +30 -0
  60. package/lib/init/template/nodejs/files/.vscode/tasks.json +1 -14
  61. package/lib/init/template/nodejs/files/package.json.hbs +2 -4
  62. package/lib/init/template/nodejs/index.js +8 -12
  63. package/lib/init/template/sample/files/app/common.cds +1 -1
  64. package/lib/init/template/sample/index.js +5 -6
  65. package/lib/init/template/sqlite/files/package.json.hbs +7 -1
  66. package/lib/init/template/test/index.js +1 -1
  67. package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +6 -0
  68. package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds +5 -0
  69. package/lib/init/template/tiny-sample/index.js +2 -2
  70. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +6 -0
  71. package/lib/init/template/workzone-standard/index.js +13 -3
  72. package/lib/init/validate.js +11 -6
  73. package/lib/util/checksum.js +2 -2
  74. package/lib/util/fs.js +109 -53
  75. package/lib/util/glob.js +2 -1
  76. package/lib/util/term.js +4 -8
  77. package/lib/util/write.js +2 -4
  78. package/lib/watch/livereload-server.js +4 -3
  79. package/lib/watch/node-watch.js +5 -8
  80. package/lib/watch/watched.js +1 -1
  81. package/lib/watch/watcher.js +136 -0
  82. package/npm-shrinkwrap.json +245 -284
  83. package/package.json +2 -3
  84. package/lib/init/template/nodejs/files/.vscode/settings.json +0 -11
  85. package/lib/init/template/tiny-sample/files/nodejs/db/data/my.bookshop-Books.csv +0 -3
  86. package/lib/init/template/tiny-sample/files/nodejs/db/schema.cds +0 -7
  87. package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds.hbs +0 -5
package/bin/minify.js CHANGED
@@ -34,13 +34,11 @@ class Minify extends Command {
34
34
  async exec ( args, options ) {
35
35
  let [sources,o] = this.parseArgs ({ args, options })
36
36
  try {
37
- let csn = await this.load (sources,o); this.csn = csn
38
- let min = await this.minify (csn,o); this.min = min
37
+ this.csn = await this.load (sources,o)
38
+ this.min = await this.minify (this.csn,o)
39
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()
40
+ if (o.inspect) return this.inspect (this.min,o)
41
+ else return this.output (this.min,o)
44
42
  } catch (e) {
45
43
  if (typeof e === 'string') return console.error (e)
46
44
  if (e.code) return console.error (e.message)
@@ -66,7 +64,7 @@ class Minify extends Command {
66
64
  * Subclasses override this method to provide custom minification behavior.
67
65
  */
68
66
  async minify (csn,o) {
69
- let m = this.minifier ??= o.dry ? new Dryifier(csn) : new Minifier()
67
+ let m = this.minifier ??= o.dry ? (new Dryifier).for(csn) : new Minifier
70
68
  csn = await this.cleanse (csn,o)
71
69
  return m.minify (csn,o)
72
70
  }
@@ -79,7 +77,7 @@ class Minify extends Command {
79
77
  /**
80
78
  * Subclasses override this method to provide custom print behavior.
81
79
  */
82
- print (csn,o) { // eslint-disable-line no-unused-vars
80
+ output (csn,o) { // eslint-disable-line no-unused-vars
83
81
  console.log (cds.compile.to.json (csn))
84
82
  }
85
83
 
@@ -95,12 +93,13 @@ class Minify extends Command {
95
93
  */
96
94
  class Dryifier extends Minifier {
97
95
 
98
- constructor(csn) { super(); this.all = Object.keys (csn.definitions) }
96
+ for (csn) { this.all = Object.keys (csn.definitions); return this }
99
97
  capture = {}
100
98
  level = 0
101
99
 
102
100
  keep (each,d) {
103
- if (each in this.kept) return; else this.capture[each] = ++this.level
101
+ if (each in this.kept) return
102
+ if (each in this.defs) this.capture[each] = ++this.level
104
103
  super.keep (each, d)
105
104
  this.level--
106
105
  }
@@ -109,10 +108,10 @@ class Dryifier extends Minifier {
109
108
  const { GREEN, GRAY, DIMMED, RESET } = cds.utils.colors
110
109
  const min = super.minify (csn, o)
111
110
  const kept = Object.keys(this.capture)
112
- console.log (RESET+'\n', 'Keep:', kept.length, '\n')
111
+ console.log (RESET+'\n', 'Kept:', kept.length, '\n')
113
112
  for (let [ each, level ] of Object.entries(this.capture))
114
113
  console.log (' '+GRAY,'•'.repeat(level)+GREEN, each, RESET)
115
- console.log (RESET+'\n', 'Skip:', this.all.length - kept.length, '\n')
114
+ console.log (RESET+'\n', 'Skipped:', this.all.length - kept.length, '\n')
116
115
  for (let each of this.all)
117
116
  each in this.capture || console.log (DIMMED, ' -', each, RESET)
118
117
  console.log (RESET+'\n', 'Total:', this.all.length, '\n')
package/bin/mock.js CHANGED
@@ -1,22 +1,30 @@
1
- const cds = require ('../lib/cds')
2
- const serve = cds._local('@sap/cds/bin/serve')
3
- module.exports = Object.assign (
4
- cds_mock, serve, {help: `
1
+ const cds = require ('../lib/cds'); require = cds._local // eslint-disable-line no-global-assign
2
+ const serve = require ('@sap/cds/bin/serve') // eslint-disable-line cds-dk/use-api-require, cds-dk/use-relative-require
3
+
4
+ module.exports = exports = Object.assign (cds_mock, serve)
5
+
6
+ exports.help = `
5
7
  # SYNOPSIS
6
8
 
7
9
  *cds mock* <service> [<options>]
8
10
 
9
11
  This command is essentially just a convenience shortcut to
10
- _cds serve --mocked <service> --in-memory? --port 0_
12
+ _cds serve --mocked <service> --in-memory? --port 0_.
13
+
14
+ If <service> resolves to an existing CAP project root, that changes to
15
+ _cds serve --mocked --project <service> --in-memory? --port 0_.
11
16
 
12
17
  # OPTIONS
13
18
 
14
19
  - see _cds help serve_
15
20
 
16
21
  # OPTIONS
17
- `})
22
+ `
18
23
 
19
- function cds_mock (projects, options) {
20
- if (!projects || !projects.length) return this.help('mock')
21
- return serve (projects, { mocked:true, port:'0', 'in-memory?':true, ...options })
24
+ function cds_mock (argv, options) {
25
+ const [service] = argv
26
+ const o = { mocked: true, port: '0', 'in-memory?': true, ...options }
27
+ if (!o.project && cds.utils.isfile (service, 'package.json'))
28
+ o.project = true
29
+ return serve (argv, o)
22
30
  }
package/bin/repl.js CHANGED
@@ -46,7 +46,6 @@ async function repl ([project], options={}) {
46
46
  inspect.defaultOptions.depth = 11
47
47
 
48
48
  Object.defineProperty (cds,'repl',{value:true})
49
- await cds.service.bindings // REVISIT: this pre-populates many of the cds facade's lazy properties
50
49
  let context={}, repl = { context, displayPrompt(){} }
51
50
  if ((options.run ??= project)) await _run (options.run)
52
51
  if (options.use) _export (...options.use.split(','))
@@ -95,8 +94,9 @@ async function repl ([project], options={}) {
95
94
 
96
95
  async function _run (project) {
97
96
  await require('../bin/serve') ([project], { project, port: 0,
97
+ 'with-bindings': true,
98
+ 'with-mocks': true,
98
99
  'in-memory?': true,
99
- 'with-mocks': true
100
100
  })
101
101
  _export ('entities', 'services')
102
102
  }
package/bin/up.js CHANGED
@@ -33,15 +33,16 @@ const { execSync } = require('node:child_process')
33
33
  const { tmpdir } = require('node:os')
34
34
  const crypto = require('node:crypto')
35
35
  const cds = require('../lib/cds')
36
- const { highlight, warn, dim, info } = require('../lib/util/term')
36
+ const { highlight, warn, dim, info, bold, link } = require('../lib/util/term')
37
37
  const { exists, fs, path, path: { join }, read, write } = cds.utils
38
38
  const { ask4 } = require('../lib/util/question')
39
39
  const { readProject } = require('../lib/init/projectReader')
40
40
  const { mkdirp, copy } = cds.utils
41
41
  const { containerize } = require('../bin/util/containerize')
42
42
  const { calcDirChecksum } = require('../lib/util/checksum')
43
+ const { URLS } = require('../lib/init/constants')
43
44
 
44
- const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) ? console.debug : undefined
45
+ const DEBUG = /\b(y|all|cli|up)\b/.test(process.env.DEBUG) ? console.debug : undefined
45
46
 
46
47
  const run = (cmd, { continueOnFail = false, silent = false } = {}) => {
47
48
  const redactedCmd = cmd.replace(/(--docker-password=)(['"]?)[^'"\s]+(['"]?)/, '$1[REDACTED]$3')
@@ -101,9 +102,26 @@ function writeStoredChecksum(appName, checksum) {
101
102
  async function up() {
102
103
  const project = readProject()
103
104
  const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice, hasKyma, hasMta, k8s } = project
104
- const toKyma = cds.cli.options.to === 'k8s' || hasKyma && !hasMta
105
+ const toKyma = cds.cli.options.to === 'k8s' || hasKyma && !hasMta, toCf = !toKyma
105
106
 
106
- const html5DeployerPath = join(cds.root, cds.env.folders.app, 'html5-deployer', 'resources')
107
+ if (toCf) {
108
+ if (!hasCommand('mbt')) {
109
+ throw `Missing 'mbt' command. Run ${bold('npm install -g mbt')} to install the Cloud MTA Build Tool.`
110
+ }
111
+ if (!hasCommand('cf')) {
112
+ throw `Missing 'cf' command. Refer to ${link(URLS.CF_INSTALL)} to install the Cloud Foundry CLI.`
113
+ }
114
+ } else if (toKyma) {
115
+ if (!hasCommand('docker')) {
116
+ throw `Missing 'docker' command. Refer to ${link(URLS.DOCKER_INSTALL)} to install Docker.`
117
+ }
118
+ if (!hasCommand('helm')) {
119
+ throw `Missing 'helm' command. Refer to ${link(URLS.HELM_INSTALL)} to install Helm.`
120
+ }
121
+ if (!hasCommand('kubectl')) {
122
+ throw `Missing 'kubectl' command. Refer to ${link(URLS.KUBECTL_INSTALL)} to install kubectl.`
123
+ }
124
+ }
107
125
 
108
126
  for (const { app } of apps) {
109
127
  const appPath = path.join(cds.root, cds.env.folders.app, app)
@@ -137,6 +155,8 @@ async function up() {
137
155
  if (!exists('chart') || !exists('containerize.yaml')) run('cds add kyma')
138
156
 
139
157
  if (project.hasHtml5Repo) {
158
+ const html5DeployerPath = join(cds.root, cds.env.folders.app, 'html5-deployer', 'resources')
159
+
140
160
  if (apps.length > 0) {
141
161
  await mkdirp(html5DeployerPath)
142
162
  }
@@ -270,3 +290,13 @@ async function up() {
270
290
  run(`cf deploy gen/mta.tar -f ${retries}`)
271
291
  }
272
292
  }
293
+
294
+ const hasCommand = cmd => {
295
+ try {
296
+ execSync(process.platform === 'win32' ? `where ${cmd}` : `command -v ${cmd}`, { stdio: 'ignore' })
297
+ return true
298
+ } catch (e) {
299
+ DEBUG?.(e)
300
+ return false
301
+ }
302
+ }
package/bin/version.js CHANGED
@@ -1,166 +1,237 @@
1
- const { join, resolve } = require ('path')
2
- const { execSync, spawnSync } = require('child_process')
3
- module.exports = Object.assign(list_versions, {
4
- flags: [ '--info', '--markdown', '--all', '--npm-list', '--npm-tree', '--no-colors'],
5
- shortcuts: [ '-i', '-m','-a', '-ls', '-ll' ],
6
- info,
7
- help: `
1
+
2
+ module.exports = exports = async function cds_version (pattern, options) {
3
+ console.log()
4
+ const o = canonic_options4 (pattern, options)
5
+ const all = await Promise.all([
6
+ node_versions(o),
7
+ java_versions(o),
8
+ ])
9
+ print_versions (all.flat(),o)
10
+ if (o.info) console.log ('\n','> Project:', project_info (o))
11
+ if (o._elsp) console.log ('\n', `${RED}ELSPROBLEMS${RESET} reported by ${BOLD}npm ls${RESET}. Run ${BOLD}npm install${RESET} to fix dependency problems, then rerun ${BOLD}cds version${RESET}.`)
12
+ console.log()
13
+ }
14
+
15
+ exports.options = [ '--depth', '--omit' ]
16
+ exports.flags = [ '--info', '--markdown', '--abs', '--all' ]
17
+ exports.shortcuts = [ '-d', '-o', '-i', '-m', '-a' ]
18
+ exports.help = `
8
19
  # SYNOPSIS
9
20
 
10
21
  *cds version* <options>
11
22
  *cds -v* <option>
12
23
 
13
- Prints the versions of all @sap/cds packages in your package dependencies.
24
+ Uses _npm ls_ to fetch the versions of installed CAP packages, and
25
+ prints them in a tabular layout.
14
26
 
15
27
  # OPTIONS
16
28
 
17
- *-i | --info*
29
+ *-d | --depth* <n>
18
30
 
19
- Prints version information in a tabular markdown format, which you
20
- can embed into your bug reports.
31
+ Specifies the depth of the dependency tree to traverse.
32
+ Passed to _npm ls --depth_.
21
33
 
22
- *-a | --all*
34
+ *-o | --omit* <dev/optional/peer>
23
35
 
24
- Also lists sub-packages and optional dependencies.
36
+ Omits certain types of dependencies when traversing the
37
+ dependency tree. Passed to _npm ls --omit_.
25
38
 
26
- *-ls | --npm-list* <pattern>
27
- *-ll | --npm-tree* <pattern>
39
+ *-m | --markdown*
28
40
 
29
- Prints an npm ls tree filtered to the specified pattern.
30
- (default: '@sap/cds')
41
+ Prints version information in a tabular markdown format,
42
+ which you can embed into your bug reports.
31
43
 
32
- `})
44
+ *-i | --info*
33
45
 
34
- const MISSING = '-- missing --'
46
+ Same as *--markdown*, plus prints project name and
47
+ repository URL at the end as a markdown link.
48
+ `
35
49
 
36
- function list_versions (args, options) {
37
- const {colors} = require('../lib/util/term')
38
50
 
39
- if (options['npm-list'] || options['npm-tree']) {
40
- let [pattern] = args, re = pattern ? RegExp(pattern) : /@sap\/cd[rs]|@sap\/eslint-plugin-cds/
41
- let cmd = 'npm ls --depth ' + (options['npm-tree'] ? 11 : 0)
42
- console.log (cmd,'| grep', re.source)
43
- return require('child_process').exec(cmd, (e,stdout)=>{
44
- // if (e) console.error(e)
45
- const replacement = (!colors ? '$1 $2$3$4' : '\x1b[91m$1 \x1b[32m$2\x1b[0m\x1b[2m$3\x1b[32m$4\x1b[0m');
46
- for (let line of stdout.split(/\n/)) if (line.match(re)) console.log(
47
- line.replace(/(@sap[^@]*)@([\S]+)( -> [\S]+)?(deduped)?/,replacement)
48
- )
49
- })
50
- }
51
- const versions = info (options)
52
-
53
- process.stdout.isTTY && console.log()
54
- if (options.markdown || options.info) {
55
- _markdown (versions, options)
56
- } else {
57
- const mark = !colors ? s => s : require('../lib/util/term').info
58
- for (let each of Object.keys(versions)) console.log(`${mark(each)}: ${versions[each]}`)
59
- }
60
- process.stdout.isTTY && console.log()
51
+ const DEBUG = process.env.DEBUG ? (...args) => console.debug('[cds.version] -',...args) : undefined
52
+ const child_process = require ('node:child_process')
53
+ const $ = (cmd,..._) => {
54
+ if (cmd.raw) cmd = String.raw (cmd,..._) // the cmd may be a tagged template
55
+ DEBUG?.(cmd)
56
+ return new Promise ((resolve, reject) => child_process.exec (cmd,
57
+ (e,stdout) => e ? reject(e) : resolve (stdout.trim().split('\n'))
58
+ ))
61
59
  }
62
60
 
63
- function info({ root: cwd = process.cwd() }) {
64
-
65
- const cds = require('../lib')
66
- const env = cds.env.for('cds', cwd)
67
-
68
- // Fetch versions for all CAP Node packages in the current project
69
- const npmRoot = execSync('npm root -g').toString().trim()
70
- const versions = {
71
- ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // linked in cap/dev or installed globally
72
- ..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
73
- ..._versions_from(_pkg_path('@sap/cds', __dirname)), // @sap/cds from here
74
- ..._versions_from(_pkg_path('@sap/cds', cds.home)), // ... or local @sap/cds
75
- ..._versions_from(_pkg_path('@cap-js/db-service', cwd)), // only required transitively
76
- ..._versions_from(cwd,false), // ... more from local app dependencies
77
- '@sap/cds-compiler': cds.compiler.version(),
78
- 'Node.js': process.version,
79
- 'home': cds.home, // effective cds home
80
- }
61
+ // Like $, but tolerates npm ls ELSPROBLEMS
62
+ const $npm_ls = (cmd,..._) => {
63
+ if (cmd.raw) cmd = String.raw (cmd,..._) // the cmd may be a tagged template
64
+ DEBUG?.(cmd)
65
+ return new Promise ((resolve, reject) => child_process.exec (cmd, (e, stdout, stderr) => {
66
+ const out = (stdout || '').trim()
67
+ const err = (stderr || '').trim()
68
+ if (err) DEBUG?.('npm ls stderr:\n' + err)
69
+ const isELSPROBLEMS = !!(e && /\bELSPROBLEMS\b/.test(err))
70
+ if (e && !isELSPROBLEMS) {
71
+ const code = e.code ?? e.signal ?? 'UNKNOWN'
72
+ const msg =
73
+ `Failed to run ${BOLD}npm ls${RESET} to collect Node.js package versions.\n` +
74
+ `\n` +
75
+ ` Command: ${cmd}\n` +
76
+ ` Exit: ${code}\n` +
77
+ `\n` +
78
+ `Re-run with DEBUG=1 to see npm output.\n` +
79
+ `Run ${BOLD}npm install${RESET}, then rerun ${BOLD}cds version${RESET}.`
80
+ const nice = new Error(msg, {cause: e})
81
+ return reject(nice)
82
+ }
83
+ resolve({
84
+ lines: out ? out.split('\n') : [],
85
+ elsp: isELSPROBLEMS
86
+ })
87
+ }))
88
+ }
81
89
 
82
- // Sort entries, with @sap/cds packages on top, followed by @cap-js packages
83
- const keys = Object.keys(versions).sort(), sorted={}
84
- for (let k of ['@sap/cds']) if (!(k in sorted)) sorted[k] = versions[k]
85
- for (let k of keys) if (!(k in sorted) && k.startsWith('@sap/cds')) sorted[k] = versions[k]
86
- for (let k of keys) if (!(k in sorted) && k.startsWith('@cap-js')) sorted[k] = versions[k]
87
- for (let k of keys) if (!(k in sorted)) sorted[k] = versions[k]
88
-
89
- // For Java projects we also need to collect the Java SDK version
90
- const javaInfo = !env.cli?.version?.skipjava && env['project-nature'] === 'java'
91
- if (javaInfo) {
92
- if (process.stderr.isTTY) console.error('\x1b[2m'+'This takes a while. Collecting Java versions...', '\x1b[0m')
93
- Object.assign(sorted, capJavaVersion(cwd))
94
- }
90
+ const cds = require('../lib')
91
+ const home = require('os').homedir()
92
+ const cap_packages = /@sap\/cds|@cap-js\//
93
+ const { GREEN, YELLOW, RED, DIMMED, BOLD, RESET } = cds.utils.colors
94
+ const { join, dirname } = require ('node:path')
95
95
 
96
- return sorted
97
- }
98
96
 
99
- function _versions_from(pkg_path, pkg_label) {
100
- let pkg; try { pkg = require(join(pkg_path, 'package.json')) } catch { return {} }
101
- let versions = {}
102
- if (pkg.name && pkg_label !== false) versions[pkg_label || pkg.name] = pkg.version
103
-
104
- const deps = { ...pkg.dependencies, ...pkg.devDependencies }
105
- for (let dep in deps) {
106
- if (!dep.match(/@sap\/.*cds.*/) && !dep.startsWith('@cap-js/')) continue
107
- try {
108
- const dep_path = require.resolve(join(dep, 'package.json'), {paths:[pkg_path]})
109
- const dep_pkg = require(dep_path)
110
- versions[dep_pkg.name] = dep_pkg.version
111
- } catch (e) {
112
- if (e.code !== 'MODULE_NOT_FOUND') console.error(e)
113
- }
97
+ function canonic_options4 ([pattern], options) {
98
+ const o = {
99
+ root: cds.root,
100
+ depth: options.all ? 11 : 1,
101
+ ...options,
114
102
  }
115
- return versions
103
+ if (pattern) o.pattern = pattern
104
+ if (o.info) o.markdown ??= true
105
+ DEBUG?.('options:',o)
106
+ return o
116
107
  }
117
108
 
118
- function _markdown (versions, { root: cwd = process.cwd() }) {
119
- const pkg = { name:'', repository:'', version:'' }; try {
120
- Object.assign (pkg, require (require.resolve('./package.json', { paths:[cwd] })))
121
- } catch (e) {/* ignored */}
122
- const repo = pkg.repository.url || pkg.repository || 'https://github.com/<your/repo>'
123
- const valLength = Math.max(repo.length, versions.home.length)
124
-
125
- console.log ('|', $(pkg.name, repo), '|')
126
- console.log ('|', $('----------------------', '-'.repeat(valLength)), '|')
109
+ async function node_versions(o) {
110
+
111
+ // Call npm root and npm ls in parallel...
112
+ const [ [npm_root_g],[npm_root_l], { lines, elsp } ] = await Promise.all ([
113
+ $`npm root --global`,
114
+ $`npm root --local`,
115
+ $npm_ls`npm ls -lp ${
116
+ (o.omit ? o.omit.split(',').map(o => `--omit=${o} `).join('') : '') +
117
+ (o.depth ? `--depth=${o.depth}` : '--depth=1')
118
+ }`
119
+ ])
120
+
121
+ // Parse information returned by npm ls -lp
122
+ // Example macOS: /Users/me/projects/bookshop/node_modules/@sap/cds-dk:@sap/cds-dk@1.0.0:/Users/me/projects/bookshop/cds-dk
123
+ // Example Windows: C:\Users\me\projects\bookshop\node_modules\@sap\cds-dk:@sap/cds-dk@1.0.0:C:\Users\me\projects\bookshop\cds-dk
124
+ const _path = /(?:\\\\\?\\)?[A-Za-z]:[^:]+|[^:]+/.source
125
+ const _line = new RegExp(`^(${_path}):(@?.+)@([^:]+):?(.*)?$`)
126
+ const _pkg = o.pattern ? RegExp(o.pattern) : cap_packages
127
+ const deps = lines.map (line => {
128
+ let [, path, name, version, location = path ] = _line.exec(line) || []
129
+ if (!location || location.includes('EXTRANEOUS')) return
130
+ if (name.match(_pkg)) return [ name, version, location ]
131
+ }) .filter (x => x)
132
+
133
+ // Sort SAP packages and CAP packages on top
134
+ const entries = deps.sort (function sap_then_cap_on_top ([a],[b]) {
135
+ let a_is_cdk = a === '@sap/cds-dk'
136
+ let b_is_cdk = b === '@sap/cds-dk'
137
+ if (a_is_cdk && !b_is_cdk) return -1
138
+ if (b_is_cdk && !a_is_cdk) return 1
139
+ let a_is_sap = a.startsWith('@sap/cds')
140
+ let b_is_sap = b.startsWith('@sap/cds')
141
+ if (a_is_sap && !b_is_sap) return -1
142
+ if (b_is_sap && !a_is_sap) return 1
143
+ let a_is_cap = a.startsWith('@cap-js')
144
+ let b_is_cap = b.startsWith('@cap-js')
145
+ if (a_is_cap && !b_is_cap) return -1
146
+ if (b_is_cap && !a_is_cap) return 1
147
+ return a < b ? -1 : a > b ? 1 : 0
148
+ })
149
+
150
+ // Put global cds-dk at the top
151
+ try {
152
+ const loc = require.resolve ('@sap/cds-dk/package.json', { paths: [npm_root_g] })
153
+ const gdk = require (loc)
154
+ entries.unshift (
155
+ [ '@sap/cds-dk (global)', gdk.version, dirname(loc) ]
156
+ )
157
+ } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') throw e }
158
+
159
+ // Add some environment info at the end
160
+ entries.push (
161
+ [ 'cds.home', null, cds.home ],
162
+ [ 'cds.root', null, o.root ],
163
+ [ 'npm root -l', null, npm_root_l ],
164
+ [ 'npm root -g', null, npm_root_g ],
165
+ [ 'Node.js', process.version.slice(1), process.execPath ],
166
+ )
167
+
168
+ if (elsp) o._elsp = true
169
+
170
+ return entries
171
+ }
127
172
 
128
- Object.keys(versions)
129
- .filter(mod => mod != pkg.name)
130
- .forEach(id => console.log ('|', $(id, versions[id]), '|'))
131
173
 
132
- function $ (key, val=MISSING) {
133
- return ( key + ' '.repeat(22)).slice(0,22)
134
- +' | '+ (val + ' '.repeat(valLength)).slice(0,valLength)
135
- }
174
+ function java_versions (o) {
175
+ const javaInfo = !cds.env.cli?.version?.skipjava && cds.env['project-nature'] === 'java'
176
+ if (!javaInfo) return []
177
+ else if (process.stderr.isTTY) console.warn (DIMMED, 'This takes a while. Collecting Java versions...', RESET)
178
+ const { MAVEN_ARCHETYPE_VERSION:mav } = require ('../lib/init/constants')
179
+ const cmd = `mvn com.sap.cds:cds-maven-plugin:${mav}:version -B -ntp -Dcds.version.excludeCds -Dcds.version.json`
180
+ try {
181
+ const out = child_process.spawnSync (cmd, { cwd: o.root, shell: true }).stdout.toString().trim()
182
+ const [,match] = out.match (/===\n(.*)\n===/ms) || []
183
+ if (match) return Object.entries(JSON.parse(match)).map(([name,v]) => {
184
+ let versions = Object.entries(v).map(([k,v]) => (k === 'version' || k === 'name') ? v : `${k}:${v}`).join(', ')
185
+ return [ name, versions ]
186
+ })
187
+ } catch { /* ignored */ }
188
+ return [[ 'CAP Java SDK', '-- missing --' ]]
136
189
  }
137
190
 
138
- function _pkg_path(id, ...paths) {
191
+
192
+ function project_info (o) {
139
193
  try {
140
- const pk_path = require.resolve(join(id, 'package.json'), { paths })
141
- return resolve(pk_path, '..')
142
- } catch (e) {
143
- if (e.code !== 'MODULE_NOT_FOUND') throw e
144
- }
194
+ const { name, repository } = require (join (o.root,'package.json'))
195
+ const repo = repository?.url || repository || ''
196
+ return `[${GREEN}${name}${RESET}](${DIMMED}${repo}${RESET})`
197
+ } catch { /* ignored */ }
145
198
  }
146
199
 
147
- function capJavaVersion(cwd) {
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`
150
200
 
151
- let out = ''
152
- try {
153
- out = spawnSync(cmd, {cwd, shell: true}).stdout.toString().trim()
154
- } catch (e) { /* ignored */ }
155
-
156
- const match = out.match(/===\n(.*)\n===/ms)
157
- if (match?.[1]) {
158
- const res = {}
159
- Object.entries(JSON.parse(match[1])).forEach(([k,v]) => {
160
- const entries = Object.entries(v)
161
- res[k] = entries.map(([k,v]) => (k === 'version' || k === 'name') ? v : `${k}: ${v}`).join(', ')
162
- })
163
- return res
201
+ function print_versions (rows, o, cwd = o.root+'/') {
202
+
203
+ if (!rows || rows.length === 0) return console.error (YELLOW, ' No CAP packages found.', RESET)
204
+ const local = o.abs ? p => p : p => !p ? '' : p.startsWith(cwd) ? p.replace(cwd,'./') : p.replace(home,'~')
205
+
206
+ if (cds.env['project-nature'] === 'java') { // cds10: remove
207
+ console.log(rows.map(([name,version,location]) => `${GREEN}${name}:${RESET} ${version??location}`).join('\n'))
208
+ return
164
209
  }
165
- return { 'CAP Java SDK': MISSING }
210
+
211
+ // compute column widths for tabular printing
212
+ const header = o.markdown && [ 'Package', 'Version', 'Location' ]
213
+ const all = Object.values(rows); if (header) all.push (header)
214
+ const w1 = Math.max (...all.map(s => s[0]?.length || 0))
215
+ const w2 = Math.max (...all.map(s => s[1]?.length || 0))
216
+ const w3 = Math.max (...all.map(s => (s[2] = local(s[2]))?.length || 0))
217
+
218
+ // prepare line printer function for column-aligned output
219
+ const I = header ? '|' : ''
220
+ const print = ([ name, version, location ], [c1,c2,c3]) => console.log(
221
+ RESET,I+c1, (name||'').padEnd(w1),
222
+ RESET+I+c2, (version||'').padEnd(w2),
223
+ RESET+I+c3, (location||'').padEnd(w3),
224
+ RESET+I
225
+ )
226
+
227
+ // print table header
228
+ if (header) {
229
+ const uncolored = ['','','']
230
+ print (header, uncolored)
231
+ print ([w1,w2,w3].map(n => '-'.repeat(n)), uncolored)
232
+ }
233
+
234
+ // print table rows
235
+ const colored = [ GREEN, YELLOW, DIMMED ]
236
+ for (let each of rows) print (each, colored)
166
237
  }
@@ -15,6 +15,10 @@ const DEBUG = cds.debug('cli|build')
15
15
  const LOG_LIMIT = 30
16
16
 
17
17
  class BuildTaskEngine {
18
+
19
+ /**
20
+ * @param {import('../build').BuildOptions} options - command options
21
+ */
18
22
  constructor(options) {
19
23
  this._taskFactory = new BuildTaskFactory(options)
20
24
  }
@@ -1,13 +1,11 @@
1
1
  // @ts-check
2
- const fs = require('fs')
3
- const path = require('path')
4
2
  const cds = require('../cds')
5
3
  const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
6
- const { hasJavaNature, getProperty, getDefaultModelOptions, hasOptionValue, BuildError } = require('./util')
7
- const { FILE_EXT_CDS, OPTION_WS, IGNORE_DEFAULT_MODELS, NODEJS_MODEL_EXCLUDE_LIST, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF } = require("./constants")
4
+ const { hasJavaNature, getProperty, getDefaultModelPaths, hasOptionValue, BuildError } = require('./util')
5
+ const { OPTION_WS, IGNORE_DEFAULT_MODELS, NODEJS_MODEL_EXCLUDE_LIST, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF } = require("./constants")
8
6
  const DEBUG = cds.debug('cli|build')
9
7
 
10
- class BuildTaskFactory {
8
+ module.exports = class BuildTaskFactory {
11
9
  constructor(options) {
12
10
  this._providerFactory = new BuildTaskProviderFactory(options)
13
11
  }
@@ -86,11 +84,11 @@ class BuildTaskFactory {
86
84
  task.options.model = [task.options.model]
87
85
  }
88
86
  })
89
- const modelPaths = await getDefaultModelOptions(false)
87
+ const modelPaths = await getDefaultModelPaths(false)
90
88
  let wsModelPaths
91
89
  // calculate only once
92
90
  if (cds.cli.options?.[OPTION_WS] || tasks.some(task => hasOptionValue(task.options?.[OPTION_WS], true))) {
93
- wsModelPaths = await getDefaultModelOptions(true)
91
+ wsModelPaths = await getDefaultModelPaths(true)
94
92
  }
95
93
  // set default model options
96
94
  tasks.forEach(task => {
@@ -197,32 +195,4 @@ class BuildTaskFactory {
197
195
  }
198
196
  task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(modelPaths) : modelPaths)]
199
197
  }
200
-
201
- /**
202
- * Determines the module folder from the past list that may represent files or folders w or w/o .cds file extension.
203
- * @param {Array} filesOrFolders
204
- */
205
- static _getModuleFolder(filesOrFolders) {
206
- const resources = [...filesOrFolders]
207
- filesOrFolders.forEach(fileOrFolder => {
208
- if (path.extname(fileOrFolder) !== FILE_EXT_CDS) {
209
- resources.push(fileOrFolder + FILE_EXT_CDS)
210
- }
211
- })
212
- return resources.reduce((acc, resource) => {
213
- if (!acc) {
214
- let resourcePath = path.resolve(cds.root, resource)
215
- if (fs.existsSync(resourcePath)) {
216
- if (cds.utils.isdir(resourcePath)) {
217
- acc = resource
218
- } else {
219
- // represents file
220
- acc = path.dirname(resource)
221
- }
222
- }
223
- }
224
- return acc
225
- }, null)
226
- }
227
198
  }
228
- module.exports = BuildTaskFactory