@sap/cds-dk 9.7.1 → 9.8.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 +44 -1
- package/bin/bind.js +7 -2
- package/bin/build.js +14 -4
- package/bin/cds.js +2 -2
- package/bin/completion/completionFs.js +1 -1
- package/bin/completion/index.js +2 -2
- package/bin/data-products/get-csn-via-dp-cli.js +1 -1
- package/bin/debug.js +1 -1
- package/bin/deploy/index.js +3 -2
- package/bin/gen.js +1 -1
- package/bin/help.js +1 -1
- package/bin/import.js +3 -0
- package/bin/minify.js +1 -1
- package/bin/mock.js +1 -1
- package/bin/repl.js +212 -39
- package/bin/test.js +0 -2
- package/bin/up.js +27 -3
- package/bin/util/containerize.js +1 -3
- package/bin/version.js +27 -7
- package/bin/watch.js +20 -4
- package/lib/bind/cf.js +2 -2
- package/lib/bind/k8s.js +0 -1
- package/lib/bind/plugin.js +2 -0
- package/lib/bind/shared.js +6 -1
- package/lib/build/constants.js +0 -8
- package/lib/build/index.js +569 -25
- package/lib/build/{provider/edmxBuildPlugin.js → plugins/edmx.js} +6 -15
- package/lib/build/{provider/featureToggleBuildPlugin.js → plugins/featureToggles.js} +3 -23
- package/lib/build/{provider → plugins}/fiori/index.js +27 -38
- package/lib/build/{provider → plugins}/hana/index.js +61 -83
- package/lib/build/{provider → plugins}/hana/migrationtable.js +4 -3
- package/lib/build/plugins/helm/index.js +6 -6
- package/lib/build/{provider/internalBuildPlugin.js → plugins/internal.js} +7 -31
- package/lib/build/{provider → plugins}/java/index.js +18 -26
- package/lib/build/plugins/mtx/index.js +41 -0
- package/lib/build/plugins/mtx/resourcesTarBuilder.js +41 -0
- package/lib/build/{provider → plugins}/mtx-extension/extensionCompilation.js +1 -1
- package/lib/build/{provider → plugins}/mtx-extension/index.js +35 -44
- package/lib/build/{provider → plugins}/mtx-sidecar/index.js +18 -4
- package/lib/build/{provider → plugins}/nodejs/index.js +29 -28
- package/lib/build/{plugin.js → plugins/plugin.js} +45 -59
- package/lib/build/plugins.js +11 -8
- package/lib/build/util.js +18 -16
- package/lib/compile/to-xsuaa.js +2 -2
- package/lib/deploy/to-hana/hana.js +64 -56
- package/lib/import/common.js +1 -1
- package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -3
- package/lib/import/odata/v2parser.js +28 -28
- package/lib/import/odata/v4parser.js +37 -2
- package/lib/import/openapi/index.js +1 -1
- package/lib/init/constants.js +0 -1
- package/lib/init/template/app-frontend/index.js +1 -1
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/values.yaml.hbs +1 -0
- package/lib/init/template/completion/completionSetup.js +1 -1
- package/lib/init/template/data/as-csv.js +1 -1
- package/lib/init/template/github-actions/index.js +1 -1
- package/lib/init/template/handler/index.js +1 -1
- package/lib/init/template/helm/index.js +3 -3
- package/lib/init/template/initial/files/readme.md +2 -4
- package/lib/init/template/kyma/index.js +9 -5
- package/lib/init/template/lint/index.js +7 -21
- package/lib/init/template/multitenancy/files/package.json.hbs +0 -5
- package/lib/init/template/postgres/files/containerize.yaml.hbs +15 -0
- package/lib/init/template/postgres/files/package.json.hbs +35 -0
- package/lib/init/template/postgres/files/values.yaml.hbs +23 -0
- package/lib/init/template/postgres/index.js +34 -9
- package/lib/init/template/test/files/package.json +1 -1
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +1 -0
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/lint/io.js +9 -13
- package/lib/mtx/pull.js +1 -1
- package/lib/mtx/settings_manager.js +11 -5
- package/lib/mtx/tenant_action.js +1 -1
- package/lib/mtx/util/fs.js +2 -2
- package/lib/util/cf.js +1 -1
- package/lib/util/fs.js +3 -3
- package/lib/util/git.js +1 -1
- package/lib/util/glob.js +1 -1
- package/lib/watch/hot-reload.js +202 -0
- package/lib/watch/livereload-server.js +6 -4
- package/lib/watch/node-watch.js +5 -1
- package/lib/watch/watched.js +7 -3
- package/lib/watch/watcher.js +41 -3
- package/npm-shrinkwrap.json +35 -26
- package/package.json +1 -1
- package/lib/build/buildTaskEngine.js +0 -377
- package/lib/build/buildTaskFactory.js +0 -198
- package/lib/build/buildTaskProvider.js +0 -38
- package/lib/build/buildTaskProviderFactory.js +0 -233
- package/lib/build/provider/internalBuildTaskProvider.js +0 -227
- package/lib/build/provider/java-cf/index.js +0 -1
- package/lib/build/provider/mtx/index.js +0 -36
- package/lib/build/provider/mtx/resourcesTarBuilder.js +0 -102
- package/lib/build/provider/node-cf/index.js +0 -1
- package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +0 -40
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/fixed/schema.cds +0 -4
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/invalid/schema.cds +0 -4
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +0 -15
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/valid/schema.cds +0 -6
- package/lib/init/types.d.ts +0 -23
- /package/lib/build/{provider → plugins}/hana/2migration.js +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-all +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-cloud +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdinamespace +0 -0
- /package/lib/build/{provider → plugins}/hana/template/package.json +0 -0
- /package/lib/build/{provider → plugins}/hana/template/undeploy.json +0 -0
package/lib/watch/watcher.js
CHANGED
|
@@ -2,13 +2,47 @@ const fs = require('node:fs')
|
|
|
2
2
|
const { resolve, sep } = require('node:path')
|
|
3
3
|
const { EventEmitter } = require('node:events')
|
|
4
4
|
|
|
5
|
-
module.exports = function watch(paths, { filter, ignore } = {}) {
|
|
5
|
+
module.exports = function watch(paths, { filter, ignore, hotReload } = {}) {
|
|
6
6
|
const ee = new EventEmitter()
|
|
7
7
|
const roots = [...new Set((Array.isArray(paths) ? paths : [paths]).filter(Boolean).map(p => resolve(p)))]
|
|
8
8
|
const ac = new AbortController() // eslint-disable-line no-undef
|
|
9
9
|
let closed = false
|
|
10
10
|
|
|
11
11
|
const _allow = p => !filter || (filter instanceof RegExp ? filter.test(p) : filter(p))
|
|
12
|
+
const dataFiles = hotReload && [
|
|
13
|
+
new RegExp(`test.*data.*\\.(csv|json)`),
|
|
14
|
+
new RegExp(`db.*data.*\\.(csv|json)`)
|
|
15
|
+
]
|
|
16
|
+
const _hotReload = !hotReload ? ()=>false : fullPath => {
|
|
17
|
+
if (!hotReload || !_allow(fullPath)) return false;
|
|
18
|
+
// Hot reload all CDS files, services are
|
|
19
|
+
if (fullPath.endsWith('.cds')) return true;
|
|
20
|
+
// .properties files are used for i18n -> hot reload i18n
|
|
21
|
+
if (fullPath.endsWith('.properties') && fullPath.includes('i18n')) return true;
|
|
22
|
+
// .json & .csv files used in data folders trigger a hot reload, e.g. delete and insert for the table
|
|
23
|
+
if (dataFiles.some(regex => regex.test(fullPath))) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const reloadRelevantJSONfiles = hotReload ? [
|
|
30
|
+
new RegExp(`\\.cdsrc\\.json`),
|
|
31
|
+
new RegExp(`\\package\\.json`),
|
|
32
|
+
new RegExp(`\\.cdsrc-private\\.json`),
|
|
33
|
+
new RegExp(`default-env\\.json`),
|
|
34
|
+
] : []
|
|
35
|
+
/**
|
|
36
|
+
* Helper function to in case of hot reload restrict reloads based on .json files to just
|
|
37
|
+
* .cdsrc.json, cdsrc-private.json and root package.json
|
|
38
|
+
*/
|
|
39
|
+
const _narrowJSONreloads = !hotReload ? ()=> true : fullPath => {
|
|
40
|
+
if (!hotReload) return true;
|
|
41
|
+
if (reloadRelevantJSONfiles.some(regex => regex.test(fullPath))) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
12
46
|
|
|
13
47
|
const useNativeRecursive = process.platform === 'darwin' || process.platform === 'win32'
|
|
14
48
|
if (useNativeRecursive) {
|
|
@@ -20,7 +54,9 @@ module.exports = function watch(paths, { filter, ignore } = {}) {
|
|
|
20
54
|
if (closed) break
|
|
21
55
|
const name = event.filename?.toString?.() ?? event.filename ?? ''
|
|
22
56
|
const fullPath = resolve(root, name)
|
|
23
|
-
if (
|
|
57
|
+
if (_hotReload(fullPath)) {
|
|
58
|
+
ee.emit('refreshModel', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
59
|
+
} else if (_allow(fullPath) && _narrowJSONreloads(fullPath)) {
|
|
24
60
|
ee.emit('change', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
25
61
|
}
|
|
26
62
|
}
|
|
@@ -84,7 +120,9 @@ module.exports = function watch(paths, { filter, ignore } = {}) {
|
|
|
84
120
|
if (fs.lstatSync(fullPath).isDirectory()) _enqueue(fullPath)
|
|
85
121
|
} catch { /* ignore */ }
|
|
86
122
|
}
|
|
87
|
-
if (
|
|
123
|
+
if (_hotReload(fullPath)) {
|
|
124
|
+
ee.emit('refreshModel', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
125
|
+
} else if (_allow(fullPath) && _narrowJSONreloads(fullPath)) {
|
|
88
126
|
ee.emit('change', fs.existsSync(fullPath) ? 'update' : 'remove', fullPath)
|
|
89
127
|
}
|
|
90
128
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.8.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sap/cds-dk",
|
|
9
|
-
"version": "9.
|
|
9
|
+
"version": "9.8.0",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@cap-js/asyncapi": "^1.0.0",
|
|
@@ -77,20 +77,28 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"node_modules/@eslint/js": {
|
|
80
|
-
"version": "
|
|
81
|
-
"integrity": "sha512-
|
|
80
|
+
"version": "10.0.1",
|
|
81
|
+
"integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
|
|
82
82
|
"license": "MIT",
|
|
83
83
|
"peer": true,
|
|
84
84
|
"engines": {
|
|
85
|
-
"node": "^
|
|
85
|
+
"node": "^20.19.0 || ^22.13.0 || >=24"
|
|
86
86
|
},
|
|
87
87
|
"funding": {
|
|
88
88
|
"url": "https://eslint.org/donate"
|
|
89
|
+
},
|
|
90
|
+
"peerDependencies": {
|
|
91
|
+
"eslint": "^10.0.0"
|
|
92
|
+
},
|
|
93
|
+
"peerDependenciesMeta": {
|
|
94
|
+
"eslint": {
|
|
95
|
+
"optional": true
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
},
|
|
91
99
|
"node_modules/@sap/cds": {
|
|
92
|
-
"version": "9.
|
|
93
|
-
"integrity": "sha512-
|
|
100
|
+
"version": "9.8.0",
|
|
101
|
+
"integrity": "sha512-Tiax/4R/y1dJJsl53VjMKe5VM0FtxLwG4BOPuLVnJhCawMxMlebXM+jlIFkrXhUL2bVet0uUdjAH/Dq4uVwVOg==",
|
|
94
102
|
"license": "SEE LICENSE IN LICENSE",
|
|
95
103
|
"dependencies": {
|
|
96
104
|
"@sap/cds-compiler": "^6.4",
|
|
@@ -106,7 +114,7 @@
|
|
|
106
114
|
"node": ">=20"
|
|
107
115
|
},
|
|
108
116
|
"peerDependencies": {
|
|
109
|
-
"@eslint/js": "^9",
|
|
117
|
+
"@eslint/js": "^9 || ^10",
|
|
110
118
|
"tar": "^7.5.6"
|
|
111
119
|
},
|
|
112
120
|
"peerDependenciesMeta": {
|
|
@@ -116,8 +124,8 @@
|
|
|
116
124
|
}
|
|
117
125
|
},
|
|
118
126
|
"node_modules/@sap/cds-compiler": {
|
|
119
|
-
"version": "6.
|
|
120
|
-
"integrity": "sha512-
|
|
127
|
+
"version": "6.8.0",
|
|
128
|
+
"integrity": "sha512-yRoTZcH8DFiP4PcEuIHe42YAaNt51V736+6RRr+U4nbO91sz36L8be2djcE8n760t8IbqLxzZ/UkATiL0fjRmA==",
|
|
121
129
|
"license": "SEE LICENSE IN LICENSE",
|
|
122
130
|
"bin": {
|
|
123
131
|
"cdsc": "bin/cdsc.js",
|
|
@@ -129,16 +137,16 @@
|
|
|
129
137
|
}
|
|
130
138
|
},
|
|
131
139
|
"node_modules/@sap/cds-fiori": {
|
|
132
|
-
"version": "2.
|
|
133
|
-
"integrity": "sha512-
|
|
140
|
+
"version": "2.3.0",
|
|
141
|
+
"integrity": "sha512-6oWov+DSpFrSTgxXR0dZhak6aZ/IVRZvaHERMi0EgSTzIJdlvZlpw3Kf18ePMcTrRrtEXwD4RIjKt8pbs0g2Hg==",
|
|
134
142
|
"license": "SEE LICENSE IN LICENSE",
|
|
135
143
|
"peerDependencies": {
|
|
136
144
|
"@sap/cds": ">=8"
|
|
137
145
|
}
|
|
138
146
|
},
|
|
139
147
|
"node_modules/@sap/cds-mtxs": {
|
|
140
|
-
"version": "3.
|
|
141
|
-
"integrity": "sha512-
|
|
148
|
+
"version": "3.8.0",
|
|
149
|
+
"integrity": "sha512-8P/CKQbdbKH/1ZmgrRWiReXFb9MjLf/TjEH/MrbiJEGWCIv/bTKL6nNb0BIcBa7HQhekVqapa/cwx241NEnAVQ==",
|
|
142
150
|
"license": "SEE LICENSE IN LICENSE",
|
|
143
151
|
"dependencies": {
|
|
144
152
|
"@sap/hdi-deploy": "^5"
|
|
@@ -251,12 +259,12 @@
|
|
|
251
259
|
"license": "MIT"
|
|
252
260
|
},
|
|
253
261
|
"node_modules/axios": {
|
|
254
|
-
"version": "1.13.
|
|
255
|
-
"integrity": "sha512-
|
|
262
|
+
"version": "1.13.6",
|
|
263
|
+
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
|
256
264
|
"license": "MIT",
|
|
257
265
|
"dependencies": {
|
|
258
|
-
"follow-redirects": "^1.15.
|
|
259
|
-
"form-data": "^4.0.
|
|
266
|
+
"follow-redirects": "^1.15.11",
|
|
267
|
+
"form-data": "^4.0.5",
|
|
260
268
|
"proxy-from-env": "^1.1.0"
|
|
261
269
|
}
|
|
262
270
|
},
|
|
@@ -1283,6 +1291,7 @@
|
|
|
1283
1291
|
"node_modules/prebuild-install": {
|
|
1284
1292
|
"version": "7.1.3",
|
|
1285
1293
|
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
|
1294
|
+
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
|
|
1286
1295
|
"license": "MIT",
|
|
1287
1296
|
"optional": true,
|
|
1288
1297
|
"dependencies": {
|
|
@@ -1324,8 +1333,8 @@
|
|
|
1324
1333
|
"license": "MIT"
|
|
1325
1334
|
},
|
|
1326
1335
|
"node_modules/pump": {
|
|
1327
|
-
"version": "3.0.
|
|
1328
|
-
"integrity": "sha512-
|
|
1336
|
+
"version": "3.0.4",
|
|
1337
|
+
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
|
1329
1338
|
"license": "MIT",
|
|
1330
1339
|
"optional": true,
|
|
1331
1340
|
"dependencies": {
|
|
@@ -1334,8 +1343,8 @@
|
|
|
1334
1343
|
}
|
|
1335
1344
|
},
|
|
1336
1345
|
"node_modules/qs": {
|
|
1337
|
-
"version": "6.
|
|
1338
|
-
"integrity": "sha512-
|
|
1346
|
+
"version": "6.15.0",
|
|
1347
|
+
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
|
1339
1348
|
"license": "BSD-3-Clause",
|
|
1340
1349
|
"dependencies": {
|
|
1341
1350
|
"side-channel": "^1.1.0"
|
|
@@ -1439,16 +1448,16 @@
|
|
|
1439
1448
|
"license": "MIT"
|
|
1440
1449
|
},
|
|
1441
1450
|
"node_modules/sax": {
|
|
1442
|
-
"version": "1.
|
|
1443
|
-
"integrity": "sha512-
|
|
1451
|
+
"version": "1.5.0",
|
|
1452
|
+
"integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==",
|
|
1444
1453
|
"license": "BlueOak-1.0.0",
|
|
1445
1454
|
"engines": {
|
|
1446
1455
|
"node": ">=11.0.0"
|
|
1447
1456
|
}
|
|
1448
1457
|
},
|
|
1449
1458
|
"node_modules/semver": {
|
|
1450
|
-
"version": "7.7.
|
|
1451
|
-
"integrity": "sha512-
|
|
1459
|
+
"version": "7.7.4",
|
|
1460
|
+
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
|
1452
1461
|
"devOptional": true,
|
|
1453
1462
|
"license": "ISC",
|
|
1454
1463
|
"bin": {
|
package/package.json
CHANGED
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
const fs = require('node:fs')
|
|
3
|
-
const path = require('node:path')
|
|
4
|
-
const { inspect } = require('node:util')
|
|
5
|
-
// @ts-expect-error - _log not declared in cds-types
|
|
6
|
-
const cds = require('../cds'), { _log: log } = cds
|
|
7
|
-
const { sortMessagesSeverityAware, deduplicateMessages, CompilationError } = cds.compiler
|
|
8
|
-
const { relativePaths, BuildError, BuildMessage, resolveRequiredSapModels } = require('./util')
|
|
9
|
-
const { SEVERITIES, SEVERITY_ERROR, LOG_LEVELS } = require('./constants')
|
|
10
|
-
const BuildTaskFactory = require('./buildTaskFactory')
|
|
11
|
-
const InternalBuildPlugin = require('./provider/internalBuildPlugin')
|
|
12
|
-
const COMPILATION_ERROR = 'CompilationError'
|
|
13
|
-
const COMPILE_MESSAGE = 'CompileMessage'
|
|
14
|
-
const DEBUG = cds.debug('cli|build')
|
|
15
|
-
const LOG_LIMIT = 30
|
|
16
|
-
|
|
17
|
-
class BuildTaskEngine {
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @param {import('../build').BuildOptions} options - command options
|
|
21
|
-
*/
|
|
22
|
-
constructor(options) {
|
|
23
|
-
this._taskFactory = new BuildTaskFactory(options)
|
|
24
|
-
}
|
|
25
|
-
get taskFactory() {
|
|
26
|
-
return this._taskFactory
|
|
27
|
-
}
|
|
28
|
-
get options() {
|
|
29
|
-
return this.taskFactory.options
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async processTasks(tasks) {
|
|
33
|
-
const startTime = Date.now()
|
|
34
|
-
|
|
35
|
-
tasks ??= await this.taskFactory.getTasks()
|
|
36
|
-
console.log(`building project with`, inspect({
|
|
37
|
-
versions: { cds: cds.version, compiler: cds.compiler.version(), dk: require('../../package.json').version },
|
|
38
|
-
target: cds.env.build.target,
|
|
39
|
-
tasks: tasks.map(({ _uses, ...task }) => task),
|
|
40
|
-
...(!this.options.clean && { clean: this.options.clean }),
|
|
41
|
-
}, { depth: 11, colors: cds.utils.colors.enabled, compact: 3, breakLength: 250 }))
|
|
42
|
-
|
|
43
|
-
// validate required @sap namespace models - log only
|
|
44
|
-
const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
|
|
45
|
-
if (unresolved.length > 0) {
|
|
46
|
-
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.`)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// create build plugins
|
|
50
|
-
const plugins = []
|
|
51
|
-
tasks.forEach((task) => {
|
|
52
|
-
if (task) {
|
|
53
|
-
const plugin = this._createPlugin(task)
|
|
54
|
-
plugins.push(plugin)
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
await this._executePrepare(plugins)
|
|
60
|
-
await this._executeCleanBuildTasks(plugins)
|
|
61
|
-
|
|
62
|
-
// throwing Exception in case of compilation errors
|
|
63
|
-
const buildResult = await this._executeBuildTasks(plugins)
|
|
64
|
-
|
|
65
|
-
await this._writeGenerationLog(plugins)
|
|
66
|
-
this._logOutput(plugins)
|
|
67
|
-
this._logMessages(BuildTaskEngine._getMessages(plugins))
|
|
68
|
-
this._logTimer(startTime, Date.now())
|
|
69
|
-
|
|
70
|
-
return buildResult
|
|
71
|
-
} catch (error) {
|
|
72
|
-
this._logOutput(plugins)
|
|
73
|
-
throw error
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Plugin#prepare has been deprecated and was never part of the public API.
|
|
79
|
-
* Currently only used by internal FioriBuildPlugin.
|
|
80
|
-
* @deprecated
|
|
81
|
-
* @private
|
|
82
|
-
* @param {*} plugins
|
|
83
|
-
* @returns
|
|
84
|
-
*/
|
|
85
|
-
async _executePrepare(plugins) {
|
|
86
|
-
const pluginGroups = new Map()
|
|
87
|
-
|
|
88
|
-
// group plugins by type
|
|
89
|
-
plugins.forEach(plugin => {
|
|
90
|
-
pluginGroups.has(plugin.task.for) ? pluginGroups.get(plugin.task.for).push(plugin) : pluginGroups.set(plugin.task.for, [plugin])
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const promises = []
|
|
94
|
-
for (let pluginGroup of pluginGroups.values()) {
|
|
95
|
-
promises.push(this._doPrepare(pluginGroup))
|
|
96
|
-
}
|
|
97
|
-
return Promise.all(promises)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @deprecated
|
|
102
|
-
* @private
|
|
103
|
-
* @param {*} pluginGroup
|
|
104
|
-
*/
|
|
105
|
-
async _doPrepare(pluginGroup) {
|
|
106
|
-
for (let plugin of pluginGroup) {
|
|
107
|
-
// prepare has been deprecated
|
|
108
|
-
if (plugin instanceof InternalBuildPlugin) {
|
|
109
|
-
DEBUG?.(`preparing, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
|
|
110
|
-
const result = await plugin.prepare()
|
|
111
|
-
if (result === false) {
|
|
112
|
-
break
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** @private */
|
|
119
|
-
async _executeCleanBuildTasks(plugins) {
|
|
120
|
-
if (this.options.clean) {
|
|
121
|
-
// clean entire build staging folder once
|
|
122
|
-
if (cds.env.build.target !== '.') {
|
|
123
|
-
const target = path.resolve(cds.root, cds.env.build.target)
|
|
124
|
-
DEBUG?.(`cleaning staging folder ${target}`)
|
|
125
|
-
await fs.promises.rm(target, { force: true, recursive: true })
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const results = await Promise.allSettled(plugins.map((plugin) => {
|
|
129
|
-
DEBUG?.(`cleaning, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
|
|
130
|
-
return plugin.clean()
|
|
131
|
-
}))
|
|
132
|
-
// check for errors and throw exception
|
|
133
|
-
this._resolveHandlerResponse(results)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** @private */
|
|
138
|
-
async _executeBuildTasks(plugins) {
|
|
139
|
-
// sort plugins based on priority in
|
|
140
|
-
plugins = plugins.sort((a, b) => {
|
|
141
|
-
return a.priority === b.priority ? 0 : a.priority > b.priority ? -1 : 1
|
|
142
|
-
})
|
|
143
|
-
// group plugins with same priority in order to execute in parallel
|
|
144
|
-
const buildPipeline = plugins.reduce((acc, plugin) => {
|
|
145
|
-
if (acc.length === 0) {
|
|
146
|
-
acc.push([plugin])
|
|
147
|
-
} else {
|
|
148
|
-
const currGroup = acc[acc.length - 1]
|
|
149
|
-
if (currGroup[0].priority === plugin.priority) {
|
|
150
|
-
currGroup.push(plugin)
|
|
151
|
-
} else {
|
|
152
|
-
acc.push([plugin])
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return acc
|
|
156
|
-
}, [])
|
|
157
|
-
|
|
158
|
-
const results = await this._executePipeline(buildPipeline)
|
|
159
|
-
|
|
160
|
-
// check for errors and throw exception - return results otherwise including any compiler and build status messages
|
|
161
|
-
return this._resolveHandlerResponse(results, BuildTaskEngine._getMessages(plugins))
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** @private */
|
|
165
|
-
async _executePipeline(pipeline) {
|
|
166
|
-
let allResults = []
|
|
167
|
-
for (const group of pipeline) {
|
|
168
|
-
const results = await Promise.allSettled(group.map((plugin) => {
|
|
169
|
-
DEBUG?.(`building, plugin [${plugin.constructor.name}], src [${relativePaths(cds.root, plugin.task.src)}]`)
|
|
170
|
-
return plugin.build()
|
|
171
|
-
.then(pluginResult => {
|
|
172
|
-
return Promise.resolve({
|
|
173
|
-
task: plugin.task,
|
|
174
|
-
result: pluginResult,
|
|
175
|
-
messages: this._sortMessagesUnique(plugin.messages)
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
}))
|
|
179
|
-
allResults = allResults.concat(results)
|
|
180
|
-
}
|
|
181
|
-
return allResults
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** @private */
|
|
185
|
-
_resolveHandlerResponse(results, pluginMessages = []) {
|
|
186
|
-
/** @type {(BuildError | Error)[]}*/
|
|
187
|
-
const errors = []
|
|
188
|
-
const resolvedResults = results.reduce((acc, r) => {
|
|
189
|
-
if (r.status === 'fulfilled') {
|
|
190
|
-
acc.push(r.value)
|
|
191
|
-
}
|
|
192
|
-
if (r.status === 'rejected' && r.reason) {
|
|
193
|
-
errors.push(r.reason)
|
|
194
|
-
}
|
|
195
|
-
return acc
|
|
196
|
-
}, [])
|
|
197
|
-
|
|
198
|
-
if (errors.length > 0) {
|
|
199
|
-
const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
|
|
200
|
-
if (error) {
|
|
201
|
-
// throw original BuildError including existing BuildMessages
|
|
202
|
-
if (!(error instanceof BuildError)) {
|
|
203
|
-
throw error
|
|
204
|
-
}
|
|
205
|
-
error.messages = error.messages?.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
|
|
206
|
-
error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
|
|
207
|
-
throw error
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// propagate existing CompilationErrors
|
|
211
|
-
// merge all existing compilation messages into a single CompilationError
|
|
212
|
-
// compiler warning and info messages are returned as plugin messages
|
|
213
|
-
const compileErrors = errors.filter(e => e.constructor.name === COMPILATION_ERROR)
|
|
214
|
-
const compileMessages = pluginMessages.filter(message => message.constructor.name === COMPILE_MESSAGE)
|
|
215
|
-
if (compileErrors.length) {
|
|
216
|
-
throw new CompilationError(this._sortMessagesUnique(BuildTaskEngine._getErrorMessages(compileErrors), compileMessages))
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return resolvedResults
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** @private */
|
|
223
|
-
_createPlugin(task) {
|
|
224
|
-
const plugin = this.taskFactory.createPlugin(task)
|
|
225
|
-
plugin.init()
|
|
226
|
-
|
|
227
|
-
if (!(plugin instanceof InternalBuildPlugin) && plugin.priority >= 0 && plugin.priority <= 512) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
`Invalid priority value for ${plugin.constructor.name}.
|
|
230
|
-
The valid priority value range for custom plugins is -1024..-1 and 512..1024.
|
|
231
|
-
Range 0..512 is blocked for internal plugins.
|
|
232
|
-
The higher the value the earlier the plugin is run.`
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
this._logTaskHandler(plugin)
|
|
237
|
-
return plugin
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** @private */
|
|
241
|
-
_logOutput(plugins) {
|
|
242
|
-
// log all generated files
|
|
243
|
-
const files = BuildTaskEngine._getOutput(plugins)
|
|
244
|
-
if (files.length > 0) {
|
|
245
|
-
const term = require('../util/term')
|
|
246
|
-
if (!process.env.DEBUG && files.length > LOG_LIMIT) {
|
|
247
|
-
const length = files.length
|
|
248
|
-
files.length = LOG_LIMIT
|
|
249
|
-
files.push(`... ${length - files.length} more. Run with DEBUG=build to show all files.`)
|
|
250
|
-
}
|
|
251
|
-
console.log(`done > wrote output to:\n ${term.dim(files.join("\n "))}\n`)
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/** @private */
|
|
256
|
-
async _writeGenerationLog(plugins) {
|
|
257
|
-
const outputFile = cds.env.build.outputfile || process.env.GENERATION_LOG
|
|
258
|
-
if (outputFile) {
|
|
259
|
-
const files = BuildTaskEngine._getOutput(plugins)
|
|
260
|
-
console.log(`writing generation log to [${outputFile}]\n`)
|
|
261
|
-
try {
|
|
262
|
-
await fs.promises.mkdir(path.dirname(outputFile), { recursive: true })
|
|
263
|
-
await fs.promises.writeFile(outputFile, files.join('\n'))
|
|
264
|
-
} catch (error) {
|
|
265
|
-
console.error(`failed to write generation log`)
|
|
266
|
-
console.error(error.stack || error)
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/** @private */
|
|
272
|
-
static _getOutput(plugins) {
|
|
273
|
-
const files = plugins.reduce((acc, plugin) => acc.concat(plugin.files), []).sort()
|
|
274
|
-
return files.map(file => {
|
|
275
|
-
if (path.isAbsolute(cds.env.build.target)) {
|
|
276
|
-
return file
|
|
277
|
-
}
|
|
278
|
-
return path.relative(cds.root, file)
|
|
279
|
-
})
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/** @private */
|
|
283
|
-
_logTimer(start, end) {
|
|
284
|
-
console.log(`build completed in ${end - start} ms`)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/** @private */
|
|
288
|
-
_logTaskHandler(plugin) {
|
|
289
|
-
DEBUG?.(`plugin ${plugin.constructor.name}`)
|
|
290
|
-
DEBUG?.(`details src [${relativePaths(cds.root, plugin.task.src)}], dest [${relativePaths(cds.root, plugin.task.dest)}], options [${JSON.stringify(plugin.task.options)}]`)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** @private */
|
|
294
|
-
_logMessages(messages) {
|
|
295
|
-
if (messages.length > 0) {
|
|
296
|
-
const options = {
|
|
297
|
-
log: console.log,
|
|
298
|
-
"log-level": this._getLogLevel() // ensures that for tests the correct cds.env is used
|
|
299
|
-
}
|
|
300
|
-
deduplicateMessages(messages)
|
|
301
|
-
sortMessagesSeverityAware(messages)
|
|
302
|
-
log(messages, options)
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/** @private */
|
|
307
|
-
static _resolveRequiredSapServices(tasks) {
|
|
308
|
-
const taskModelPaths = tasks.reduce((acc, task) => {
|
|
309
|
-
const model = task.options?.model
|
|
310
|
-
if (model) {
|
|
311
|
-
if (Array.isArray(model)) {
|
|
312
|
-
model.forEach(m => acc.add(m))
|
|
313
|
-
} else {
|
|
314
|
-
acc.add(model)
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return acc
|
|
318
|
-
}, new Set())
|
|
319
|
-
|
|
320
|
-
return resolveRequiredSapModels([...taskModelPaths])
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Returns a sorted and flattened list of all messages extracted from the given errors.
|
|
325
|
-
* @private
|
|
326
|
-
* @param {(string | BuildError)[]} errors
|
|
327
|
-
*/
|
|
328
|
-
static _getErrorMessages(errors) {
|
|
329
|
-
let messages = []
|
|
330
|
-
// flatten all compile messages in order to filter duplicates and sort later on
|
|
331
|
-
errors.forEach(error => {
|
|
332
|
-
if (typeof error !== 'string' && Array.isArray(error.errors) && error.errors.length > 0) {
|
|
333
|
-
messages = messages.concat(this._getErrorMessages(error.errors))
|
|
334
|
-
} else {
|
|
335
|
-
messages.push(error)
|
|
336
|
-
}
|
|
337
|
-
})
|
|
338
|
-
return messages
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Returns compiler messages and validation messages issued by plugins.
|
|
343
|
-
* @param {Array} plugins
|
|
344
|
-
*/
|
|
345
|
-
static _getMessages(plugins) {
|
|
346
|
-
return plugins.reduce((acc, plugin) => acc.concat(plugin.messages), [])
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Sort and filter the given errors of type CompileMessage or BuildMessage according to their severity and location,
|
|
351
|
-
* but leave any other errors untouched as part of the result array.<br>
|
|
352
|
-
* The log level ('command line' or 'cds.env' option) is used to filter the given messages.
|
|
353
|
-
* @private
|
|
354
|
-
* @param {...{[key:string]: any, severity?: import('./constants').Severity}} messages
|
|
355
|
-
*/
|
|
356
|
-
_sortMessagesUnique(...messages) {
|
|
357
|
-
const logLevelIdx = LOG_LEVELS.indexOf(this._getLogLevel())
|
|
358
|
-
// flatten
|
|
359
|
-
messages = messages.reduce((acc, m) => acc.concat(m), [])
|
|
360
|
-
// filter according to log-level
|
|
361
|
-
const filteredMessages = messages.filter(message => message && (!message.severity || logLevelIdx >= SEVERITIES.indexOf(message.severity)))
|
|
362
|
-
// remove duplicates
|
|
363
|
-
// @ts-expect-error - formally expects CompileMessage, but actually, an .severity is the only expected property
|
|
364
|
-
deduplicateMessages(filteredMessages)
|
|
365
|
-
// sort
|
|
366
|
-
// @ts-expect-error - see above
|
|
367
|
-
return sortMessagesSeverityAware(filteredMessages)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Return user defined log level or default value 'warn'
|
|
372
|
-
*/
|
|
373
|
-
_getLogLevel() {
|
|
374
|
-
return this.options["log-level"] || cds.env["log-level"]
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
module.exports = BuildTaskEngine
|