@sap/cds-dk 7.0.2 → 7.0.3

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 (64) hide show
  1. package/bin/add.js +2 -2
  2. package/bin/bind.js +0 -1
  3. package/bin/cds.js +6 -4
  4. package/bin/deploy/to-hana.js +1 -1
  5. package/bin/init.js +2 -2
  6. package/lib/bind/cfKeyProvider.js +1 -0
  7. package/lib/bind/index.js +3 -0
  8. package/lib/bind/userCdsrcJsonStorageProvider.js +2 -10
  9. package/lib/build/constants.js +16 -14
  10. package/lib/build/provider/mtx/resourcesTarBuilder.js +2 -6
  11. package/lib/build/provider/mtx-sidecar/index.js +13 -8
  12. package/lib/client/base_command.js +3 -6
  13. package/lib/compile/asyncapi/components/messages/index.js +41 -18
  14. package/lib/compile/asyncapi/index.js +23 -2
  15. package/lib/import/odata/metadataconverter/MetadataConverter.js +1 -0
  16. package/lib/init/constants.js +1 -1
  17. package/lib/init/index.js +58 -66
  18. package/lib/init/template/approuter/index.js +3 -4
  19. package/lib/init/template/auditlog/index.js +3 -4
  20. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +15 -23
  21. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +22 -17
  22. package/lib/init/template/cf-manifest/index.js +2 -2
  23. package/lib/init/template/connectivity/index.js +2 -3
  24. package/lib/init/template/data/index.js +5 -5
  25. package/lib/init/template/destination/index.js +3 -5
  26. package/lib/init/template/enterprise-messaging/index.js +4 -5
  27. package/lib/init/template/enterprise-messaging-shared/index.js +3 -4
  28. package/lib/init/template/extensibility/index.js +2 -3
  29. package/lib/init/template/file-based-messaging/index.js +2 -2
  30. package/lib/init/template/hana/files/mta.yaml.hbs +1 -1
  31. package/lib/init/template/hana/index.js +39 -63
  32. package/lib/init/template/helm/index.js +15 -14
  33. package/lib/init/template/html5-repo/index.js +3 -7
  34. package/lib/init/template/java/index.js +3 -3
  35. package/lib/init/template/kibana/index.js +3 -4
  36. package/lib/init/template/lint/index.js +8 -7
  37. package/lib/init/template/mta/index.js +3 -2
  38. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -1
  39. package/lib/init/template/multitenancy/index.js +16 -37
  40. package/lib/init/template/nodejs/index.js +2 -3
  41. package/lib/init/template/pipeline/index.js +2 -2
  42. package/lib/init/template/redis-messaging/index.js +3 -4
  43. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +1 -1
  44. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  45. package/lib/init/template/sample/files/srv/admin-service.cds +1 -1
  46. package/lib/init/template/sample/files/srv/cat-service.cds +1 -1
  47. package/lib/init/template/sample/index.js +6 -5
  48. package/lib/init/template/sqlite/index.js +1 -1
  49. package/lib/init/template/templateBase.js +9 -19
  50. package/lib/init/template/tiny-sample/index.js +5 -5
  51. package/lib/init/template/toggles/index.js +1 -2
  52. package/lib/init/template/typer/files/xsconfig.json +0 -1
  53. package/lib/init/template/typer/index.js +1 -1
  54. package/lib/init/template/xsuaa/index.js +3 -4
  55. package/lib/init/util/indent.js +57 -0
  56. package/lib/init/util/merge.js +71 -15
  57. package/lib/init/util/mvn.js +23 -9
  58. package/lib/init/util/projectReader.js +52 -168
  59. package/lib/init/util/template.js +12 -11
  60. package/lib/init/util/xml.js +6 -6
  61. package/lib/util/pruneAxiosErrors.js +22 -0
  62. package/lib/watch/open.js +6 -3
  63. package/npm-shrinkwrap.json +11 -17
  64. package/package.json +1 -2
