@sap/cds-dk 7.3.0 → 7.3.2

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 (88) hide show
  1. package/bin/add.js +3 -1
  2. package/bin/build.js +2 -1
  3. package/bin/cds.js +1 -1
  4. package/bin/compile/index.js +11 -0
  5. package/bin/extend.js +5 -1
  6. package/bin/init.js +31 -37
  7. package/bin/repl.js +23 -43
  8. package/bin/watch.js +5 -9
  9. package/lib/bind/bindingResolver.js +2 -1
  10. package/lib/bind/index.js +1 -1
  11. package/lib/build/buildPlugin.js +18 -3
  12. package/lib/build/buildTaskEngine.js +2 -1
  13. package/lib/build/buildTaskFactory.js +33 -9
  14. package/lib/build/buildTaskProvider.js +1 -1
  15. package/lib/build/buildTaskProviderFactory.js +15 -61
  16. package/lib/build/constants.js +2 -0
  17. package/lib/build/index.js +5 -2
  18. package/lib/build/plugins.js +35 -0
  19. package/lib/build/provider/edmxBuildPlugin.js +9 -1
  20. package/lib/build/provider/featureToggleBuildPlugin.js +2 -3
  21. package/lib/build/provider/internalBuildPlugin.js +4 -0
  22. package/lib/build/provider/internalBuildTaskProvider.js +1 -1
  23. package/lib/build/provider/java/index.js +12 -6
  24. package/lib/build/provider/nodejs/index.js +3 -2
  25. package/lib/build/util.js +5 -0
  26. package/lib/client/base_command.js +8 -18
  27. package/lib/client/download_legacy.js +2 -10
  28. package/lib/client/params.js +6 -4
  29. package/lib/client/settings_manager.js +38 -35
  30. package/lib/compile/openapi/csdl2openapi.js +1 -1
  31. package/lib/deploy/to-hana/hana.js +53 -35
  32. package/lib/index.js +7 -1
  33. package/lib/init/constants.js +4 -2
  34. package/lib/init/template/_merging/registry-mta.js +12 -0
  35. package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +1 -1
  36. package/lib/init/template/enterprise-messaging/index.js +1 -1
  37. package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +1 -1
  38. package/lib/init/template/enterprise-messaging-shared/index.js +19 -5
  39. package/lib/init/template/helm/index.js +1 -1
  40. package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +4 -4
  41. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +4 -2
  42. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +4 -3
  43. package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +9 -83
  44. package/lib/init/template/helm/subcharts/content-deployment/values.yaml +54 -15
  45. package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +4 -4
  46. package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +1 -1
  47. package/lib/init/template/helm/subcharts/service-instance/values.schema.json +0 -17
  48. package/lib/init/template/helm/subcharts/service-instance/values.yaml +28 -0
  49. package/lib/init/template/helm/subcharts/web-application/Chart.yaml +5 -5
  50. package/lib/init/template/helm/subcharts/web-application/templates/_api.tpl +60 -0
  51. package/lib/init/template/helm/subcharts/web-application/templates/_helpers.tpl +34 -73
  52. package/lib/init/template/helm/subcharts/web-application/templates/api-rule.yaml +4 -4
  53. package/lib/init/template/helm/subcharts/web-application/templates/deployment.yaml +96 -17
  54. package/lib/init/template/helm/subcharts/web-application/templates/network-policy.yaml +19 -3
  55. package/lib/init/template/helm/subcharts/web-application/templates/pod-disruption-budget.yaml +3 -3
  56. package/lib/init/template/helm/subcharts/web-application/templates/rbac.yaml +26 -0
  57. package/lib/init/template/helm/subcharts/web-application/templates/secret.yaml +1 -1
  58. package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +1 -1
  59. package/lib/init/template/helm/subcharts/web-application/templates/service-binding.yaml +1 -1
  60. package/lib/init/template/helm/subcharts/web-application/templates/service.yaml +21 -3
  61. package/lib/init/template/helm/subcharts/web-application/templates/virtual-service.yaml +36 -0
  62. package/lib/init/template/helm/subcharts/web-application/values.schema.json +535 -142
  63. package/lib/init/template/helm/subcharts/web-application/values.yaml +168 -44
  64. package/lib/init/template/local-messaging/files/package.json.hbs +15 -0
  65. package/lib/init/template/local-messaging/index.js +25 -0
  66. package/lib/init/template/mta/index.js +2 -2
  67. package/lib/init/template/nodejs/files/.gitignore.hbs +3 -0
  68. package/lib/init/template/nodejs/files/.vscode/launch.json +0 -3
  69. package/lib/init/template/notifications/files/mta.yaml.hbs +17 -0
  70. package/lib/init/template/notifications/files/package.json.hbs +5 -0
  71. package/lib/init/template/notifications/index.js +43 -0
  72. package/lib/init/template/typer/files/package.json +1 -1
  73. package/lib/init/util/projectReader.js +1 -0
  74. package/lib/util/pruneAxiosErrors.js +4 -1
  75. package/lib/util/term.js +1 -2
  76. package/lib/watch/livereload-server.js +38 -2
  77. package/lib/watch/node-watch.js +3 -2
  78. package/npm-shrinkwrap.json +438 -2205
  79. package/package.json +2 -2
  80. package/lib/init/template/helm/subcharts/content-deployment/templating.md +0 -37
  81. package/lib/init/template/helm/subcharts/content-deployment/values-lint.yaml +0 -5
  82. package/lib/init/template/helm/subcharts/content-deployment/values.md +0 -1011
  83. package/lib/init/template/helm/subcharts/service-instance/templating.md +0 -18
  84. package/lib/init/template/helm/subcharts/service-instance/values-lint.yaml +0 -2
  85. package/lib/init/template/helm/subcharts/service-instance/values.md +0 -186
  86. package/lib/init/template/helm/subcharts/web-application/templating.md +0 -51
  87. package/lib/init/template/helm/subcharts/web-application/values-lint.yaml +0 -8
  88. package/lib/init/template/helm/subcharts/web-application/values.md +0 -1363
