@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.
Files changed (108) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/bin/bind.js +7 -2
  3. package/bin/build.js +14 -4
  4. package/bin/cds.js +2 -2
  5. package/bin/completion/completionFs.js +1 -1
  6. package/bin/completion/index.js +2 -2
  7. package/bin/data-products/get-csn-via-dp-cli.js +1 -1
  8. package/bin/debug.js +1 -1
  9. package/bin/deploy/index.js +3 -2
  10. package/bin/gen.js +1 -1
  11. package/bin/help.js +1 -1
  12. package/bin/import.js +3 -0
  13. package/bin/minify.js +1 -1
  14. package/bin/mock.js +1 -1
  15. package/bin/repl.js +212 -39
  16. package/bin/test.js +0 -2
  17. package/bin/up.js +27 -3
  18. package/bin/util/containerize.js +1 -3
  19. package/bin/version.js +27 -7
  20. package/bin/watch.js +20 -4
  21. package/lib/bind/cf.js +2 -2
  22. package/lib/bind/k8s.js +0 -1
  23. package/lib/bind/plugin.js +2 -0
  24. package/lib/bind/shared.js +6 -1
  25. package/lib/build/constants.js +0 -8
  26. package/lib/build/index.js +569 -25
  27. package/lib/build/{provider/edmxBuildPlugin.js → plugins/edmx.js} +6 -15
  28. package/lib/build/{provider/featureToggleBuildPlugin.js → plugins/featureToggles.js} +3 -23
  29. package/lib/build/{provider → plugins}/fiori/index.js +27 -38
  30. package/lib/build/{provider → plugins}/hana/index.js +61 -83
  31. package/lib/build/{provider → plugins}/hana/migrationtable.js +4 -3
  32. package/lib/build/plugins/helm/index.js +6 -6
  33. package/lib/build/{provider/internalBuildPlugin.js → plugins/internal.js} +7 -31
  34. package/lib/build/{provider → plugins}/java/index.js +18 -26
  35. package/lib/build/plugins/mtx/index.js +41 -0
  36. package/lib/build/plugins/mtx/resourcesTarBuilder.js +41 -0
  37. package/lib/build/{provider → plugins}/mtx-extension/extensionCompilation.js +1 -1
  38. package/lib/build/{provider → plugins}/mtx-extension/index.js +35 -44
  39. package/lib/build/{provider → plugins}/mtx-sidecar/index.js +18 -4
  40. package/lib/build/{provider → plugins}/nodejs/index.js +29 -28
  41. package/lib/build/{plugin.js → plugins/plugin.js} +45 -59
  42. package/lib/build/plugins.js +11 -8
  43. package/lib/build/util.js +18 -16
  44. package/lib/compile/to-xsuaa.js +2 -2
  45. package/lib/deploy/to-hana/hana.js +64 -56
  46. package/lib/import/common.js +1 -1
  47. package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -3
  48. package/lib/import/odata/v2parser.js +28 -28
  49. package/lib/import/odata/v4parser.js +37 -2
  50. package/lib/import/openapi/index.js +1 -1
  51. package/lib/init/constants.js +0 -1
  52. package/lib/init/template/app-frontend/index.js +1 -1
  53. package/lib/init/template/approuter/files/package.json +1 -1
  54. package/lib/init/template/approuter/files/values.yaml.hbs +1 -0
  55. package/lib/init/template/completion/completionSetup.js +1 -1
  56. package/lib/init/template/data/as-csv.js +1 -1
  57. package/lib/init/template/github-actions/index.js +1 -1
  58. package/lib/init/template/handler/index.js +1 -1
  59. package/lib/init/template/helm/index.js +3 -3
  60. package/lib/init/template/initial/files/readme.md +2 -4
  61. package/lib/init/template/kyma/index.js +9 -5
  62. package/lib/init/template/lint/index.js +7 -21
  63. package/lib/init/template/multitenancy/files/package.json.hbs +0 -5
  64. package/lib/init/template/postgres/files/containerize.yaml.hbs +15 -0
  65. package/lib/init/template/postgres/files/package.json.hbs +35 -0
  66. package/lib/init/template/postgres/files/values.yaml.hbs +23 -0
  67. package/lib/init/template/postgres/index.js +34 -9
  68. package/lib/init/template/test/files/package.json +1 -1
  69. package/lib/init/template/typescript/files/package.json +1 -1
  70. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +1 -0
  71. package/lib/lint/eslintHelp.json +1 -1
  72. package/lib/lint/io.js +9 -13
  73. package/lib/mtx/pull.js +1 -1
  74. package/lib/mtx/settings_manager.js +11 -5
  75. package/lib/mtx/tenant_action.js +1 -1
  76. package/lib/mtx/util/fs.js +2 -2
  77. package/lib/util/cf.js +1 -1
  78. package/lib/util/fs.js +3 -3
  79. package/lib/util/git.js +1 -1
  80. package/lib/util/glob.js +1 -1
  81. package/lib/watch/hot-reload.js +202 -0
  82. package/lib/watch/livereload-server.js +6 -4
  83. package/lib/watch/node-watch.js +5 -1
  84. package/lib/watch/watched.js +7 -3
  85. package/lib/watch/watcher.js +41 -3
  86. package/npm-shrinkwrap.json +35 -26
  87. package/package.json +1 -1
  88. package/lib/build/buildTaskEngine.js +0 -377
  89. package/lib/build/buildTaskFactory.js +0 -198
  90. package/lib/build/buildTaskProvider.js +0 -38
  91. package/lib/build/buildTaskProviderFactory.js +0 -233
  92. package/lib/build/provider/internalBuildTaskProvider.js +0 -227
  93. package/lib/build/provider/java-cf/index.js +0 -1
  94. package/lib/build/provider/mtx/index.js +0 -36
  95. package/lib/build/provider/mtx/resourcesTarBuilder.js +0 -102
  96. package/lib/build/provider/node-cf/index.js +0 -1
  97. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +0 -40
  98. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/fixed/schema.cds +0 -4
  99. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/invalid/schema.cds +0 -4
  100. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +0 -15
  101. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/valid/schema.cds +0 -6
  102. package/lib/init/types.d.ts +0 -23
  103. /package/lib/build/{provider → plugins}/hana/2migration.js +0 -0
  104. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-all +0 -0
  105. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-cloud +0 -0
  106. /package/lib/build/{provider → plugins}/hana/template/.hdinamespace +0 -0
  107. /package/lib/build/{provider → plugins}/hana/template/package.json +0 -0
  108. /package/lib/build/{provider → plugins}/hana/template/undeploy.json +0 -0