package/bin/add.js CHANGED
@@ -91,8 +91,8 @@ module.exports = Object.assign(add, {
91
91
 
92
92
  `});
93
93
 
94
- async function add(args, options = {}) {
94
+ async function add(args) {
95
95
  const CDSGenerator = require('../lib/init');
96
96
  const generator = new CDSGenerator();
97
- await generator.addCmd(args[0], options);
97
+ await generator.addCmd(args[0]);
98
98
  }
package/bin/bind.js CHANGED
@@ -72,7 +72,6 @@ async function cds_bind(args, options) {
72
72
  if (process.env.CDS_ENV && process.argv[2] === 'bind') {
73
73
  throw new CliError('Option --profile is no longer supported. Use --for instead to specify the profile to use when storing connection settings.');
74
74
  }
75
- options.for = options.for ?? 'hybrid';
76
75
 
77
76
  if (!options.to) {
78
77
  throw new CliError(`Use option --to or -2 to specify the target instance, e.g. cds bind --to myInstance:myService`);
package/bin/cds.js CHANGED
@@ -152,13 +152,15 @@ function _dieForUnknownCommand(cmd) {
152
152
 
153
153
  let bestMatchText = '';
154
154
 
155
- const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js'];
155
+ const excludeList = ['cds.js', 'cds-ts.js', 'fix-redirects.js', 'util'];
156
156
  const { readdirSync } = require('fs');
157
157
  const { resolve } = require('path');
158
- const commands = readdirSync(__dirname, { withFileTypes: true })
158
+ let commands = readdirSync(__dirname, { withFileTypes: true })
159
159
  .concat(readdirSync(resolve(_local('@sap/cds'), '..', '..', 'bin'), { withFileTypes: true }))
160
- .filter((f) => f.isFile() && !excludeList.includes(f.name))
161
- .map((f) => f.name.split('.')[0]).sort();
160
+ .filter((f) => !excludeList.includes(f.name))
161
+ .map((f) => f.name.split('.')[0])
162
+ commands = [...new Set(commands)] // make unique
163
+ .sort();
162
164
 
163
165
  const bestMatches = fuzzySearch(cmd, commands, DEBUG);
164
166
 
@@ -8,6 +8,6 @@ exports.deploy = (model, serviceName, options) => {
8
8
  tunnelAddress: options['tunnel-address'],
9
9
  vcapFile: options['vcap-file'],
10
10
  hdiOptions: { autoUndeploy: options['auto-undeploy'] },
11
- storeCredentials: options['store-credentials']
11
+ storeCredentials: options['store-credentials'] // TODO: Remove with cds-dk 8
12
12
  });
13
13
  }
package/bin/init.js CHANGED
@@ -98,11 +98,11 @@ module.exports = Object.assign(init, {
98
98
 
99
99
  `})
100
100
 