package/bin/add.js CHANGED
@@ -27,13 +27,15 @@ module.exports = Object.assign(add, {
27
27
 
28
28
  *approuter* - add support for application routing
29
29
 
30
+ *local-messaging* - add support for local messaging
31
+
30
32
  *file-based-messaging* - add support for file-based messaging
31
33
 
32
34
  *enterprise-messaging* - add support for SAP Event Mesh
33
35
 
34
36
  *redis-messaging* - add support for Redis messaging
35
37
 
36
- *kibana-logging* - add support for Kibana formatting
38
+ *kibana* - add support for Kibana formatting
37
39
 
38
40
  *mta* - add support for MTA-based deployment
39
41
 
package/bin/build.js CHANGED
@@ -88,8 +88,9 @@ module.exports = Object.assign(build, {
88
88
 
89
89
  async function build([project], options = {}) {
90
90
  const path = require('path')
91
+
91
92
  const cds = require('../lib/cds')
92
-
93
+
93
94
  // plugins are loaded based on cds.root path
94
95
  cds.root = path.resolve(process.cwd(), project || options.project || '.')
95
96
 
package/bin/cds.js CHANGED
@@ -30,7 +30,7 @@ const cli = { //NOSONAR
30
30
  if (cmd in this.Shortcuts) cmd = process.argv[2] = this.Shortcuts[cmd]
31
31
  if (argv.some(a => this.Shortcuts[a]==='help')) return this.help (cmd)
32
32
 
33
- const cds = require('../lib/cds')
33
+ const cds = require('../lib') // make sure to load cds-dk API for plugins
34
34
  DEBUG?.(`[cds] - @sap/cds ${cds.version} loaded: ${cds.home}`)
35
35
 
36
36
  try {
@@ -53,6 +53,17 @@ module.exports = Object.assign ( compile, {
53
53
 
54
54
  - odata
55
55
  - sql
56
+
57
+ *--dialect* <dialect>
58
+
59
+ Needs option *--to*.
60
+ Specify the dialect in combination with *--to sql*.
61
+ Currently supported:
62
+
63
+ - sqlite
64
+ - h2
65
+ - postgres
66
+ - hana
56
67
 
57
68
  *-s* | *--service* <name> | all
58
69
 
package/bin/extend.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = Object.assign(extend, {
2
- options: ['--directory', '--subdomain', '--passcode', '--username', '--clientid', '--tagRule'],
2
+ options: ['--directory', '--subdomain', '--passcode', '--username', '--clientid', '--tagRule', '--defaultTag'],
3
3
  flags: ['--wsdl', '--templates', '--force', '--download-migrated-projects'],
4
4
  shortcuts: ['-d', '-s', '-p', '-u', '-c'],
5
5
  help: `
@@ -58,6 +58,10 @@ module.exports = Object.assign(extend, {
58
58
 
59
59
  Rule used to split up migrated extension projects into separate projects (please check migration documentation for more).
60
60
 
61
+ *--defaultTag*
62
+
63
+ Default tag used for migrated extension projects, default is 'migrated' (please check migration documentation for more).
64
+
61
65
  # SEE ALSO
62
66
 
63
67
  *cds activate* to activate an extension project.
package/bin/init.js CHANGED
@@ -22,56 +22,51 @@ module.exports = Object.assign(init, {
22
22
  Add one or more features while creating the project.
23
23
  <feature> can be one of the following:
24
24
 
25
- *nodejs* - prepares as a Node.js-based project, cannot be used with *java*
25
+ *nodejs* - creates as a Node.js-based project
26
26
 
27
- *java* - prepares as a Java-based project, cannot be used with *nodejs*
27
+ *java* - creates as a Java-based project
28
28
 
29
- This feature uses a Java Maven archetype to create a
30
- project using the *Java* programming language.
29
+ *hana* - add support for SAP HANA
31
30
 
32
- *hana* - adds configuration for SAP HANA databases
31
+ *sqlite* - add support for SQLite databases
33
32
 
34
- *sqlite* - adds configuration for SQLite databases
33
+ *xsuaa* - add support for authentication via XSUAA
35
34
 
36
- *xsuaa* - adds configuration for authentication via XSUAA
35
+ *multitenancy* - add support for multitenancy
37
36
 
38
- *multitenancy* - adds configuration for multitenancy
37
+ *toggles* - add support for feature toggles
39
38
 
40
- *toggles* - adds configuration for feature toggles
39
+ *extensibility* - add support for extensibility
41
40
 
42
- *extensibility* - adds configuration for extensibility
41
+ *approuter* - add support for application routing
43
42
 
44
- *mtx* - adds configuration for multitenancy, feature toggles and
45
- extensibility
43
+ *local-messaging* - add support for local messaging
46
44
 
47
- *approuter* - adds configuration for the Cloud Foundry approuter
45
+ *enterprise-messaging* - add support for SAP Event Mesh
48
46
 
49
- *mta* - adds an _mta.yaml_ file for MTA-based SAP Business
50
- Technology Platform deployment
47
+ *redis-messaging* - add support for Redis messaging
51
48
 
52
- *cf-manifest* - adds _manifest.yml_ and _services-manifest.yml_ files for
53
- Cloud Foundry native deployment
49
+ *kibana* - add support for Kibana formatting
54
50
 
55
- This approach makes use of the 'Create-Service-Push
56
- Plugin' that has to be installed separately using
57
- _cf install-plugin Create-Service-Push_.
58
- _cf create-service-push_ creates the service(s) and pushes
59
- the application(s) to the SAP Business Technology
60
- Platform.
51
+ *mta* - add support for MTA-based deployment
61
52
 
62
- *pipeline* - adds files for CI/CD pipeline integration
53
+ *cf-manifest* - add support for CF-native deployment
63
54
 
64
- *sample* - adds sample files including Fiori UI
55
+ *helm* - add support for Helm-based Kyma deployment
65
56
 
66
- *sample-tiny* - adds minimal sample files
57
+ *html5-repo* - add support for the HTML5 repository
67
58
 
68
- *data* - adds data content for CSN model entities (as csv files)
59
+ *pipeline* - add files for CI/CD pipeline integration
69
60
 
70
- *helm* - adds helm chart folder that contains the files needed to
71
- deploy the application using Kyma.
61
+ *tiny-sample* - add minimal sample files
72
62
 
73
- *html5-repo* - adds configuration for HTML5 Application Deployer to Helm
74
- Chart (Kyma Only).
63
+ *sample* - add sample files including Fiori UI
64
+
65
+ *data* - add CSV headers for modeled entities
66
+
67
+ *typer* - add type generation for CDS models
68
+
69
+ *lint* - add support for CDS Lint
75
70
 
76
71
  *--java:mvn* <Comma separated maven archetype specific parameters>
77
72
 
@@ -85,16 +80,15 @@ module.exports = Object.assign(init, {
85
80
 
86
81
  # EXAMPLES
87
82
 
88
- *cds init* test
89
- *cds init* test --add java
90
- *cds init* test --add java,hana
91
- *cds init* --add mta
92
- *cds init* --add cf-manifest
83
+ *cds init* bookshop
84
+ *cds init* bookshop --add java
85
+ *cds init* bookshop --add java,hana
86
+ *cds init* bookshop --add multitenancy,mta
93
87
  *cds init* --add java --java:mvn groupId=myGroup,artifactId=newId,package=my.company
94
88
 
95
89
  # SEE ALSO
96
90
 
97
- *cds add* as shortcut for _cds init --add_.
91
+ *cds add* - to augment your projects later on
98
92
 
99
93
  `})
100
94
 
package/bin/repl.js CHANGED
@@ -12,62 +12,42 @@ module.exports = Object.assign (repl, { options: [], help: `
12
12
  *cds eval* to evaluate and execute JavaScript.
13
13
  `})
14
14
 
15
- function repl (_, options={}) {
16
-
17
- // enable `await` statement for Node.js < 16
18
- if (!process.env['CDS_REPL_SPAWNED'] && process.version.split('.')[0].slice(1) < '16') {
19
- process.env['CDS_REPL_SPAWNED'] = true
20
- return require('child_process').spawn('node',['--experimental-repl-await',
21
- ...process.argv.slice(1) ], {stdio:'inherit'}
22
- )
23
- }
15
+ async function repl (_, options={}) {
24
16
 
25
17
  const cds = require('../lib/cds')
26
- const path = require('path')
27
- const {inspect} = require('util')
18
+ console.log('\x1b[32m\x1b[1m'+'Welcome to cds repl v', cds.version, '\x1b[0m')
19
+
20
+ await cds.plugins
21
+
22
+ const { inspect } = require('util')
23
+ inspect.defaultOptions.colors = true
24
+ inspect.defaultOptions.depth = 11
28
25
 
29
- const colors = options.colors !== false
30
- const log = options.log || console.log
31
- const info = colors ? require('../lib/util/term').info : s => s
32
- log(info('Welcome to cds repl v'+ cds.version))
33
26
 
34
- const cdsr = require('repl').start ({...options, writer:_writer, ignoreUndefined:true })
35
- Object.defineProperty (cds,'repl',{value:cdsr})
27
+ const repl = require('repl').start ({
28
+ ...options, ignoreUndefined: true,
29
+ writer: o => {
30
+ if (!o || typeof o !== 'object') return o
31
+ return inspect(o).replace(/\[Object: null prototype\] /g, '')
32
+ }
33
+ })
36
34
 
37
- cds.lazify (Object.assign (cdsr.context,{ cds,
35
+ Object.defineProperty (cds,'repl',{value:repl})
36
+ cds.lazify (Object.assign (repl.context,{ cds,
38
37
  Foo: lazy => new cds.entity ({name:'Foo'}),
39
38
  Bar: lazy => new cds.entity ({name:'Bar'}),
40
39
  }))
41
40
 
42
- // cosmetics: don't show cds.env when inspecting cds
43
- if (Reflect.getOwnPropertyDescriptor(cds,'env')) {
44
- Reflect.defineProperty (cds,'env',{ value:cds.env, enumerable:false })
45
- }
46
-
47
- inspect.defaultOptions.colors = true
48
- inspect.defaultOptions.depth = 11
49
- function _writer (o) {
50
- if (!o || typeof o !== 'object') return o
51
- return inspect(o).replace(/\[Object: null prototype\] /g, '')
52
- }
53
-
41
+ repl.history = new Array(222)
54
42
  const home = process.env.HOME || process.env.USERPROFILE
55
- const history = path.join(home,'.cds-repl-history')
56
- if (cdsr.setupHistory) { // since Node.js 11
57
- cdsr.setupHistory (history,()=>{})
58
- } else {
59
- const fs = require('fs')
60
- const historySize = 111
61
- fs.readFile(history, 'utf-8', (e, txt) => e || (cdsr.history = txt.split('\n')))
62
- cdsr.on('exit', () => {
63
- if (cdsr.history) fs.writeFile(history, cdsr.history.slice(-historySize).join('\n'), () => {})
64
- })
65
- }
66
-
43
+ const history = require('path').join(home,'.cds-repl-history')
44
+ const fs = require('fs')
45
+ fs.readFile(history, 'utf-8', (e, txt) => e || Object.assign(repl.history, txt.split('\n')))
46
+ repl.on('exit', () => fs.writeFile(history, repl.history.join('\n'), () => {}))
67
47
 
68
48
  process.on('uncaughtException', console.error)
69
49
  process.on('unhandledRejection', console.error)
70
50
 
71
51
  }
72
- /* eslint no-console:0 */
52
+
73
53
  if (!module.parent) repl()
package/bin/watch.js CHANGED
@@ -59,8 +59,7 @@ const t = module.exports.codes = {
59
59
  orange: colors ? '\x1b[38;2;255;140;0m' : '' // darker orange, works with bright and dark background
60
60
  }
61
61
  const extDefaults = 'cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml,env'
62
- const fioriIncludes = /_?(change|variant)$/ // according to fiori: change,variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change.
63
- const ignore = RegExp(`(node_modules\\${sep}|app(\\${sep}.+)?\\${sep}((webapp|dist|target)\\${sep}|\\.cds-services\\.json$|tsconfig\\.json$|.*\\.tsbuildinfo$))`)
62
+ const ignore = RegExp(`(node_modules\\${sep}|@cds-models\\${sep}|@types\\${sep}|app(\\${sep}.+)?\\${sep}((webapp|dist|target)\\${sep}|\\.cds-services\\.json$|tsconfig\\.json$|.*\\.tsbuildinfo$))`)
64
63
 
65
64
  async function watch ([cwd], {
66
65
  args = ['serve', 'all'],
@@ -88,16 +87,14 @@ async function watch ([cwd], {
88
87
  env.NODE_PATH = [...paths(cwd||process.cwd()), ...paths(__dirname)].join(delimiter)
89
88
 
90
89
  if (typeof ext !== 'string') ext = extDefaults
90
+ const delay = parseInt(process.env.CDS_WATCH_DELAY) || 200
91
91
 
92
- let includes
93
92
  let liveReload //= ...
94
93
  let firstTime = true
95
94
  if (!/false|no/.test(options['livereload']) && cds.env.for('cds', cwd||process.cwd()).livereload !== false) {
96
- ext += ',css,gif,html,jpg,png,svg'
97
- includes = fioriIncludes
98
95
  liveReload = new (require ('../lib/watch/livereload-server'))
99
96
  env.CDS_LIVERELOAD_PATH = require.resolve ('../lib/watch/livereload-connect')
100
- env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'])
97
+ env.CDS_LIVERELOAD_URL = await liveReload.start (options['livereload'], cwd, delay)
101
98
  .catch(err => { console.log(`Error starting live reload: ${err.message}`); liveReload = null })
102
99
  }
103
100
 
@@ -113,7 +110,6 @@ async function watch ([cwd], {
113
110
  log ()
114
111
  log (`${t.bold}cds ${args.join(' ')}`)
115
112
  DEBUG && log (process.pid, 'watching:', ext, '...')
116
- includes && DEBUG && log (`also watching: ${includes.source}`)
117
113
  DEBUG && log ('ignoring:', ignore.source)
118
114
  if (liveReload) log (`live reload enabled for browsers`)
119
115
 
@@ -124,8 +120,8 @@ async function watch ([cwd], {
124
120
 
125
121
  const nodemon = require('../lib/watch/node-watch')
126
122
  const watcher = nodemon ({
127
- script: resolve(__dirname,'watched'), cwd, env, ext, includes, ignore,
128
- delay: parseInt(process.env.CDS_WATCH_DELAY) || 200,
123
+ script: resolve(__dirname,'watched'), cwd, env, ext, ignore,
124
+ delay,
129
125
  options
130
126
  }).on('restart', (files)=>{
131
127
  log (`\n${t.bold} ___________________________\n`)
@@ -1,10 +1,11 @@
1
1
  const cds = require('../cds');
2
+ const path = require('path');
2
3
  const { platformText, bindingText, logger } = require('./bindingUtil');
3
4
 
4
5
  class BindingResolver {
5
6
  getUnresolvedBindings({ cwd = process.cwd(), env } = {}) {
6
7
  if (!env) {
7
- env = cds.env.for('cds', cwd) // REVISIT: This might load cds.env too early!
8
+ env = cds.env.for('cds', path.resolve(cwd)) // REVISIT: This might load cds.env too early!
8
9
  }
9
10
  const bindings = {}
10
11
  const requires = env.requires || {}
package/lib/bind/index.js CHANGED
@@ -101,7 +101,7 @@ function runCmd(profile, isJavaProject) {
101
101
  profile = parseProfile(profile);
102
102
  if (isJavaProject) {
103
103
  const p = profile === 'hybrid' ? '' : ` --profile ${profile}`;
104
- return `cds bind exec${p} mvn spring-boot:run`;
104
+ return `cds bind --exec${p} mvn spring-boot:run`;
105
105
  } else {
106
106
  const p = profile === 'development' ? '' : ` --profile ${profile}`;
107
107
  return `cds watch${p}`;
@@ -3,7 +3,7 @@ const fs = require('fs').promises
3
3
  const cds = require('../cds')
4
4
 
5
5
  const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, SEVERITY_INFO, SEVERITY_WARNING, SEVERITY_ERROR } = require('./constants')
6
- const { hasOptionValue, getProperty, relativePaths, BuildMessage } = require('./util')
6
+ const { hasOptionValue, getProperty, relativePaths, BuildMessage, pathExists } = require('./util')
7
7
 
8
8
  /**
9
9
  * The build plugin creates the build output for a dedicated build task. It is uniquely identified
@@ -27,6 +27,9 @@ class BuildPlugin {
27
27
  this._written = new Set()
28
28
  this._messages = []
29
29
  }
30
+ static hasTask() {
31
+ return true
32
+ }
30
33
  static get INFO() {
31
34
  return SEVERITY_INFO
32
35
  }
@@ -86,7 +89,11 @@ class BuildPlugin {
86
89
  * Called by the framework immediately after this instance has been created.
87
90
  * The instance has already been fully initialized.
88
91
  */
89
- init() { }
92
+ init() {
93
+ if (cds.env.build.target === '.') {
94
+ this.task.dest = path.join(this.task.dest, 'gen')
95
+ }
96
+ }
90
97
 
91
98
  /**
92
99
  * @abstract
@@ -96,13 +103,21 @@ class BuildPlugin {
96
103
  }
97
104
 
98
105
  /**
99
- * @abstract
100
106
  * Called by the framework immediately before 'build' to delete any output created by this build plugin.
101
107
  * <p>
102
108
  * Note: The <code>BuildTaskEngine</code> is cleaning the common generation target folder if the build is
103
109
  * executed in staging mode, e.g. build.target: "gen".
104
110
  */
105
111
  async clean() {
112
+ function isSubDirectory(parent, child) {
113
+ return !path.relative(parent, child).startsWith('..')
114
+ }
115
+ if (cds.env.build.target === '.'
116
+ && this.task.src !== this.task.dest
117
+ && !isSubDirectory(this.task.dest, this.task.src)
118
+ && await pathExists(this.task.dest)) {
119
+ return fs.rm(this.task.dest, { force: true, recursive: true })
120
+ }
106
121
  }
107
122
 
108
123
  /**
@@ -219,7 +219,8 @@ class BuildTaskEngine {
219
219
  // log all generated files
220
220
  const files = BuildTaskEngine._getOutput(plugins)
221
221
  if (files.length > 0) {
222
- this.logger.log(`done > wrote output to:\n ${files.join("\n ")}\n`)
222
+ const term = require('../util/term')
223
+ this.logger.log(`done > wrote output to:\n ${term.secondary(files.join("\n "))}\n`)
223
224
  }
224
225
  }
225
226
 
@@ -4,6 +4,7 @@ const cds = require('../cds')
4
4
  const BuildTaskProviderFactory = require('./buildTaskProviderFactory')
5
5
  const { hasJavaNature, getProperty, redactCredentials, flatten, getDefaultModelOptions } = require('./util')
6
6
  const { FILE_EXT_CDS, BUILD_TASK_JAVA_CF, BUILD_TASK_JAVA } = require("./constants")
7
+ const term = require('../util/term')
7
8
 
8
9
  class BuildTaskFactory {
9
10
  constructor(options) {
@@ -27,12 +28,11 @@ class BuildTaskFactory {
27
28
  const tasks = await this._createTasks()
28
29
 
29
30
  // log build tasks
30
- this.logger.log(`the following build tasks will be executed`)
31
- let tasksOutput = " {\n \"build\": {\n \"target\": \"" + cds.env.build.target + "\",\n \"tasks\": ["
31
+ let tasksOutput = "{\n build: {\n target: " + term.info("'" + cds.env.build.target + "'") + ",\n tasks: [\n"
32
32
  for (let i = 0; i < tasks.length; i++) {
33
- tasksOutput += "\n " + BuildTaskFactory._stringifyTask(tasks[i]) + (i + 1 < tasks.length ? "," : "")
33
+ tasksOutput += " " + BuildTaskFactory._stringifyTask(tasks[i]) + (i + 1 < tasks.length ? ",\n" : "\n")
34
34
  }
35
- tasksOutput += "\n ]\n }\n }\n"
35
+ tasksOutput += " ]\n }\n}\n"
36
36
  this.logger.log(tasksOutput)
37
37
 
38
38
  // for testing purposes
@@ -196,12 +196,36 @@ class BuildTaskFactory {
196
196
  static _stringifyTask(task) {
197
197
  // ensures identical order of properties
198
198
  const order = ["for", "use", "src", "dest", "options"]
199
- const keys = Object.keys(task).sort((a, b) => order.indexOf(a) - order.indexOf(b))
199
+ const keys = Object.keys(task).filter(key => order.includes(key)).sort((a, b) => order.indexOf(a) - order.indexOf(b))
200
200
 
201
- return keys.reduce((acc, key, idx) => {
202
- // render either "for" OR "use" value
203
- return acc + (!key.match("^_") ? (key !== "use" || keys[idx - 1] !== "for" ? (acc !== "{" ? ", " : "") + JSON.stringify(key) + ":" + JSON.stringify(task[key]) : "") : "")
204
- }, "{") + "}"
201
+ return keys.reduce((acc, key, i) => {
202
+ const prefix = acc !== '{' ? ', ' : (i === 0 ? ' ' : '')
203
+ if (!key.startsWith('_')) {
204
+ // render either "for" OR "use" value
205
+ if (key in { src: 1, for: 1 } || (!task.for && key === 'use')) {
206
+ return `${acc}${prefix}${key}: ${term.info(`'${task[key]}'`)}`
207
+ } else if (key === 'options') {
208
+ const options = Object.entries(task.options)
209
+ .map(([key, value]) => {
210
+ if (Array.isArray(value)) {
211
+ return `${key}: [${value.map(v => term.info(`'${v}'`)).join(', ')}]`
212
+ } else if (typeof value === 'object') {
213
+ const nested = Object.entries(value)
214
+ .map(([key, value]) => `${key}: ${term.info(`'${value}'`)}`)
215
+ .join(', ')
216
+ return `${key}: { ${nested} }`
217
+ } else {
218
+ return `${key}: ${term.info(`'${value}'`)}`
219
+ }
220
+ })
221
+ .join(', ')
222
+ return `${acc}${prefix}${key}: { ${options} }`
223
+ } else if (key !== 'use') {
224
+ return `${acc}${prefix}${key}: ${term.info(`'${task[key]}'`)}`
225
+ }
226
+ }
227
+ return acc
228
+ }, '{') + '}'
205
229
  }
206
230
 
207
231
  static _scanTaskOptionParams(optsParams) {
@@ -14,7 +14,7 @@ module.exports = class BuildTaskProvider {
14
14
  /**
15
15
  * @param {object} task
16
16
  */
17
- loadPlugin(task) { }
17
+ getPluginClass(task) { }
18
18
  /**
19
19
  * @param {Array} tasks
20
20
  * @param {boolean} dependencies
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const cds = require('../cds')
4
- const { LOG_MODULE_NAMES, OUTPUT_MODE_FILESYSTEM } = require('./constants')
4
+ const { LOG_MODULE_NAMES, OUTPUT_MODE_FILESYSTEM, DEFAULT_SRC_FOLDER } = require('./constants')
5
5
  const InternalBuildTaskProvider = require('./provider/internalBuildTaskProvider')
6
6
  const BuildTaskProvider = require('./buildTaskProvider')
7
7
  const { BuildError } = require('./util')
@@ -74,11 +74,14 @@ class BuildTaskProviderFactory {
74
74
  * @param {*} task
75
75
  */
76
76
  createPlugin(task) {
77
- const BuildPluginClass = this.loadPlugin(task)
77
+ const BuildPluginClass = this.getPluginClass(task)
78
78
  const resolvedTask = this._resolveTask(task)
79
79
  this.logger._debug && this.logger.debug(`loaded build plugin [${resolvedTask.use}]`)
80
80
 
81
81
  const plugin = new BuildPluginClass()
82
+ if (!(plugin instanceof BuildPlugin)) {
83
+ throw new Error(`Invalid Build plugin type ${task.for}`)
84
+ }
82
85
  plugin._task = resolvedTask
83
86
  plugin._logger = this.logger
84
87
  plugin._context = this.context
@@ -93,10 +96,10 @@ class BuildTaskProviderFactory {
93
96
  * 'use' defines the fully qualified module name of custom build plugins implementations.
94
97
  * @param {object} task
95
98
  */
96
- loadPlugin(task) {
99
+ getPluginClass(task) {
97
100
  const provider = this._getProvider(task)
98
101
  try {
99
- return provider.loadPlugin(task)
102
+ return provider.getPluginClass(task)
100
103
  } catch (e) {
101
104
  this.logger.error(`Provider failed to load build plugin class - provider: ${provider.constructor.name}, task: ${task.for || task.use} :\n` + e)
102
105
  throw e
@@ -113,9 +116,6 @@ class BuildTaskProviderFactory {
113
116
  * @param {*} task
114
117
  */
115
118
  _resolveTask(task) {
116
- // first validate plugin implementation
117
- this.loadPlugin(task)
118
-
119
119
  // second validate src path
120
120
  const resolvedTask = JSON.parse(JSON.stringify(task))
121
121
 
@@ -181,7 +181,7 @@ class DefaultBuildTaskProvider extends BuildTaskProvider {
181
181
  return !!key.use
182
182
  }
183
183
 
184
- loadPlugin(task) {
184
+ getPluginClass(task) {
185
185
  try {
186
186
  return require(require.resolve(task.use, { paths: [cds.root] }))
187
187
  } catch (e) {
@@ -192,36 +192,14 @@ class DefaultBuildTaskProvider extends BuildTaskProvider {
192
192
 
193
193
  /**
194
194
  * Default provider implementation handling fully qualified custom build task declarations.
195
- * Has to be the last entry in the providers list. // REVISIT: Above it says DefaultBuildTaskProvider should be last entry? -> clarify
196
195
  */
197
196
  class PluginBuildTaskProvider extends BuildTaskProvider {
198
197
  constructor(logger) {
199
198
  super()
200
199
  this._logger = logger
201
- const plugins = new Map()
202
-
203
- // REVISIT: Remove once fetch mechanism is removed
204
- function _local (id) {
205
- return require(require.resolve (id, {paths:[
206
- process.cwd(), // project-local module is preferred
207
- __dirname // otherwise from our own dependencies
208
- ]}))
209
- }
210
-
211
- const cdsPlugins = _local('@sap/cds/lib/plugins').fetch?.() // REVISIT: Get rid of fetch mechanism
212
- const buildPlugins = cds.env.build.plugins // REVISIT: Compat to old plugin mechanism, remove if possible
213
-
214
- for (const [id, plugin] of Object.entries({ ...cdsPlugins, ...buildPlugins })) {
215
- try {
216
- const clazz = this._loadPlugin(id, plugin)
217
- if (!clazz) continue
218
- plugins.set(id, { id: id, impl: plugin.impl, class: clazz })
219
- } catch (e) {
220
- this._logger.error(`Skipping build plugin '${id}', it cannot be loaded.\n` + e + '\n') // REVISIT: why not throw? seems like an error one should fix, not exit with code 0
221
- }
222
- }
223
- this._plugins = plugins
200
+ this._plugins = require('./plugins').getPlugins()
224
201
  }
202
+
225
203
  get plugins() {
226
204
  return this._plugins
227
205
  }
@@ -230,8 +208,8 @@ class PluginBuildTaskProvider extends BuildTaskProvider {
230
208
  return this.plugins.has(key.for)
231
209
  }
232
210
 
233
- loadPlugin(task) {
234
- return this._loadPlugin(task.for, this.plugins.get(task.for))
211
+ getPluginClass(task) {
212
+ return this.plugins.get(task.for).class
235
213
  }
236
214
 
237
215
  async lookupTasks(tasks, dependencies) {
@@ -258,35 +236,11 @@ class PluginBuildTaskProvider extends BuildTaskProvider {
258
236
  }
259
237
 
260
238
  getTask(key) {
261
- const task = this.plugins.get(key.for).class.getTaskDefaults() ?? key
262
- task.src ??= '.'
263
- if (task.src) {
264
- task.src = task.src.replace(/\/$/, '')
265
- }
239
+ const task = this.plugins.get(key.for).taskDefaults
266
240
  task.for = key.for
241
+ task.src ??= DEFAULT_SRC_FOLDER
242
+ task.src = task.src.replace(/\/$/, '')
267
243
  return task
268
244
  }
269
-
270
- _loadPlugin(id, plugin) {
271
- let pluginClass
272
- try {
273
- pluginClass = require(require.resolve(plugin.impl, { paths: [cds.root] }))
274
- const inheritsFrom = (child, parent) => {
275
- let proto = Object.getPrototypeOf(child)
276
- while (proto) {
277
- if (proto === parent) return true
278
- proto = Object.getPrototypeOf(proto)
279
- }
280
- return false
281
- }
282
- if (!inheritsFrom(pluginClass, BuildPlugin)) return // REVISIT: better way to check if plugin is build-enabled than traversing prototype chain?
283
- } catch (e) {
284
- throw new Error(`Custom build plugin implementation '${plugin.impl}' for '${id}' cannot be loaded:\n` + e)
285
- }
286
- if (!pluginClass.hasTask || !pluginClass.getTaskDefaults) {
287
- throw new Error(`Custom build plugin '${id}' must implement 'hasTask' and 'getTaskDefaults' methods`)
288
- }
289
- return pluginClass
290
- }
291
245
  }
292
246
  module.exports = BuildTaskProviderFactory
@@ -3,6 +3,7 @@ exports.OUTPUT_MODE_NONE = 0x0
3
3
  exports.OUTPUT_MODE_FILESYSTEM = 0x1
4
4
  exports.OUTPUT_MODE_RESULT = 0x2
5
5
 
6
+ exports.DEFAULT_SRC_FOLDER = '.'
6
7
  exports.BUILD_TASK_NPM_SCOPE = "@sap"
7
8
  exports.BUILD_TASK_PREFIX = exports.BUILD_TASK_NPM_SCOPE + "/cds/build"
8
9
  exports.BUILD_TASK_JAVA = "java"
@@ -23,6 +24,7 @@ exports.CONTENT_LANGUAGE_BUNDLES = "contentLanguageBundles" // create i18n.j
23
24
  exports.CONTENT_DEFAULT_CSN = "contentDefaultCsn" // create default CSN format flavor: "inferred"
24
25
  exports.FLAVOR_LOCALIZED_EDMX = "flavorLocalizedEdmx" // create localized EDMX languages based on existing properties files
25
26
  exports.CONTENT_EDMX = "contentEdmx" // create EDMX for required languages - ENABLED by default
27
+ exports.CONTENT_LOCALIZED_EDMX = "contentLocalizedEdmx" // create EDMX for required languages - ENABLED for Java by default
26
28
  exports.CONTENT_PACKAGE_JSON = "contentPackageJson" // create package.json file if not existing, or modify existing package.json - ENABLED by default
27
29
  exports.CONTENT_PACKAGELOCK_JSON = "contentPackageLockJson" // copy package-lock.json file if existing into deployment folder - ENABLED by default
28
30
  exports.CONTENT_HDBTABLEDATA = "contentHdbtabledata" // create .hdbtabledata files for .csv files if not existing - ENABLED by default