@@ -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 (_allow(fullPath)) {
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 (_allow(fullPath)) {
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
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.7.1",
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.7.1",
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": "9.39.2",
81
- "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
80
+ "version": "10.0.1",
81
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
82
82
  "license": "MIT",
83
83
  "peer": true,
84
84
  "engines": {
85
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
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.7.0",
93
- "integrity": "sha512-E2ntY7l98fIIbhRbgjxL/M3+iM0mAOz8ehNu+evCUusQHveTWyx3b4DQomJaD4yOAlzMq1m26iDYQBT8HuSIpg==",
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.7.2",
120
- "integrity": "sha512-5UpDA7boOmwtTBWjhEDbpXKgxjqz+0zObBr12P3g5D7QIMF+hPrPG+J0Bm2AQRPE/lv8VcnzFEaMdibRgmGtUw==",
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.2.0",
133
- "integrity": "sha512-NnvpmU40Eez5Q3SkyCCLRuBd1XUjk7N+pbXcIPIkKw4rwvqR2/FbjNveRzc8mcrLcQA3/5b/CXYsafDoozUhng==",
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.7.0",
141
- "integrity": "sha512-SojfvUrcPaVmaPpddUqosAC6Eqft+wdZMLs56C5a0XM54l3P+aWKgd4+G2IBbUjtAgeSznzmwb64dT5oOncSOQ==",
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.4",
255
- "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
262
+ "version": "1.13.6",
263
+ "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
256
264
  "license": "MIT",
257
265
  "dependencies": {
258
- "follow-redirects": "^1.15.6",
259
- "form-data": "^4.0.4",
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.3",
1328
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
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.14.1",
1338
- "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
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.4.4",
1443
- "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
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.3",
1451
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.7.1",
3
+ "version": "9.8.0",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -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