101
- async function init (args, options = {}) {
101
+ async function init (args) {
102
102
  if (process.argv.some(a => legacyOptions.includes(a))) return _showWarningMessage()
103
103
  const CDSGenerator = require('../lib/init')
104
104
  const generator = new CDSGenerator()
105
- await generator.initCmd(args[0], options)
105
+ await generator.initCmd(args[0])
106
106
  }
107
107
 
108
108
 
@@ -4,6 +4,7 @@ const DEBUG = cds.debug('cli')
4
4
  const cfUtil = require('../util/cfUtil');
5
5
 
6
6
  const axios = require('axios');
7
+ require('../util/pruneAxiosErrors');
7
8
  const { URL } = require('url');
8
9
  const { bold, highlight } = require('../util/term');
9
10
  const { CliError } = require('../client/helper/errors');
package/lib/bind/index.js CHANGED
@@ -15,6 +15,9 @@ module.exports = { bind, parseProfile }
15
15
  * @param {object} options - command options as defined by build command.
16
16
  */
17
17
  async function bind(options) {
18
+
19
+ options.for = options.for || 'hybrid';
20
+
18
21
  let on;
19
22
  switch(options.on) {
20
23
  case 'k8s':
@@ -58,11 +58,7 @@ class UserCdsrcJsonStorageProvider {
58
58
 
59
59
  cdsRoot.requires = cdsRoot.requires || {};
60
60
  let section;
61
- if (options.for) {
62
- section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
63
- } else {
64
- section = cdsRoot.requires;
65
- }
61
+ section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
66
62
 
67
63
  for (const service of services) {
68
64
  const { name, kind, binding, credentials } = service;
@@ -72,11 +68,7 @@ class UserCdsrcJsonStorageProvider {
72
68
  }
73
69
 
74
70
  await this.writeJsonFile(jsonPath, jsonObject);
75
- if (options.for) {
76
- logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.for)}`);
77
- } else {
78
- logger.log(`Saving bindings to ${highlight(jsonPath)}`);
79
- }
71
+ logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.for)}`);
80
72
  }
81
73
 
82
74
  async readJsonSafely(filePath) {
@@ -19,20 +19,22 @@ exports.BUILD_TASKS = [this.BUILD_TASK_JAVA, this.BUILD_TASK_JAVA_CF, this.BUILD
19
19
  exports.ODATA_VERSION_V2 = "v2"
20
20
  exports.ODATA_VERSION_V4 = "v4"
21
21
 
22
- exports.CONTENT_LANGUAGE_BUNDLES = "contentLanguageBundles" // create i18n.json language bundles
23
- exports.CONTENT_DEFAULT_CSN = "contentDefaultCsn" // create default CSN format flavor: "inferred"
24
- exports.CONTENT_EDMX = "contentEdmx" // create EDMX for required languages
25
- exports.CONTENT_PACKAGE_JSON = "contentPackageJson" // create package.json file if not existing, or modify existing package.json
26
- exports.CONTENT_PACKAGELOCK_JSON = "contentPackageLockJson" // copy package-lock.json file if existing into deployment folder
27
- exports.CONTENT_HDBTABLEDATA = "contentHdbtabledata" // create .hdbtabledata files for .csv files if not existing
28
- exports.CONTENT_NPMRC = "contentNpmrc" // copy .npmrc file if existing into deployment folder
29
- exports.CONTENT_CDSRC_JSON = "contentCdsrcJson" // copy .cdsrc.json file if existing into deployment folder
30
- exports.CONTENT_NODE_MODULES = "contentNodeModules" // copy node_modules folder if existing into deployment folder
31
- exports.CONTENT_ENV = "contentEnv" // copy .env file if existing into deployment folder
32
- exports.CONTENT_DEFAULT_ENV_JSON = "contentDefaultEnvJson" // copy default-env.json file if existing into deployment folder
33
- exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location
34
- exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app
35
- exports.DEFAULT_BUILT_IN_MODELS = "defaultBuiltInModels" // add @sap/cds* built-in models to build tasks
22
+ exports.CONTENT_LANGUAGE_BUNDLES = "contentLanguageBundles" // create i18n.json language bundles - build task specific
23
+ exports.CONTENT_DEFAULT_CSN = "contentDefaultCsn" // create default CSN format flavor: "inferred" - build task specific
24
+ exports.CONTENT_EDMX = "contentEdmx" // create EDMX for required languages - build task specific
25
+ exports.CONTENT_PACKAGE_JSON = "contentPackageJson" // create package.json file if not existing, or modify existing package.json - ENABLED by default
26
+ exports.CONTENT_PACKAGELOCK_JSON = "contentPackageLockJson" // copy package-lock.json file if existing into deployment folder - ENABLED by default
27
+ exports.CONTENT_HDBTABLEDATA = "contentHdbtabledata" // create .hdbtabledata files for .csv files if not existing - ENABLED by default
28
+ exports.CONTENT_NPMRC = "contentNpmrc" // copy .npmrc file if existing into deployment folder - ENABLED by default
29
+ exports.CONTENT_CDSRC_JSON = "contentCdsrcJson" // copy .cdsrc.json file if existing into deployment folder - ENABLED by default
30
+ exports.CONTENT_NODE_MODULES = "contentNodeModules" // copy node_modules folder if existing into deployment folder - DISABLED by default
31
+ exports.CONTENT_ENV = "contentEnv" // copy .env file if existing into deployment folder - DISABLED by default
32
+ exports.CONTENT_DEFAULT_ENV_JSON = "contentDefaultEnvJson" // copy default-env.json file if existing into deployment folder - DISABLED by default
33
+
34
+ exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location - ENABLED by default
35
+ exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app - ENABLED by default
36
+ exports.DEFAULT_BUILT_IN_MODELS = "defaultBuiltInModels" // add @sap/cds* built-in models to build tasks - ENABLED by default
37
+ exports.MTX_SIDECAR_DB_VALIDATION = "mtxSidecarDbValidation" // validate consistency of app and sidecar db configuration - ENABLED by default
36
38
 
37
39
  exports.FOLDER_GEN = "gen"
38
40
  exports.FILE_EXT_CDS = ".cds"
@@ -83,12 +83,8 @@ class ResourcesTarBuilder {
83
83
  }
84
84
 
85
85
  async _getHanaTenantDbDest() {
86
- let hanaTask = this.handler.context.tasks.find(task => task.for === BUILD_TASK_HANA)
87
- if (!hanaTask) {
88
- this.handler.pushMessage(`Found SQLite database configuration. Ensure that productive cloud deployments requiring SAP HANA database are built with 'production' profile.`, WARNING)
89
- return null
90
- }
91
- return hanaTask.dest
86
+ const hanaTask = this.handler.context.tasks.find(task => task.for === BUILD_TASK_HANA)
87
+ return hanaTask?.dest
92
88
  }
93
89
  }
94
90
  module.exports = ResourcesTarBuilder
@@ -1,10 +1,10 @@
1
1
  const path = require('path')
2
2
  const cds = require('../../../cds')
3
3
  const fs = require('fs')
4
- const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
4
+ const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME, MTX_SIDECAR_DB_VALIDATION } = require('../../constants')
5
5
  const NodeCfModuleBuilder = require('../nodejs')
6
6
  const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
7
- const { WARNING, ERROR } = NodeCfModuleBuilder
7
+ const { ERROR } = NodeCfModuleBuilder
8
8
  const { relativePaths, BuildError, resolveRequiredSapModels, getI18nDefaultFolder } = require('../../util')
9
9
 
10
10
  const DEFAULT_MAIN_FOLDER = "_main"
@@ -61,13 +61,18 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
61
61
  if (!main) {
62
62
  throw new BuildError(`Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured and an up-to-date version of @sap/cds-mtxs is installed.`)
63
63
  }
64
- const profiles = cds.env.profiles || []
64
+ if (!this.hasBuildOption(MTX_SIDECAR_DB_VALIDATION, false)
65
+ && !cds.env.build.tasks?.length // disable if custom build tasks exist
66
+ && sidecarEnv.requires.db && cds.env.requires.db // ensure database is configured
67
+ && sidecarEnv.requires.db.kind !== cds.env.requires.db.kind) {
68
+ throw new BuildError(`Inconsistent database configuration found - application db '${cds.env.requires.db?.kind}', sidecar db '${sidecarEnv.requires.db?.kind}'`)
69
+ }
70
+ const profiles = cds.env.profiles ?? []
71
+ if (!profiles.includes("production") && !profiles.includes("prod")) {
72
+ // need to overwrite 'development' profile setting '../..' with '_main', although it is questionable why cds build is executed with development profile
73
+ main = DEFAULT_MAIN_FOLDER
74
+ }
65
75
 
66
- if (!profiles.includes("production") && !profiles.includes("prod")) {
67
- main = DEFAULT_MAIN_FOLDER
68
- // root should represent the production use case and not development
69
- this.pushMessage(`MTX sidecar build results are created in folder '${main}'. Enable 'production' or 'prod' profile if the folder is configured differently.`, WARNING)
70
- }
71
76
  const destRoot = this.task.dest
72
77
  const destMain = path.join(destRoot, main)
73
78
  const destMainSrv = path.join(destMain, cds.env.folders.srv)
@@ -1,6 +1,8 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs').promises;
3
3
 
4
+ require('../util/pruneAxiosErrors');
5
+
4
6
  const { CliError } = require('./helper/errors');
5
7
 
6
8
  const MTX_URL_PATH_PREFIX = 'mtx/v1/model/';
@@ -26,17 +28,12 @@ module.exports = class BaseCommand {
26
28
  case 403:
27
29
  throw new CliError(prefix + ': insufficient authorization. Ensure client has all necessary roles.', { error, command });
28
30
  case 404:
29
- throw new CliError(prefix + ': resource not found.', { command });
31
+ throw new CliError(prefix + ': resource not found.', { error, command });
30
32
  case 422:
31
33
  // Compilation or native-extension error.
32
34
  throw new CliError(prefix + '.', { error, command });
33
35
  default:
34
36
  // Unexpected error should be transparent to user.
35
- if (!require('..').debug('axios')) {
36
- error.config?.auth && (error.config.auth = '...'); // TODO use &&= with Node > 14
37
- delete error.request;
38
- delete error.response;
39
- }
40
37
  throw error;
41
38
  }
42
39
  }
@@ -16,11 +16,31 @@ function getHeaderProperties(name) {
16
16
  return { type: { const: name } };
17
17
  }
18
18
 
19
+ function nestedAnnotation(resObj, asyncapiProperty, keys, value) {
20
+ if (resObj[asyncapiProperty] === undefined) {
21
+ resObj[asyncapiProperty] = {};
22
+ }
23
+
24
+ let node = resObj[asyncapiProperty];
25
+
26
+ // traverse the annotation property and define the objects if they're not defined
27
+ for (let nestedIndex = 1; nestedIndex < keys.length - 1; nestedIndex++) {
28
+ const nestedElement = keys[nestedIndex];
29
+ if (node[nestedElement] === undefined) {
30
+ node[nestedElement] = {};
31
+ }
32
+ node = node[nestedElement];
33
+ }
34
+
35
+ // set value annotation property
36
+ node[keys[keys.length - 1]] = value;
37
+ }
38
+
19
39
  function readAnnotations(definition, presets) {
20
40
  let resObj = {};
21
41
 
22
42
  for (const [key, value] of Object.entries(definition)) {
23
- if (key.startsWith('@AsyncAPI')) {
43
+ if (key.startsWith('@AsyncAPI') && !key.startsWith('@AsyncAPI.Extensions')) {
24
44
  // get annotation value after @AsyncAPI
25
45
  const annotationProperties = key.split('@AsyncAPI.')[1];
26
46
  const keys = annotationProperties.split('.');
@@ -35,23 +55,22 @@ function readAnnotations(definition, presets) {
35
55
  if (keys.length === 1) {
36
56
  resObj[asyncapiProperty] = value;
37
57
  } else {
38
- if (resObj[asyncapiProperty] === undefined) {
39
- resObj[asyncapiProperty] = {};
40
- }
41
-
42
- let node = resObj[asyncapiProperty];
58
+ nestedAnnotation(resObj, asyncapiProperty, keys, value)
59
+ }
60
+ }
43
61
 
44
- // traverse the annotation property and define the objects if they're not defined
45
- for (let nestedIndex = 1; nestedIndex < keys.length-1; nestedIndex++) {
46
- const nestedElement = keys[nestedIndex];
47
- if (node[nestedElement] === undefined) {
48
- node[nestedElement] = {};
49
- }
50
- node = node[nestedElement];
51
- }
62
+ else if (key.startsWith('@AsyncAPI.Extensions')) {
63
+ const annotationProperties = key.split('@AsyncAPI.Extensions.')[1];
64
+ const keys = annotationProperties.split('.');
65
+ keys[0] = "x-" + keys[0];
52
66
 
53
- // set value annotation property
54
- node[keys[keys.length-1]] = value;
67
+ if (keys.length === 1) {
68
+ // if the key is already present, that means, Annotations have already been considered
69
+ if (resObj[keys[0]]) continue;
70
+ // else we populate
71
+ resObj[keys[0]] = value;
72
+ } else {
73
+ nestedAnnotation(resObj, keys[0], keys, value);
55
74
  }
56
75
  }
57
76
  }
@@ -142,9 +161,13 @@ function entriesToMessages(definitions, events, presets) {
142
161
  /**
143
162
  * Combining the definition objects from CSN into one schema
144
163
  */
145
- module.exports = function definitionsToMessages(definitions, events, presets) {
164
+ function definitionsToMessages(definitions, events, presets) {
146
165
  const messages = {};
147
-
148
166
  Object.assign(messages, entriesToMessages(definitions, events, presets));
149
167
  return messages;
150
168
  }
169
+
170
+ module.exports = {
171
+ definitionsToMessages,
172
+ nestedAnnotation
173
+ }
@@ -1,6 +1,6 @@
1
1
  const csnToJSONSchema = require('./components/schemas');
2
2
  const getMessageTraits = require('./components/messageTraits');
3
- const definitionsToMessages = require('./components/messages');
3
+ const { definitionsToMessages, nestedAnnotation } = require('./components/messages');
4
4
  const getChannels = require('./channels');
5
5
  const cds = require('../../cds');
6
6
  const messages = require("./message");
@@ -75,13 +75,14 @@ function _getAsyncApi(csn, events, serviceName, applicationNamespace, presets) {
75
75
  const infoObject = getInfoObject(csn.definitions[serviceName]);
76
76
  const stateObj = getStateInfo(csn.definitions[serviceName]);
77
77
  const shortTextObj = getShortText(csn.definitions[serviceName]);
78
-
78
+ const extensionAnnotationObj = getExtensions(csn.definitions[serviceName], shortTextObj, stateObj);
79
79
  return {
80
80
  asyncapi: '2.0.0',
81
81
  'x-sap-catalog-spec-version': '1.2',
82
82
  'x-sap-application-namespace': applicationNamespace,
83
83
  ...Object.keys(stateObj).length && { 'x-sap-stateInfo': stateObj },
84
84
  ...Object.keys(shortTextObj).length && { 'x-sap-shortText': shortTextObj },
85
+ ...Object.keys(extensionAnnotationObj).length && extensionAnnotationObj,
85
86
  info: infoObject,
86
87
  defaultContentType: 'application/json',
87
88
  channels: getChannels(csn.definitions, events),
@@ -89,6 +90,26 @@ function _getAsyncApi(csn, events, serviceName, applicationNamespace, presets) {
89
90
  };
90
91
  }
91
92
 
93
+ function getExtensions(serviceCsn, shortTextObj, stateObj) {
94
+ let extensionObj = {};
95
+ for (const [key, value] of Object.entries(serviceCsn)) {
96
+ if (key.startsWith('@AsyncAPI.Extensions')) {
97
+ const annotationProperties = key.split('@AsyncAPI.Extensions.')[1];
98
+ const keys = annotationProperties.split('.');
99
+ keys[0] = "x-" + keys[0];
100
+ if ((keys[0] === 'x-sap-shortText' && Object.keys(shortTextObj).length > 0) ||
101
+ (keys[0] === 'x-sap-stateInfo' && Object.keys(stateObj).length > 0)
102
+ ) continue;
103
+ if (keys.length === 1) {
104
+ extensionObj[keys[0]] = value;
105
+ } else {
106
+ nestedAnnotation(extensionObj, keys[0], keys, value);
107
+ }
108
+ }
109
+ }
110
+ return extensionObj;
111
+ }
112
+
92
113
  function getShortText(serviceCsn, presetValue = undefined) {
93
114
  let shortTextValue = presetValue || serviceCsn["@AsyncAPI.ShortText"];
94
115
  if (shortTextValue) {
@@ -371,6 +371,7 @@ class Expression {
371
371
  annotate(prefix = '', target, annotationExpressionResult) {
372
372
  let _target = target;
373
373
  for (const key of Object.keys(annotationExpressionResult)) {
374
+ if (prefix !== '') prefix = prefix + ".";
374
375
  _target[prefix + key] = annotationExpressionResult[key];
375
376
  }
376
377
  return target;
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
 
3
- const MAVEN_ARCHETYPE_VERSION = '2.0.1';
3
+ const MAVEN_ARCHETYPE_VERSION = '2.0.2';
4
4
 
5
5
  const constants = {
6
6
  IS_WIN: (os.platform() === 'win32'),
package/lib/init/index.js CHANGED
@@ -2,7 +2,7 @@ const path = require('path');
2
2
 
3
3
  const term = require('../util/term');
4
4
 
5
- const { OPTIONS, COMMAND_ADD, COMMAND_INIT, PROJECT_FILES, REGEX_PROJECT_NAME } = require('./constants')
5
+ const { OPTIONS, COMMAND_INIT, PROJECT_FILES, REGEX_PROJECT_NAME } = require('./constants')
6
6
  const { NODEJS, JAVA, HANA, MTA,PIPELINE, SAMPLES, CF_MANIFEST } = OPTIONS
7
7
 
8
8
  const cds = require('../cds'), { exists, readdir } = cds.utils
@@ -90,67 +90,56 @@ module.exports = class CDSGenerator {
90
90
  * @param {string} facets, comma separated list of templates, no blanks
91
91
  * @param {any} options, additional options
92
92
  */
93
- async addCmd(facets, options = {}) {
94
- options._cmd = COMMAND_ADD;
95
- options.add = facets;
96
-
93
+ async addCmd(facets) {
97
94
  if (!facets) throw 'You must specify a facet to add to the project.'
98
95
 
99
- this._initialize(null, options);
100
- this._greetings(options._cmd);
96
+ this._initialize(null);
97
+ this._greetings();
101
98
 
102
99
  if (!PROJECT_FILES.find(exists)) {
103
100
  throw `The current folder doesn't seem to contain a project. None of the following files found: ${PROJECT_FILES.join(', ')}.`
104
101
  }
105
102
 
106
- await this._process(options);
107
- await this.stepEnd(options);
103
+ await this._process();
104
+ await this.stepEnd();
108
105
  }
109
106
 
110
107
  /**
111
108
  * @param {string} projectName, the project name
112
- * @param {any} options, additional options
113
109
  */
114
- async initCmd(projectName, options = {}) {
115
- await this.stepInit(projectName, options);
110
+ async initCmd(projectName) {
111
+ await this.stepInit(projectName);
116
112
  await this.stepEnd();
117
113
  }
118
114
 
119
- async stepInit(projectName, options = {}) {
120
- options._cmd = COMMAND_INIT;
115
+ async stepInit(projectName) {
121
116
 
122
- this._initialize(projectName, options);
123
- this._greetings(options._cmd);
117
+ this._initialize(projectName);
118
+ this._greetings();
124
119
 
125
120
  await this._process();
126
121
  }
127
122
 
128
- _initialize(projectName, options = {}) {
129
- // deprecated, only for compatibility
130
- if (options.verbose) {
131
- LOG.warn(`Parameter 'verbose' is no longer supported. Use environment variable 'DEBUG=true' to obtain detailed output.`);
132
- }
133
-
123
+ _initialize(projectName) {
124
+ const { options } = cds.cli
134
125
  this.cwd = options.cwd || process.cwd();
135
126
  cds.root = path.resolve(this.cwd, projectName || '.');
136
127
  this.projectName = path.basename(cds.root);
137
128
 
138
- this.options = options;
139
129
  this._cleanupOptions();
140
130
  }
141
131
 
142
132
  _greetings() {
143
- switch (cds.cli.command) {
144
- case COMMAND_INIT: {
145
- const relativeProjectPath = path.relative(this.cwd, cds.root);
146
- const folderName = (relativeProjectPath ? `.${path.sep}${relativeProjectPath}` : 'current folder');
147
- LOG.info(`Creating new CAP project in ${term.bold(folderName)}\n`);
148
- break;
149
- }
133
+ if (cds.cli.command === COMMAND_INIT) {
134
+ const relativeProjectPath = path.relative(this.cwd, cds.root)
135
+ const folderName = relativeProjectPath ? `.${path.sep}${relativeProjectPath}` : 'current folder'
136
+ LOG.info(`Creating new CAP project in ${term.bold(folderName)}\n`)
150
137
  }
151
-
152
- if (this.options.force) {
153
- LOG.info(`Using '--force' ... existing files will be overwritten`);
138
+ if (cds.cli.options.for && typeof cds.cli.options.for !== 'string') {
139
+ throw 'The --for argument must not be empty.'
140
+ }
141
+ if (cds.cli.options.force) {
142
+ LOG.info(`Using '--force' ... existing files will be overwritten`)
154
143
  }
155
144
  }
156
145
 
@@ -161,9 +150,9 @@ module.exports = class CDSGenerator {
161
150
  await this._fillTemplateList();
162
151
 
163
152
  for (const template of this.templateList) {
164
- const msg = `Adding feature '${template.name}'...`;
165
- LOG.info(msg);
153
+ LOG.info(`Adding feature '${template.name}'...`);
166
154
  await template.run();
155
+ await template.runDependentMerging();
167
156
  await template.runRelatedMerging();
168
157
  }
169
158
 
@@ -176,7 +165,6 @@ module.exports = class CDSGenerator {
176
165
  }
177
166
 
178
167
  async _createTemplate(filename) {
179
-
180
168
  try {
181
169
  const TemplateClass = require(`./template/${filename}`);
182
170
  return new TemplateClass(this);
@@ -199,21 +187,22 @@ module.exports = class CDSGenerator {
199
187
  }
200
188
 
201
189
  async _fillTemplateList() {
202
- const templates = new Map();
203
- for (let option of this.options.add) {
190
+ const templates = new Map
191
+ const { command, options, argv } = cds.cli
192
+ const facets = command === COMMAND_INIT ? options.add : new Set(argv[0].split(','))
193
+
194
+ for (let facet of facets) {
204
195
  // Compat
205
- if (option === 'sample-tiny') option = 'tiny-sample'
206
- if (option === 'kibana-logging') option = 'kibana'
196
+ if (facet === 'sample-tiny') facet = 'tiny-sample'
197
+ if (facet === 'kibana-logging') facet = 'kibana'
207
198
 
208
- if (!templates.has(option)) {
209
- const template = await this._createTemplate(option);
199
+ if (!templates.has(facet)) {
200
+ const template = await this._createTemplate(facet)
210
201
  if (await template.canRun()) {
211
- DEBUG?.(`Adding feature '${template.name}'`);
212
- templates.set(option, template);
213
- const deps = await template.getDependencies();
214
- if (deps) {
215
- deps.forEach(this.options.add.add, this.options.add) // REVISIT: Ugly add.add
216
- }
202
+ DEBUG?.(`Adding feature '${template.name}'`)
203
+ templates.set(facet, template)
204
+ const dependencies = await template.getDependencies()
205
+ dependencies?.forEach(d => facets.add(d))
217
206
  }
218
207
  }
219
208
  }
@@ -236,56 +225,59 @@ module.exports = class CDSGenerator {
236
225
  _cleanupOptions() {
237
226
  let tokens = [];
238
227
 
239
- if (typeof this.options.add === 'string') {
240
- tokens = this.options.add.split(/[,\s+]/g);
228
+ const { options } = cds.cli
229
+ if (typeof options.add === 'string') {
230
+ tokens = options.add.split(/[,\s+]/g);
241
231
 
242
- } else if (Array.isArray(this.options.add)) {
243
- tokens = this.options.add
232
+ } else if (Array.isArray(options.add)) {
233
+ tokens = options.add
244
234
 
245
- } else if (this.options.add instanceof Set) {
246
- tokens = [...this.options.add];
235
+ } else if (options.add instanceof Set) {
236
+ tokens = [...options.add];
247
237
  }
248
238
 
249
239
  const trimmedTokens = tokens.map((token) => {
250
240
  token = token.replace(/\s+/g, '');
251
241
  const tokens = token.split(':'), [facet] = tokens
252
242
  if (tokens.length > 1) {
253
- if (this.options[facet]) {
254
- this.options[facet].add(token);
243
+ if (options[facet]) {
244
+ options[facet].add(token);
255
245
  } else {
256
- this.options[facet] = new Set([token]);
246
+ options[facet] = new Set([token]);
257
247
  }
258
248
  }
259
249
  return facet;
260
250
  }).filter(Boolean);
261
251
 
262
252
  // set is ordered ... always
263
- this.options.add = new Set(trimmedTokens);
253
+ options.add = new Set(trimmedTokens);
264
254
 
265
- if (this.options._cmd === COMMAND_INIT && !this.options.add.has(JAVA)) {
266
- this.options.add.add(NODEJS);
255
+ if (cds.cli.command === COMMAND_INIT && !options.add.has(JAVA)) {
256
+ options.add.add(NODEJS);
267
257
  }
268
258
 
269
259
  this._checkAdditionalOptions();
270
260
  }
271
261
 
272
262
  _checkAdditionalOptions() {
273
- for (const [key] of Object.entries(this.options)) {
274
- const keySplit = key.split(/:/);
263
+ const { options } = cds.cli
264
+ for (const [key] of Object.entries(options)) {
265
+ const keySplit = key.split(/:/)
275
266
  if (keySplit.length === 2) {
276
- if (!this.options.add.has(keySplit[0])) {
277
- LOG.warn(`ignoring option ${key} since ${keySplit[0]} is not part of specified feature list: ${Array.from(this.options.add).join(', ')}`);
267
+ if (!options.add.has(keySplit[0])) {
268
+ console.log(`ignoring option ${key} since ${keySplit[0]} is not part of specified feature list: ${Array.from(options.add).join(', ')}`)
278
269
  }
279
270
  }
280
271
  }
281
272
  }
282
273
 
283
274
  async _validateOptions() {
284
- if (this.options.force) {
275
+ const { options, command } = cds.cli
276
+ if (options.force) {
285
277
  return;
286
278
  }
287
279
 
288
- if (this.options._cmd === COMMAND_INIT) {
280
+ if (command === COMMAND_INIT) {
289
281
  await this._validateProjectName(this.projectName);
290
282
  await this._validateProjectFolder(this.cwd);
291
283
  }