@sap/cds-dk 8.9.3 → 9.0.4

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 (100) hide show
  1. package/CHANGELOG.md +95 -2012
  2. package/LICENSE +15 -21
  3. package/bin/bind.js +1 -1
  4. package/bin/build.js +35 -37
  5. package/bin/cds.js +12 -29
  6. package/bin/compile/index.js +2 -2
  7. package/bin/debug.js +27 -7
  8. package/bin/deploy/index.js +6 -23
  9. package/bin/deploy/to-hana.js +4 -2
  10. package/bin/help.js +3 -1
  11. package/bin/init.js +1 -1
  12. package/bin/up.js +45 -12
  13. package/bin/watch.js +45 -63
  14. package/lib/bind/cf.js +2 -1
  15. package/lib/bind/k8s.js +2 -2
  16. package/lib/bind/plugin.js +1 -1
  17. package/lib/bind/shared.js +16 -25
  18. package/lib/build/buildTaskEngine.js +26 -10
  19. package/lib/build/buildTaskFactory.js +3 -2
  20. package/lib/build/buildTaskProvider.js +18 -7
  21. package/lib/build/buildTaskProviderFactory.js +35 -13
  22. package/lib/build/constants.js +4 -0
  23. package/lib/build/index.js +1 -1
  24. package/lib/build/plugin.js +89 -54
  25. package/lib/build/plugins/helm/files/subcharts/web-application/Chart.yaml +3 -1
  26. package/lib/build/plugins/helm/files/subcharts/web-application/templates/_helpers.tpl +4 -2
  27. package/lib/build/plugins/helm/files/subcharts/web-application/templates/api-rule.yaml +4 -2
  28. package/lib/build/plugins/helm/files/subcharts/web-application/templates/deployment.yaml +18 -1
  29. package/lib/build/plugins/helm/files/subcharts/web-application/templates/service-binding.yaml +0 -1
  30. package/lib/build/plugins/helm/files/subcharts/web-application/values.schema.json +34 -2
  31. package/lib/build/plugins/helm/files/subcharts/web-application/values.yaml +9 -2
  32. package/lib/build/plugins/helm/index.js +2 -1
  33. package/lib/build/plugins.js +24 -10
  34. package/lib/build/provider/edmxBuildPlugin.js +1 -11
  35. package/lib/build/provider/hana/index.js +12 -5
  36. package/lib/build/provider/hana/template/package.json +1 -1
  37. package/lib/build/provider/internalBuildPlugin.js +1 -1
  38. package/lib/build/provider/internalBuildTaskProvider.js +3 -24
  39. package/lib/build/provider/java/index.js +1 -1
  40. package/lib/build/provider/mtx-extension/index.js +3 -8
  41. package/lib/build/provider/nodejs/index.js +28 -6
  42. package/lib/build/util.js +186 -125
  43. package/lib/deploy/index.js +5 -0
  44. package/lib/deploy/to-hana/hana.js +27 -33
  45. package/lib/deploy/to-hana/hdiDeployUtil.js +2 -2
  46. package/lib/import/odata/v2parser.js +34 -3
  47. package/lib/import/openapi/importOpenAPI.js +4 -0
  48. package/lib/index.js +2 -11
  49. package/lib/init/constants.js +1 -2
  50. package/lib/init/merge.js +101 -77
  51. package/lib/init/mvn.js +3 -3
  52. package/lib/init/projectReader.js +1 -1
  53. package/lib/init/template/ams/files/mta.yaml.hbs +6 -1
  54. package/lib/init/template/ams/files/package.json.hbs +1 -1
  55. package/lib/init/template/approuter/files/mta.yaml.hbs +5 -0
  56. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +1 -1
  57. package/lib/init/template/cloud-logging/files/package.json.hbs +3 -3
  58. package/lib/init/template/dynatrace/files/mta.yaml.hbs +1 -1
  59. package/lib/init/template/dynatrace/files/package.json.hbs +2 -2
  60. package/lib/init/template/dynatrace/index.js +2 -1
  61. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +6 -0
  62. package/lib/init/template/enterprise-messaging/index.js +5 -1
  63. package/lib/init/template/extensibility/files/package.json.hbs +1 -1
  64. package/lib/init/template/hana/files/db/package.json +1 -1
  65. package/lib/init/template/hana/files/package.json.hbs +2 -4
  66. package/lib/init/template/helm/index.js +13 -22
  67. package/lib/init/template/helm-unified-runtime/index.js +2 -1
  68. package/lib/init/template/html5-repo/files/values.yaml.hbs +12 -1
  69. package/lib/init/template/html5-repo/index.js +10 -5
  70. package/lib/init/template/http/index.js +1 -1
  71. package/lib/init/template/ias/files/mta.yaml.hbs +1 -1
  72. package/lib/init/template/ias/files/sidecar.package.json.hbs +0 -7
  73. package/lib/init/template/ias/index.js +5 -4
  74. package/lib/init/template/lint/files/package.json +6 -0
  75. package/lib/init/template/lint/index.js +61 -204
  76. package/lib/init/template/mta/files/package.json +1 -1
  77. package/lib/init/template/multitenancy/files/mta.yaml.hbs +2 -2
  78. package/lib/init/template/multitenancy/files/package.json.hbs +1 -1
  79. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +7 -4
  80. package/lib/init/template/multitenancy/index.js +3 -0
  81. package/lib/init/template/nodejs/files/package.json.hbs +5 -2
  82. package/lib/init/template/redis/files/package.json.hbs +1 -1
  83. package/lib/init/template/sqlite/files/package.json +1 -1
  84. package/lib/init/template/toggles/files/package.json.hbs +1 -1
  85. package/lib/init/template/typescript/files/package.json +1 -3
  86. package/lib/init/template/workzone-standard/index.js +2 -0
  87. package/lib/init/template/xsuaa/index.js +2 -6
  88. package/lib/lint/Linter.js +203 -331
  89. package/lib/lint/generators.js +52 -46
  90. package/lib/lint/io.js +77 -289
  91. package/lib/util/fs.js +2 -1
  92. package/lib/util/git.js +1 -1
  93. package/lib/util/glob.js +77 -0
  94. package/lib/watch/livereload-server.js +3 -4
  95. package/lib/watch/node-watch.js +3 -4
  96. package/lib/watch/watched.js +1 -1
  97. package/npm-shrinkwrap.json +48 -868
  98. package/package.json +6 -8
  99. package/readme.md +1 -1
  100. package/lib/lint/checks.js +0 -135
