@sap/cds-dk 9.8.3 → 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.
- package/CHANGELOG.md +22 -0
- package/bin/add.js +3 -3
- package/bin/cds.js +16 -2
- package/bin/import.js +6 -2
- package/bin/logout.js +9 -12
- package/lib/bind/shared.js +2 -2
- package/lib/build/constants.js +0 -1
- package/lib/build/index.js +420 -463
- package/lib/build/plugins/hana/index.js +121 -2
- package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/templates/job.yaml +1 -1
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.schema.json +4 -0
- package/lib/build/plugins/helm/files/subcharts/content-deployment/values.yaml +5 -2
- package/lib/build/plugins/internal.js +1 -1
- package/lib/build/plugins/mtx-extension/index.js +0 -2
- package/lib/deploy/index.js +23 -3
- package/lib/deploy/to-hana/hana.js +24 -15
- package/lib/import/common.js +6 -3
- package/lib/init/constants.js +3 -1
- package/lib/init/indent.js +0 -1
- package/lib/init/merge.js +0 -1
- package/lib/init/mvn.js +4 -2
- package/lib/init/projectReader.js +0 -1
- package/lib/init/registries/mta.js +4 -2
- package/lib/init/template/app-frontend/index.js +39 -4
- package/lib/init/template/data/as-csv.js +2 -0
- package/lib/init/template/esm/index.js +5 -0
- package/lib/init/template/extensibility/index.js +1 -1
- package/lib/init/template/hana/index.js +1 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +9 -20
- package/lib/init/template/http/index.js +34 -6
- package/lib/init/template/kyma/index.js +1 -0
- package/lib/init/template/postgres/files/values.yaml.hbs +1 -1
- package/lib/init/template/postgres/index.js +1 -1
- package/lib/init/template/react/files/index.html +13 -0
- package/lib/init/template/react/files/package.json.hbs +22 -0
- package/lib/init/template/react/files/public/favicon.svg +1 -0
- package/lib/init/template/react/files/src/App.jsx.hbs +29 -0
- package/lib/init/template/react/files/src/index.css +28 -0
- package/lib/init/template/react/files/src/main.jsx +10 -0
- package/lib/init/template/react/files/vite.config.js +19 -0
- package/lib/init/template/react/index.js +29 -0
- package/lib/init/template/sample/index.js +1 -4
- package/lib/init/template/typer/index.js +1 -1
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/typescript/index.js +1 -1
- package/lib/init/template/ui5/index.js +2 -1
- package/lib/init/template/vue/files/index.html +13 -0
- package/lib/init/template/vue/files/package.json.hbs +19 -0
- package/lib/init/template/vue/files/public/favicon.svg +1 -0
- package/lib/init/template/vue/files/src/App.vue.hbs +34 -0
- package/lib/init/template/vue/files/src/main.js +5 -0
- package/lib/init/template/vue/files/src/style.css +20 -0
- package/lib/init/template/vue/files/vite.config.js +19 -0
- package/lib/init/template/vue/index.js +29 -0
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +2 -13
- package/lib/init/xml.js +0 -2
- package/lib/mtx/auth_manager.js +21 -11
- package/lib/mtx/download_legacy.js +9 -8
- package/lib/mtx/pull.js +9 -8
- package/lib/mtx/push.js +42 -28
- package/lib/mtx/settings_manager.js +17 -10
- package/lib/mtx/tenant_action.js +9 -9
- package/lib/mtx/util/request.js +158 -0
- package/lib/util/cf.js +30 -29
- package/lib/watch/node-watch.js +1 -1
- package/lib/watch/vite-watch.js +76 -0
- package/lib/watch/watched.js +5 -4
- package/npm-shrinkwrap.json +25 -165
- package/package.json +1 -2
- package/lib/init/template/sample/files/db/hana/index.cds +0 -10
- package/lib/init/template/sample/files/db/sqlite/index.cds +0 -10
- package/lib/init/template/sample/files/package.json.hbs +0 -14
- package/lib/util/axios.js +0 -118
package/lib/build/index.js
CHANGED
|
@@ -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(
|
|
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?:
|
|
24
|
-
* dest?:
|
|
22
|
+
* src?: import('node:fs').PathLike,
|
|
23
|
+
* dest?: import('node:fs').PathLike,
|
|
25
24
|
* model?: string[],
|
|
26
|
-
* root?:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
98
|
+
return buildResult
|
|
99
|
+
} catch (error) {
|
|
100
|
+
logOutput(plugins)
|
|
101
|
+
throw error
|
|
102
|
+
}
|
|
103
|
+
}
|
|
114
104
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
155
|
-
DEBUG?.(`determining build tasks for project [${cds.root}].`)
|
|
152
|
+
await applyCommonTaskDefaults(tasks)
|
|
156
153
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
235
|
-
|
|
213
|
+
})
|
|
214
|
+
}
|
|
236
215
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
258
|
-
if (
|
|
259
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
298
|
-
|
|
265
|
+
async function lookupTasks(tasks = [], dependencies, runner) {
|
|
266
|
+
const existingTasks = [...tasks]
|
|
299
267
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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
|
-
|
|
409
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
406
|
+
plugins.forEach(p => getLevel(p.constructor))
|
|
444
407
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
453
|
-
|
|
415
|
+
const sortedLevels = [...groupsMap.keys()].sort((a, b) => a - b)
|
|
416
|
+
const pipeline = sortedLevels.map(lvl => groupsMap.get(lvl))
|
|
454
417
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
418
|
+
const results = await executePipeline(pipeline, runner.options)
|
|
419
|
+
return resolveHandlerResponse(results, plugins.flatMap(plugin => plugin.messages), runner.options)
|
|
420
|
+
}
|
|
458
421
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
return allResults
|
|
432
|
+
}))
|
|
433
|
+
allResults = allResults.concat(results)
|
|
473
434
|
}
|
|
435
|
+
return allResults
|
|
436
|
+
}
|
|
474
437
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
503
|
-
const
|
|
504
|
-
if (
|
|
505
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
-
|
|
561
|
-
|
|
518
|
+
return resolveRequiredSapModels([...taskModelPaths])
|
|
519
|
+
}
|
|
562
520
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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
|
-
|
|
589
|
-
|
|
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
|
|
550
|
+
module.exports = { build, register, Plugin, BuildError }
|