@sap/cds-dk 9.8.4 → 9.9.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 (74) hide show
  1. package/CHANGELOG.md +18 -2
  2. package/bin/add.js +3 -3
  3. package/bin/cds.js +16 -2
  4. package/bin/import.js +6 -2
  5. package/bin/logout.js +9 -12
  6. package/lib/bind/shared.js +2 -2
  7. package/lib/build/constants.js +0 -1
  8. package/lib/build/index.js +420 -463
  9. package/lib/build/plugins/hana/index.js +121 -2
  10. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +1 -1
  11. package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +1 -1
  12. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +4 -0
  13. package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +5 -2
  14. package/lib/build/plugins/internal.js +1 -1
  15. package/lib/build/plugins/mtx-extension/index.js +0 -2
  16. package/lib/deploy/index.js +23 -3
  17. package/lib/deploy/to-hana/hana.js +24 -15
  18. package/lib/import/common.js +6 -3
  19. package/lib/init/constants.js +3 -1
  20. package/lib/init/indent.js +0 -1
  21. package/lib/init/merge.js +0 -1
  22. package/lib/init/mvn.js +4 -2
  23. package/lib/init/projectReader.js +0 -1
  24. package/lib/init/registries/mta.js +4 -2
  25. package/lib/init/template/app-frontend/index.js +39 -4
  26. package/lib/init/template/data/as-csv.js +2 -0
  27. package/lib/init/template/esm/index.js +5 -0
  28. package/lib/init/template/extensibility/index.js +1 -1
  29. package/lib/init/template/hana/index.js +1 -1
  30. package/lib/init/template/html5-repo/files/mta.yaml.hbs +9 -20
  31. package/lib/init/template/http/index.js +34 -6
  32. package/lib/init/template/kyma/index.js +1 -0
  33. package/lib/init/template/postgres/files/values.yaml.hbs +1 -1
  34. package/lib/init/template/postgres/index.js +1 -1
  35. package/lib/init/template/react/files/index.html +13 -0
  36. package/lib/init/template/react/files/package.json.hbs +22 -0
  37. package/lib/init/template/react/files/public/favicon.svg +1 -0
  38. package/lib/init/template/react/files/src/App.jsx.hbs +29 -0
  39. package/lib/init/template/react/files/src/index.css +28 -0
  40. package/lib/init/template/react/files/src/main.jsx +10 -0
  41. package/lib/init/template/react/files/vite.config.js +19 -0
  42. package/lib/init/template/react/index.js +29 -0
  43. package/lib/init/template/sample/index.js +1 -4
  44. package/lib/init/template/typer/index.js +1 -1
  45. package/lib/init/template/typescript/files/package.json +1 -1
  46. package/lib/init/template/typescript/index.js +1 -1
  47. package/lib/init/template/ui5/index.js +2 -1
  48. package/lib/init/template/vue/files/index.html +13 -0
  49. package/lib/init/template/vue/files/package.json.hbs +19 -0
  50. package/lib/init/template/vue/files/public/favicon.svg +1 -0
  51. package/lib/init/template/vue/files/src/App.vue.hbs +34 -0
  52. package/lib/init/template/vue/files/src/main.js +5 -0
  53. package/lib/init/template/vue/files/src/style.css +20 -0
  54. package/lib/init/template/vue/files/vite.config.js +19 -0
  55. package/lib/init/template/vue/index.js +29 -0
  56. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +2 -13
  57. package/lib/init/xml.js +0 -2
  58. package/lib/mtx/auth_manager.js +21 -11
  59. package/lib/mtx/download_legacy.js +9 -8
  60. package/lib/mtx/pull.js +9 -8
  61. package/lib/mtx/push.js +42 -28
  62. package/lib/mtx/settings_manager.js +17 -10
  63. package/lib/mtx/tenant_action.js +9 -9
  64. package/lib/mtx/util/request.js +158 -0
  65. package/lib/util/cf.js +30 -29
  66. package/lib/watch/node-watch.js +1 -1
  67. package/lib/watch/vite-watch.js +76 -0
  68. package/lib/watch/watched.js +5 -4
  69. package/npm-shrinkwrap.json +14 -154
  70. package/package.json +1 -2
  71. package/lib/init/template/sample/files/db/hana/index.cds +0 -10
  72. package/lib/init/template/sample/files/db/sqlite/index.cds +0 -10
  73. package/lib/init/template/sample/files/package.json.hbs +0 -14
  74. package/lib/util/axios.js +0 -118
@@ -6,24 +6,24 @@ const { exists, isdir, rimraf, mkdirp, write, fs, colors: { YELLOW, RESET } } =
6
6
  const Plugin = require('./plugins/plugin')
7
7
  const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels, normalizePath, getDefaultModelPaths, hasOptionValue } = require('./util')
8
8
  const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
9
- const { SEVERITIES, SEVERITY_ERROR, LOG_LEVELS, DEFAULT_SRC_FOLDER, OPTION_WS, IGNORE_DEFAULT_MODELS, NODEJS_MODEL_EXCLUDE_LIST, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_JAVA_CF, BUILD_TASK_JAVA } = require("./constants")
9
+ const { SEVERITIES, SEVERITY_ERROR, LOG_LEVELS, DEFAULT_SRC_FOLDER, OPTION_WS, IGNORE_DEFAULT_MODELS, NODEJS_MODEL_EXCLUDE_LIST, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_JAVA_CF, BUILD_TASK_JAVA } = require('./constants')
10
10
  const COMPILATION_ERROR = 'CompilationError'
11
11
  const COMPILE_MESSAGE = 'CompileMessage'
12
12
  const DEBUG = cds.debug('cli|build')
13
13
  const LOG_LIMIT = 30
14
14
 
15
15
  /**
16
- * @typedef {import('./plugins/plugin')} Plugin_
17
16
  * @typedef {import('./plugins/plugin').Task} Task
18
17
  * @typedef {import('../env/schemas/cds-rc')['$defs']['buildTaskType']['enum'][number]} BuildFor
19
18
  * using (string & {}) prevents widening of `'x' | string` to just `string`
20
19
  * @typedef {{
21
20
  * clean?: boolean,
22
21
  * for?: BuildFor | (string & {}),
23
- * src?: string,
24
- * dest?: string,
22
+ * src?: import('node:fs').PathLike,
23
+ * dest?: import('node:fs').PathLike,
25
24
  * model?: string[],
26
- * root?: string
25
+ * root?: import('node:fs').PathLike
26
+ * tasks?: Task[]
27
27
  * } & {[key:string]:unknown}} BuildOptions
28
28
  */