package/bin/watch.js CHANGED
@@ -68,13 +68,11 @@ module.exports = Object.assign ( watch, {
68
68
  `})
69
69
 
70
70
  const cds = require('../lib/cds')
71
- const DEBUG = cds.log('cli|watch')
72
- const {delimiter,dirname,relative,resolve,sep} = require ('path')
71
+ const DEBUG = /\b(y|all|cli|watch)\b/.test (process.env.DEBUG) ? ((..._) => console.debug ('[cli] -', ..._)) : undefined
72
+ const {delimiter,relative,resolve,sep} = require ('path')
73
73
  const {codes} = require('../lib/util/term'), t = codes
74
74
  const fs = require ('fs')
75
75
 
76
- const extDefaults = 'cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml,env'
77
-
78
76
  const escapedSep = sep === '\\' ? '\\\\' : sep // escape backslash for windows
79
77
  const ignoreDefaults = [
80
78
  /\.git/,
@@ -93,21 +91,14 @@ const ignoreDefaults = [
93
91
 
94
92
  async function watch ([cwd], {
95
93
  args = ['serve', 'all'],
96
- ext = extDefaults,
97
- include = cds.env.watch?.include ?? [],
98
- exclude = cds.env.watch?.exclude ?? [],
94
+ ext = 'cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml,env',
95
+ include = [],
96
+ exclude = [],
99
97
  ...options
100
98
  }={}) {
99
+ const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
101
100
  const paths4 = p => typeof p === 'string' ? p.split(',').map(p => p.trim()) : (p || [])
102
101
  const included = paths4(include), excluded = paths4(exclude)
103
- const nonexisting = included.filter(p => !fs.existsSync(p))
104
- if (nonexisting.length > 0) throw `${nonexisting.length > 1 ? 'Paths do' : 'Path does'} not exist: ${nonexisting.join(', ')}`
105
- if (cwd && !cds.utils.exists(cwd)) try { // try module name like `@capire/bookshop`
106
- cwd = require.resolve(cwd+'/package.json', {paths:[ process.cwd() ]}).slice(0,-13)
107
- } catch (err) {
108
- throw `Error: No such folder or package: '${cwd}'`
109
- }
110
- const _cwd = cwd || process.cwd()
111
102
 
112
103
  if (!Object.keys(options).includes('with-mocks')) args.push ('--with-mocks')
113
104
  if (!Object.keys(options).find(a => /^in-memory\??$/.test(a))) args.push ('--in-memory?')
@@ -117,59 +108,61 @@ async function watch ([cwd], {
117
108
  .filter(a => a !== '--watch') // from `cds serve --watch`
118
109
  .filter(a => a !== cwd))
119
110
 
120
- let env = Object.assign (process.env, {
111
+ if (cwd) {
112
+ const ocwd = process.env._original_cwd = process.cwd()
113
+ if (fs.existsSync(cwd)) cwd = resolve (cwd)
114
+ else try { // try module name like `@capire/bookshop`
115
+ cwd = require.resolve (cwd+'/package.json', {paths:[ ocwd ]}).slice(0,-13)
116
+ } catch (err) { throw `Error: No such folder or package: '${cwd}'` }
117
+ log (`cd ${relative(ocwd,cwd)}`)
118
+ }
119
+ else cwd = cds.root
120
+
121
+ log (`\n${t.bold}cds ${args.join(' ')}`)
122
+
123
+ const env = Object.assign (process.env, {
121
124
  _args: JSON.stringify(args),
122
125
  _options: JSON.stringify(options)
123
126
  })
124
127
  if (process.env.CDS_TYPESCRIPT === 'ts-node' || process.env.CDS_TYPESCRIPT === 'true') {
125
128
  // Needed by ts-node/tsc to find the tsconfig.json. Same as `--project`.
126
- env.TS_NODE_PROJECT = env.TS_NODE_PROJECT ?? _cwd
129
+ env.TS_NODE_PROJECT ??= cwd
127
130
  }
128
131
 
129
132
  // if sap/cds is not installed locally, run with cds-dk's deps. (sap/cds, sqlite etc)
130
- if (!installed('@sap/cds', _cwd)) {
131
- env.NODE_PATH = [...paths(_cwd), ...paths(__dirname)].join(delimiter)
133
+ if (!installed('@sap/cds', cwd)) {
134
+ env.NODE_PATH = [...paths(cwd), ...paths(__dirname)].join(delimiter)
132
135
  }
133
136
 
134
- if (typeof ext !== 'string') ext = extDefaults
135
137
  const delay = parseInt(process.env.CDS_WATCH_DELAY) || 200
138
+ const escapedRegex = s => s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
139
+ const resolved = p => resolve(cwd, p)
140
+ const excludedPaths = excluded.map(resolved).map(escapedRegex)
141
+ const ignore = excluded.length
142
+ ? new RegExp(ignoreDefaults.map(d => d.source).concat(excludedPaths).join('|'))
143
+ : new RegExp(ignoreDefaults.map(d => d.source).join('|'))
144
+ const watched = [ cwd, ...included.map(resolved) ]
136
145
 
137
146
  let liveReload //= ...
138
- let firstTime = true
139
- if (!/false|no/.test(options['livereload']) && cds.env.for('cds', _cwd).livereload !== false) {
147
+ if (!/false|no/.test(options.livereload)) {
148
+ log ('( live reload enabled for browsers )')
140
149
  liveReload = new (require ('../lib/watch/livereload-server'))
141
150
  env.CDS_LIVERELOAD_PATH = require.resolve ('../lib/watch/livereload-connect')
142
151
  env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'], cwd, delay)
143
152
  .catch(err => { console.log(`Error starting live reload: ${err.message}`); liveReload = null })
144
153
  }
145
154
 
146
- const log = (first,...more) => console.log (t.yellow + (first||''), ...more, t.reset)
147
- if (cwd) {
148
- const ocwd = process.env._original_cwd = process.cwd()
149
- if (fs.existsSync(cwd)) log (`cd ${cwd}`)
150
- else try {
151
- const resolved = dirname (require.resolve(cwd+'/package.json', {paths:[ocwd]}))
152
- log (`cd ${relative(ocwd,cwd = resolved)}`)
153
- } catch(_){ throw new Error(`No such folder or package: '${ocwd}' -> '${cwd}'`) }
154
- }
155
- log ()
156
- log (`${t.bold}cds ${args.join(' ')}`)
157
- const escapedRegex = s => s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
158
- const resolved = p => resolve(_cwd, p)
159
- const excludedPaths = excluded.map(resolved).map(escapedRegex)
160
- const ignore = excluded.length
161
- ? new RegExp(ignoreDefaults.map(d => d.source).concat(excludedPaths).join('|'))
162
- : new RegExp(ignoreDefaults.map(d => d.source).join('|'))
163
- const watched = [ _cwd, ...included.map(resolved) ]
164
- if (DEBUG._debug) {
165
- console.log(`\nwatching (pid ${process.pid})`, {
166
- extensions:ext, roots:watched, ignoring: [...excludedPaths.map(s => new RegExp(s)), ...ignoreDefaults]
167
- })
168
- }
169
- if (liveReload) console.log (`live reload enabled for browsers`)
155
+ DEBUG && DEBUG('watching:', {
156
+ pid: process.pid,
157
+ folders: watched,
158
+ extensions: ext,
159
+ ignoring: [ ...excludedPaths.map(s => new RegExp(s)), ...ignoreDefaults ]
160
+ })
170
161
 
171
- const { bindingEnv, updateBindings } = require('../lib/bind/shared')
172
- Object.assign(env, await bindingEnv({cwd}))
162
+ if (process.argv.includes('--profile') || process.env.CDS_ENV) {
163
+ const bind = require('../lib/bind/shared')
164
+ Object.assign(env, await bind.env({cwd}))
165
+ }
173
166
 
174
167
  const nodemon = require('../lib/watch/node-watch')
175
168
  const watcher = nodemon ({
@@ -179,23 +172,12 @@ async function watch ([cwd], {
179
172
  }).on('restart', (files)=>{
180
173
  log (`\n${t.bold} ___________________________\n`)
181
174
  if (liveReload) liveReload.markPending(files)
182
- if (this.delayed) clearTimeout (this.delayed)
183
- this.delayed = setTimeout(()=>{
184
- updateBindings({
185
- onBeforeUpdate: () => watcher.stop(),
186
- onAfterUpdate: ({env}) => {
187
- Object.keys(env).forEach( name => watcher.setEnv(name, env[name]) )
188
- watcher.restart()
189
- }
190
- })
191
- FileHandlers.changed (files,cwd)
192
- }, 111) .unref()
193
175
  }).on('message', async (msg) => { switch (msg.code) { // messages from child process
194
176
  case 'listening':
195
- env.PORT = (msg.address && msg.address.port) || msg.port
196
- liveReload && liveReload.reload()
197
- firstTime && options['open'] && require('../lib/watch/open').openURL (options['open'], env.PORT)
198
- firstTime = false
177
+ env.PORT = msg.address?.port || msg.port
178
+ if (options.open) require('../lib/watch/open').openURL (options.open, env.PORT)
179
+ else liveReload?.reload()
180
+ delete options.open
199
181
  break
200
182
  case 'EADDRINUSE': env.PORT = 0; _addr_in_use(msg); break
201
183
  default: console.error (msg)
package/lib/bind/cf.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const cds = require('../cds');
2
+ const { merge } = cds.utils
2
3
  const DEBUG = cds.log('cli')._debug
3
4
  const cf = require('../util/cf');
4
5
  const { bold } = require('../util/term');
@@ -127,7 +128,7 @@ module.exports = new class CFBind extends require('./plugin') {
127
128
  tags: instanceObj.tags
128
129
  }
129
130
  },
130
- credentials: mergeCredentials(credentials, customCredentials)
131
+ credentials: merge?.(credentials, customCredentials) ?? mergeCredentials(credentials, customCredentials)
131
132
  };
132
133
  return resolvedBinding;
133
134
  }
package/lib/bind/k8s.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { execSync } = require('node:child_process')
2
- const { yaml } = require('../cds').utils
2
+ const { yaml, merge } = require('../cds').utils
3
3
  const { mergeCredentials } = require('./shared')
4
4
  const { bold } = require('../util/term')
5
5
 
@@ -72,7 +72,7 @@ module.exports = new class K8SBind extends require('./plugin') {
72
72
  cluster, instance, namespace, secret,
73
73
  vcap
74
74
  },
75
- credentials: mergeCredentials(credentials, customCredentials)
75
+ credentials: merge?.(credentials, customCredentials) ?? mergeCredentials(credentials, customCredentials)
76
76
  }
77
77
  return resolvedBinding
78
78
  }
@@ -1,7 +1,7 @@
1
1
  module.exports = class BindPlugin {
2
2
 
3
3
  /**
4
- * Returns the display name of the binding plugin
4
+ * The product name used in the command line logs
5
5
  * @returns {string} The display name
6
6
  */
7
7
  displayName() {
@@ -47,11 +47,15 @@ async function bind(options = { on: 'cf' }) {
47
47
  on = 'cf'
48
48
  break
49
49
  default:
50
- throw `invalid value '${options.on}' for option --on.`
50
+ throw `invalid value '${options.on}' for option --on`
51
51
  }
52
+
53
+ /** @type {import('./plugin')} */
52
54
  const plugin = require(`./${on}`)
53
55
 
54
- if (!options['to-app-services']) console.log(`retrieving data from ${bold(plugin.displayName())}...`)
56
+ if (!options['to-app-services']) {
57
+ console.log(`retrieving data from ${bold(plugin.displayName())}...`)
58
+ }
55
59
 
56
60
  const services = options.to.split(/,/g)
57
61
  const resolvedServices = await Promise.all(services.map(async service => {
@@ -116,6 +120,7 @@ async function storeBindings(resolvedServices, options) {
116
120
  console.log(`saving bindings to ${bold(jsonPath)} in profile ${bold(options.for)}`)
117
121
  }
118
122
 
123
+ // MAJOR: Remove when cds 8 support is dropped
119
124
  function mergeCredentials(credentials, customCredentials) {
120
125
  if (!customCredentials) return credentials
121
126
  for (const key in customCredentials) {
@@ -134,28 +139,13 @@ function mergeCredentials(credentials, customCredentials) {
134
139
  return credentials
135
140
  }
136
141
 
137
- async function bindingEnv(options) {
142
+ async function env(options) {
138
143
  const bindings = _getUnresolvedBindings(options)
139
144
  const resolvedBindings = await _resolveBindings({ ...options, bindings, cache: {} })
140
145
  return resolvedBindings ? { VCAP_SERVICES: _toVcapServices(resolvedBindings) } : undefined
141
146
  }
142
147
 
143
- async function updateBindings(options, { onBeforeUpdate, onAfterUpdate } = {}) {
144
- const bindings = _getUnresolvedBindings(options)
145
- if (onBeforeUpdate) await onBeforeUpdate()
146
- let resolvedBindings
147
- try {
148
- resolvedBindings = await _resolveBindings({ ...options, bindings, cache: {} })
149
- } catch (error) {
150
- console.error(error.message) // REVISIT: Why not throw here?
151
- return
152
- }
153
- const env = resolvedBindings ? { VCAP_SERVICES: _toVcapServices(resolvedBindings) } : { VCAP_SERVICES: '{}' }
154
- if (onAfterUpdate) await onAfterUpdate({ bindings: resolvedBindings, env })
155
- return { bindings: resolvedBindings, env }
156
- }
157
-
158
- module.exports = { bind, storeBindings, mergeCredentials, bindingEnv, updateBindings }
148
+ module.exports = { bind, storeBindings, mergeCredentials, env }
159
149
 
160
150
  /* Private helpers */
161
151
 
@@ -185,8 +175,7 @@ function _determineKinds(services) {
185
175
  }
186
176
 
187
177
  // Auto configure services based on VCAP_SERVICES
188
- const cds = require('../cds')
189
- const env = cds.env.for('cds', process.cwd())
178
+ const env = _getEnv (process.cwd())
190
179
  if (!env._find_credentials_for_required_service) { // REVISIT: Proper API
191
180
  throw new Error(`Please provide a service kind or update @sap/cds version to use the kind detection.`)
192
181
  }
@@ -210,10 +199,11 @@ function _determineKinds(services) {
210
199
  }
211
200
  }
212
201
 
213
- function _getUnresolvedBindings({ cwd = process.cwd(), env } = {}) {
214
- if (!env) {
215
- env = cds.env.for('cds', path.resolve(cwd)) // REVISIT: This might load cds.env too early!
216
- }
202
+ function _getEnv (cwd) {
203
+ return _getEnv.cached ??= cwd === cds.root ? cds.env : cds.env.for('cds', path.resolve(cwd))
204
+ }
205
+
206
+ function _getUnresolvedBindings({ cwd = process.cwd(), env = _getEnv(cwd) } = {}) {
217
207
  const bindings = {}
218
208
  const requires = env.requires || {}
219
209
  for (const name in requires) {
@@ -246,6 +236,7 @@ async function _resolveBindings({ bindings, cwd = process.cwd(), env, cache = {}
246
236
  }
247
237
 
248
238
  function _toVcapServices(bindings) {
239
+ if (!bindings) return '{}'
249
240
  const services = {}
250
241
  for (const name in bindings) {
251
242
  const { binding, credentials } = bindings[name]
@@ -1,9 +1,11 @@
1
+ // @ts-check
1
2
  const fs = require('node:fs')
2
3
  const path = require('node:path')
3
4
  const { inspect } = require('node:util')
5
+ // @ts-expect-error - _log not declared in cds-types
4
6
  const cds = require('../cds'), { _log: log } = cds
5
7
  const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
6
- const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels, redactCredentials } = require('./util')
8
+ const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
7
9
  const { SEVERITIES, LOG_LEVELS } = require('./constants')
8
10
  const BuildTaskFactory = require('./buildTaskFactory')
9
11
  const InternalBuildPlugin = require('./provider/internalBuildPlugin')
@@ -11,8 +13,6 @@ const COMPILATION_ERROR = 'CompilationError'
11
13
  const COMPILE_MESSAGE = 'CompileMessage'
12
14
  const DEBUG = cds.debug('cli|build')
13
15
  const LOG_LIMIT = 30
14
- // TODO replace with cds.utils.clors.colors.enabled in cds-dk 9
15
- const colors = process.stdout.isTTY && !process.env.NO_COLOR || process.env.FORCE_COLOR
16
16
 
17
17
  class BuildTaskEngine {
18
18
  constructor(options) {
@@ -34,10 +34,7 @@ class BuildTaskEngine {
34
34
  target: cds.env.build.target,
35
35
  tasks: tasks.map(({ _uses, ...task }) => task),
36
36
  ...(!this.options.clean && { clean: this.options.clean }),
37
- }, { depth: 11, colors, compact: 3, breakLength: 250 }))
38
-
39
- DEBUG?.("cds configuration settings:")
40
- DEBUG?.(redactCredentials(cds.env))
37
+ }, { depth: 11, colors: cds.utils.colors.enabled, compact: 3, breakLength: 250 }))
41
38
 
42
39
  // validate required @sap namespace models - log only
43
40
  const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
@@ -77,6 +74,7 @@ class BuildTaskEngine {
77
74
  * Plugin#prepare has been deprecated and was never part of the public API.
78
75
  * Currently only used by internal FioriBuildPlugin.
79
76
  * @deprecated
77
+ * @private
80
78
  * @param {*} plugins
81
79
  * @returns
82
80
  */
@@ -97,6 +95,7 @@ class BuildTaskEngine {
97
95
 
98
96
  /**
99
97
  * @deprecated
98
+ * @private
100
99
  * @param {*} pluginGroup
101
100
  */
102
101
  async _doPrepare(pluginGroup) {
@@ -112,6 +111,7 @@ class BuildTaskEngine {
112
111
  }
113
112
  }
114
113
 
114
+ /** @private */
115
115
  async _executeCleanBuildTasks(plugins) {
116
116
  if (this.options.clean) {
117
117
  // clean entire build staging folder once
@@ -130,6 +130,7 @@ class BuildTaskEngine {
130
130
  }
131
131
  }
132
132
 
133
+ /** @private */
133
134
  async _executeBuildTasks(plugins) {
134
135
  // sort plugins based on priority in
135
136
  plugins = plugins.sort((a, b) => {
@@ -156,6 +157,7 @@ class BuildTaskEngine {
156
157
  return this._resolveHandlerResponse(results, BuildTaskEngine._getMessages(plugins))
157
158
  }
158
159
 
160
+ /** @private */
159
161
  async _executePipeline(pipeline) {
160
162
  let allResults = []
161
163
  for (const group of pipeline) {
@@ -175,7 +177,9 @@ class BuildTaskEngine {
175
177
  return allResults
176
178
  }
177
179
 
180
+ /** @private */
178
181
  _resolveHandlerResponse(results, pluginMessages = []) {
182
+ /** @type {BuildError[]}*/
179
183
  const errors = []
180
184
  const resolvedResults = results.reduce((acc, r) => {
181
185
  if (r.status === 'fulfilled') {
@@ -207,6 +211,7 @@ class BuildTaskEngine {
207
211
  return resolvedResults
208
212
  }
209
213
 
214
+ /** @private */
210
215
  _createPlugin(task) {
211
216
  const plugin = this.taskFactory.createPlugin(task)
212
217
  plugin.init()
@@ -224,6 +229,7 @@ The higher the value the earlier the plugin is run.`
224
229
  return plugin
225
230
  }
226
231
 
232
+ /** @private */
227
233
  _logOutput(plugins) {
228
234
  // log all generated files
229
235
  const files = BuildTaskEngine._getOutput(plugins)
@@ -238,6 +244,7 @@ The higher the value the earlier the plugin is run.`
238
244
  }
239
245
  }
240
246
 
247
+ /** @private */
241
248
  async _writeGenerationLog(plugins) {
242
249
  const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
243
250
  if (outputFile) {
@@ -253,6 +260,7 @@ The higher the value the earlier the plugin is run.`
253
260
  }
254
261
  }
