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