@sap/cds-dk 9.7.2 → 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 (105) hide show
  1. package/CHANGELOG.md +35 -2
  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 +17 -3
  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 +0 -8
  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/completion/completionSetup.js +1 -1
  55. package/lib/init/template/data/as-csv.js +1 -1
  56. package/lib/init/template/github-actions/index.js +1 -1
  57. package/lib/init/template/handler/index.js +1 -1
  58. package/lib/init/template/helm/index.js +3 -3
  59. package/lib/init/template/kyma/index.js +3 -3
  60. package/lib/init/template/lint/index.js +7 -21
  61. package/lib/init/template/multitenancy/files/package.json.hbs +0 -5
  62. package/lib/init/template/postgres/files/containerize.yaml.hbs +15 -0
  63. package/lib/init/template/postgres/files/package.json.hbs +35 -0
  64. package/lib/init/template/postgres/files/values.yaml.hbs +23 -0
  65. package/lib/init/template/postgres/index.js +34 -9
  66. package/lib/init/template/test/files/package.json +1 -1
  67. package/lib/init/template/typescript/files/package.json +1 -1
  68. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +1 -0
  69. package/lib/lint/eslintHelp.json +1 -1
  70. package/lib/lint/io.js +9 -13
  71. package/lib/mtx/pull.js +1 -1
  72. package/lib/mtx/settings_manager.js +11 -5
  73. package/lib/mtx/tenant_action.js +1 -1
  74. package/lib/mtx/util/fs.js +2 -2
  75. package/lib/util/fs.js +3 -3
  76. package/lib/util/git.js +1 -1
  77. package/lib/util/glob.js +1 -1
  78. package/lib/watch/hot-reload.js +202 -0
  79. package/lib/watch/livereload-server.js +6 -4
  80. package/lib/watch/node-watch.js +5 -1
  81. package/lib/watch/watched.js +7 -3
  82. package/lib/watch/watcher.js +41 -3
  83. package/npm-shrinkwrap.json +31 -22
  84. package/package.json +1 -1
  85. package/lib/build/buildTaskEngine.js +0 -377
  86. package/lib/build/buildTaskFactory.js +0 -198
  87. package/lib/build/buildTaskProvider.js +0 -38
  88. package/lib/build/buildTaskProviderFactory.js +0 -233
  89. package/lib/build/provider/internalBuildTaskProvider.js +0 -227
  90. package/lib/build/provider/java-cf/index.js +0 -1
  91. package/lib/build/provider/mtx/index.js +0 -36
  92. package/lib/build/provider/mtx/resourcesTarBuilder.js +0 -102
  93. package/lib/build/provider/node-cf/index.js +0 -1
  94. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +0 -40
  95. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/fixed/schema.cds +0 -4
  96. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/invalid/schema.cds +0 -4
  97. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +0 -15
  98. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/valid/schema.cds +0 -6
  99. package/lib/init/types.d.ts +0 -23
  100. /package/lib/build/{provider → plugins}/hana/2migration.js +0 -0
  101. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-all +0 -0
  102. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-cloud +0 -0
  103. /package/lib/build/{provider → plugins}/hana/template/.hdinamespace +0 -0
  104. /package/lib/build/{provider → plugins}/hana/template/package.json +0 -0
  105. /package/lib/build/{provider → plugins}/hana/template/undeploy.json +0 -0
@@ -1,15 +1,22 @@
1
1
  const fs = require('fs')
2
- const path = require('path')
3
2
  const cds = require('../../../cds')
4
- const EdmxBuildPlugin = require('../edmxBuildPlugin')
5
- const { OUTPUT_MODE, OUTPUT_MODE_RESULT, FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME,
6
- OUTPUT_MODE_FILESYSTEM, FLAVOR_LOCALIZED_EDMX, CONTENT_EDMX, CONTENT_LOCALIZED_EDMX } = require('../../constants')
7
- const { getI18nDefaultFolder } = require('../../util')
3
+ const { exists, isdir, path } = cds.utils
4
+ const { FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME, FLAVOR_LOCALIZED_EDMX, CONTENT_EDMX, CONTENT_LOCALIZED_EDMX } = require('../../constants')
5
+ const { getI18nDefaultFolder, normalizePath } = require('../../util')
8
6
  const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
9
7
  const DEBUG = cds.debug('cli|build')
10
8
  const LOG = cds.log('cli|build')
11
9
 