255
262
 
263
+ /** @private */
256
264
  static _getOutput(plugins) {
257
265
  const files = plugins.reduce((acc, plugin) => acc.concat(plugin.files), []).sort()
258
266
  return files.map(file => {
@@ -263,15 +271,18 @@ The higher the value the earlier the plugin is run.`
263
271
  })
264
272
  }
265
273
 
274
+ /** @private */
266
275
  _logTimer(start, end) {
267
276
  console.log(`build completed in ${end - start} ms`)
268
277
  }
269
278
 
279
+ /** @private */
270
280
  _logTaskHandler(plugin) {
271
281
  DEBUG?.(`plugin ${plugin.constructor.name}`)
272
282
  DEBUG?.(`details src [${relativePaths(cds.root, plugin.task.src)}], dest [${relativePaths(cds.root, plugin.task.dest)}], options [${JSON.stringify(plugin.task.options)}]`)
273
283
  }
274
284
 
285
+ /** @private */
275
286
  _logMessages(messages) {
276
287
  if (messages.length > 0) {
277
288
  const options = {
@@ -284,6 +295,7 @@ The higher the value the earlier the plugin is run.`
284
295
  }
285
296
  }
286
297
 
298
+ /** @private */
287
299
  static _resolveRequiredSapServices(tasks) {
288
300
  const taskModelPaths = tasks.reduce((acc, task) => {
289
301
  const model = task.options?.model
@@ -302,13 +314,14 @@ The higher the value the earlier the plugin is run.`
302
314
 
303
315
  /**
304
316
  * Returns a sorted and flattened list of all messages extracted from the given errors.
305
- * @param {Array<Error>} errors
317
+ * @private
318
+ * @param {(string | BuildError)[]} errors
306
319
  */
307
320
  static _getErrorMessages(errors) {
308
321
  let messages = []
309
322
  // flatten all compile messages in order to filter duplicates and sort later on
310
323
  errors.forEach(error => {
311
- if (Array.isArray(error.errors) && error.errors.length > 0) {
324
+ if (typeof error !== 'string' && Array.isArray(error.errors) && error.errors.length > 0) {
312
325
  messages = messages.concat(this._getErrorMessages(error.errors))
313
326
  } else {
314
327
  messages.push(error)
@@ -329,7 +342,8 @@ The higher the value the earlier the plugin is run.`
329
342
  * Sort and filter the given errors of type CompileMessage or BuildMessage according to their severity and location,
330
343
  * but leave any other errors untouched as part of the result array.<br>
331
344
  * The log level ('command line' or 'cds.env' option) is used to filter the given messages.
332
- * @param {...Error} messages
345
+ * @private
346
+ * @param {...{[key:string]: any, severity?: import('./constants').Severity}} messages
333
347
  */
334
348
  _sortMessagesUnique(...messages) {
335
349
  const logLevelIdx = LOG_LEVELS.indexOf(this._getLogLevel())
@@ -338,8 +352,10 @@ The higher the value the earlier the plugin is run.`
338
352
  // filter according to log-level
339
353
  const filteredMessages = messages.filter(message => message && (!message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity)))
340
354
  // remove duplicates
355
+ // @ts-expect-error - formally expects CompileMessage, but actually, an .severity is the only expected property
341
356
  deduplicateMessages(filteredMessages)
342
357
  // sort
358
+ // @ts-expect-error - see above
343
359
  return sortMessagesSeverityAware(filteredMessages)
344
360
  }
345
361
 
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  const fs = require('fs')
2
3
  const path = require('path')
3
4
  const cds = require('../cds')
@@ -70,7 +71,7 @@ class BuildTaskFactory {
70
71
  // obligatory task defaults shared by all tasks
71
72
  await BuildTaskFactory._applyCommonTaskDefaults(tasks)
72
73
 
73
- BuildTaskFactory._setDefaultBuildTargetFolder(tasks)
74
+ BuildTaskFactory._setDefaultBuildTargetFolder()
74
75
  return tasks
75
76
  }
76
77
 
@@ -212,7 +213,7 @@ class BuildTaskFactory {
212
213
  if (!acc) {
213
214
  let resourcePath = path.resolve(cds.root, resource)
214
215
  if (fs.existsSync(resourcePath)) {
215
- if (fs.lstatSync(resourcePath).isDirectory()) {
216
+ if (cds.utils.isdir(resourcePath)) {
216
217
  acc = resource
217
218
  } else {
218
219
  // represents file
@@ -1,27 +1,38 @@
1
1
  /* eslint-disable no-unused-vars */
2
+
3
+ /** @typedef {import("./buildTaskProviderFactory").Task} Task */
4
+ /** @typedef {import("./plugin")} Plugin_ */
5
+
2
6
  /**
3
7
  * @abstract
4
8
  */
5
9
  module.exports = class BuildTaskProvider {
6
10
  /**
7
- * @param {object} task
11
+ * @abstract
12
+ * @param {{for: string}} key
13
+ * @returns {boolean | undefined}
8
14
  */
9
15
  providesTask(key) { }
10
16
  /**
11
- * @returns {object} task
17
+ * @param {{for: string}} key
18
+ * @returns {} task
12
19
  */
13
20
  getTask(key) { return key }
14
21
  /**
15
- * @param {object} task
22
+ * @abstract
23
+ * @param {Task} task
24
+ * @returns {typeof Plugin_}
16
25
  */
17
- getPlugin(task) { }
26
+ getPlugin(task) { /** abstract */ }
18
27
  /**
28
+ * @abstract
19
29
  * @param {Array} tasks
20
30
  * @param {boolean} dependencies
21
31
  */
22
- async lookupTasks(tasks, dependencies) { }
32
+ async lookupTasks(tasks, dependencies) { /* abstract */ }
23
33
  /**
24
- * @param {object} task
34
+ * @abstract
35
+ * @param {Task} task
25
36
  */
26
- async applyTaskDefaults(task) { }
37
+ async applyTaskDefaults(task) { /** abstract */ }
27
38
  }
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  const fs = require('fs')
2
3
  const path = require('path')
3
4
  const cds = require('../cds')
@@ -7,7 +8,25 @@ const BuildTaskProvider = require('./buildTaskProvider')
7
8
  const { BuildError, normalizePath } = require('./util')
8
9
  const DEBUG = cds.debug('cli|build')
9
10
 
11
+ /**
12
+ * @typedef {{
13
+ * clean?: boolean,
14
+ * outputMode?: number,
15
+ * for?: string,
16
+ * src?: string,
17
+ * dest?: string,
18
+ * } & {[key:string]:unknown}} BuildTaskProviderFactoryOptions
19
+ */
20
+
21
+ /**
22
+ * TODO
23
+ * @typedef {object} Task
24
+ */
25
+
10
26
  class BuildTaskProviderFactory {
27
+ /**
28
+ * @param {BuildTaskProviderFactoryOptions} options - build options
29
+ */
11
30
  constructor(options = {}) {
12
31
  options.outputMode = options.outputMode || OUTPUT_MODE_FILESYSTEM
13
32
  if (options.clean === undefined) {
@@ -24,6 +43,7 @@ class BuildTaskProviderFactory {
24
43
  }
25
44
  }
26
45
  })
46
+ /** @type {{ options: BuildTaskProviderFactoryOptions, tasks: Task}} */
27
47
  this._context = { options, tasks: [] } // resolved tasks
28
48
  }
29
49
  get options() {
@@ -53,8 +73,7 @@ class BuildTaskProviderFactory {
53
73
  }
54
74
 
55
75
  async lookupTasks(tasks = [], dependencies) {
56
- for (let i = 0; i < this.providers.length; i++) {
57
- const provider = this.providers[i]
76
+ for (const provider of this.providers) {
58
77
  const existingTasks = [...tasks]
59
78
  await this._lookupTasks(provider, tasks, dependencies)
60
79
  if (existingTasks.length < tasks.length) {
@@ -70,7 +89,7 @@ class BuildTaskProviderFactory {
70
89
  /**
71
90
  * Create a Plugin instance for the given build task.
72
91
  * The implementation is loaded based on the build task's 'for' or 'use' option.
73
- * @param {*} task
92
+ * @param {Task} task
74
93
  */
75
94
  createPlugin(task) {
76
95
  const Plugin = this.getPlugin(task)
@@ -92,7 +111,7 @@ class BuildTaskProviderFactory {
92
111
  * Loads the build plugin implementation for the given build task.
93
112
  * 'for' defines an alias for built-in plugins like 'hana', 'java', 'node', 'fiori' or 'mtx'.
94
113
  * 'use' defines the fully qualified module name of custom build plugins implementations.
95
- * @param {object} task
114
+ * @param {Task} task
96
115
  */
97
116
  getPlugin(task) {
98
117
  const provider = this._getProvider(task)
@@ -104,6 +123,9 @@ class BuildTaskProviderFactory {
104
123
  }
105
124
  }
106
125
 
126
+ /**
127
+ * @param {Task[]} tasks
128
+ */
107
129
  resolveTasks(tasks) {
108
130
  return tasks.map(task => this._resolveTask(task))
109
131
  }
@@ -111,7 +133,8 @@ class BuildTaskProviderFactory {
111
133
  /**
112
134
  * Resolves the given build task based on the project root folder.<br>
113
135
  * The task is validated in order to ensure that 'src' refers to a valid folder and 'for' or 'use' reference can be required.
114
- * @param {*} task
136
+ * @private
137
+ * @param {Task} task
115
138
  */
116
139
  _resolveTask(task) {
117
140
  // second validate src path
@@ -126,7 +149,7 @@ class BuildTaskProviderFactory {
126
149
  try {
127
150
  //validate source path
128
151
  fs.realpathSync(resolvedTask.src)
129
- } catch (e) {
152
+ } catch {
130
153
  throw new BuildError(`The 'src' folder '${path.resolve(cds.root, task.src)}' for build task '${resolvedTask.for}' does not exist`)
131
154
  }
132
155
  }
@@ -170,15 +193,14 @@ class BuildTaskProviderFactory {
170
193
  * Default provider implementation handling fully qualified custom build task declarations.
171
194
  */
172
195
  class PluginBuildTaskProvider extends BuildTaskProvider {
173
- constructor() {
174
- super()
175
- this._plugins = require('./plugins').plugins
176
- }
177
-
196
+ #plugins = require('./plugins').plugins
178
197
  get plugins() {
179
- return this._plugins
198
+ return this.#plugins
180
199
  }
181
200
 
201
+ /**
202
+ * @param {{for: string}} key
203
+ */
182
204
  providesTask(key) {
183
205
  return this.plugins.has(key.for)
184
206
  }
@@ -207,7 +229,7 @@ class PluginBuildTaskProvider extends BuildTaskProvider {
207
229
 
208
230
  getTask(key) {
209
231
  // plugin entry is already validated, a taskDefaults default member exists
210
- const task = JSON.parse(JSON.stringify(this.plugins.get(key.for).taskDefaults ?? {}))
232
+ const task = JSON.parse(JSON.stringify(this.plugins.get(key.for)?.taskDefaults ?? {}))
211
233
  task.for = key.for
212
234
  task.src ??= DEFAULT_SRC_FOLDER
213
235
  task.src = task.src.replace(/\/$/, '')
@@ -61,3 +61,7 @@ exports.LOG_LEVEL_WARN = "warn"
61
61
  exports.LOG_LEVEL_INFO = "info"
62
62
  exports.LOG_LEVEL_DEBUG = "debug"
63
63
  exports.LOG_LEVELS = [exports.LOG_LEVEL_ERROR, exports.LOG_LEVEL_WARN, exports.LOG_LEVEL_INFO, exports.LOG_LEVEL_DEBUG]
64
+
65
+ /**
66
+ * @typedef {'Error' | 'Warning' | 'Info' | 'Debug'} Severity
67
+ **/