@sap/cds-dk 6.8.3 → 7.0.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/bin/activate.js +15 -7
- package/bin/add.js +29 -15
- package/bin/bind.js +49 -116
- package/bin/build.js +114 -0
- package/bin/cds.js +67 -24
- package/bin/compile/index.js +2 -2
- package/bin/deploy/index.js +46 -39
- package/bin/deploy/to-hana.js +11 -8
- package/bin/deploy/to-postgres.js +8 -2
- package/bin/deploy/to-sqlite.js +0 -1
- package/bin/env.js +75 -41
- package/bin/eval.js +3 -3
- package/bin/extend.js +17 -10
- package/bin/help.js +22 -21
- package/bin/import.js +10 -7
- package/bin/init.js +38 -17
- package/bin/login.js +69 -55
- package/bin/logout.js +12 -9
- package/bin/migrate.js +10 -4
- package/bin/pull.js +12 -5
- package/bin/push.js +24 -13
- package/bin/repl.js +1 -0
- package/bin/run.js +25 -0
- package/bin/subscribe.js +13 -8
- package/bin/unsubscribe.js +15 -8
- package/bin/upgrade.js +15 -9
- package/bin/util/log.js +24 -0
- package/bin/version.js +124 -0
- package/bin/watch.js +1 -35
- package/bin/watched.js +39 -0
- package/lib/bind/KubernetesApiAccessor.js +2 -2
- package/lib/bind/bindingManager.js +1 -1
- package/lib/bind/bindingUtil.js +0 -9
- package/lib/bind/cfKeyProvider.js +10 -72
- package/lib/bind/index.js +114 -0
- package/lib/bind/kubernetesCredentialsProvider.js +2 -2
- package/lib/bind/userCdsrcJsonStorageProvider.js +7 -44
- package/lib/build/buildTaskEngine.js +355 -0
- package/lib/build/buildTaskFactory.js +284 -0
- package/lib/build/buildTaskHandler.js +240 -0
- package/lib/build/buildTaskProvider.js +22 -0
- package/lib/build/buildTaskProviderFactory.js +182 -0
- package/lib/build/constants.js +56 -0
- package/lib/build/index.js +21 -0
- package/lib/build/provider/buildTaskHandlerEdmx.js +83 -0
- package/lib/build/provider/buildTaskHandlerFeatureToggles.js +132 -0
- package/lib/build/provider/buildTaskHandlerInternal.js +228 -0
- package/lib/build/provider/buildTaskProviderInternal.js +370 -0
- package/lib/build/provider/fiori/index.js +167 -0
- package/lib/build/provider/hana/2migration.js +179 -0
- package/lib/build/provider/hana/index.js +522 -0
- package/lib/build/provider/hana/migrationtable.js +472 -0
- package/lib/build/provider/hana/template/.hdiconfig-all +163 -0
- package/lib/build/provider/hana/template/.hdiconfig-cloud +137 -0
- package/lib/build/provider/hana/template/.hdinamespace +4 -0
- package/lib/build/provider/hana/template/package.json +12 -0
- package/lib/build/provider/hana/template/undeploy.json +5 -0
- package/lib/build/provider/java/index.js +93 -0
- package/lib/build/provider/java-cf/index.js +1 -0
- package/lib/build/provider/mtx/index.js +37 -0
- package/lib/build/provider/mtx/resourcesTarBuilder.js +94 -0
- package/lib/build/provider/mtx-extension/index.js +131 -0
- package/lib/build/provider/mtx-sidecar/index.js +138 -0
- package/lib/build/provider/node-cf/index.js +1 -0
- package/lib/build/provider/nodejs/index.js +178 -0
- package/lib/build/util.js +263 -0
- package/lib/cds.js +9 -21
- package/lib/client/auth_manager.js +39 -16
- package/lib/client/helper/dependency_helper.js +7 -6
- package/lib/client/push.js +2 -1
- package/lib/client/settings_manager.js +10 -39
- package/lib/deploy/to-hana/hana.js +273 -0
- package/lib/deploy/to-hana/hdiDeployUtil.js +68 -0
- package/lib/deploy/to-hana/mtaUtil.js +171 -0
- package/lib/import/asyncapi/importAsyncAPI.js +3 -2
- package/lib/import/asyncapi/index.js +5 -2
- package/lib/import/asyncapi/parserContext.js +67 -26
- package/lib/import/asyncapi/utils.js +34 -3
- package/lib/import/common.js +40 -30
- package/lib/import/importapi.js +36 -35
- package/lib/index.js +12 -17
- package/lib/init/constants.js +36 -71
- package/lib/init/index.js +18 -52
- package/lib/init/template/_merging/registry-mta.js +21 -3
- package/lib/init/template/approuter/files/mta.yaml.hbs +29 -29
- package/lib/init/template/approuter/files/values.yaml.hbs +5 -5
- package/lib/init/template/approuter/files/xs-app.json.hbs +8 -2
- package/lib/init/template/approuter/index.js +7 -8
- package/lib/init/template/auditlog/files/mta.yaml.hbs +5 -5
- package/lib/init/template/auditlog/files/package.json.hbs +13 -13
- package/lib/init/template/auditlog/index.js +3 -3
- package/lib/init/template/cf-manifest/files/manifest.yml.hbs +22 -22
- package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +3 -3
- package/lib/init/template/connectivity/files/mta.yaml.hbs +14 -0
- package/lib/init/template/connectivity/files/package.json.hbs +17 -0
- package/lib/init/template/connectivity/index.js +21 -4
- package/lib/init/template/data/index.js +5 -1
- package/lib/init/template/destination/files/mta.yaml.hbs +14 -0
- package/lib/init/template/destination/files/package.json.hbs +17 -0
- package/lib/init/template/{destinations → destination}/index.js +19 -13
- package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +3 -3
- package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +5 -5
- package/lib/init/template/enterprise-messaging/files/package.json.hbs +9 -9
- package/lib/init/template/enterprise-messaging/files/xs-security.json.hbs +19 -0
- package/lib/init/template/enterprise-messaging/index.js +27 -6
- package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +3 -3
- package/lib/init/template/enterprise-messaging-shared/files/mta.yaml.hbs +5 -5
- package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +9 -9
- package/lib/init/template/enterprise-messaging-shared/index.js +4 -5
- package/lib/init/template/extensibility/files/package.json.hbs +10 -10
- package/lib/init/template/extensibility/index.js +8 -9
- package/lib/init/template/file-based-messaging/files/package.json.hbs +9 -9
- package/lib/init/template/file-based-messaging/index.js +3 -4
- package/lib/init/template/hana/files/db/package.json +1 -1
- package/lib/init/template/hana/files/mta.yaml.hbs +21 -21
- package/lib/init/template/hana/files/package.json.hbs +16 -22
- package/lib/init/template/hana/files/values.yaml.hbs +1 -1
- package/lib/init/template/hana/index.js +5 -15
- package/lib/init/template/helm/files/Chart.yaml.hbs +4 -4
- package/lib/init/template/helm/files/values.yaml.hbs +6 -6
- package/lib/init/template/helm/index.js +5 -10
- package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +2 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +37 -31
- package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +13 -14
- package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +2 -2
- package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +1 -1
- package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +3 -3
- package/lib/init/template/helm/subcharts/content-deployment/values.md +116 -116
- package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +2 -0
- package/lib/init/template/helm/subcharts/service-instance/templates/_helpers.tpl +9 -9
- package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +2 -2
- package/lib/init/template/helm/subcharts/service-instance/values.md +15 -15
- package/lib/init/template/helm/subcharts/web-application/Chart.yaml +2 -0
- package/lib/init/template/helm/subcharts/web-application/templates/NOTES.txt +15 -0
- package/lib/init/template/helm/subcharts/web-application/templates/_helpers.tpl +23 -23
- package/lib/init/template/helm/subcharts/web-application/templates/api-rule.yaml +4 -4
- package/lib/init/template/helm/subcharts/web-application/templates/deployment.yaml +17 -17
- package/lib/init/template/helm/subcharts/web-application/templates/network-policy.yaml +24 -3
- package/lib/init/template/helm/subcharts/web-application/templates/pod-disruption-budget.yaml +3 -3
- package/lib/init/template/helm/subcharts/web-application/templates/secret.yaml +2 -2
- package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +1 -1
- package/lib/init/template/helm/subcharts/web-application/templates/service-binding.yaml +3 -3
- package/lib/init/template/helm/subcharts/web-application/templates/service.yaml +3 -3
- package/lib/init/template/helm/subcharts/web-application/values.md +141 -123
- package/lib/init/template/helm/subcharts/web-application/values.schema.json +7 -0
- package/lib/init/template/helm/subcharts/web-application/values.yaml +1 -0
- package/lib/init/template/html5-repo/index.js +4 -5
- package/lib/init/template/java/index.js +5 -5
- package/lib/init/template/kibana-logging/files/mta.yaml.hbs +5 -5
- package/lib/init/template/kibana-logging/files/package.json.hbs +9 -9
- package/lib/init/template/kibana-logging/index.js +3 -4
- package/lib/init/template/lint/index.js +3 -3
- package/lib/init/template/mta/files/mta.yaml.hbs +14 -14
- package/lib/init/template/mta/files/package.json +5 -0
- package/lib/init/template/mta/index.js +12 -13
- package/lib/init/template/mtx/index.js +2 -2
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +29 -27
- package/lib/init/template/multitenancy/files/package.json.hbs +17 -14
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +7 -7
- package/lib/init/template/multitenancy/files/values.yaml.hbs +19 -19
- package/lib/init/template/multitenancy/index.js +14 -16
- package/lib/init/template/nodejs/files/package.json.hbs +1 -1
- package/lib/init/template/nodejs/index.js +5 -8
- package/lib/init/template/redis-messaging/files/mta.yaml.hbs +5 -5
- package/lib/init/template/redis-messaging/files/package.json.hbs +9 -9
- package/lib/init/template/redis-messaging/index.js +4 -5
- package/lib/init/template/sample/files/app/_i18n/i18n.properties +43 -0
- package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +14 -0
- package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +114 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +8 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +11 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +129 -0
- package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +57 -0
- package/lib/init/template/sample/files/app/common.cds +263 -0
- package/lib/init/template/sample/files/app/index.html +30 -0
- package/lib/init/template/sample/files/app/services.cds +5 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +5 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +6 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books_texts.csv +5 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +16 -0
- package/lib/init/template/sample/files/db/schema.cds +31 -0
- package/lib/init/template/sample/files/nodejs/admin-service.js +12 -0
- package/lib/init/template/sample/files/nodejs/cat-service.js +26 -0
- package/lib/init/template/sample/files/srv/admin-service.cds +5 -0
- package/lib/init/template/sample/files/srv/cat-service.cds +16 -0
- package/lib/init/template/sample/index.js +24 -0
- package/lib/init/template/sample-tiny/index.js +39 -0
- package/lib/init/template/samples/index.js +5 -56
- package/lib/init/template/sqlite/files/package.json.hbs +13 -13
- package/lib/init/template/sqlite/index.js +2 -2
- package/lib/init/template/templateBase.js +24 -1
- package/lib/init/template/toggles/files/package.json.hbs +10 -10
- package/lib/init/template/toggles/index.js +3 -3
- package/lib/init/template/typer/files/gitignore +1 -0
- package/lib/init/template/typer/files/package.json +8 -0
- package/lib/init/template/typer/files/xsconfig.json +9 -0
- package/lib/init/template/typer/index.js +36 -0
- package/lib/init/template/xsuaa/files/mta.yaml.hbs +12 -12
- package/lib/init/template/xsuaa/files/package.json.hbs +13 -13
- package/lib/init/template/xsuaa/files/values.yaml.hbs +8 -8
- package/lib/init/template/xsuaa/index.js +8 -19
- package/lib/init/util/merge.js +73 -59
- package/lib/init/util/mvn.js +21 -17
- package/lib/init/util/projectReader.js +49 -87
- package/lib/init/util/xml.js +14 -23
- package/lib/util/cfUtil.js +366 -0
- package/lib/util/command.js +3 -3
- package/lib/util/gitUtil.js +55 -0
- package/lib/util/version.js +27 -0
- package/npm-shrinkwrap.json +337 -275
- package/package.json +6 -6
- package/CHANGELOG.md +0 -1460
- package/bin/build/all.js +0 -88
- package/bin/build/index.js +0 -82
- package/lib/compile/index.js +0 -14
- /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data/my.bookshop-Books.csv +0 -0
- /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data-model.cds +0 -0
- /package/lib/init/template/{samples → sample-tiny}/files/nodejs/srv/cat-service.cds.hbs +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const cds = require('../cds'), { _log: log } = cds
|
|
4
|
+
const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
|
|
5
|
+
const { relativePaths, BuildError, resolveRequiredSapModels } = require('./util')
|
|
6
|
+
const { SEVERITIES, LOG_LEVELS } = require('./constants')
|
|
7
|
+
const BuildTaskFactory = require('./buildTaskFactory')
|
|
8
|
+
const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
|
|
9
|
+
const BuildTaskHandlerInternal = require('./provider/buildTaskHandlerInternal')
|
|
10
|
+
|
|
11
|
+
const COMPILATION_ERROR = 'CompilationError'
|
|
12
|
+
const COMPILE_MESSAGE = 'CompileMessage'
|
|
13
|
+
|
|
14
|
+
class BuildTaskEngine {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this._providerFactory = new BuildTaskProviderFactory(options)
|
|
17
|
+
}
|
|
18
|
+
get providerFactory() {
|
|
19
|
+
return this._providerFactory
|
|
20
|
+
}
|
|
21
|
+
get logger() {
|
|
22
|
+
return this.providerFactory.logger
|
|
23
|
+
}
|
|
24
|
+
get buildOptions() {
|
|
25
|
+
return this.providerFactory.buildOptions
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async processTasks(tasks) {
|
|
29
|
+
const startTime = Date.now()
|
|
30
|
+
const messages = []
|
|
31
|
+
|
|
32
|
+
this.logger.log(`building project [${cds.root}], clean [${this.buildOptions.clean}]`)
|
|
33
|
+
this.logger.log(`cds [${cds.version}], compiler [${cds.compiler.version()}], home [${cds.home}]\n`)
|
|
34
|
+
|
|
35
|
+
if (!tasks) {
|
|
36
|
+
const buildTaskFactory = new BuildTaskFactory(this.buildOptions)
|
|
37
|
+
tasks = await buildTaskFactory.getTasks()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// validate required @sap namespace models - log only
|
|
41
|
+
const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
|
|
42
|
+
if (unresolved.length > 0) {
|
|
43
|
+
throw new BuildError(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Make sure up-to-date versions of the missing modules are installed.`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// create build task handlers
|
|
47
|
+
const handlers = []
|
|
48
|
+
tasks.forEach((task) => {
|
|
49
|
+
if (task) {
|
|
50
|
+
const handler = this._createHandler(task)
|
|
51
|
+
handlers.push(handler)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// use resolved tasks
|
|
56
|
+
this.providerFactory.context.tasks = handlers.map(handler => handler.task)
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await this._executePrepare(handlers)
|
|
60
|
+
await this._executeCleanBuildTasks(handlers)
|
|
61
|
+
|
|
62
|
+
// throwing Exception in case of compilation errors
|
|
63
|
+
const buildResult = await this._executeBuildTasks(handlers)
|
|
64
|
+
|
|
65
|
+
await this._writeGenerationLog(handlers)
|
|
66
|
+
this._logBuildOutput(handlers)
|
|
67
|
+
this._logMessages([...BuildTaskEngine._getHandlerMessages(handlers), ...messages])
|
|
68
|
+
this._logTimer(startTime, Date.now())
|
|
69
|
+
|
|
70
|
+
return buildResult
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this._logBuildOutput(handlers)
|
|
73
|
+
|
|
74
|
+
// cds CLI layer logs in case of an exception if invoked from CLI
|
|
75
|
+
if (!this.buildOptions.cli) {
|
|
76
|
+
this._logMessages([...BuildTaskEngine._getErrorMessages([error]), ...messages])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
throw error
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* BuildTaskHandler#prepare has been deprecated and was never part of the public API.
|
|
85
|
+
* Currently only used by internal FioriBuildTaskHandler.
|
|
86
|
+
* @deprecated
|
|
87
|
+
* @param {*} handlers
|
|
88
|
+
* @returns
|
|
89
|
+
*/
|
|
90
|
+
async _executePrepare(handlers) {
|
|
91
|
+
const handlerGroups = new Map()
|
|
92
|
+
|
|
93
|
+
// group handlers by type
|
|
94
|
+
handlers.forEach(handler => {
|
|
95
|
+
handlerGroups.has(handler.task.for) ? handlerGroups.get(handler.task.for).push(handler) : handlerGroups.set(handler.task.for, [handler])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const promises = []
|
|
99
|
+
for (let handlerGroup of handlerGroups.values()) {
|
|
100
|
+
promises.push(this._doPrepare(handlerGroup))
|
|
101
|
+
}
|
|
102
|
+
return Promise.all(promises)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @deprecated
|
|
107
|
+
* @param {*} handlerGroup
|
|
108
|
+
*/
|
|
109
|
+
async _doPrepare(handlerGroup) {
|
|
110
|
+
for (let handler of handlerGroup) {
|
|
111
|
+
// prepare has been deprecated
|
|
112
|
+
if (handler instanceof BuildTaskHandlerInternal) {
|
|
113
|
+
this.logger._debug && this.logger.debug(`preparing, handler [${handler.constructor.name}], src [${relativePaths(cds.root, handler.task.src)}]`)
|
|
114
|
+
const result = await handler.prepare()
|
|
115
|
+
if (result === false) {
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async _executeCleanBuildTasks(handlers) {
|
|
123
|
+
if (this.buildOptions.clean) {
|
|
124
|
+
// clean entire build staging folder once
|
|
125
|
+
if (cds.env.build.target !== '.') {
|
|
126
|
+
const target = path.resolve(cds.root, cds.env.build.target)
|
|
127
|
+
this.logger._debug && this.logger.debug(`cleaning staging folder ${target}`)
|
|
128
|
+
await fs.promises.rm(target, { force: true, recursive: true })
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const results = await Promise.allSettled(handlers.map((handler) => {
|
|
132
|
+
this.logger._debug && this.logger.debug(`cleaning, handler [${handler.constructor.name}], src [${relativePaths(cds.root, handler.task.src)}]`)
|
|
133
|
+
return handler.clean()
|
|
134
|
+
}))
|
|
135
|
+
// check for errors and throw exception
|
|
136
|
+
this._resolveHandlerResponse(results)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async _executeBuildTasks(handlers) {
|
|
141
|
+
// sort handlers based on priority in
|
|
142
|
+
handlers = handlers.sort((a, b) => {
|
|
143
|
+
return a.priority === b.priority ? 0 : a.priority > b.priority ? -1 : 1
|
|
144
|
+
})
|
|
145
|
+
// group handlers with same priority in order to execute in parallel
|
|
146
|
+
const buildPipeline = handlers.reduce((acc, handler) => {
|
|
147
|
+
if (acc.length === 0) {
|
|
148
|
+
acc.push([handler])
|
|
149
|
+
} else {
|
|
150
|
+
const currGroup = acc[acc.length - 1]
|
|
151
|
+
if (currGroup[0].priority === handler.priority) {
|
|
152
|
+
currGroup.push(handler)
|
|
153
|
+
} else {
|
|
154
|
+
acc.push([handler])
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return acc
|
|
158
|
+
}, [])
|
|
159
|
+
|
|
160
|
+
const results = await this._executePipeline(buildPipeline)
|
|
161
|
+
|
|
162
|
+
// check for errors and throw exception - return results otherwise including any compiler and build status messages
|
|
163
|
+
return this._resolveHandlerResponse(results, BuildTaskEngine._getHandlerMessages(handlers))
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async _executePipeline(pipeline) {
|
|
167
|
+
let allResults = []
|
|
168
|
+
for (const group of pipeline) {
|
|
169
|
+
const results = await Promise.allSettled(group.map((handler) => {
|
|
170
|
+
this.logger._debug && this.logger.debug(`building, handler [${handler.constructor.name}], src [${relativePaths(cds.root, handler.task.src)}]`)
|
|
171
|
+
return handler.build()
|
|
172
|
+
.then(handlerResult => {
|
|
173
|
+
return Promise.resolve({
|
|
174
|
+
task: handler.task,
|
|
175
|
+
result: handlerResult,
|
|
176
|
+
messages: this._sortMessagesUnique(handler.messages)
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
}))
|
|
180
|
+
allResults = allResults.concat(results)
|
|
181
|
+
}
|
|
182
|
+
return allResults
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_resolveHandlerResponse(results, handlerMessages = []) {
|
|
186
|
+
const errors = []
|
|
187
|
+
const resolvedResults = results.reduce((acc, r) => {
|
|
188
|
+
if (r.status === 'fulfilled') {
|
|
189
|
+
acc.push(r.value)
|
|
190
|
+
}
|
|
191
|
+
if (r.status === 'rejected' && r.reason) {
|
|
192
|
+
errors.push(r.reason)
|
|
193
|
+
}
|
|
194
|
+
return acc
|
|
195
|
+
}, [])
|
|
196
|
+
|
|
197
|
+
if (errors.length > 0) {
|
|
198
|
+
const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
|
|
199
|
+
if (error) {
|
|
200
|
+
// throw original error, including BuildErrors
|
|
201
|
+
// for BuildErrors we do not merge other build messages that might exist in order to keep the original error intact
|
|
202
|
+
throw error
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// propagate existing CompilationErrors
|
|
206
|
+
// merge all existing compilation messages into a single CompilationError
|
|
207
|
+
// compiler warning and info messages are returned as handler messages
|
|
208
|
+
const compileErrors = errors.filter(e => e.constructor.name === COMPILATION_ERROR)
|
|
209
|
+
const compileMessages = handlerMessages.filter(message => message.constructor.name === COMPILE_MESSAGE)
|
|
210
|
+
if (compileErrors.length) {
|
|
211
|
+
throw new CompilationError(this._sortMessagesUnique(BuildTaskEngine._getErrorMessages(compileErrors), compileMessages))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return resolvedResults
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_createHandler(task) {
|
|
218
|
+
try {
|
|
219
|
+
const handler = this.providerFactory.createHandler(task)
|
|
220
|
+
handler.init()
|
|
221
|
+
|
|
222
|
+
if (!(handler instanceof BuildTaskHandlerInternal) && handler.priority !== 1) {
|
|
223
|
+
// Custom build handlers are executed before internal handlers to ensure
|
|
224
|
+
// that generated content cannot be overwritten by mistake
|
|
225
|
+
throw new Error(`Illegal priority for ${handler.constructor.name} encountered for custom handler - in this version only priority value '1' is allowed`)
|
|
226
|
+
}
|
|
227
|
+
this._logTaskHandler(handler)
|
|
228
|
+
|
|
229
|
+
return handler
|
|
230
|
+
} catch (error) {
|
|
231
|
+
log(error, { log: this.logger.log })
|
|
232
|
+
throw error
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_logBuildOutput(handlers) {
|
|
237
|
+
// log all generated files
|
|
238
|
+
const files = BuildTaskEngine._getBuildOutput(handlers)
|
|
239
|
+
if (files.length > 0) {
|
|
240
|
+
this.logger.log(`done > wrote output to:\n ${files.join("\n ")}\n`)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async _writeGenerationLog(handlers) {
|
|
245
|
+
const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
|
|
246
|
+
if (outputFile) {
|
|
247
|
+
const files = BuildTaskEngine._getBuildOutput(handlers)
|
|
248
|
+
this.logger.log(`writing generation log to [${outputFile}]\n`)
|
|
249
|
+
try {
|
|
250
|
+
await fs.promises.mkdir(path.dirname(outputFile), { recursive: true })
|
|
251
|
+
await fs.promises.writeFile(outputFile, files.join('\n'))
|
|
252
|
+
} catch (error) {
|
|
253
|
+
this.logger.error(`failed to write generation log`)
|
|
254
|
+
this.logger.error(error.stack || error)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static _getBuildOutput(handlers) {
|
|
260
|
+
const files = handlers.reduce((acc, handler) => acc.concat(handler.files), []).sort()
|
|
261
|
+
return files.map(file => {
|
|
262
|
+
if (path.isAbsolute(cds.env.build.target)) {
|
|
263
|
+
return file
|
|
264
|
+
}
|
|
265
|
+
return path.relative(cds.root, file)
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_logTimer(start, end) {
|
|
270
|
+
this.logger.log(`build completed in ${end - start} ms\n`)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
_logTaskHandler(handler) {
|
|
274
|
+
this.logger._debug && this.logger.debug(`handler ${handler.constructor.name}`)
|
|
275
|
+
this.logger._debug && this.logger.debug(`details src [${relativePaths(cds.root, handler.task.src)}], dest [${relativePaths(cds.root, handler.task.dest)}], use [${handler.task.use}], options [${JSON.stringify(handler.task.options)}]`) //NOSONAR
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_logMessages(messages) {
|
|
279
|
+
if (messages.length > 0) {
|
|
280
|
+
const options = {
|
|
281
|
+
log: this.logger.log,
|
|
282
|
+
"log-level": this._getLogLevel() // ensures that for tests the correct cds.env is used
|
|
283
|
+
}
|
|
284
|
+
deduplicateMessages(messages)
|
|
285
|
+
log(messages, options)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static _resolveRequiredSapServices(tasks) {
|
|
290
|
+
const taskModelPaths = tasks.reduce((acc, task) => {
|
|
291
|
+
const model = task.options?.model
|
|
292
|
+
if (model) {
|
|
293
|
+
if (Array.isArray(model)) {
|
|
294
|
+
model.forEach(m => acc.add(m))
|
|
295
|
+
} else {
|
|
296
|
+
acc.add(model)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return acc
|
|
300
|
+
}, new Set())
|
|
301
|
+
|
|
302
|
+
return resolveRequiredSapModels([...taskModelPaths])
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Returns a sorted and flattened list of all messages extracted from the given errors.
|
|
307
|
+
* @param {Array<Error>} errors
|
|
308
|
+
*/
|
|
309
|
+
static _getErrorMessages(errors) {
|
|
310
|
+
let messages = []
|
|
311
|
+
// flatten all compile messages in order to filter duplicates and sort later on
|
|
312
|
+
errors.forEach(error => {
|
|
313
|
+
if (Array.isArray(error.errors) && error.errors.length > 0) {
|
|
314
|
+
messages = messages.concat(this._getErrorMessages(error.errors))
|
|
315
|
+
} else {
|
|
316
|
+
messages.push(error)
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
return messages
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Returns compiler messages and validation messages issued by handlers.
|
|
324
|
+
* @param {Array<BuildTaskHandler>} handlers
|
|
325
|
+
*/
|
|
326
|
+
static _getHandlerMessages(handlers) {
|
|
327
|
+
return handlers.reduce((acc, handler) => acc.concat(handler.messages), [])
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Sort and filter the given errors of type CompileMessage or BuildMessage according to their severity and location,
|
|
332
|
+
* but leave any other errors untouched as part of the result array.<br>
|
|
333
|
+
* The log level passed as 'buildOptions.log-level' or defined in 'cds.env' is used to filter the given messages.
|
|
334
|
+
* @param {...Error} messages
|
|
335
|
+
*/
|
|
336
|
+
_sortMessagesUnique(...messages) {
|
|
337
|
+
const logLevelIdx = LOG_LEVELS.indexOf(this._getLogLevel())
|
|
338
|
+
// flatten
|
|
339
|
+
messages = messages.reduce((acc, m) => acc.concat(m), [])
|
|
340
|
+
// filter according to log-level
|
|
341
|
+
const filteredMessages = messages.filter(message => !message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity))
|
|
342
|
+
// remove duplicates
|
|
343
|
+
deduplicateMessages(filteredMessages)
|
|
344
|
+
// sort
|
|
345
|
+
return sortMessagesSeverityAware(filteredMessages)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Return user defined log level or default value 'warn'
|
|
350
|
+
*/
|
|
351
|
+
_getLogLevel() {
|
|
352
|
+
return this.buildOptions["log-level"] || cds.env["log-level"]
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
module.exports = BuildTaskEngine
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const cds = require('../cds'), { _log:log } = cds
|
|
4
|
+
const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
|
|
5
|
+
const { hasJavaNature, getProperty, redactCredentials, flatten, getDefaultModelOptions } = require('./util')
|
|
6
|
+
const { FILE_EXT_CDS, BUILD_TASK_JAVA_CF, BUILD_TASK_JAVA } = require("./constants")
|
|
7
|
+
|
|
8
|
+
class BuildTaskFactory {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this._providerFactory = new BuildTaskProviderFactory(options)
|
|
11
|
+
}
|
|
12
|
+
get providerFactory() {
|
|
13
|
+
return this._providerFactory
|
|
14
|
+
}
|
|
15
|
+
get logger() {
|
|
16
|
+
return this.providerFactory.logger
|
|
17
|
+
}
|
|
18
|
+
get buildOptions() {
|
|
19
|
+
return this.providerFactory.buildOptions
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// the following order for determining build tasks is used
|
|
23
|
+
// 1. create from command line input, e.g. cds build/all --for hana --src db --model srv --dest db
|
|
24
|
+
// 2. read using cds.env.build.tasks
|
|
25
|
+
// 3. create from cds.env.folders config data
|
|
26
|
+
async getTasks() {
|
|
27
|
+
try {
|
|
28
|
+
const tasks = await this._createTasks()
|
|
29
|
+
|
|
30
|
+
// log build tasks
|
|
31
|
+
this.logger.log(`the following build tasks will be executed`)
|
|
32
|
+
let tasksOutput = " {\n \"build\": {\n \"target\": \"" + cds.env.build.target + "\",\n \"tasks\": ["
|
|
33
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
34
|
+
tasksOutput += "\n " + BuildTaskFactory._stringifyTask(tasks[i]) + (i + 1 < tasks.length ? "," : "")
|
|
35
|
+
}
|
|
36
|
+
tasksOutput += "\n ]\n }\n }\n"
|
|
37
|
+
this.logger.log(tasksOutput)
|
|
38
|
+
|
|
39
|
+
// for testing purposes
|
|
40
|
+
this.logger.debug("cds.env used for build:")
|
|
41
|
+
this.logger._debug && this.logger.debug(redactCredentials(cds.env))
|
|
42
|
+
|
|
43
|
+
// always resolve tasks for input validation
|
|
44
|
+
const resolvedTasks = this.providerFactory.resolveTasks(tasks)
|
|
45
|
+
if (this.buildOptions.resolve) {
|
|
46
|
+
return resolvedTasks
|
|
47
|
+
}
|
|
48
|
+
return tasks
|
|
49
|
+
} catch (e) {
|
|
50
|
+
this.logger.log('')
|
|
51
|
+
// cds CLI layer is doing the logging if invoked using CLI
|
|
52
|
+
if (!this.buildOptions.cli) {
|
|
53
|
+
log(e, { withStack: true, log: (message) => this.logger.log(message) })
|
|
54
|
+
}
|
|
55
|
+
throw e
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async _createTasks() {
|
|
60
|
+
this.logger.debug(`determining build tasks for project [${cds.root}].`)
|
|
61
|
+
|
|
62
|
+
// clearing model cache (details https://github.tools.sap/cap/cds/pull/181) is no longer required
|
|
63
|
+
// because of changes https://github.tools.sap/cap/cds/pull/1121
|
|
64
|
+
// cds.resolve.cache = {}
|
|
65
|
+
|
|
66
|
+
let tasks = BuildTaskFactory._getExistingTasks()
|
|
67
|
+
if (tasks.length === 0) {
|
|
68
|
+
tasks = await this.providerFactory.lookupTasks()
|
|
69
|
+
} else {
|
|
70
|
+
// 1. apply default values including task.for and task.use and ensure that for all tasks a provider exists - throwing error otherwise
|
|
71
|
+
await this.providerFactory.applyTaskDefaults(tasks)
|
|
72
|
+
// ensure that dependencies get wired up before filtering
|
|
73
|
+
await this.providerFactory.lookupTasks(tasks)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 3. filters the list of build tasks and adapts according to given CLI options
|
|
77
|
+
// Note: A new task might get created, e.g. 'cds build --for hana' will enforce a hana build even if sqlite has been configured
|
|
78
|
+
let existingTasks = tasks
|
|
79
|
+
tasks = this._applyCliOptionsFilter(tasks)
|
|
80
|
+
if (tasks.length === 0) {
|
|
81
|
+
return tasks
|
|
82
|
+
}
|
|
83
|
+
// a new array is returned to indicate new build tasks have been created
|
|
84
|
+
if (tasks !== existingTasks) {
|
|
85
|
+
// a different task shall be executed
|
|
86
|
+
await this.providerFactory.applyTaskDefaults(tasks)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. add dependencies
|
|
90
|
+
existingTasks = [...tasks]
|
|
91
|
+
await this.providerFactory.lookupTasks(tasks)
|
|
92
|
+
if (tasks.length > existingTasks.length) {
|
|
93
|
+
const newTasks = tasks.filter(task => !existingTasks.includes(task))
|
|
94
|
+
// a dependant task was added
|
|
95
|
+
this._applyCliOptions(newTasks)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// obligatory task defaults shared by all tasks
|
|
99
|
+
BuildTaskFactory._applyCommonTaskDefaults(tasks)
|
|
100
|
+
|
|
101
|
+
// ensure correct values for optional build task properties, error for missing mandatory properties
|
|
102
|
+
BuildTaskFactory._validateBuildTasks(tasks)
|
|
103
|
+
|
|
104
|
+
this._setDefaultBuildTargetFolder(tasks)
|
|
105
|
+
return tasks
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static _getExistingTasks() {
|
|
109
|
+
return Array.isArray(getProperty(cds.env, 'build.tasks')) ? JSON.parse(JSON.stringify(cds.env.build.tasks)) : []
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static _applyCommonTaskDefaults(tasks) {
|
|
113
|
+
const modelPaths = getDefaultModelOptions()
|
|
114
|
+
|
|
115
|
+
tasks.forEach(task => {
|
|
116
|
+
this._setTaskModelOptions(task, modelPaths)
|
|
117
|
+
if (!task.src) {
|
|
118
|
+
throw new Error(`Invalid build task definition - value of property 'src' is missing in [${task.for || task.use}].`)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static _validateBuildTasks(tasks) {
|
|
124
|
+
tasks.forEach(task => {
|
|
125
|
+
if (!task.src) {
|
|
126
|
+
throw new Error(`Invalid build task definition - value of property 'src' is missing in [${task.for || task.use}].`)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_setDefaultBuildTargetFolder(tasks) {
|
|
132
|
+
const task = tasks.find(task => task.for === BUILD_TASK_JAVA_CF || task.for === BUILD_TASK_JAVA)
|
|
133
|
+
const srv = task ? task.src : BuildTaskFactory._getModuleFolder(flatten([cds.env.folders.srv])) || "srv"
|
|
134
|
+
|
|
135
|
+
// Java projects use "." as the default build target folder
|
|
136
|
+
if (hasJavaNature([path.join(cds.root, srv), cds.root]) && BuildTaskFactory._adaptBuildTargetSettingForJava()) {
|
|
137
|
+
this.logger.debug("using inplace build for java project instead of default staging build")
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Use inplace build for java projects if build.target has not been configured.
|
|
143
|
+
* @returns {boolean} true if changed, false otherwise
|
|
144
|
+
*/
|
|
145
|
+
static _adaptBuildTargetSettingForJava() {
|
|
146
|
+
if (cds.env.build.target !== ".") {
|
|
147
|
+
// filter user settings of cds.env
|
|
148
|
+
const userEnv = cds.env.for("cds", cds.root, false)
|
|
149
|
+
|
|
150
|
+
// use helper as env.build might be undefined
|
|
151
|
+
if (!userEnv.build?.target) {
|
|
152
|
+
cds.env.build.target = "."
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_applyCliOptionsFilter(tasks) {
|
|
160
|
+
const buildOptions = this.buildOptions
|
|
161
|
+
// filter tasks using either option for, use, src
|
|
162
|
+
let resultTasks = tasks.filter(task => {
|
|
163
|
+
return (!buildOptions.use || buildOptions.use === task.use)
|
|
164
|
+
&& (!buildOptions.for || buildOptions.for === task.for)
|
|
165
|
+
&& (!buildOptions.src || buildOptions.src === task.src)
|
|
166
|
+
})
|
|
167
|
+
if (resultTasks.length === 0) {
|
|
168
|
+
if (buildOptions.for || buildOptions.use) {
|
|
169
|
+
const task = {}
|
|
170
|
+
if ((buildOptions.for)) {
|
|
171
|
+
task.for = buildOptions.for
|
|
172
|
+
}
|
|
173
|
+
if ((buildOptions.use)) {
|
|
174
|
+
task.use = buildOptions.use
|
|
175
|
+
}
|
|
176
|
+
if (buildOptions.src) {
|
|
177
|
+
task.src = buildOptions.src
|
|
178
|
+
}
|
|
179
|
+
resultTasks.push(task)
|
|
180
|
+
}
|
|
181
|
+
} else if (resultTasks.length < tasks.length) {
|
|
182
|
+
// return the same array as long as it contains a subset of the given tasks
|
|
183
|
+
tasks.length = 0
|
|
184
|
+
resultTasks.forEach(task => tasks.push(task))
|
|
185
|
+
resultTasks = tasks
|
|
186
|
+
}
|
|
187
|
+
this._applyCliOptions(resultTasks)
|
|
188
|
+
return resultTasks
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_applyCliOptions(tasks) {
|
|
192
|
+
const buildOptions = this.buildOptions
|
|
193
|
+
// apply remaining cli options to filtered tasks
|
|
194
|
+
tasks.forEach(task => {
|
|
195
|
+
if (buildOptions.dest) {
|
|
196
|
+
task.dest = buildOptions.dest
|
|
197
|
+
}
|
|
198
|
+
if (buildOptions.opts) {
|
|
199
|
+
const opts = BuildTaskFactory._scanTaskOptionParams(buildOptions.opts)
|
|
200
|
+
task.options = task.options ? Object.assign(task.options, opts) : opts
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static _setTaskModelOptions(task, defaultModelPaths) {
|
|
206
|
+
task.options = task.options || {}
|
|
207
|
+
if (!task.options.model || Array.isArray(task.options.model) && task.options.model.length === 0) {
|
|
208
|
+
defaultModelPaths = new Set(defaultModelPaths)
|
|
209
|
+
if (task.src) {
|
|
210
|
+
defaultModelPaths.add(task.src)
|
|
211
|
+
}
|
|
212
|
+
task.options.model = [...defaultModelPaths]
|
|
213
|
+
} else if (!Array.isArray(task.options.model)) {
|
|
214
|
+
task.options.model = [task.options.model]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static _stringifyTask(task) {
|
|
219
|
+
// ensures identical order of properties
|
|
220
|
+
const order = ["for", "use", "src", "dest", "options"]
|
|
221
|
+
const keys = Object.keys(task).sort((a, b) => order.indexOf(a) - order.indexOf(b))
|
|
222
|
+
|
|
223
|
+
return keys.reduce((acc, key, idx) => {
|
|
224
|
+
// render either "for" OR "use" value
|
|
225
|
+
return acc + (!key.match("^_") ? (key !== "use" || keys[idx - 1] !== "for" ? (acc !== "{" ? ", " : "") + JSON.stringify(key) + ":" + JSON.stringify(task[key]) : "") : "")
|
|
226
|
+
}, "{") + "}"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static _scanTaskOptionParams(optsParams) {
|
|
230
|
+
// need to create new regex every call since a constant would keep the match state
|
|
231
|
+
const quoteRegex = /([\w-]+)=([\w/.]+|\[([\w/,.]+)\])/g
|
|
232
|
+
|
|
233
|
+
// captures a=1 => a:1
|
|
234
|
+
// a=[x,y,z] => a:[x,y,z]
|
|
235
|
+
// a=1,b=[x,y,z] => a:1 b=[x,y,z]
|
|
236
|
+
let match = quoteRegex.exec(optsParams)
|
|
237
|
+
const taskOptions = {}
|
|
238
|
+
|
|
239
|
+
while (match != null) {
|
|
240
|
+
const key = match[1]
|
|
241
|
+
const value = match[3] || match[2]
|
|
242
|
+
const valueArray = value.split(",")
|
|
243
|
+
taskOptions[key] = valueArray.length > 1 ? valueArray.map((entry) => entry.trim()) : value
|
|
244
|
+
match = quoteRegex.exec(optsParams)
|
|
245
|
+
}
|
|
246
|
+
return taskOptions
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* For valid paths remove trailing '/'. Otherwise return as is - important!!
|
|
251
|
+
* @param {*} dir
|
|
252
|
+
*/
|
|
253
|
+
static _normalizePath(dir) {
|
|
254
|
+
return typeof dir === "string" ? dir.replace(/\/$/, '') : dir
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Determines the module folder from the past list that may represent files or folders w or w/o .cds file extension.
|
|
259
|
+
* @param {Array} filesOrFolders
|
|
260
|
+
*/
|
|
261
|
+
static _getModuleFolder(filesOrFolders) {
|
|
262
|
+
const resources = [...filesOrFolders]
|
|
263
|
+
filesOrFolders.forEach(fileOrFolder => {
|
|
264
|
+
if (path.extname(fileOrFolder) !== FILE_EXT_CDS) {
|
|
265
|
+
resources.push(fileOrFolder + FILE_EXT_CDS)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
return resources.reduce((acc, resource) => {
|
|
269
|
+
if (!acc) {
|
|
270
|
+
let resourcePath = path.resolve(cds.root, resource)
|
|
271
|
+
if (fs.existsSync(resourcePath)) {
|
|
272
|
+
if (fs.lstatSync(resourcePath).isDirectory()) {
|
|
273
|
+
acc = resource
|
|
274
|
+
} else {
|
|
275
|
+
// represents file
|
|
276
|
+
acc = path.dirname(resource)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return acc
|
|
281
|
+
}, null)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
module.exports = BuildTaskFactory
|