12
- class JavaBuildPlugin extends EdmxBuildPlugin {
10
+ module.exports = class JavaBuildPlugin extends require('../edmx') {
11
+
12
+ static get taskDefaults() { return { src: normalizePath(cds.env.folders.srv) } }
13
+
14
+ static hasTask() {
15
+ const src = path.join(cds.root, this.taskDefaults?.src ?? 'srv')
16
+ const hasSrc = exists(src) || this.taskDefaults?.src === '.'
17
+ return cds.env['project-nature'] === 'java' && hasSrc && !cds.env.extends
18
+ }
19
+
13
20
  init() {
14
21
  super.init()
15
22
  this.task.options.compileDest = path.resolve(this.task.dest, this.task.options.compileDest || DEFAULT_COMPILE_DEST_FOLDER)
@@ -45,15 +52,11 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
45
52
 
46
53
  if (!this.hasBuildOption(CONTENT_LANGUAGE_BUNDLES, false)) {
47
54
  // collect and write language bundles containing all features
48
- const i18n = await this.collectLanguageBundles(model, path.join(compileDest, getI18nDefaultFolder()))
49
- if (i18n && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
50
- this._result.languageBundles = i18n.bundles
51
- }
55
+ await this.collectLanguageBundles(model, path.join(compileDest, getI18nDefaultFolder()))
52
56
  }
53
- if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
57
+ if (this.isStagingBuild()) {
54
58
  await this._copyNativeContent(src, dest)
55
59
  }
56
- return this._result
57
60
  }
58
61
 
59
62
  async clean() {
@@ -66,7 +69,7 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
66
69
 
67
70
  async _copyNativeContent(src, dest) {
68
71
  return super.copyNativeContent(src, dest, (entry) => {
69
- if (cds.utils.isdir(entry)) {
72
+ if (isdir(entry)) {
70
73
  return true // using common filter for folders
71
74
  } else {
72
75
  const extname = path.extname(entry)
@@ -84,21 +87,10 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
84
87
  const csnFile = path.join(csnDest, DEFAULT_CSN_FILE_NAME)
85
88
  // adding csn to build result containing @source and _where persisted properties
86
89
  if (this.hasBuildOption(CONTENT_DEFAULT_CSN, true)) { //default true or undefined
87
- const csnStr = await this.compileToJson(model, csnFile)
88
- if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
89
- const csnModel = JSON.parse(csnStr)
90
- csnModel.meta = model.meta
91
- this._result.csn = csnModel
92
- }
90
+ await this.compileToJson(model, csnFile)
93
91
  } else {
94
- const csnStr = await this.compileToJson(m, csnFile)
95
- if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
96
- const csnModel = JSON.parse(csnStr)
97
- csnModel.meta = m.meta
98
- this._result.csn = csnModel
99
- }
92
+ await this.compileToJson(m, csnFile)
100
93
  }
101
94
  return m
102
95
  }
103
96
  }
104
- module.exports = JavaBuildPlugin
@@ -0,0 +1,41 @@
1
+ const cds = require('../../../cds')
2
+ const { path, exists, rimraf } = cds.utils
3
+ const ResourcesTarBuilder = require('./resourcesTarBuilder')
4
+ const { FOLDER_GEN } = require('../../constants')
5
+ const { normalizePath } = require('../../util')
6
+
7
+ module.exports = class MtxBuildPlugin extends require('../edmx') {
8
+
9
+ static get taskDefaults() { return { src: normalizePath(cds.env.folders.srv) } }
10
+
11
+ static hasTask() {
12
+ const isMtxs = cds.env.requires.multitenancy || cds.env.requires.toggles || cds.env.requires['cds.xt.ModelProviderService']
13
+ const hasSidecar = exists(path.join(cds.root, 'mtx/sidecar'))
14
+ return isMtxs && !hasSidecar && cds.env['project-nature'] === 'nodejs' && !cds.env.extends
15
+ }
16
+
17
+ get requires() {
18
+ return ['hana']
19
+ }
20
+
21
+ init() {
22
+ super.init()
23
+ }
24
+
25
+ async build() {
26
+ // Only create resources TAR, compiled CSN(s) are created by the Nodejs app build task.
27
+ const dest = cds.env.build.target !== '.' ? this.task.dest : path.join(this.task.dest, FOLDER_GEN)
28
+ const model = await this.model()
29
+ if (!model) {
30
+ return
31
+ }
32
+ return new ResourcesTarBuilder(this).createTar(dest, model)
33
+ }
34
+
35
+ async clean() {
36
+ // staging build content is deleted by default
37
+ if (cds.env.build.target === '.') {
38
+ return rimraf(this.task.dest, FOLDER_GEN)
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ const cds = require('../../../cds')
2
+ const { path, exists, tar } = cds.utils
3
+ const { BUILD_TASK_HANA } = require('../../constants')
4
+ const { WARNING } = require('../internal')
5
+
6
+ module.exports = class ResourcesTarBuilder {
7
+ constructor(plugin) {
8
+ this._plugin = plugin
9
+ }
10
+
11
+ get plugin() { return this._plugin }
12
+
13
+ async createTar(dest, model) {
14
+ let root = getHanaTenantDbTask(this.plugin)?.dest
15
+ let resources
16
+ if (root) {
17
+ const items = ['src', 'cfg', 'undeploy.json', '.hdiignore']
18
+ resources = items.map(item => path.join(root, item)).filter(exists)
19
+ } else {
20
+ root = cds.root
21
+ resources = Object.keys(await cds.deploy.resources(model))
22
+ }
23
+ if (resources.length === 0) {
24
+ this.plugin.pushMessage("No deployment resources found - skip resources.tgz", WARNING)
25
+ return
26
+ }
27
+ const tarFile = path.join(dest, "resources.tgz")
28
+ await tar.czfd(tarFile, root, resources)
29
+ this.plugin.pushFile(tarFile)
30
+ }
31
+ }
32
+
33
+ function getHanaTenantDbTask({ task, context: { tasks } }) {
34
+ const hanaTasks = tasks.filter(task => task.for === BUILD_TASK_HANA)
35
+ if (hanaTasks.length === 1) return hanaTasks[0]
36
+ return hanaTasks.find(({ src }) => {
37
+ if (!src) return true // an existing second shared db always requires src to be set
38
+ const srcFolder = path.isAbsolute(src) ? path.relative(cds.root, src) : src
39
+ return task.options?.model?.includes(srcFolder) || srcFolder === cds.env.folders.db.replace('/', '')
40
+ })
41
+ }
@@ -31,7 +31,7 @@ class ExtensionCompilation {
31
31
  const pkg = require(path.join(dirname, 'package.json'))
32
32
  return pkg.name
33
33
  }
34
- } catch (e) {
34
+ } catch {
35
35
  return getPkgName(path.dirname(dirname))
36
36
  }
37
37
  }
@@ -1,8 +1,5 @@
1
- const path = require('path')
2
- const fs = require('fs')
3
1
  const cds = require('../../../cds')
4
- const InternalBuildPlugin = require('../internalBuildPlugin')
5
- const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
2
+ const { exists, path, isdir, rimraf, tar } = cds.utils
6
3
  const ExtensionCompilation = require('./extensionCompilation')
7
4
  const { FOLDER_GEN, EXTENSION_POINT_VALIDATION } = require('../../constants')
8
5
  const { BuildError, BuildMessage } = require('../../util')
@@ -10,7 +7,12 @@ const DEBUG = cds.debug('cli|build')
10
7
 
11
8
  const BUILT_IN_NAMESPACES = ['cds.core', 'cds.outbox', 'cds.xt']
12
9
 
13
- class MtxExtensionBuildPlugin extends InternalBuildPlugin {
10
+ module.exports = class MtxExtensionBuildPlugin extends require('../internal') {
11
+
12
+ static get taskDefaults() { return { src: '.' } }
13
+
14
+ static hasTask() { return cds.env.extends }
15
+
14
16
  init() {
15
17
  super.init()
16
18
  if (cds.env.build.target === '.') {
@@ -27,7 +29,11 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
27
29
  // REVISIT: check existence of SaaS app package folder only
28
30
  // cds.resolve will fail as no index.csn file exists in this folder for the extension migration use case
29
31
  // a compilation error is thrown anyhow if any base model refs cannot be resolved
30
- const saasPkgPath = MtxExtensionBuildPlugin._getSaasPkgPath()
32
+
33
+ if (!cds.env.extends) throw new BuildError(`Missing configuration 'cds.extends' in package.json`)
34
+ // cds.env.extends holds the SaaS app package name, also if the extends field is defined on root level
35
+ // older projects might not have a workspace definition
36
+ const saasPkgPath = _getWorkspacePath(cds.env.extends) ?? _getNodeModulesPath()
31
37
  const extensionCompilation = new ExtensionCompilation(this)
32
38
 
33
39
  // full model compilation including base model ensures consistency
@@ -92,8 +98,7 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
92
98
  // throws error in case of linting errors
93
99
  this._lintExtModel(extensionModel, model, saasPkgPath)
94
100
  } catch (error) {
95
- // cleanup existing files in case of an error
96
- await fs.promises.rm(dest, { recursive: true })
101
+ await rimraf(dest)
97
102
  throw error
98
103
  }
99
104
  }
@@ -105,7 +110,7 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
105
110
  // copy handlers from srv folder
106
111
  const folder = path.join(cds.root, cds.env.folders.srv).replace(/[/\\]$/, '')
107
112
  await this.copyNativeContent(cds.root, destExt, res => {
108
- if (cds.utils.isdir(res)) {
113
+ if (isdir(res)) {
109
114
  return res.startsWith(folder)
110
115
  }
111
116
  if (path.dirname(res).startsWith(folder) && /\.js$/.test(res)) {
@@ -114,7 +119,9 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
114
119
  })
115
120
 
116
121
  // add all resources contained in 'gen/ext' folder
117
- return new ResourcesTarBuilder(this).writeTarFile(path.join(dest, 'extension.tgz'), destExt)
122
+ const tarFile = path.join(dest, 'extension.tgz')
123
+ await tar.czfd(tarFile, destExt)
124
+ this.pushFile(tarFile)
118
125
  }
119
126
 
120
127
  _lintExtModel(extModel, model, saasPkgPath) {
@@ -135,48 +142,32 @@ class MtxExtensionBuildPlugin extends InternalBuildPlugin {
135
142
 
136
143
  _linter() {
137
144
  try {
138
- // Make sure cds-mtxs APIs are loaded, from local mtxs preferrably
139
- const mtxs = require(require.resolve('@sap/cds-mtxs', { paths: [cds.root, __dirname] }))
140
- return mtxs.xt.linter
145
+ // Make sure cds-mtxs APIs are loaded, from local mtxs preferably
146
+ return require(require.resolve('@sap/cds-mtxs', { paths: [cds.root, __dirname] })).xt.linter
141
147
  } catch (e) {
142
148
  if (e.code !== 'MODULE_NOT_FOUND') throw e
143
149
  this.pushMessage('MTXS linter cannot be loaded, @sap/cds-mtxs not installed. Skipping extension model lint step.')
144
150
  }
145
151
  }
152
+ }
146
153
 
147
- static _getSaasPkgPath() {
148
- if (!cds.env.extends) {
149
- throw new BuildError(`Missing configuration 'cds.extends' in package.json`)
150
- }
151
- // cds.env.extends holds the SaaS app package name, also if the extends field is defined on root level
152
- let saasPkgPath = this._getWorkspacePath(cds.env.extends)
153
- if (!saasPkgPath) {
154
- // older projects might not have a workspace definition
155
- saasPkgPath = this._getNodeModulesPath(saasPkgPath)
156
- }
157
- return saasPkgPath
158
- }
159
-
160
- static _getWorkspacePath(pkg) {
161
- const pkgJson = require(path.join(cds.root, 'package.json'))
162
- if (pkgJson.workspaces) {
163
- try {
164
- // loading package.json of SaaS app - might not exist for older projects
165
- return path.dirname(require.resolve(path.join(pkg, 'package.json'), { paths: [cds.root] }))
166
- } catch (e) {
167
- if (e.code !== 'MODULE_NOT_FOUND') throw e
168
- throw new BuildError(`The SaaS application base model is missing. Did you execute 'cds pull' and 'npm install'?`)
169
- }
170
- }
171
- }
172
-
173
- static _getNodeModulesPath() {
174
- const saasPkgPath = path.join(cds.root, 'node_modules', cds.env.extends)
175
- if (!fs.existsSync(saasPkgPath)) {
154
+ function _getWorkspacePath(pkg) {
155
+ const pkgJson = require(path.join(cds.root, 'package.json'))
156
+ if (pkgJson.workspaces) {
157
+ try {
158
+ // loading package.json of SaaS app - might not exist for older projects
159
+ return path.dirname(require.resolve(path.join(pkg, 'package.json'), { paths: [cds.root] }))
160
+ } catch (e) {
161
+ if (e.code !== 'MODULE_NOT_FOUND') throw e
176
162
  throw new BuildError(`The SaaS application base model is missing. Did you execute 'cds pull' and 'npm install'?`)
177
163
  }
178
- return saasPkgPath
179
164
  }
180
165
  }
181
166
 
182
- module.exports = MtxExtensionBuildPlugin
167
+ function _getNodeModulesPath() {
168
+ const saasPkgPath = path.join(cds.root, 'node_modules', cds.env.extends)
169
+ if (!exists(saasPkgPath)) {
170
+ throw new BuildError(`The SaaS application base model is missing. Did you execute 'cds pull' and 'npm install'?`)
171
+ }
172
+ return saasPkgPath
173
+ }
@@ -10,11 +10,25 @@ const DEFAULT_MAIN_FOLDER = "_main"
10
10
  const [major, minor] = cds.version.split('.').map(Number)
11
11
  const COMPAT = major === 9 && minor < 7 // cds10: remove
12
12
 
13
- module.exports = class extends require('../nodejs') {
13
+ module.exports = class MtxSidecarBuildPlugin extends require('../nodejs') {
14
14
 
15
- get priority() {
16
- return super.priority - 10 // must run after 'hana' build task
15
+ static get taskDefaults() { return { src: 'mtx/sidecar' } }
16
+
17
+ static hasTask() {
18
+ if (cds.env.extends) return
19
+ const mps = cds.env.requires['cds.xt.ModelProviderService']
20
+ const isMtxs = cds.env.requires.multitenancy || cds.env.requires.toggles || cds.env.requires['cds.xt.ModelProviderService']
21
+ const src = path.join(cds.root, this.taskDefaults?.src ?? 'mtx/sidecar')
22
+ if (mps?._in_sidecar) {
23
+ throw new BuildError(`Seems that you are executing 'cds build' in the 'mtx/sidecar' folder. Execute 'cds build' in the project root folder instead.`)
24
+ }
25
+ return isMtxs && exists(src)
17
26
  }
27
+
28
+ get requires() {
29
+ return ['hana']
30
+ }
31
+
18
32
  init() {
19
33
  super.init()
20
34
  if (cds.env.build.target === '.') {
@@ -1,26 +1,26 @@
1
- const fs = require('fs')
2
- const path = require('path')
3
1
  const cds = require('../../../cds')
4
- const cmd = require('../../../util/command');
5
- const EdmxBuildPlugin = require('../edmxBuildPlugin')
6
- const { BuildError, getWorkspaces, findWorkspaceRoot } = require('../../util')
7
- const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
2
+ const { path, exists, rimraf, isdir, read, write } = cds.utils
3
+ const cmd = require('../../../util/command')
4
+ const { BuildError, getWorkspaces, findWorkspaceRoot, normalizePath } = require('../../util')
5
+ const { ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
8
6
  CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX,
9
- OPTION_WS_PACK} = require('../../constants');
10
- const { WARNING } = EdmxBuildPlugin
7
+ OPTION_WS_PACK, SEVERITY_WARNING } = require('../../constants');
11
8
  const LOG = cds.log('cli|build')
12
9
  const DEBUG = cds.debug('cli|build')
13
10
  const NODE_ENGINE_VERSION = '>=20'
14
11
  const { exec } = require('node:child_process')
15
- const pexec = require('node:util').promisify(exec);
12
+ const pexec = require('node:util').promisify(exec)
16
13
 
17
- /**
18
- * @param {T | T[]} x
19
- * @returns {T[]}
20
- */
21
- const arrayify = x => Array.isArray(x) ? x : [x]
14
+ module.exports = class NodejsBuildPlugin extends require('../edmx') {
15
+
16
+ static get taskDefaults() { return { src: normalizePath(cds.env.folders.srv) } }
17
+
18
+ static hasTask() {
19
+ const src = path.join(cds.root, this.taskDefaults?.src || 'srv')
20
+ const hasSrc = exists(src) || (this.taskDefaults?.src === '.')
21
+ return cds.env['project-nature'] === 'nodejs' && hasSrc && !cds.env.extends
22
+ }
22
23
 
23
- class NodejsBuildPlugin extends EdmxBuildPlugin {
24
24
  init() {
25
25
  super.init()
26
26
  // fallback if src has been defined as '.'
@@ -36,11 +36,15 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
36
36
  }
37
37
 
38
38
  async build() {
39
+ if (!exists('package.json')) {
40
+ throw new BuildError(`No 'package.json' found in project root folder '${cds.root}'`)
41
+ }
42
+
39
43
  const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, cds.env.folders.srv)
40
44
  const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
41
45
  if (cds.env.odata.version === ODATA_VERSION_V2) {
42
46
  // log warning as nodejs is only supporting odata version V4
43
- this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", WARNING)
47
+ this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", SEVERITY_WARNING)
44
48
  }
45
49
  // by default model contains all features
46
50
  const model = await this.model()
@@ -61,7 +65,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
61
65
  await this.compileToEdmx(baseModel, path.join(this.destSrv, 'odata', cds.env.odata.version), compileOptions)
62
66
  }
63
67
  }
64
- if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
68
+ if (this.isStagingBuild()) {
65
69
  const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
66
70
  await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
67
71
  try {
@@ -87,8 +91,6 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
87
91
  }
88
92
 
89
93
  await this.#cleanupPackageJson(destRoot)
90
-
91
- return this._result
92
94
  }
93
95
 
94
96
  /**
@@ -101,7 +103,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
101
103
 
102
104
  const packageJsonPath = path.join(this.task.dest, 'package.json')
103
105
  try {
104
- const packageJson = await cds.utils.read(packageJsonPath)
106
+ const packageJson = await read(packageJsonPath)
105
107
 
106
108
  DEBUG?.(`Sanitizing the package.json for "${this.task.src}'...`)
107
109
 
@@ -124,9 +126,9 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
124
126
  }
125
127
  // overwrite the package.json if it was modified only
126
128
  if (modified) {
127
- await cds.utils.write(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8')
129
+ await write(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8')
128
130
  // update the package-lock.json if it exists
129
- if (cds.utils.exists(path.join(this.task.dest, 'package-lock.json'))) {
131
+ if (exists(path.join(this.task.dest, 'package-lock.json'))) {
130
132
  DEBUG?.(`Updating the package-lock.json for '${this.task.src}'...`)
131
133
  // run the npm install --package-lock-only to only update the package-lock.json
132
134
  // without installing the dependencies to node_modules
@@ -145,12 +147,12 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
145
147
  }
146
148
 
147
149
  async clean() {
148
- // staging build content is deleted by BuildTaskEngine
150
+ // staging build content is deleted by default
149
151
  if (cds.env.build.target === '.') {
150
152
  // delete the entire 'task.dest' folder otherwise, for details see #constructor
151
153
  // - the value of the folder 'src' has been appended to the origin 'task.dest' dir
152
154
  DEBUG?.(`Deleting build target folder ${this.destSrv}`)
153
- await fs.promises.rm(this.isStagingBuild() ? this.task.dest : this.destSrv, { force: true, recursive: true })
155
+ await rimraf(this.isStagingBuild() ? this.task.dest : this.destSrv)
154
156
  }
155
157
  }
156
158
 
@@ -180,7 +182,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
180
182
  // 1. copy all files to 'destSrv' except those contained in the blocklist (including node_modules)
181
183
  // project/srv -> 'gen/srv/srv'
182
184
  await super.copyNativeContent(src, destSrv, (entry) => {
183
- if (cds.utils.isdir(entry)) {
185
+ if (isdir(entry)) {
184
186
  // TODO shall not copy language bundles - return !/(\/|\\)(node_modules|_i18n)(\/|\\)?$/.test(entry)
185
187
  return !/(\/|\\)node_modules(\/|\\)?$/.test(entry)
186
188
  }
@@ -230,7 +232,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
230
232
  srvAllowList = new RegExp(srvAllowList)
231
233
 
232
234
  await super.copyNativeContent(src, dest, (entry) => {
233
- if (cds.utils.isdir(entry)) {
235
+ if (isdir(entry)) {
234
236
  return folders.some(folder => entry.startsWith(folder))
235
237
  }
236
238
  if (/\.js$|\.properties$/.test(entry)) {
@@ -246,6 +248,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
246
248
  const allWorkspaces = await getWorkspaces(wsRoot, true, false)
247
249
  let workspaces
248
250
  if (this.task.options.workspaces) {
251
+ const arrayify = x => Array.isArray(x) ? x : [x]
249
252
  const relevantWorkspaces = new Set(arrayify(this.task.options.workspaces))
250
253
  workspaces = allWorkspaces
251
254
  .filter(ws => relevantWorkspaces.has(ws.workspace))
@@ -302,5 +305,3 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
302
305
  return JSON.parse(result)
303
306
  }
304
307
  }
305
-
306
- module.exports = NodejsBuildPlugin