29
29
 
@@ -32,562 +32,519 @@ const LOG_LIMIT = 30
32
32
  *
33
33
  * @param {BuildOptions} options - command options
34
34
  */
35
- async function build({ root = cds.root, tasks, ...options } = {}) {
35
+ function build({ root = cds.root, tasks, clean = true, ...options } = {}) {
36
36
  if (!exists(root) || !isdir(root)) {
37
37
  throw `Project folder '${root}' does not exist`
38
38
  }
39
-
40
- return await new BuildTaskEngine(options).processTasks(tasks)
39
+ const runner = createBuildRunner({ ...options, clean })
40
+ if (options.resolve) return resolveBuildTasks(runner)
41
+ return processTasks(tasks, runner)
41
42
  }
42
43
 
43
44
  function register(id, plugin) {
44
45
  return require('./plugins').register(id, plugin)
45
46
  }
46
47
 
47
- class BuildTaskEngine {
48
-
49
- /**
50
- * @param {BuildOptions} options
51
- */
52
- constructor(options = {}) {
53
- if (options.clean === undefined) {
54
- options.clean = true
48
+ /** @param {BuildOptions} options - command options */
49
+ function createBuildRunner(options) {
50
+ for (const key of ['for', 'src', 'dest']) if (options[key]) {
51
+ if (typeof options[key] !== 'string') {
52
+ throw new BuildError(`Invalid build options - property '${key}' must be a string`)
55
53
  }
56
- for (const key of ['for', 'src', 'dest']) if (options[key]) {
57
- if (typeof options[key] !== 'string') {
58
- throw new BuildError(`Invalid build options - property '${key}' must be a string`)
59
- }
60
- if (key === 'src' || key === 'dest') {
61
- // normalize path separator and remove trailing slash
62
- options[key] = normalizePath(options[key])
63
- }
54
+ if (key === 'src' || key === 'dest') {
55
+ options[key] = normalizePath(options[key])
64
56
  }
65
- /** @type {{ options: BuildOptions, tasks: Task[]}} */
66
- this._context = { options, tasks: [] } // resolved tasks
67
- const plugins = require('./plugins')
68
- this._plugins = plugins.all
69
- plugins.registerDefaults()
70
57
  }
58
+ const context = { options, tasks: [] }
59
+ const plugins = require('./plugins')
60
+ plugins.registerDefaults()
61
+ return { options, context, plugins: plugins.all }
62
+ }
71
63
 
72
- get options() {
73
- return this._context.options
74
- }
75
- get context() {
76
- return this._context
77
- }
78
- get plugins() {
79
- return this._plugins
64
+ async function processTasks(tasks, runner) {
65
+ const startTime = Date.now()
66
+
67
+ tasks ??= await resolveBuildTasks(runner)
68
+ console.log(`building project with`, inspect({
69
+ versions: { cds: cds.version, compiler: cds.compiler.version(), dk: require('../../package.json').version },
70
+ target: cds.env.build.target,
71
+ tasks,
72
+ ...(!runner.options.clean && { clean: runner.options.clean }),
73
+ }, { depth: 11, colors: cds.utils.colors.enabled, compact: 3, breakLength: 250 }))
74
+
75
+ const unresolved = resolveRequiredSapServices(tasks)
76
+ if (unresolved.length > 0) {
77
+ throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Run the 'npm install' command to install up-to-date versions of the missing packages.`)
80
78
  }
81
79
 
82
- async processTasks(tasks) {
83
- const startTime = Date.now()
84
-
85
- tasks ??= await this.getTasks()
86
- console.log(`building project with`, inspect({
87
- versions: { cds: cds.version, compiler: cds.compiler.version(), dk: require('../../package.json').version },
88
- target: cds.env.build.target,
89
- tasks,
90
- ...(!this.options.clean && { clean: this.options.clean }),
91
- }, { depth: 11, colors: cds.utils.colors.enabled, compact: 3, breakLength: 250 }))
92
-
93
- // validate required @sap namespace models - log only
94
- const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
95
- if (unresolved.length > 0) {
96
- throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Run the 'npm install' command to install up-to-date versions of the missing packages.`)
80
+ const plugins = []
81
+ tasks.forEach((task) => {
82
+ if (task) {
83
+ const plugin = createPlugin(task, runner)
84
+ plugin.init()
85
+ plugins.push(plugin)
97
86
  }
87
+ })
98
88
 
99
- // create build plugins
100
- const plugins = []
101
- tasks.forEach((task) => {
102
- if (task) {
103
- const plugin = this.createPlugin(task)
104
- plugin.init()
105
- plugins.push(plugin)
106
- }
107
- })
89
+ try {
90
+ await executeCleanBuildTasks(plugins, runner)
91
+ const buildResult = await executeBuildTasks(plugins, runner)
108
92
 
109
- try {
110
- await this._executeCleanBuildTasks(plugins)
93
+ await writeGenerationLog(plugins)
94
+ logOutput(plugins)
95
+ logMessages(plugins.reduce((acc, plugin) => acc.concat(plugin.messages), []), runner.options)
96
+ console.log(`build completed in ${Date.now() - startTime} ms`)
111
97
 
112
- // throwing Exception in case of compilation errors
113
- const buildResult = await this._executeBuildTasks(plugins)
98
+ return buildResult
99
+ } catch (error) {
100
+ logOutput(plugins)
101
+ throw error
102
+ }
103
+ }
114
104
 
115
- await this._writeGenerationLog(plugins)
116
- this._logOutput(plugins)
117
- this._logMessages(plugins.reduce((acc, plugin) => acc.concat(plugin.messages), []))
118
- console.log(`build completed in ${Date.now() - startTime} ms`)
105
+ async function resolveBuildTasks(runner) {
106
+ const tasks = await createTasks(runner)
107
+ const resolvedTasks = resolveTasks(tasks, runner.options)
108
+ if (runner.options.resolve) return resolvedTasks
109
+ return tasks
110
+ }
119
111
 
120
- return buildResult
121
- } catch (error) {
122
- this._logOutput(plugins)
123
- throw error
124
- }
112
+ function createPlugin(task, runner) {
113
+ const PluginClass = getPlugin(task, runner.plugins)
114
+ const resolvedTask = resolveTask(task, runner.options.ws)
115
+ DEBUG?.(`loaded build plugin [${resolvedTask.for}]`)
116
+
117
+ const plugin = new PluginClass()
118
+ if (!(plugin instanceof PluginClass)) {
119
+ throw new Error(`Invalid Build plugin type ${task.for}`)
125
120
  }
121
+ plugin._task = resolvedTask
122
+ plugin._context = runner.context
123
+ runner.context.tasks.push(resolvedTask)
124
+ DEBUG?.(`created build plugin [${resolvedTask.for}]`)
125
+ return plugin
126
+ }
127
+
128
+ async function createTasks(runner) {
129
+ DEBUG?.(`determining build tasks for project [${cds.root}].`)
126
130
 
127
- async getTasks() {
128
- const tasks = await this._createTasks()
129
- const resolvedTasks = this.resolveTasks(tasks)
130
- if (this.options.resolve) return resolvedTasks
131
- return tasks
131
+ let tasks = Array.isArray(cds.env.build?.tasks) ? cds.clone(cds.env.build.tasks) : []
132
+ if (tasks.length === 0) {
133
+ tasks = await lookupTasks([], false, runner)
134
+ applyCliOptions(tasks, runner.options)
135
+ } else {
136
+ applyCliOptions(tasks, runner.options)
137
+ await applyTaskDefaults(tasks, runner)
138
+ await lookupTasks(tasks, true, runner)
132
139
  }
133
140
 
134
- /**
135
- * Create a Plugin instance for the given build task.
136
- * @param {Task} task
137
- */
138
- createPlugin(task) {
139
- const PluginClass = this.getPlugin(task)
140
- const resolvedTask = this._resolveTask(task)
141
- DEBUG?.(`loaded build plugin [${resolvedTask.for}]`)
141
+ let existingTasks = tasks
142
+ tasks = await filterTasksForCli(tasks, runner)
143
+ if (tasks.length === 0) return tasks
142
144
 
143
- const plugin = new PluginClass()
144
- if (!(plugin instanceof PluginClass)) {
145
- throw new Error(`Invalid Build plugin type ${task.for}`)
146
- }
147
- plugin._task = resolvedTask
148
- plugin._context = this.context
149
- this.context.tasks.push(resolvedTask)
150
- DEBUG?.(`created build plugin [${resolvedTask.for}]`)
151
- return plugin
145
+ existingTasks = [...tasks]
146
+ await lookupTasks(tasks, true, runner)
147
+ if (tasks.length > existingTasks.length) {
148
+ const newTasks = tasks.filter(task => !existingTasks.includes(task))
149
+ applyCliOptions(newTasks, runner.options)
152
150
  }
153
151
 
154
- async _createTasks() {
155
- DEBUG?.(`determining build tasks for project [${cds.root}].`)
152
+ await applyCommonTaskDefaults(tasks)
156
153
 
157
- let tasks = Array.isArray(cds.env.build?.tasks) ? cds.clone(cds.env.build.tasks) : []
158
- if (tasks.length === 0) {
159
- tasks = await this.lookupTasks()
160
- this._applyCliOptions(tasks)
161
- } else {
162
- this._applyCliOptions(tasks)
163
- // 1. apply default values including task.for and ensure that for all tasks a provider exists - throwing error otherwise
164
- await this.applyTaskDefaults(tasks)
165
- // ensure that dependencies get wired up before filtering
166
- await this.lookupTasks(tasks, true)
154
+ if (cds.env['project-nature'] === 'java' && cds.env.build.target !== '.') {
155
+ const userEnv = cds.env.for('cds', cds.root, false)
156
+ if (!userEnv.build?.target) {
157
+ cds.env.build.target = '.'
158
+ DEBUG?.('using inplace build for java project instead of default staging build')
167
159
  }
160
+ }
161
+ return tasks
162
+ }
168
163
 
169
- // 2. filters the list of build tasks
170
- let existingTasks = tasks
171
- tasks = await this._filterTasksForCli(tasks)
172
- if (tasks.length === 0) return tasks
173
-
174
- // 3. add dependencies
175
- existingTasks = [...tasks]
176
- await this.lookupTasks(tasks, true)
177
- if (tasks.length > existingTasks.length) {
178
- const newTasks = tasks.filter(task => !existingTasks.includes(task))
179
- this._applyCliOptions(newTasks)
164
+ async function applyCommonTaskDefaults(tasks) {
165
+ tasks.forEach(task => {
166
+ if (task.options?.model && !Array.isArray(task.options.model)) {
167
+ task.options.model = [task.options.model]
180
168
  }
181
-
182
- await BuildTaskEngine._applyCommonTaskDefaults(tasks)
183
-
184
- // Java projects use "." as the default build target folder
185
- if (cds.env['project-nature'] === 'java' && cds.env.build.target !== ".") {
186
- const userEnv = cds.env.for("cds", cds.root, false)
187
- if (!userEnv.build?.target) {
188
- cds.env.build.target = "."
189
- DEBUG?.("using inplace build for java project instead of default staging build")
190
- }
169
+ })
170
+ const modelPaths = await getDefaultModelPaths(false)
171
+ let wsModelPaths
172
+ if (cds.cli.options?.[OPTION_WS] || tasks.some(task => hasOptionValue(task.options?.[OPTION_WS], true))) {
173
+ wsModelPaths = await getDefaultModelPaths(true)
174
+ }
175
+ tasks.forEach(task => {
176
+ if (!task.src) {
177
+ throw new BuildError(`Mandatory property 'src' not defined for build task '${task.for}'.`)
191
178
  }
192
- return tasks
193
- }
179
+ const optionWs = cds.cli.options?.[OPTION_WS] || hasOptionValue(task.options?.[OPTION_WS], true)
180
+ setTaskModelOptions(task, optionWs ? [...wsModelPaths] : [...modelPaths])
181
+ })
182
+ }
194
183
 
195
- static async _applyCommonTaskDefaults(tasks) {
196
- tasks.forEach(task => {
197
- if (task.options?.model && !Array.isArray(task.options.model)) {
198
- task.options.model = [task.options.model]
199
- }
200
- })
201
- const modelPaths = await getDefaultModelPaths(false)
202
- let wsModelPaths
203
- if (cds.cli.options?.[OPTION_WS] || tasks.some(task => hasOptionValue(task.options?.[OPTION_WS], true))) {
204
- wsModelPaths = await getDefaultModelPaths(true)
184
+ async function filterTasksForCli(tasks, runner) {
185
+ const options = runner.options
186
+ let resultTasks = tasks.filter(task => {
187
+ return (!options.for || options.for === task.for)
188
+ && (!options.src || options.src === task.src)
189
+ })
190
+ if (resultTasks.length === 0) {
191
+ if (options.for) {
192
+ const task = getTask({ for: options.for }, runner.plugins)
193
+ if (options.src) task.src = options.src
194
+ resultTasks.push(task)
195
+ applyCliOptions(resultTasks, options)
196
+ await applyTaskDefaults(resultTasks, runner)
205
197
  }
206
- tasks.forEach(task => {
207
- if (!task.src) {
208
- throw new BuildError(`Mandatory property 'src' not defined for build task '${task.for}'.`)
209
- }
210
- const optionWs = cds.cli.options?.[OPTION_WS] || hasOptionValue(task.options?.[OPTION_WS], true)
211
- this._setTaskModelOptions(task, optionWs ? [...wsModelPaths] : [...modelPaths])
212
- })
198
+ } else if (resultTasks.length <= tasks.length) {
199
+ tasks.length = 0
200
+ resultTasks.forEach(task => tasks.push(task))
201
+ resultTasks = tasks
213
202
  }
203
+ return resultTasks
204
+ }
214
205
 
215
- async _filterTasksForCli(tasks) {
216
- const options = this.options
217
- let resultTasks = tasks.filter(task => {
218
- return (!options.for || options.for === task.for)
219
- && (!options.src || options.src === task.src)
220
- })
221
- if (resultTasks.length === 0) {
222
- if (options.for) {
223
- const task = this.getTask({ for: options.for })
224
- if (options.src) task.src = options.src
225
- resultTasks.push(task)
226
- this._applyCliOptions(resultTasks)
227
- await this.applyTaskDefaults(resultTasks)
228
- }
229
- } else if (resultTasks.length <= tasks.length) {
230
- tasks.length = 0
231
- resultTasks.forEach(task => tasks.push(task))
232
- resultTasks = tasks
206
+ function applyCliOptions(tasks, options) {
207
+ tasks.forEach(task => {
208
+ if (options.dest) task.dest = options.dest
209
+ if (options.taskOptions) {
210
+ const taskOptions = cds.clone(options.taskOptions)
211
+ task.options = task.options ? Object.assign(task.options, taskOptions) : taskOptions
233
212
  }
234
- return resultTasks
235
- }
213
+ })
214
+ }
236
215
 
237
- _applyCliOptions(tasks) {
238
- const options = this.options
239
- tasks.forEach(task => {
240
- if (options.dest) task.dest = options.dest
241
- if (options.taskOptions) {
242
- const taskOptions = cds.clone(options.taskOptions)
243
- task.options = task.options ? Object.assign(task.options, taskOptions) : taskOptions
244
- }
245
- })
246
- }
216
+ function escapeRegExpInput(str) {
217
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
218
+ }
247
219
 
248
- static escapeRegExpInput(str) {
249
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
220
+ function setTaskModelOptions(task, defaultModelPaths) {
221
+ if (task.for !== BUILD_TASK_NODEJS && task.for !== BUILD_TASK_NODE_CF) {
222
+ defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
250
223
  }
251
224
 
252
- static _setTaskModelOptions(task, defaultModelPaths) {
253
- if (task.for !== BUILD_TASK_NODEJS && task.for !== BUILD_TASK_NODE_CF) {
254
- defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
225
+ let taskModelPaths = task.options?.model
226
+ if (taskModelPaths && !Array.isArray(taskModelPaths)) {
227
+ taskModelPaths = [taskModelPaths]
228
+ }
229
+ const allowList = new RegExp(`^@sap/cds${cds.env.requires.toggles
230
+ ? '|^' + escapeRegExpInput(cds.env.features.folders.replace('*', ''))
231
+ : ''}`)
232
+ task.options = task.options || {}
233
+ let modelPaths = []
234
+ if (taskModelPaths?.length) {
235
+ if (!hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
236
+ modelPaths = defaultModelPaths.filter(p => p.match(allowList))
255
237
  }
256
-
257
- let taskModelPaths = task.options?.model
258
- if (taskModelPaths && !Array.isArray(taskModelPaths)) {
259
- taskModelPaths = [taskModelPaths]
238
+ } else {
239
+ modelPaths = [...defaultModelPaths]
240
+ if (!modelPaths.includes(task.src)) modelPaths.push(task.src)
241
+ if (hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
242
+ modelPaths = modelPaths.filter(p => !p.match(allowList))
260
243
  }
261
- const allowList = new RegExp(`^@sap/cds${cds.env.requires.toggles
262
- ? '|^' + BuildTaskEngine.escapeRegExpInput(cds.env.features.folders.replace('*', ''))
263
- : ''}`)
264
- task.options = task.options || {}
265
- let modelPaths = []
266
- if (taskModelPaths?.length) {
267
- if (!hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
268
- modelPaths = defaultModelPaths.filter(p => p.match(allowList))
269
- }
270
- } else {
271
- modelPaths = [...defaultModelPaths]
272
- if (!modelPaths.includes(task.src)) modelPaths.push(task.src)
273
- if (hasOptionValue(task.options?.[IGNORE_DEFAULT_MODELS], true)) {
274
- modelPaths = modelPaths.filter(p => !p.match(allowList))
275
- }
276
- }
277
- task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(modelPaths) : modelPaths)]
278
244
  }
245
+ task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(modelPaths) : modelPaths)]
246
+ }
279
247
 
280
- getTask(key) {
281
- this._normalizeKey(key)
282
- this._assertProvided(key)
283
- const task = cds.clone(this.plugins.get(key.for)?.taskDefaults ?? {})
284
- task.for = key.for
285
- task.src ??= DEFAULT_SRC_FOLDER
286
- task.src = task.src.replace(/\/$/, '')
287
- return task
288
- }
248
+ function getTask(key, plugins) {
249
+ normalizeKey(key)
250
+ assertProvided(key, plugins)
251
+ const task = cds.clone(plugins.get(key.for)?.taskDefaults ?? {})
252
+ task.for = key.for
253
+ task.src ??= DEFAULT_SRC_FOLDER
254
+ task.src = task.src.replace(/\/$/, '')
255
+ return task
256
+ }
289
257
 
290
- async applyTaskDefaults(tasks) {
291
- return Promise.all(tasks.map(async (task) => {
292
- if (!task.for) throw new BuildError(`Mandatory property 'for' not defined for build task.`)
293
- return this._applyTaskDefaults(task)
294
- }))
295
- }
258
+ async function applyTaskDefaults(tasks, runner) {
259
+ return Promise.all(tasks.map(async (task) => {
260
+ if (!task.for) throw new BuildError(`Mandatory property 'for' not defined for build task.`)
261
+ return applyTaskDefault(task, runner)
262
+ }))
263
+ }
296
264
 
297
- async lookupTasks(tasks = [], dependencies) {
298
- const existingTasks = [...tasks]
265
+ async function lookupTasks(tasks = [], dependencies, runner) {
266
+ const existingTasks = [...tasks]
299
267
 
300
- if (!dependencies) {
301
- for (const [id, PluginClass] of this.plugins) {
302
- if (PluginClass.hasTask()) tasks.push(this.getTask({ for: id }))
303
- }
304
- } else {
305
- const toAdd = []
306
- for (const task of tasks) {
307
- const PluginClass = this.getPlugin(task)
308
- const reqs = new PluginClass().requires || []
309
- for (const req of reqs) {
310
- const reqId = this._normalizeKey({ for: req }).for
311
- const ReqPluginClass = this.plugins.get(reqId)
312
- if (ReqPluginClass && !tasks.some(t => t.for === reqId) && !toAdd.some(t => t.for === reqId)) {
313
- const defaultTaskSrc = this.getTask({ for: reqId }).src
314
- const configuredTasks = cds.env.build?.tasks?.filter(t => t.for === reqId)
315
- let configuredTask
316
- if (configuredTasks?.length) {
317
- configuredTask = configuredTasks.find(t => !t.src || t.src === defaultTaskSrc) || configuredTasks[0]
318
- }
319
-
320
- if (configuredTask) {
321
- configuredTask = cds.clone(configuredTask)
322
- await this._applyTaskDefaults(configuredTask)
323
- toAdd.push(configuredTask)
324
- } else if (ReqPluginClass.hasTask?.()) {
325
- toAdd.push(this.getTask({ for: reqId }))
326
- }
268
+ if (!dependencies) {
269
+ for (const [id, PluginClass] of runner.plugins) {
270
+ if (PluginClass.hasTask()) tasks.push(getTask({ for: id }, runner.plugins))
271
+ }
272
+ } else {
273
+ const toAdd = []
274
+ for (const task of tasks) {
275
+ const PluginClass = getPlugin(task, runner.plugins)
276
+ const reqs = new PluginClass().requires || []
277
+ for (const req of reqs) {
278
+ const reqId = normalizeKey({ for: req }).for
279
+ const ReqPluginClass = runner.plugins.get(reqId)
280
+ if (ReqPluginClass && !tasks.some(t => t.for === reqId) && !toAdd.some(t => t.for === reqId)) {
281
+ const defaultTaskSrc = getTask({ for: reqId }, runner.plugins).src
282
+ const configuredTasks = cds.env.build?.tasks?.filter(t => t.for === reqId)
283
+ let configuredTask
284
+ if (configuredTasks?.length) {
285
+ configuredTask = configuredTasks.find(t => !t.src || t.src === defaultTaskSrc) || configuredTasks[0]
286
+ }
287
+
288
+ if (configuredTask) {
289
+ configuredTask = cds.clone(configuredTask)
290
+ await applyTaskDefault(configuredTask, runner)
291
+ toAdd.push(configuredTask)
292
+ } else if (ReqPluginClass.hasTask?.()) {
293
+ toAdd.push(getTask({ for: reqId }, runner.plugins))
327
294
  }
328
295
  }
329
296
  }
330
- tasks.push(...toAdd)
331
297
  }
332
-
333
- if (existingTasks.length < tasks.length) {
334
- const newTasks = tasks.filter(task => !existingTasks.includes(task))
335
- await Promise.all(newTasks.map(t => this._applyTaskDefaults(t)))
336
- DEBUG?.(`Build task provider ${this.constructor.name} returned build tasks ${JSON.stringify(newTasks)}`)
337
- }
338
- return tasks
298
+ tasks.push(...toAdd)
339
299
  }
340
300
 
341
- /**
342
- * Loads the build plugin implementation for the given build task.
343
- * @param {Task} task
344
- * @returns {typeof import('./plugins/plugin')}
345
- */
346
- getPlugin(task) {
347
- this._normalizeKey(task)
348
- this._assertProvided(task)
349
- try {
350
- return this.plugins.get(task.for)
351
- } catch (e) {
352
- console.error(`Provider failed to load build plugin class ${task.for} - provider: ${this.constructor.name}`)
353
- throw e
354
- }
301
+ if (existingTasks.length < tasks.length) {
302
+ const newTasks = tasks.filter(task => !existingTasks.includes(task))
303
+ await Promise.all(newTasks.map(t => applyTaskDefault(t, runner)))
304
+ DEBUG?.(`Build task provider build returned build tasks ${JSON.stringify(newTasks)}`)
355
305
  }
306
+ return tasks
307
+ }
356
308
 
357
- /**
358
- * @param {Task[]} tasks
359
- */
360
- resolveTasks(tasks) {
361
- return tasks.map(task => this._resolveTask(task))
309
+ /**
310
+ * Loads the build plugin implementation for the given build task.
311
+ * @param {Task} task
312
+ * @param {Map<string, typeof import('./plugins/plugin')>} plugins
313
+ * @returns {typeof import('./plugins/plugin')}
314
+ */
315
+ function getPlugin(task, plugins) {
316
+ normalizeKey(task)
317
+ assertProvided(task, plugins)
318
+ try {
319
+ return plugins.get(task.for)
320
+ } catch (e) {
321
+ console.error(`Provider failed to load build plugin class ${task.for} - provider: build`)
322
+ throw e
362
323
  }
324
+ }
363
325
 
364
- /**
365
- * @param {Task} task
366
- */
367
- _resolveTask(task) {
368
- const resolvedTask = cds.clone(task)
369
- resolvedTask.src = path.resolve(cds.root, task.src)
370
- if (!this.options.ws) {
371
- try {
372
- fs.realpathSync(resolvedTask.src)
373
- } catch {
374
- throw new BuildError(`The 'src' folder '${path.resolve(cds.root, task.src)}' for build task '${resolvedTask.for}' does not exist`)
375
- }
376
- }
377
- resolvedTask.dest = path.resolve(cds.root, cds.env.build.target, task.dest || task.src)
378
- resolvedTask.options = task.options || {}
379
- return resolvedTask
380
- }
326
+ function resolveTasks(tasks, options = {}) {
327
+ return tasks.map(task => resolveTask(task, options.ws))
328
+ }
381
329
 
382
- _normalizeKey(key) {
383
- const compat = { [BUILD_TASK_NODE_CF]: BUILD_TASK_NODEJS, [BUILD_TASK_JAVA_CF]: BUILD_TASK_JAVA }
384
- if (compat[key.for]) {
385
- console.warn(`${YELLOW}Build task '${key.for}' is deprecated, use '${compat[key.for]}' instead${RESET}`)
386
- key.for = compat[key.for]
330
+ function resolveTask(task, ws) {
331
+ const resolvedTask = cds.clone(task)
332
+ resolvedTask.src = path.resolve(cds.root, task.src)
333
+ if (!ws) {
334
+ try {
335
+ fs.realpathSync(resolvedTask.src)
336
+ } catch {
337
+ throw new BuildError(`The 'src' folder '${path.resolve(cds.root, task.src)}' for build task '${resolvedTask.for}' does not exist`)
387
338
  }
388
- return key
389
339
  }
340
+ resolvedTask.dest = path.resolve(cds.root, cds.env.build.target, task.dest || task.src)
341
+ resolvedTask.options = task.options || {}
342
+ return resolvedTask
343
+ }
390
344
 
391
- _assertProvided(key) {
392
- if (!this.plugins.has(key.for)) {
393
- throw new BuildError(`No provider found for build task '${key.for}'. Ensure that all required dependencies have been added and 'npm install' has been executed.`)
394
- }
345
+ function normalizeKey(key) {
346
+ const compat = { [BUILD_TASK_NODE_CF]: BUILD_TASK_NODEJS, [BUILD_TASK_JAVA_CF]: BUILD_TASK_JAVA }
347
+ if (compat[key.for]) {
348
+ console.warn(`${YELLOW}Build task '${key.for}' is deprecated, use '${compat[key.for]}' instead${RESET}`)
349
+ key.for = compat[key.for]
395
350
  }
351
+ return key
352
+ }
396
353
 
397
- async _applyTaskDefaults(task) {
398
- const defaultTask = this.getTask(task)
399
- const names = Object.getOwnPropertyNames(defaultTask)
400
- names.forEach(name => {
401
- task[name] ??= defaultTask[name]
402
- })
354
+ function assertProvided(key, plugins) {
355
+ if (!plugins.has(key.for)) {
356
+ throw new BuildError(`No provider found for build task '${key.for}'. Ensure that all required dependencies have been added and 'npm install' has been executed.`)
403
357
  }
358
+ }
404
359
 
405
- async _executeCleanBuildTasks(plugins) {
406
- if (!this.options.clean) return
360
+ async function applyTaskDefault(task, runner) {
361
+ const defaultTask = getTask(task, runner.plugins)
362
+ const names = Object.getOwnPropertyNames(defaultTask)
363
+ names.forEach(name => {
364
+ task[name] ??= defaultTask[name]
365
+ })
366
+ }
407
367
 
408
- if (cds.env.build.target !== '.') {
409
- const target = path.resolve(cds.root, cds.env.build.target)
410
- DEBUG?.(`cleaning staging folder ${target}`)
411
- await rimraf(target)
412
- }
368
+ async function executeCleanBuildTasks(plugins, runner) {
369
+ if (!runner.options.clean) return
413
370
 
414
- const results = await Promise.allSettled(plugins.map((plugin) => {
415
- DEBUG?.(`cleaning, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
416
- return plugin.clean()
417
- }))
418
- this._resolveHandlerResponse(results)
371
+ if (cds.env.build.target !== '.') {
372
+ const target = path.resolve(cds.root, cds.env.build.target)
373
+ DEBUG?.(`cleaning staging folder ${target}`)
374
+ await rimraf(target)
419
375
  }
420
376
 
421
- async _executeBuildTasks(plugins) {
422
- const levels = new Map()
423
- const getLevel = (PluginClass) => {
424
- if (levels.has(PluginClass)) return levels.get(PluginClass)
425
- const plugin = new PluginClass()
426
- if (plugin.priority && plugin.priority < 0) { // cds10: compat for cds-plugin-ui5 using undocumented priority
427
- const level = Number.MAX_SAFE_INTEGER
428
- levels.set(PluginClass, level)
429
- return level
430
- }
431
- let maxDepLevel = -1
432
- const reqs = plugin.requires || []
433
- for (const req of reqs) {
434
- const reqId = this._normalizeKey({ for: req }).for
435
- const ReqPluginClass = this.plugins.get(reqId)
436
- if (ReqPluginClass) maxDepLevel = Math.max(maxDepLevel, getLevel(ReqPluginClass))
437
- }
438
- const level = maxDepLevel + 1
377
+ const results = await Promise.allSettled(plugins.map((plugin) => {
378
+ DEBUG?.(`cleaning, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
379
+ return plugin.clean()
380
+ }))
381
+ resolveHandlerResponse(results, [], runner.options)
382
+ }
383
+
384
+ async function executeBuildTasks(plugins, runner) {
385
+ const levels = new Map()
386
+ const getLevel = (PluginClass) => {
387
+ if (levels.has(PluginClass)) return levels.get(PluginClass)
388
+ const plugin = new PluginClass()
389
+ if (plugin.priority && plugin.priority < 0) {
390
+ const level = Number.MAX_SAFE_INTEGER
439
391
  levels.set(PluginClass, level)
440
392
  return level
441
393
  }
394
+ let maxDepLevel = -1
395
+ const reqs = plugin.requires || []
396
+ for (const req of reqs) {
397
+ const reqId = normalizeKey({ for: req }).for
398
+ const ReqPluginClass = runner.plugins.get(reqId)
399
+ if (ReqPluginClass) maxDepLevel = Math.max(maxDepLevel, getLevel(ReqPluginClass))
400
+ }
401
+ const level = maxDepLevel + 1
402
+ levels.set(PluginClass, level)
403
+ return level
404
+ }
442
405
 
443
- plugins.forEach(p => getLevel(p.constructor))
406
+ plugins.forEach(p => getLevel(p.constructor))
444
407
 
445
- const groupsMap = new Map()
446
- plugins.forEach(p => {
447
- const lvl = levels.get(p.constructor)
448
- if (!groupsMap.has(lvl)) groupsMap.set(lvl, [])
449
- groupsMap.get(lvl).push(p)
450
- })
408
+ const groupsMap = new Map()
409
+ plugins.forEach(p => {
410
+ const lvl = levels.get(p.constructor)
411
+ if (!groupsMap.has(lvl)) groupsMap.set(lvl, [])
412
+ groupsMap.get(lvl).push(p)
413
+ })
451
414
 
452
- const sortedLevels = [...groupsMap.keys()].sort((a, b) => a - b)
453
- const pipeline = sortedLevels.map(lvl => groupsMap.get(lvl))
415
+ const sortedLevels = [...groupsMap.keys()].sort((a, b) => a - b)
416
+ const pipeline = sortedLevels.map(lvl => groupsMap.get(lvl))
454
417
 
455
- const results = await this._executePipeline(pipeline)
456
- return this._resolveHandlerResponse(results, plugins.flatMap(plugin => plugin.messages))
457
- }
418
+ const results = await executePipeline(pipeline, runner.options)
419
+ return resolveHandlerResponse(results, plugins.flatMap(plugin => plugin.messages), runner.options)
420
+ }
458
421
 
459
- async _executePipeline(pipeline) {
460
- let allResults = []
461
- for (const group of pipeline) {
462
- const results = await Promise.allSettled(group.map((plugin) => {
463
- DEBUG?.(`building, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
464
- return plugin.build().then(pluginResult => Promise.resolve({
465
- task: plugin.task,
466
- result: pluginResult,
467
- messages: this._sortMessagesUnique(plugin.messages)
468
- }))
422
+ async function executePipeline(pipeline, options) {
423
+ let allResults = []
424
+ for (const group of pipeline) {
425
+ const results = await Promise.allSettled(group.map((plugin) => {
426
+ DEBUG?.(`building, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
427
+ return plugin.build().then(pluginResult => Promise.resolve({
428
+ task: plugin.task,
429
+ result: pluginResult,
430
+ messages: sortMessagesUnique(options, plugin.messages)
469
431
  }))
470
- allResults = allResults.concat(results)
471
- }
472
- return allResults
432
+ }))
433
+ allResults = allResults.concat(results)
473
434
  }
435
+ return allResults
436
+ }
474
437
 
475
- _resolveHandlerResponse(results, pluginMessages = []) {
476
- /** @type {(BuildError | Error)[]}*/
477
- const errors = []
478
- const resolvedResults = results.reduce((acc, r) => {
479
- if (r.status === 'fulfilled') acc.push(r.value)
480
- if (r.status === 'rejected' && r.reason) errors.push(r.reason)
481
- return acc
482
- }, [])
483
-
484
- if (errors.length > 0) {
485
- const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
486
- if (error) {
487
- if (!(error instanceof BuildError)) throw error
488
- error.messages = error.messages?.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
489
- error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
490
- throw error
491
- }
492
-
493
- const compileErrors = errors.filter(e => e.constructor.name === COMPILATION_ERROR)
494
- const compileMessages = pluginMessages.filter(message => message.constructor.name === COMPILE_MESSAGE)
495
- if (compileErrors.length) {
496
- throw new CompilationError(this._sortMessagesUnique(BuildTaskEngine._getErrorMessages(compileErrors), compileMessages))
497
- }
438
+ function resolveHandlerResponse(results, pluginMessages = [], options) {
439
+ /** @type {(BuildError | Error)[]}*/
440
+ const errors = []
441
+ const resolvedResults = results.reduce((acc, r) => {
442
+ if (r.status === 'fulfilled') acc.push(r.value)
443
+ if (r.status === 'rejected' && r.reason) errors.push(r.reason)
444
+ return acc
445
+ }, [])
446
+
447
+ if (errors.length > 0) {
448
+ const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
449
+ if (error) {
450
+ if (!(error instanceof BuildError)) throw error
451
+ error.messages = error.messages?.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
452
+ error.messages = sortMessagesUnique(options, pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
453
+ throw error
498
454
  }
499
- return resolvedResults
500
- }
501
455
 
502
- _logOutput(plugins) {
503
- const files = BuildTaskEngine._getOutput(plugins)
504
- if (files.length > 0) {
505
- const term = require('../util/term')
506
- if (!process.env.DEBUG && files.length > LOG_LIMIT) {
507
- const length = files.length
508
- files.length = LOG_LIMIT
509
- files.push(`... ${length - files.length} more. Run with DEBUG=build to show all files.`)
510
- }
511
- console.log(`done > wrote output to:\n ${term.dim(files.join("\n "))}\n`)
456
+ const compileErrors = errors.filter(e => e.constructor.name === COMPILATION_ERROR)
457
+ const compileMessages = pluginMessages.filter(message => message.constructor.name === COMPILE_MESSAGE)
458
+ if (compileErrors.length) {
459
+ throw new CompilationError(sortMessagesUnique(options, getErrorMessages(compileErrors), compileMessages))
512
460
  }
513
461
  }
462
+ return resolvedResults
463
+ }
514
464
 
515
- async _writeGenerationLog(plugins) {
516
- const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
517
- if (outputFile) {
518
- const files = BuildTaskEngine._getOutput(plugins)
519
- console.log(`writing generation log to [${outputFile}]\n`)
520
- try {
521
- await mkdirp(path.dirname(outputFile))
522
- await write(outputFile, files.join('\n'))
523
- } catch (error) {
524
- console.error(`failed to write generation log`)
525
- console.error(error.stack || error)
526
- }
465
+ function logOutput(plugins) {
466
+ const files = getOutput(plugins)
467
+ if (files.length > 0) {
468
+ const term = require('../util/term')
469
+ if (!process.env.DEBUG && files.length > LOG_LIMIT) {
470
+ const length = files.length
471
+ files.length = LOG_LIMIT
472
+ files.push(`... ${length - files.length} more. Run with DEBUG=build to show all files.`)
527
473
  }
474
+ console.log(`done > wrote output to:\n ${term.dim(files.join("\n "))}\n`)
528
475
  }
476
+ }
529
477
 
530
- static _getOutput(plugins) {
531
- const files = plugins.flatMap(plugin => plugin.files).sort()
532
- return files.map(file => {
533
- if (path.isAbsolute(cds.env.build.target)) return file
534
- return path.relative(cds.root, file)
535
- })
478
+ async function writeGenerationLog(plugins) {
479
+ const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
480
+ if (outputFile) {
481
+ const files = getOutput(plugins)
482
+ console.log(`writing generation log to [${outputFile}]\n`)
483
+ await mkdirp(path.dirname(outputFile))
484
+ await write(outputFile, files.join('\n'))
536
485
  }
486
+ }
537
487
 
538
- _logMessages(messages) {
539
- if (messages.length > 0) {
540
- const options = {
541
- log: console.log,
542
- "log-level": this._getLogLevel()
543
- }
544
- deduplicateMessages(messages)
545
- sortMessagesSeverityAware(messages)
546
- log(messages, options)
488
+ function getOutput(plugins) {
489
+ const files = plugins.flatMap(plugin => plugin.files).sort()
490
+ return files.map(file => {
491
+ if (path.isAbsolute(cds.env.build.target)) return file
492
+ return path.relative(cds.root, file)
493
+ })
494
+ }
495
+
496
+ function logMessages(messages, options) {
497
+ if (messages.length > 0) {
498
+ const logOptions = {
499
+ log: console.log,
500
+ 'log-level': getLogLevel(options)
547
501
  }
502
+ deduplicateMessages(messages)
503
+ sortMessagesSeverityAware(messages)
504
+ log(messages, logOptions)
548
505
  }
506
+ }
549
507
 
550
- static _resolveRequiredSapServices(tasks) {
551
- const taskModelPaths = tasks.reduce((acc, task) => {
552
- const model = task.options?.model
553
- if (model) {
554
- if (Array.isArray(model)) model.forEach(m => acc.add(m))
555
- else acc.add(model)
556
- }
557
- return acc
558
- }, new Set())
508
+ function resolveRequiredSapServices(tasks) {
509
+ const taskModelPaths = tasks.reduce((acc, task) => {
510
+ const model = task.options?.model
511
+ if (model) {
512
+ if (Array.isArray(model)) model.forEach(m => acc.add(m))
513
+ else acc.add(model)
514
+ }
515
+ return acc
516
+ }, new Set())
559
517
 
560
- return resolveRequiredSapModels([...taskModelPaths])
561
- }
518
+ return resolveRequiredSapModels([...taskModelPaths])
519
+ }
562
520
 
563
- /**
564
- * @param {(string | BuildError)[]} errors
565
- */
566
- static _getErrorMessages(errors) {
567
- let messages = []
568
- errors.forEach(error => {
569
- if (typeof error !== 'string' && Array.isArray(error.errors) && error.errors.length > 0) {
570
- messages = messages.concat(this._getErrorMessages(error.errors))
571
- } else {
572
- messages.push(error)
573
- }
574
- })
575
- return messages
576
- }
521
+ /**
522
+ * @param {(string | BuildError)[]} errors
523
+ */
524
+ function getErrorMessages(errors) {
525
+ let messages = []
526
+ errors.forEach(error => {
527
+ if (typeof error !== 'string' && Array.isArray(error.errors) && error.errors.length > 0) {
528
+ messages = messages.concat(getErrorMessages(error.errors))
529
+ } else {
530
+ messages.push(error)
531
+ }
532
+ })
533
+ return messages
534
+ }
577
535
 
578
- _sortMessagesUnique(...messages) {
579
- const logLevelIdx = LOG_LEVELS.indexOf(this._getLogLevel())
580
- messages = messages.reduce((acc, m) => acc.concat(m), [])
581
- const filteredMessages = messages.filter(message => message && (!message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity)))
582
- // @ts-expect-error - formally expects CompileMessage, but actually, an .severity is the only expected property
583
- deduplicateMessages(filteredMessages)
584
- // @ts-expect-error - see above
585
- return sortMessagesSeverityAware(filteredMessages)
586
- }
536
+ function sortMessagesUnique(options, ...messages) {
537
+ const logLevelIdx = LOG_LEVELS.indexOf(getLogLevel(options))
538
+ messages = messages.reduce((acc, m) => acc.concat(m), [])
539
+ const filteredMessages = messages.filter(message => message && (!message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity)))
540
+ // @ts-expect-error - formally expects CompileMessage, but actually, an .severity is the only expected property
541
+ deduplicateMessages(filteredMessages)
542
+ // @ts-expect-error - see above
543
+ return sortMessagesSeverityAware(filteredMessages)
544
+ }
587
545
 
588
- _getLogLevel() {
589
- return this.options["log-level"] || cds.env["log-level"]
590
- }
546
+ function getLogLevel(options) {
547
+ return options['log-level'] || cds.env['log-level']
591
548
  }
592
549
 
593
- module.exports = { build, register, Plugin, BuildError, BuildTaskEngine }
550
+ module.exports = { build, register, Plugin, BuildError }