@sap/cds-dk 7.8.1 → 7.9.1

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 (90) hide show
  1. package/bin/add.js +28 -7
  2. package/bin/bind.js +64 -21
  3. package/bin/build.js +35 -3
  4. package/bin/cds.js +32 -13
  5. package/bin/compile/index.js +44 -3
  6. package/bin/completion/completionFs.js +61 -0
  7. package/bin/completion/index.js +212 -0
  8. package/bin/completion/scripts/cds-bash.sh +17 -0
  9. package/bin/completion/scripts/cds-ps.ps1 +18 -0
  10. package/bin/completion/scripts/cds-zsh.sh +12 -0
  11. package/bin/help.js +8 -2
  12. package/bin/init.js +34 -4
  13. package/bin/subscribe.js +5 -1
  14. package/bin/watch.js +4 -2
  15. package/lib/bind/KubernetesApiAccessor.js +3 -2
  16. package/lib/bind/bindingResolver.js +3 -2
  17. package/lib/bind/bindingUtil.js +10 -8
  18. package/lib/bind/cfKeyProvider.js +58 -153
  19. package/lib/bind/index.js +46 -36
  20. package/lib/bind/kubernetesCredentialsProvider.js +6 -5
  21. package/lib/bind/userCdsrcJsonStorageProvider.js +25 -3
  22. package/lib/build/buildTaskEngine.js +1 -1
  23. package/lib/build/provider/hana/index.js +1 -1
  24. package/lib/build/provider/hana/template/undeploy.json +3 -1
  25. package/lib/build/provider/nodejs/index.js +1 -1
  26. package/lib/compile/openapi/csdl2openapi.js +4 -0
  27. package/lib/compile/openapi/index.js +5 -0
  28. package/lib/deploy/to-hana/hana.js +25 -7
  29. package/lib/env/schemas/cds-rc.js +29 -2
  30. package/lib/import/odata/v4parser.js +78 -25
  31. package/lib/init/constants.js +2 -1
  32. package/lib/init/index.js +18 -13
  33. package/lib/init/merge.js +1 -0
  34. package/lib/init/mvn.js +8 -47
  35. package/lib/init/plugin.js +0 -4
  36. package/lib/init/projectReader.js +17 -10
  37. package/lib/init/template/approuter/files/values.yaml.hbs +6 -12
  38. package/lib/init/template/approuter/index.js +1 -7
  39. package/lib/init/template/completion/completionSetup.js +184 -0
  40. package/lib/init/template/completion/index.js +22 -0
  41. package/lib/init/template/data/as-csv.js +25 -17
  42. package/lib/init/template/data/as-json.js +115 -30
  43. package/lib/init/template/data/index.js +28 -15
  44. package/lib/init/template/hana/files/undeploy.json +2 -1
  45. package/lib/init/template/helm/files/chart/values.schema.json +1 -1
  46. package/lib/init/template/helm/files/content-deployment/Chart.yaml +0 -2
  47. package/lib/init/template/helm/files/content-deployment/values.schema.json +0 -1
  48. package/lib/init/template/helm/files/service-instance/Chart.yaml +0 -2
  49. package/lib/init/template/helm/files/service-instance/values.schema.json +0 -1
  50. package/lib/init/template/helm/files/web-application/Chart.yaml +1 -3
  51. package/lib/init/template/helm/files/web-application/templates/_helpers.tpl +23 -0
  52. package/lib/init/template/helm/files/web-application/templates/deployment.yaml +3 -29
  53. package/lib/init/template/helm/files/web-application/values.schema.json +17 -2
  54. package/lib/init/template/helm/files/web-application/values.yaml +10 -2
  55. package/lib/init/template/helm-unified-runtime/files/templates/NOTES.txt +2 -0
  56. package/lib/init/template/helm-unified-runtime/files/templates/_deployment_helpers.tpl +10 -2
  57. package/lib/init/template/helm-unified-runtime/files/values.schema.json +1 -1
  58. package/lib/init/template/http/index.js +150 -104
  59. package/lib/init/template/kafka/index.js +6 -0
  60. package/lib/init/template/lint/index.js +12 -13
  61. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  62. package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +1 -1
  63. package/lib/init/template/multitenancy/files/values.yaml.hbs +0 -5
  64. package/lib/init/template/multitenancy/index.js +13 -5
  65. package/lib/init/template/nodejs/files/.vscode/settings.json +11 -0
  66. package/lib/init/template/nodejs/files/eslint.config.js +13 -0
  67. package/lib/init/template/nodejs/files/package.json.hbs +3 -1
  68. package/lib/init/template/sample/index.js +18 -20
  69. package/lib/init/template/tiny-sample/index.js +1 -2
  70. package/lib/init/template/typer/index.js +2 -2
  71. package/lib/lint/Linter.js +133 -55
  72. package/lib/lint/checks.js +5 -6
  73. package/lib/lint/io.js +119 -10
  74. package/lib/mtx/auth_manager.js +5 -5
  75. package/lib/mtx/params.js +4 -2
  76. package/lib/mtx/settings_manager.js +1 -1
  77. package/lib/mtx/tenant_action.js +12 -2
  78. package/lib/mtx/util/logging.js +10 -4
  79. package/lib/util/axios.js +2 -1
  80. package/lib/util/cf.js +32 -37
  81. package/lib/util/fs.js +1 -1
  82. package/lib/util/git.js +1 -1
  83. package/lib/util/term.js +1 -1
  84. package/{bin → lib/watch}/watched.js +2 -2
  85. package/npm-shrinkwrap.json +317 -293
  86. package/package.json +2 -2
  87. package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +0 -19
  88. package/lib/init/template/nodejs/files/.eslintrc +0 -26
  89. /package/lib/init/template/{approuter/files/mtxs-configmap-java.yaml → multitenancy/files/mtxs-configmap-java-approuter.yaml} +0 -0
  90. /package/lib/init/template/{approuter/files/mtxs-configmap-nodejs.yaml → multitenancy/files/mtxs-configmap-nodejs-approuter.yaml} +0 -0
package/bin/add.js CHANGED
@@ -23,6 +23,7 @@ const flags = ['--force', ...Object.values(conf).flatMap(p => p.flags).map(o =>
23
23
  DEBUG?.('cli config ', { options, shortcuts, flags })
24
24
 
25
25
  module.exports = Object.assign(add, {
26
+ handleCompletion,
26
27
  options,
27
28
  shortcuts,
28
29
  flags,
@@ -98,18 +99,18 @@ module.exports = Object.assign(add, {
98
99
  # FEATURE OPTIONS
99
100
 
100
101
  ${Object.entries(conf).sort(([a], [b]) => a.localeCompare(b)).flatMap(([pluginName, { options, flags }]) => {
101
- const text4 = arr => arr.map(({ key, help, short }) => help && ` ${key}${short ? ' | '+short : ''}\n\n ${help}`).join('\n\n')
102
- const optionsText = text4(options), flagsText = text4(flags)
103
- if (!optionsText && !flagsText) return []
104
- return ` *cds add ${pluginName}*\n\n${optionsText}${flagsText}`
105
- }).join('\n\n\n')}
102
+ const text4 = arr => arr.map(({ key, help, short }) => help && ` ${key}${short ? ' | '+short : ''}\n\n ${help}`).join('\n\n')
103
+ const optionsText = text4(options), flagsText = text4(flags)
104
+ if (!optionsText && !flagsText) return []
105
+ return ` *cds add ${pluginName}*\n\n${optionsText}${flagsText}`
106
+ }).join('\n\n\n')}
106
107
 
107
108
 
108
109
  # EXAMPLES
109
110
 
110
111
  *cds add* sample
111
112
  *cds add* multitenancy,hana,xsuaa --for production
112
- *cds add* data --data:for my.namespace.MyEntity
113
+ *cds add* data --filter my.namespace.MyEntity
113
114
  *cds add* mta
114
115
  *cds add* helm
115
116
 
@@ -118,7 +119,27 @@ ${Object.entries(conf).sort(([a], [b]) => a.localeCompare(b)).flatMap(([pluginNa
118
119
 
119
120
  *cds init*
120
121
 
121
- `})
122
+ `});
123
+
124
+ async function handleCompletion(currentWord, previousWord, argv, util) {
125
+ const allOptionsFlags = [
126
+ ...options ?? [],
127
+ ...flags ?? []
128
+ ].filter(e => !argv.includes(e));
129
+
130
+ if (currentWord?.startsWith('-')) {
131
+ return allOptionsFlags;
132
+ }
133
+
134
+ if (!currentWord && options?.includes(previousWord)) {
135
+ return [];
136
+ }
137
+
138
+ const entries = await util.completionFs.readTemplates(argv);
139
+ entries.push(...allOptionsFlags);
140
+
141
+ return entries;
142
+ }
122
143
 
123
144
  async function add(args) {
124
145
  // await cds.plugins
package/bin/bind.js CHANGED
@@ -1,9 +1,9 @@
1
- const { warn } = require('../lib/util/term')
1
+ const term = require('../lib/util/term')
2
2
 
3
3
  module.exports = Object.assign(cds_bind, {
4
- options: ['--to', '--for', '--on', '--kind', '--out', '--to-app-services', '--no-create-service-key', '--output-file'],
5
- flags: ['--exec'],
6
- shortcuts: ['-2', '-4', '-n', '-k', '-o', '-a'],
4
+ options: ['--to', '--for', '--on', '--kind', '--out', '--to-app-services', '--credentials', '--output-file'],
5
+ flags: ['--exec', '--no-create-service-key'],
6
+ shortcuts: ['-2', '-4', '-n', '-k', '-o', '-a', '-c'],
7
7
  help: `
8
8
  # SYNOPSIS
9
9
 
@@ -52,6 +52,11 @@ module.exports = Object.assign(cds_bind, {
52
52
 
53
53
  Skip automatic creation of service keys.
54
54
 
55
+ *-c | --credentials* <JSON|{file path}> (Beta)
56
+
57
+ JSON or file path defining custom fields overwriting service credentials.
58
+ Custom credentials are merged with the credentials read from BTP when running your application in hybrid mode.
59
+
55
60
  # EXAMPLE
56
61
 
57
62
  cds bind --to bookshop-db
@@ -64,72 +69,110 @@ module.exports = Object.assign(cds_bind, {
64
69
  cds bind --to bookshop-db --for my-profile
65
70
 
66
71
  cds bind --to-app-services bookshop-srv
72
+
73
+ cds bind messaging --to redis-cache --credentials '{"host":"localhost","port":6379}'
74
+ cds bind messaging --to redis-cache --credentials ./credentials.json
67
75
  `
68
76
  });
69
77
 
70
78
  const os = require('os');
71
79
  const IS_WIN = os.platform() === 'win32';
72
80
 
73
- const { bind, parseProfile } = require('../lib/bind');
81
+ const { bind, storeBindings, parseProfile } = require('../lib/bind');
74
82
 
75
83
 
76
84
  async function cds_bind(args, options) {
77
-
78
- const { on, 'to-app-services': app, to, out, 'output-file': outputFile, exec } = options
85
+ const { on, 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
79
86
 
80
87
  if (exec) {
81
88
  process.env.CDS_ENV = parseProfile(process.env.CDS_ENV);
82
89
  if (options.for) {
83
- console.warn('Parameter --for is not supported in combination with --exec. Its value will be ignored.');
90
+ console.warn(term.warn(`Parameter --for is not supported in combination with --exec. Its value will be ignored.`));
84
91
  }
85
92
  return cds_bind_exec(args)
86
93
  }
87
94
 
88
95
  // Check if cds command was bind. Don't abort in case of deploy
89
96
  if (process.env.CDS_ENV && process.argv[2] === 'bind') {
90
- throw 'Option --profile is no longer supported. Use --for instead to specify the profile to use when storing connection settings.';
97
+ throw `Option ${term.bold('--profile')} is no longer supported. Use ${term.bold('--for')} instead to specify the profile to use when storing connection settings.`;
91
98
  }
92
99
 
93
100
  if (outputFile) {
94
- console.warn(warn('Option --output-file is deprecated. Please use --out instead.'))
101
+ console.warn(term.warn(`Option --output-file is deprecated. Please use --out instead.`))
95
102
  options.out ??= outputFile
96
103
  }
97
104
 
98
105
  if (on === 'k8s' && app) {
99
- throw 'Option --to-app-services is only supported for Cloud Foundry.';
106
+ throw `Option ${term.bold('--to-app-services')} is only supported for Cloud Foundry.`;
107
+ }
108
+
109
+ if (credentials === true) {
110
+ throw `Option ${term.bold('--credentials')} requires a JSON object or a file path.`;
111
+ }
112
+
113
+ // read credentials from file if it is a file, from JSON if it is JSON
114
+ if (credentials) {
115
+ let credentialsJson = options.credentials;
116
+ if (!credentials.trim().startsWith('{')) {
117
+ const fs = require('fs');
118
+ try {
119
+ credentialsJson = fs.readFileSync(credentials, 'utf8');
120
+ } catch (e) {
121
+ throw `Error reading credentials from file ${credentials}: ${e.message}`
122
+ }
123
+ }
124
+ try {
125
+ options.credentials = JSON.parse(credentialsJson);
126
+ } catch (e) {
127
+ throw `Invalid JSON format for option ${term.bold('--credentials')}: ${e.message}`;
128
+ }
129
+ if (options.to) {
130
+ //at least one service must match
131
+ const toServices = options.to.split(',');
132
+ if (toServices.length > 1) {
133
+ if (!toServices.some(toService => options.credentials[toService])) {
134
+ throw `Option ${term.bold('--credentials')} cannot uniquely identify a matching service defined in ${term.bold('--to')}.\nWhen passing multiple services, include the service name in the credentials object.\nExample: cds bind --to bookshop-db,bookshop-redis --credentials '{ "bookshop-redis": {"host": "localhost", "port": 1234}}'`
135
+ }
136
+ }
137
+ }
100
138
  }
101
139
 
102
140
  if (!to && !app) {
103
- throw `Use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`;
141
+ throw `Use option ${term.bold('--to')} or ${term.bold('-2')} to specify the target instance, e.g. ${term.bold('cds bind --to myInstance:myService')}`;
104
142
  }
105
143
 
106
144
  if (app === true) {
107
- throw `Option '--to-app-services' requires an app name, for example 'cds bind --to-app-services bookshop-srv'.`
145
+ throw `Option ${term.bold('--to-app-services')} requires an app name, for example ${term.bold('cds bind --to-app-services bookshop-srv')}.`
108
146
  }
109
147
 
148
+ options.for ??= 'hybrid';
149
+
110
150
  if (app) {
111
151
  const services = await require('../lib/util/cf').getServices(app)
112
- // Later: optimize logging for parallelization, services.forEach(service => bind({ ...options, to: service.name, outputFile }))
113
- for (const service of services.filter(s => Object.keys(s.credentials).length > 0)) {
114
- await bind({ ...options, to: service.name, out })
115
- }
152
+ const allServices = await Promise.all(
153
+ services
154
+ .filter(s => Object.keys(s.credentials).length > 0)
155
+ .map(s => bind({ ...options, to: s.name, out }))
156
+ )
157
+ await storeBindings(allServices.flat(), options);
116
158
  } else {
117
159
  if (args.length > 1) {
118
- throw `Too many arguments: Please specify only one or no service.`;
160
+ throw 'Too many arguments: Please specify only one or no service.';
119
161
  }
120
162
 
121
163
  options.serviceArg = args[0]
122
164
  options.targets = options.to.split(/,/g);
123
165
 
124
166
  if (options.targets.length >= 2 && options.serviceArg) {
125
- throw `Service argument cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the service argument.`;
167
+ throw `Service argument cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the service argument.`;
126
168
  }
127
169
 
128
170
  if (options.targets.length >= 2 && options.kind) {
129
- throw `The option '--kind' cannot be specified together with multiple targets ('--to') services. Use one service per call or omit the '--kind' option.`;
171
+ throw `The option '--kind' cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the ${term.bold('--kind')} option.`;
130
172
  }
131
173
 
132
- await bind({ ...options, out });
174
+ const services = await bind({ ...options, out });
175
+ await storeBindings(services, options);
133
176
  }
134
177
  }
135
178
 
package/bin/build.js CHANGED
@@ -1,4 +1,5 @@
1
1
  module.exports = Object.assign(build, {
2
+ handleCompletion,
2
3
  options: ['--project', '--src', '--dest', '--for', '--use', '--opts', '--log-level', '--options-model'],
3
4
  shortcuts: ['-in', '-s', '-o', '-4'],
4
5
  flags: ['--clean', '--no-clean', '--ws', '--ws-pack'], // REVISIT: --clean is deprecated, backward compatibility
@@ -67,7 +68,7 @@ module.exports = Object.assign(build, {
67
68
  *-in | --project* [<project>]
68
69
  Runs 'cds build' for the specified project, default is current working
69
70
  directory.
70
-
71
+
71
72
  *--no-clean* (beta)
72
73
  Skips cleaning the build output folder before running the actual build
73
74
  operation. Clients need to clean the output folder upfront to ensure
@@ -92,7 +93,7 @@ module.exports = Object.assign(build, {
92
93
  folder of the project. This 'db' folder represents the single database shared by all workspaces.
93
94
  *Note:* Make sure to use consistent package versions in your workspace dependencies in order
94
95
  to avoid compilation errors caused by duplicate model definitions.
95
-
96
+
96
97
  # EXAMPLES
97
98
 
98
99
  *cds* build
@@ -102,10 +103,41 @@ module.exports = Object.assign(build, {
102
103
  *cds* build --for java --dest ../srv/target --opts model=[db/model,srv,schema]
103
104
  `})
104
105
 
106
+ async function handleCompletion(currentWord, previousWord, argv, util) {
107
+ const allOptionsFlags = [
108
+ ...build.options ?? [],
109
+ ...build.flags ?? []
110
+ ].filter(e => !argv.includes(e));
111
+
112
+ if (currentWord?.startsWith('-')) {
113
+ return allOptionsFlags;
114
+ }
115
+
116
+ switch (previousWord) {
117
+ case '--log-level':
118
+ return ['debug', 'info', 'warn', 'error'];
119
+ case '-4':
120
+ case '--for':
121
+ return ['hana', 'java', 'mtx', 'mtx-extension', 'mtx-sidecar', 'nodejs'];
122
+ case '--dest':
123
+ case '-o':
124
+ case '-s':
125
+ case '--src': {
126
+ return util.completionFs.readdir(currentWord, { files: false });
127
+ }
128
+ }
129
+
130
+ if (!currentWord && build.options?.includes(previousWord)) {
131
+ return [];
132
+ }
133
+
134
+ return allOptionsFlags;
135
+ }
136
+
105
137
  async function build([project], options = {}) {
106
138
  const path = require('path')
107
139
 
108
- // ensure that cds build plugins defined as devDependencies are loaded
140
+ // ensure that cds build plugins defined as devDependencies are loaded
109
141
  if (process.env.NODE_ENV === 'production') delete process.env.NODE_ENV
110
142
 
111
143
  const cds = require('../lib/cds')
package/bin/cds.js CHANGED
@@ -34,6 +34,14 @@ const cli = {
34
34
  DEBUG?.(`[cds] - @sap/cds ${cds.version} loaded: ${cds.home}`)
35
35
 
36
36
  try {
37
+ const allCmds = getCommands();
38
+
39
+ //TODO: make better; only POC for cds help
40
+ Object.assign(module.exports, { cmds: allCmds })
41
+
42
+ // const { handleCompletion } = require('./util/completion');
43
+ // await handleCompletion(process.argv, allCmds, this);
44
+
37
45
  const task = this.load(cmd, /*else*/(f) => argv.unshift(cmd) && (cmd = 'compile') && _defaultCompile(argv, f))
38
46
  if (task) {
39
47
  // REVISIT bind: '--' handling for cds bind --exec. Should be moved to cds_cli
@@ -46,7 +54,7 @@ const cli = {
46
54
  // REVISIT: Have to do it here to allow custom flags for plugins!
47
55
  if (cmd === 'add') {
48
56
  await cds.plugins
49
- for (const [_, Plugin] of cds.add.registered) {
57
+ for (const [, Plugin] of cds.add.registered) {
50
58
  task.options.push(...Object.keys((new Plugin).options()).map(key => '--'+key))
51
59
  }
52
60
  }
@@ -130,6 +138,7 @@ async function _resolveBindings(options) {
130
138
  const bindingEnv = await bindingManager.bindingEnv()
131
139
  Object.assign(process.env, bindingEnv)
132
140
  cds.env = cds.env.for('cds') // reload env // FIXME: This is too early!
141
+ cds.requires = cds.env.requires // REVISIT: We should clean this up!
133
142
  }
134
143
 
135
144
  function _local (id, _else) {
@@ -160,15 +169,7 @@ function _dieForUnknownCommand(cmd) {
160
169
 
161
170
  let bestMatchText = '';
162
171
 
163
- const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js', 'util'];
164
- const { readdirSync } = require('fs');
165
- const { resolve } = require('path');
166
- let commands = readdirSync(__dirname, { withFileTypes: true })
167
- .concat(readdirSync(resolve(_local('@sap/cds'), '..', '..', 'bin'), { withFileTypes: true }))
168
- .filter((f) => !excludeList.includes(f.name))
169
- .map((f) => f.name.split('.')[0])
170
- commands = [...new Set(commands)] // make unique
171
- .sort();
172
+ const commands = getCommands();
172
173
 
173
174
  const bestMatches = fuzzySearch(cmd, commands, DEBUG);
174
175
 
@@ -192,9 +193,7 @@ function _dieForUnknownCommand(cmd) {
192
193
 
193
194
  You can use ${term.bold("cds help [command]")} to get more information on the command.
194
195
  `
195
- console.error(msg)
196
-
197
- process.exit(1)
196
+ throw msg;
198
197
  }
199
198
 
200
199
  function _require (id,o) {
@@ -211,4 +210,24 @@ module.exports = Object.assign ((..._) => cli.exec(..._), cli)
211
210
 
212
211
  if (!module.parent) module.exports()
213
212
 
213
+
214
+
215
+ function getCommands() {
216
+ const fs = require('fs');
217
+ const path = require('path');
218
+
219
+ const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js'];
220
+
221
+ const files = fs.readdirSync(__dirname, { withFileTypes: true, encoding: 'utf8' })
222
+ const result = new Set()
223
+ for (const f of files) {
224
+ if (!excludeList.includes(f.name)) {
225
+ if (f.isDirectory() && fs.existsSync(path.join(__dirname, f.name, 'index.js')) || f.isFile() && f.name.endsWith('.js')) {
226
+ result.add(path.parse(f.name).name)
227
+ }
228
+ }
229
+ }
230
+ return Array.from(result).sort()
231
+ }
232
+
214
233
  /* eslint no-console:off */
@@ -1,6 +1,7 @@
1
1
  const path = require("path");
2
2
 
3
3
  module.exports = Object.assign ( compile, {
4
+ handleCompletion,
4
5
  options: [
5
6
  '--from', '--service', '--lang', '--for', '--to', '--dest', '--log-level', '--flavor', '--dialect', '--sql-dialect', '--openapi:url', '--openapi:servers', '--odata-version'
6
7
  ],
@@ -53,13 +54,13 @@ module.exports = Object.assign ( compile, {
53
54
 
54
55
  - odata
55
56
  - sql
56
-
57
+
57
58
  *--dialect* <dialect>
58
-
59
+
59
60
  Needs option *--to*.
60
61
  Specify the dialect in combination with *--to sql*.
61
62
  Currently supported:
62
-
63
+
63
64
  - sqlite
64
65
  - h2
65
66
  - postgres
@@ -147,6 +148,46 @@ module.exports = Object.assign ( compile, {
147
148
 
148
149
  `})
149
150
 
151
+ async function handleCompletion(currentWord, previousWord, argv, util) {
152
+ const allOptionsFlags = [
153
+ ...compile.options ?? [],
154
+ ...compile.flags ?? []
155
+ ].filter(e => !argv.includes(e))
156
+
157
+ if (currentWord?.startsWith('-')) { return allOptionsFlags }
158
+
159
+ switch (previousWord) {
160
+ case '-2':
161
+ case '--to':
162
+ return ['json', 'yml', 'edm', 'edmx', 'edmx-v2', 'edmx-v4', 'sql', 'hdbcds', 'hdbtable', 'cdl', 'xsuaa', 'openapi', 'asyncapi'];
163
+ case '-4':
164
+ case '--for':
165
+ return ['odata', 'sql'];
166
+ case '--dialect':
167
+ return ['sqlite', 'h2', 'postgres', 'hana'];
168
+ case '--log-level':
169
+ return ['debug', 'info', 'warn', 'error'];
170
+ case '--odata-version':
171
+ return ['4.0', '4.01'];
172
+ case '-f':
173
+ case '--flavor':
174
+ return ['sources', 'files', 'parsed', 'xtended', 'inferred'];
175
+ case '-s':
176
+ case '--service':
177
+ case '-l':
178
+ case '--lang':
179
+ return ['all'];
180
+ case '-o':
181
+ case '--dest':
182
+ return util.completionFs.readdir(currentWord, { files: false })
183
+ default:
184
+ }
185
+
186
+ if (!currentWord && compile.options?.includes(previousWord)) { return [] }
187
+
188
+ return util.completionFs.readdir(currentWord, { fileRegex: /\.(cds|json)/ })
189
+ }
190
+
150
191
  async function compile_all (root='.') {
151
192
 
152
193
  const {exec} = require ('child_process')
@@ -0,0 +1,61 @@
1
+ module.exports = new class CompletionFS {
2
+ async readdir(currentFolder, options) {
3
+ options = {
4
+ files: true,
5
+ folders: true,
6
+ fileRegex: /.*/,
7
+ folderRegex: /.*/,
8
+ ...options
9
+ }
10
+ const fsp = require('node:fs/promises');
11
+ const path = require('node:path');
12
+
13
+ const match = /\/|(\\)[^\s]/.exec(currentFolder);
14
+ const separationChar = match?.[1] || match?.[0] || '/';
15
+ let folder = currentFolder || process.cwd();
16
+
17
+ let entries;
18
+ try {
19
+ entries = await fsp.readdir(folder, { withFileTypes: true });
20
+ } catch (err) {
21
+ folder = path.dirname(folder);
22
+ entries = await fsp.readdir(folder, { withFileTypes: true });
23
+ }
24
+ const isCwd = folder === '.' || folder === process.cwd();
25
+
26
+ entries = entries
27
+ .filter(e =>
28
+ options.folders && e.isDirectory() && e.name.match(options.folderRegex)
29
+ || options.files && e.isFile() && e.name.match(options.fileRegex))
30
+ .map(e => {
31
+ let result = isCwd
32
+ ? path.relative(folder, e.name)
33
+ : (folder + separationChar + e.name).replace(/\\{2,}/g, '\\').replace(/\/{2,}/g, '/');
34
+
35
+ if (e.isDirectory()) {
36
+ result += separationChar;
37
+ }
38
+
39
+ return result;
40
+ });
41
+
42
+ if (entries.length === 0) {
43
+ entries.push(folder);
44
+ }
45
+ return entries;
46
+ }
47
+
48
+ async readTemplates(argv) {
49
+ const fsp = require('node:fs/promises');
50
+ const path = require('node:path');
51
+
52
+ const excludes = ['nodejs', 'java', ...argv];
53
+
54
+ return (await fsp.readdir(path.join(__dirname, '../../lib/init/template'), { withFileTypes: true }))
55
+ .filter(e =>
56
+ e.isDirectory()
57
+ && !e.name.startsWith('_')
58
+ && !excludes.includes(e.name))
59
+ .map(e => e.name);
60
+ }
61
+ }