@loopback/cli 4.0.0-alpha.9 → 4.0.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 (159) hide show
  1. package/.yo-rc.json +1697 -0
  2. package/{generators/project/templates/LICENSE → LICENSE} +2 -1
  3. package/README.md +44 -43
  4. package/bin/cli-main.js +61 -0
  5. package/generators/app/index.js +109 -15
  6. package/generators/app/templates/.dockerignore +5 -0
  7. package/generators/app/templates/Dockerfile +28 -0
  8. package/generators/app/templates/README.md.ejs +130 -0
  9. package/generators/app/templates/public/index.html.ejs +88 -0
  10. package/generators/app/templates/{test → src/__tests__}/README.md +0 -1
  11. package/generators/app/templates/src/__tests__/acceptance/home-page.acceptance.ts.ejs +31 -0
  12. package/generators/app/templates/src/__tests__/acceptance/ping.controller.acceptance.ts.ejs +21 -0
  13. package/generators/app/templates/src/__tests__/acceptance/test-helper.ts.ejs +32 -0
  14. package/generators/app/templates/src/application.ts.ejs +70 -0
  15. package/generators/app/templates/src/controllers/README.md +6 -0
  16. package/generators/app/templates/src/controllers/index.ts.ejs +1 -0
  17. package/generators/app/templates/src/controllers/ping.controller.ts.ejs +55 -0
  18. package/generators/app/templates/src/datasources/README.md +3 -0
  19. package/generators/app/templates/src/index.ts.ejs +39 -0
  20. package/generators/app/templates/src/migrate.ts.ejs +20 -0
  21. package/generators/app/templates/src/models/README.md +3 -0
  22. package/generators/app/templates/src/openapi-spec.ts.ejs +23 -0
  23. package/generators/app/templates/src/sequence.ts.ejs +3 -0
  24. package/generators/controller/index.js +240 -23
  25. package/generators/controller/templates/src/controllers/controller-rest-template.ts.ejs +150 -0
  26. package/generators/controller/templates/src/controllers/{controller-template.ts → controller-template.ts.ejs} +2 -2
  27. package/generators/copyright/fs.js +46 -0
  28. package/generators/copyright/git.js +78 -0
  29. package/generators/copyright/header.js +306 -0
  30. package/generators/copyright/index.js +230 -0
  31. package/generators/copyright/license.js +105 -0
  32. package/generators/datasource/index.js +341 -0
  33. package/generators/datasource/templates/datasource.ts.ejs +22 -0
  34. package/generators/discover/import-discovered-model.js +70 -0
  35. package/generators/discover/index.js +349 -0
  36. package/generators/example/downloader.js +16 -0
  37. package/generators/example/index.js +176 -0
  38. package/generators/extension/index.js +34 -5
  39. package/generators/extension/templates/README.md.ejs +32 -0
  40. package/generators/extension/templates/{test → src/__tests__}/acceptance/README.md +0 -0
  41. package/generators/extension/templates/{test → src/__tests__}/integration/README.md +0 -0
  42. package/generators/extension/templates/{test → src/__tests__}/unit/README.md +0 -0
  43. package/generators/extension/templates/src/component.ts.ejs +22 -0
  44. package/generators/extension/templates/src/controllers/README.md +3 -2
  45. package/generators/extension/templates/src/decorators/README.md +10 -4
  46. package/generators/extension/templates/src/index.ts.ejs +3 -0
  47. package/generators/extension/templates/src/keys.ts.ejs +11 -0
  48. package/generators/extension/templates/src/mixins/README.md +77 -21
  49. package/generators/extension/templates/src/providers/README.md +51 -25
  50. package/generators/extension/templates/src/repositories/README.md +1 -1
  51. package/generators/extension/templates/src/types.ts.ejs +15 -0
  52. package/generators/import-lb3-models/index.js +197 -0
  53. package/generators/import-lb3-models/lb3app-loader.js +31 -0
  54. package/generators/import-lb3-models/migrate-model.js +249 -0
  55. package/generators/import-lb3-models/model-names.js +32 -0
  56. package/generators/interceptor/index.js +178 -0
  57. package/generators/interceptor/templates/interceptor-template.ts.ejs +62 -0
  58. package/generators/model/index.js +536 -0
  59. package/generators/model/property-definition.js +85 -0
  60. package/generators/model/templates/model.ts.ejs +42 -0
  61. package/generators/observer/index.js +132 -0
  62. package/generators/observer/templates/observer-template.ts.ejs +40 -0
  63. package/generators/openapi/README.md +211 -0
  64. package/generators/openapi/index.js +535 -0
  65. package/generators/openapi/schema-helper.js +447 -0
  66. package/generators/openapi/spec-helper.js +484 -0
  67. package/generators/openapi/spec-loader.js +75 -0
  68. package/generators/openapi/templates/src/controllers/controller-template.ts.ejs +43 -0
  69. package/generators/openapi/templates/src/datasources/datasource.ts.ejs +42 -0
  70. package/generators/openapi/templates/src/models/model-template.ts.ejs +71 -0
  71. package/generators/openapi/templates/src/models/type-template.ts.ejs +13 -0
  72. package/generators/openapi/templates/src/services/service-proxy-template.ts.ejs +55 -0
  73. package/generators/openapi/utils.js +322 -0
  74. package/generators/project/templates/.eslintignore +4 -0
  75. package/generators/project/templates/.eslintrc.js.ejs +3 -0
  76. package/generators/project/templates/.mocharc.json +5 -0
  77. package/generators/project/templates/.prettierignore +0 -2
  78. package/generators/project/templates/.prettierrc +2 -1
  79. package/generators/project/templates/.vscode/launch.json +38 -0
  80. package/generators/project/templates/.vscode/settings.json +32 -0
  81. package/generators/project/templates/.vscode/tasks.json +29 -0
  82. package/generators/project/templates/DEVELOPING.md +36 -0
  83. package/generators/project/templates/_.gitignore +3 -5
  84. package/generators/project/templates/package.json.ejs +175 -0
  85. package/generators/project/templates/package.plain.json.ejs +176 -0
  86. package/generators/project/templates/tsconfig.json.ejs +39 -0
  87. package/generators/relation/base-relation.generator.js +220 -0
  88. package/generators/relation/belongs-to-relation.generator.js +196 -0
  89. package/generators/relation/has-many-relation.generator.js +200 -0
  90. package/generators/relation/has-many-through-relation.generator.js +331 -0
  91. package/generators/relation/has-one-relation.generator.js +200 -0
  92. package/generators/relation/index.js +795 -0
  93. package/generators/relation/references-many-relation.generator.js +142 -0
  94. package/generators/relation/templates/controller-relation-template-belongs-to.ts.ejs +38 -0
  95. package/generators/relation/templates/controller-relation-template-has-many-through.ts.ejs +110 -0
  96. package/generators/relation/templates/controller-relation-template-has-many.ts.ejs +110 -0
  97. package/generators/relation/templates/controller-relation-template-has-one.ts.ejs +110 -0
  98. package/generators/relation/utils.generator.js +260 -0
  99. package/generators/repository/index.js +576 -0
  100. package/generators/repository/templates/src/repositories/repository-crud-default-template.ts.ejs +21 -0
  101. package/generators/repository/templates/src/repositories/repository-kv-template.ts.ejs +19 -0
  102. package/generators/rest-crud/crud-rest-component.js +63 -0
  103. package/generators/rest-crud/index.js +401 -0
  104. package/generators/rest-crud/templates/src/model-endpoints/model.rest-config-template.ts.ejs +10 -0
  105. package/generators/service/index.js +351 -0
  106. package/generators/service/templates/local-service-class-template.ts.ejs +10 -0
  107. package/generators/service/templates/local-service-provider-template.ts.ejs +19 -0
  108. package/generators/service/templates/remote-service-proxy-template.ts.ejs +21 -0
  109. package/generators/update/index.js +55 -0
  110. package/intl/cs/messages.json +204 -0
  111. package/intl/de/messages.json +204 -0
  112. package/intl/en/messages.json +204 -0
  113. package/intl/es/messages.json +204 -0
  114. package/intl/fr/messages.json +204 -0
  115. package/intl/it/messages.json +204 -0
  116. package/intl/ja/messages.json +204 -0
  117. package/intl/ko/messages.json +204 -0
  118. package/intl/nl/messages.json +204 -0
  119. package/intl/pl/messages.json +204 -0
  120. package/intl/pt/messages.json +204 -0
  121. package/intl/ru/messages.json +204 -0
  122. package/intl/tr/messages.json +204 -0
  123. package/intl/zh-Hans/messages.json +204 -0
  124. package/intl/zh-Hant/messages.json +204 -0
  125. package/lib/artifact-generator.js +138 -39
  126. package/lib/ast-helper.js +214 -0
  127. package/lib/base-generator.js +509 -0
  128. package/lib/cli.js +233 -0
  129. package/lib/connectors.json +894 -0
  130. package/lib/debug.js +16 -0
  131. package/lib/globalize.js +12 -0
  132. package/lib/model-discoverer.js +118 -0
  133. package/lib/project-generator.js +154 -57
  134. package/lib/tab-completion.js +127 -0
  135. package/lib/update-index.js +44 -0
  136. package/lib/utils.js +689 -20
  137. package/lib/version-helper.js +299 -0
  138. package/package.json +183 -39
  139. package/CHANGELOG.md +0 -86
  140. package/bin/cli.js +0 -66
  141. package/generators/app/templates/index.js +0 -14
  142. package/generators/app/templates/src/application.ts +0 -27
  143. package/generators/app/templates/src/controllers/ping-controller.ts +0 -25
  144. package/generators/app/templates/src/index.ts +0 -25
  145. package/generators/app/templates/test/ping-controller.test.ts +0 -46
  146. package/generators/extension/templates/index.js +0 -8
  147. package/generators/extension/templates/src/component.ts +0 -14
  148. package/generators/extension/templates/src/index.ts +0 -6
  149. package/generators/project/templates/.npmrc +0 -1
  150. package/generators/project/templates/.yo-rc.json +0 -1
  151. package/generators/project/templates/README.md +0 -4
  152. package/generators/project/templates/index.d.ts +0 -6
  153. package/generators/project/templates/index.ts +0 -11
  154. package/generators/project/templates/package.json +0 -79
  155. package/generators/project/templates/package.plain.json +0 -82
  156. package/generators/project/templates/test/mocha.opts +0 -1
  157. package/generators/project/templates/tsconfig.json +0 -29
  158. package/generators/project/templates/tslint.build.json +0 -17
  159. package/generators/project/templates/tslint.json +0 -33
package/lib/debug.js ADDED
@@ -0,0 +1,16 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/cli
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ const debug = require('debug');
7
+
8
+ /**
9
+ * Returns a debug function whose prefix is automatically scoped to
10
+ * "loopback:cli:{scope}". If no scope is provided, it will be scoped
11
+ * to "loopback:cli".
12
+ * @param {String=} scope The scope to use for the debug statement.
13
+ */
14
+ module.exports = function (scope) {
15
+ return debug(`loopback:cli${scope ? `:${scope}` : ''}`);
16
+ };
@@ -0,0 +1,12 @@
1
+ // Copyright IBM Corp. 2020. All Rights Reserved.
2
+ // Node module: @loopback/cli
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const SG = require('strong-globalize');
10
+
11
+ SG.SetRootDir(path.join(__dirname, '..'), {autonomousMsgLoading: 'all'});
12
+ module.exports = SG();
@@ -0,0 +1,118 @@
1
+ // Copyright IBM Corp. 2019,2020. All Rights Reserved.
2
+ // Node module: @loopback/cli
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ const debug = require('./debug')('model-discoverer');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const {stringifyObject} = require('../lib/utils');
10
+
11
+ /**
12
+ * Given a datasource and discovery options,
13
+ * return a list of objects {table: 'foo', schema: 'bar}
14
+ */
15
+ async function discoverModelNames(ds, options) {
16
+ if (!ds.connected) {
17
+ await new Promise(resolve => {
18
+ ds.on('connected', resolve);
19
+ });
20
+ }
21
+ return ds.discoverModelDefinitions(options);
22
+ }
23
+
24
+ /**
25
+ * Returns the schema definition for a model
26
+ * @param ds - {Juggler.DataSource}
27
+ * @param modelName - {string}
28
+ * @param options - {object}
29
+ * @returns {Promise<Juggler.SchemaDefinition>}
30
+ */
31
+ async function discoverSingleModel(ds, modelName, options) {
32
+ const schema = await ds.discoverSchema(modelName, options);
33
+ if (schema) {
34
+ schema.settings = schema && schema.options;
35
+ }
36
+ return schema;
37
+ }
38
+
39
+ /**
40
+ * Loads a DataSource from a file
41
+ * If the path provided is a JSON, it instantiates a juggler.DataSource with the config as the only argument
42
+ * Else it requires it like a compiled loopback datasource
43
+ * @param modulePath
44
+ * @returns juggler.DataSource
45
+ */
46
+ function loadDataSource(modulePath) {
47
+ const ds = require(modulePath);
48
+ const key = Object.keys(ds)[0];
49
+ const val = new ds[key]();
50
+ return val;
51
+ }
52
+
53
+ /**
54
+ * Loads a compiled loopback datasource by name
55
+ * @param name - {string}
56
+ * @returns {*}
57
+ */
58
+ function loadDataSourceByName(name) {
59
+ debug(`Searching for specified dataSource ${name}`);
60
+ const dataSourceFiles = getAllDataSourceFiles();
61
+ debug(`Loaded ${dataSourceFiles.length} dataSource files`);
62
+
63
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
64
+ for (let i = 0; i < dataSourceFiles.length; i++) {
65
+ const f = dataSourceFiles[i];
66
+ const ds = loadDataSource(path.resolve(DEFAULT_DATASOURCE_DIRECTORY, f));
67
+ if (ds.name === name) {
68
+ debug(`Found dataSource ${name}`);
69
+ return ds;
70
+ } else {
71
+ debug(`Did not match dataSource ${name} !== ${ds.name}`);
72
+ }
73
+ }
74
+ throw new Error(
75
+ `Cannot find datasource "${name}" in ${DEFAULT_DATASOURCE_DIRECTORY}`,
76
+ );
77
+ }
78
+
79
+ const DEFAULT_DATASOURCE_DIRECTORY = './dist/datasources';
80
+
81
+ const MODEL_TEMPLATE_PATH = path.resolve(
82
+ __dirname,
83
+ '../generators/model/templates/model.ts.ejs',
84
+ );
85
+
86
+ const sanitizeProperty = function (o) {
87
+ Object.entries(o).forEach(([k, v]) => {
88
+ // Delete the null properties so the template doesn't spit out `key: ;`
89
+ if (v === null) {
90
+ delete o[k];
91
+ }
92
+
93
+ // If you are an object or array, stringify so you don't appear as [object [object]
94
+ if (typeof v === 'object' && v !== null) {
95
+ o[k] = stringifyObject(o[k], {
96
+ // don't break lines to avoid formatting problems in the model template
97
+ inlineCharacterLimit: Infinity,
98
+ });
99
+ }
100
+ });
101
+
102
+ o.tsType = o.type;
103
+ };
104
+
105
+ function getAllDataSourceFiles(dir = DEFAULT_DATASOURCE_DIRECTORY) {
106
+ return fs.readdirSync(dir).filter(s => s.endsWith('.datasource.js'));
107
+ }
108
+
109
+ module.exports = {
110
+ getAllDataSourceFiles,
111
+ sanitizeProperty,
112
+ discoverModelNames,
113
+ discoverSingleModel,
114
+ loadDataSource,
115
+ loadDataSourceByName,
116
+ DEFAULT_DATASOURCE_DIRECTORY,
117
+ MODEL_TEMPLATE_PATH,
118
+ };
@@ -1,78 +1,130 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
2
  // Node module: @loopback/cli
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  'use strict';
7
- const Generator = require('yeoman-generator');
8
- const path = require('path');
7
+
8
+ const BaseGenerator = require('./base-generator');
9
9
  const utils = require('./utils');
10
+ const chalk = require('chalk');
11
+ const cliVersion = require('../package.json').version;
12
+ const path = require('path');
13
+ const g = require('./globalize');
10
14
 
11
- module.exports = class extends Generator {
15
+ module.exports = class ProjectGenerator extends BaseGenerator {
12
16
  // Note: arguments and options should be defined in the constructor.
13
17
  constructor(args, opts) {
14
18
  super(args, opts);
15
- this._setupGenerator();
19
+ // The default list of build options available for a project
20
+ // This list gets shown to users to let them select the appropriate
21
+ // build settings for their project.
22
+ this.buildOptions = [
23
+ {
24
+ name: 'eslint',
25
+ description: g.f('add a linter with pre-configured lint rules'),
26
+ },
27
+ {
28
+ name: 'prettier',
29
+ description: g.f('install prettier to format code conforming to rules'),
30
+ },
31
+ {
32
+ name: 'mocha',
33
+ description: g.f('install mocha to run tests'),
34
+ },
35
+ {
36
+ name: 'loopbackBuild',
37
+ description: g.f('use @loopback/build helpers (e.g. lb-eslint)'),
38
+ },
39
+ {name: 'vscode', description: g.f('add VSCode config files')},
40
+ ];
16
41
  }
17
42
 
18
43
  _setupGenerator() {
19
44
  this.argument('name', {
20
45
  type: String,
21
46
  required: false,
22
- description: 'Project name for the ' + this.projectType,
47
+ description: g.f('Project name for the %s', this.projectType),
23
48
  });
24
49
 
25
50
  this.option('description', {
26
51
  type: String,
27
- description: 'Description for the ' + this.projectType,
52
+ description: g.f('Description for the %s', this.projectType),
28
53
  });
29
54
 
30
55
  this.option('outdir', {
31
56
  type: String,
32
- description: 'Project root directory for the ' + this.projectType,
57
+ description: g.f('Project root directory for the %s', this.projectType),
33
58
  });
34
59
 
35
- this.option('tslint', {
60
+ this.option('eslint', {
36
61
  type: Boolean,
37
- description: 'Enable tslint',
62
+ description: g.f('Enable eslint'),
38
63
  });
39
64
 
40
65
  this.option('prettier', {
41
66
  type: Boolean,
42
- description: 'Enable prettier',
67
+ description: g.f('Enable prettier'),
43
68
  });
44
69
 
45
70
  this.option('mocha', {
46
71
  type: Boolean,
47
- description: 'Enable mocha',
72
+ description: g.f('Enable mocha'),
48
73
  });
49
74
 
50
75
  this.option('loopbackBuild', {
51
76
  type: Boolean,
52
- description: 'Use @loopback/build',
77
+ description: g.f('Use @loopback/build'),
78
+ });
79
+
80
+ this.option('vscode', {
81
+ type: Boolean,
82
+ description: g.f('Use preconfigured VSCode settings'),
83
+ });
84
+
85
+ this.option('private', {
86
+ type: Boolean,
87
+ description: g.f('Mark the project private (excluded from npm publish)'),
53
88
  });
89
+
90
+ this._setupRenameTransformer();
91
+ super._setupGenerator();
54
92
  }
55
93
 
56
94
  /**
57
- * Override the usage text by replacing `yo loopback4:` with `lb4 `.
95
+ * Registers a Transform Stream with Yeoman. Removes `.ejs` extension
96
+ * from files that have it during project generation.
58
97
  */
59
- usage() {
60
- const text = super.usage();
61
- return text.replace(/^yo loopback4:/g, 'lb4 ');
98
+ _setupRenameTransformer() {
99
+ this.registerTransformStream(utils.renameEJS());
62
100
  }
63
101
 
64
- setOptions() {
65
- this.projectInfo = {projectType: this.projectType};
66
- [
102
+ async setOptions() {
103
+ await super.setOptions();
104
+ if (this.shouldExit()) return false;
105
+ if (this.options.name) {
106
+ const msg = utils.validate(this.options.name);
107
+ if (typeof msg === 'string') {
108
+ this.exit(msg);
109
+ return false;
110
+ }
111
+ }
112
+
113
+ this.projectInfo = {
114
+ projectType: this.projectType,
115
+ dependencies: utils.getDependencies(),
116
+ };
117
+ this.projectOptions = [
67
118
  'name',
68
119
  'description',
69
120
  'outdir',
70
- 'componentName',
71
- 'tslint',
72
- 'prettier',
73
- 'mocha',
74
- 'loopbackBuild',
75
- ].forEach(n => {
121
+ 'private',
122
+ 'apiconnect',
123
+ ].concat(this.buildOptions);
124
+ this.projectOptions.forEach(n => {
125
+ if (typeof n === 'object') {
126
+ n = n.name;
127
+ }
76
128
  if (this.options[n]) {
77
129
  this.projectInfo[n] = this.options[n];
78
130
  }
@@ -80,18 +132,21 @@ module.exports = class extends Generator {
80
132
  }
81
133
 
82
134
  promptProjectName() {
135
+ if (this.shouldExit()) return false;
83
136
  const prompts = [
84
137
  {
85
138
  type: 'input',
86
139
  name: 'name',
87
- message: 'Project name:',
140
+ message: g.f('Project name:'),
88
141
  when: this.projectInfo.name == null,
89
- default: this.options.name || this.appname,
142
+ default:
143
+ this.options.name || utils.toFileName(path.basename(process.cwd())),
144
+ validate: utils.validate,
90
145
  },
91
146
  {
92
147
  type: 'input',
93
148
  name: 'description',
94
- message: 'Project description:',
149
+ message: g.f('Project description:'),
95
150
  when: this.projectInfo.description == null,
96
151
  default: this.options.name || this.appname,
97
152
  },
@@ -103,16 +158,18 @@ module.exports = class extends Generator {
103
158
  }
104
159
 
105
160
  promptProjectDir() {
161
+ if (this.shouldExit()) return false;
106
162
  const prompts = [
107
163
  {
108
164
  type: 'input',
109
165
  name: 'outdir',
110
- message: 'Project root directory:',
166
+ message: g.f('Project root directory:'),
111
167
  when:
112
168
  this.projectInfo.outdir == null ||
113
- utils.validateyNotExisting(this.projectInfo.outdir) !== true,
114
- validate: utils.validateyNotExisting,
115
- default: this.projectInfo.name,
169
+ // prompts if option was set to a directory that already exists
170
+ utils.validateNotExisting(this.projectInfo.outdir) !== true,
171
+ validate: utils.validateNotExisting,
172
+ default: this.projectInfo.name && this.projectInfo.name.toLowerCase(),
116
173
  },
117
174
  ];
118
175
 
@@ -122,50 +179,86 @@ module.exports = class extends Generator {
122
179
  }
123
180
 
124
181
  promptOptions() {
182
+ if (this.shouldExit()) return false;
125
183
  const choices = [];
126
- ['tslint', 'prettier', 'mocha', 'loopbackBuild'].forEach(f => {
127
- if (!this.options[f]) {
184
+ this.buildOptions.forEach(f => {
185
+ if (this.options[f.name] == null) {
186
+ const name = g.f('Enable %s', f.name);
128
187
  choices.push({
129
- name: 'Enable ' + f,
130
- key: f,
188
+ name: `${name}: ${chalk.gray(f.description)}`,
189
+ key: f.name,
190
+ short: `Enable ${f.name}`,
131
191
  checked: true,
132
192
  });
193
+ } else {
194
+ this.projectInfo[f.name] = this.options[f.name];
133
195
  }
134
196
  });
135
197
  const prompts = [
136
198
  {
137
199
  name: 'settings',
138
- message: 'Select project build settings: ',
200
+ message: g.f('Select features to enable in the project'),
139
201
  type: 'checkbox',
140
202
  choices: choices,
203
+ default: choices.map(c => c.short),
141
204
  // Skip if all features are enabled by cli options
142
205
  when: choices.length > 0,
143
206
  },
144
207
  ];
145
- return this.prompt(prompts).then(props => {
146
- const settings = props.settings || choices.map(c => c.name);
208
+ return this.prompt(prompts).then(({settings}) => {
147
209
  const features = choices.map(c => {
148
210
  return {
149
211
  key: c.key,
150
- value: settings.indexOf(c.name) !== -1,
212
+ value:
213
+ settings.indexOf(c.name) !== -1 || settings.indexOf(c.short) !== -1,
151
214
  };
152
215
  });
153
216
  features.forEach(f => (this.projectInfo[f.key] = f.value));
154
217
  });
155
218
  }
156
219
 
220
+ promptYarnInstall() {
221
+ if (this.shouldExit()) return false;
222
+ const prompts = [
223
+ {
224
+ type: 'confirm',
225
+ name: 'yarn',
226
+ message: g.f('Yarn is available. Do you prefer to use it by default?'),
227
+ when: !this.options.packageManager && utils.isYarnAvailable(),
228
+ default: false,
229
+ },
230
+ ];
231
+
232
+ return this.prompt(prompts).then(props => {
233
+ if (props.yarn) {
234
+ this.options.packageManager = 'yarn';
235
+ }
236
+ });
237
+ }
238
+
157
239
  scaffold() {
240
+ if (this.shouldExit()) return false;
241
+
158
242
  this.destinationRoot(this.projectInfo.outdir);
159
243
 
244
+ // Store information for cli operation in .yo.rc.json
245
+ this.config.set('packageManager', this.options.packageManager || 'npm');
246
+ this.config.set('version', cliVersion);
247
+
160
248
  // First copy common files from ../../project/templates
161
- this.fs.copyTpl(
249
+ this.copyTemplatedFiles(
162
250
  this.templatePath('../../project/templates/**/*'),
163
251
  this.destinationPath(''),
164
252
  {
165
253
  project: this.projectInfo,
254
+ packageManager: this.config.get('packageManager'),
255
+ author: this.user.git.email()
256
+ ? {
257
+ name: this.user.git.name(),
258
+ email: this.user.git.email(),
259
+ }
260
+ : null,
166
261
  },
167
- {},
168
- {globOptions: {dot: true}}
169
262
  );
170
263
 
171
264
  // Rename `_.gitignore` back to `.gitignore`.
@@ -173,22 +266,22 @@ module.exports = class extends Generator {
173
266
  // if it's there in the templates.
174
267
  this.fs.move(
175
268
  this.destinationPath('_.gitignore'),
176
- this.destinationPath('.gitignore')
269
+ this.destinationPath('.gitignore'),
177
270
  );
178
271
 
179
272
  // Copy project type specific files from ./templates
180
- this.fs.copyTpl(
273
+ this.copyTemplatedFiles(
181
274
  this.templatePath('**/*'),
182
275
  this.destinationPath(''),
183
276
  {
184
277
  project: this.projectInfo,
278
+ packageManager: this.config.get('packageManager'),
185
279
  },
186
- {},
187
- {globOptions: {dot: true}}
188
280
  );
189
281
 
190
- if (!this.projectInfo.tslint) {
191
- this.fs.delete(this.destinationPath('tslint.*json'));
282
+ if (!this.projectInfo.eslint) {
283
+ this.fs.delete(this.destinationPath('.eslintrc.*.ejs'));
284
+ this.fs.delete(this.destinationPath('.eslintignore'));
192
285
  }
193
286
 
194
287
  if (!this.projectInfo.prettier) {
@@ -197,19 +290,23 @@ module.exports = class extends Generator {
197
290
 
198
291
  if (!this.projectInfo.loopbackBuild) {
199
292
  this.fs.move(
200
- this.destinationPath('package.plain.json'),
201
- this.destinationPath('package.json')
293
+ this.destinationPath('package.plain.json.ejs'),
294
+ this.destinationPath('package.json.ejs'),
202
295
  );
203
296
  } else {
204
- this.fs.delete(this.destinationPath('package.plain.json'));
297
+ this.fs.delete(this.destinationPath('package.plain.json.ejs'));
205
298
  }
206
299
 
207
300
  if (!this.projectInfo.mocha) {
208
- this.fs.delete(this.destinationPath('test/mocha.opts'));
301
+ this.fs.delete(this.destinationPath('.mocharc.json'));
209
302
  }
210
- }
211
303
 
212
- install() {
213
- this.npmInstall(null, {}, {cwd: this.destinationRoot()});
304
+ if (!this.projectInfo.vscode) {
305
+ this.fs.delete(this.destinationPath('.vscode'));
306
+ }
307
+
308
+ if (this.options.packageManager === 'yarn') {
309
+ this.fs.delete(this.destinationPath('.npmrc'));
310
+ }
214
311
  }
215
312
  };
@@ -0,0 +1,127 @@
1
+ // Copyright IBM Corp. 2020. All Rights Reserved.
2
+ // Node module: @loopback/cli
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ 'use strict';
7
+
8
+ const debug = require('./debug')();
9
+ const tabtab = require('tabtab');
10
+
11
+ const tabCompletionCommands = [
12
+ 'install-completion',
13
+ 'uninstall-completion',
14
+ 'completion',
15
+ ];
16
+
17
+ /**
18
+ * Get all available commands or options depends on entered input
19
+ * @param optionsAndArgs - object of available options and arguments
20
+ * - name - generator name
21
+ * - options - list of option objects
22
+ * - arguments - list of argument objects
23
+ * @param partial - completion env.partial
24
+ */
25
+ function generateCompletions(optionsAndArgs, partial) {
26
+ // Remove the `base`
27
+ delete optionsAndArgs.base;
28
+ const commands = Object.keys(optionsAndArgs);
29
+ const enteredCommand = commands.find(command => partial.includes(command));
30
+
31
+ if (!enteredCommand) {
32
+ return commands;
33
+ }
34
+
35
+ const options = optionsAndArgs[enteredCommand].options;
36
+ const optionNames = Object.keys(options).map(opt => `--${opt}`);
37
+
38
+ const enteredOptions = optionNames.filter(opt => partial.includes(opt));
39
+
40
+ if (!enteredOptions.length) {
41
+ return optionNames;
42
+ }
43
+
44
+ return optionNames.filter(opt => !enteredOptions.includes(opt));
45
+ }
46
+
47
+ /**
48
+ * Process bash-completion and take care about logging available commands
49
+ * and options
50
+ * @param optionsAndArgs - object of available options and arguments
51
+ * - name - generator name
52
+ * - options - list of option objects
53
+ * - arguments - list of argument objects
54
+ * @param env - completion environment object
55
+ * - complete A Boolean indicating whether we act in "plumbing mode" or not
56
+ * - words The Number of words in the completed line
57
+ * - point A Number indicating cursor position
58
+ * - line The String input line
59
+ * - partial The String part of line preceding cursor position
60
+ * - last The last String word of the line
61
+ * - lastPartial The last word String of partial
62
+ * - prev The String word preceding last
63
+ */
64
+ function completion(optionsAndArgs, env) {
65
+ if (!env.complete) {
66
+ return;
67
+ }
68
+
69
+ return tabtab.log(generateCompletions(optionsAndArgs, env.partial));
70
+ }
71
+
72
+ /**
73
+ * Register tabtab completion script
74
+ * Will be written in bashrc or zshrc or fish configs
75
+ */
76
+ async function installTabCompletionScript() {
77
+ try {
78
+ await tabtab.install({
79
+ name: 'lb4',
80
+ completer: 'lb4',
81
+ });
82
+ } catch (error) /* istanbul ignore next */ {
83
+ debug('tab completion install script');
84
+ throw error;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Remove tabtab completion script
90
+ * Will be removed from bashrc or zshrc or fish configs
91
+ */
92
+ async function uninstallTabCompletionScript() {
93
+ try {
94
+ await tabtab.uninstall({
95
+ name: 'lb4',
96
+ });
97
+ } catch (error) /* istanbul ignore next */ {
98
+ debug('tab completion uninstall script');
99
+ throw error;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Run corresponding tab-completion command
105
+ * @param optionsAndArgs - object of available options and arguments
106
+ * - name - generator name
107
+ * - options - list of option objects
108
+ * - arguments - list of argument objects
109
+ * @param originalCommand - command name
110
+ * @param log - Log function
111
+ */
112
+ function runTabCompletionCommand(optionsAndArgs, originalCommand, log) {
113
+ if (originalCommand === 'install-completion') {
114
+ return installTabCompletionScript().catch(log);
115
+ }
116
+
117
+ if (originalCommand === 'uninstall-completion') {
118
+ return uninstallTabCompletionScript().catch(log);
119
+ }
120
+
121
+ return completion(optionsAndArgs, tabtab.parseEnv(process.env));
122
+ }
123
+
124
+ exports.tabCompletionCommands = tabCompletionCommands;
125
+ exports.runTabCompletionCommand = runTabCompletionCommand;
126
+
127
+ exports.generateCompletions = generateCompletions;
@@ -0,0 +1,44 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/cli
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ const debug = require('./debug')('update-index');
7
+
8
+ const path = require('path');
9
+ const fse = require('fs-extra');
10
+ const defaultFs = {
11
+ read: fse.readFile,
12
+ write: fse.writeFile,
13
+ append: fse.appendFile,
14
+ exists: fse.exists,
15
+ };
16
+
17
+ /**
18
+ *
19
+ * @param {String} dir The directory in which index.ts is to be updated/created
20
+ * @param {*} file The new file to be exported from index.ts
21
+ */
22
+ module.exports = async function (dir, file, fsApis) {
23
+ const {read, write, append, exists} = {...defaultFs, ...fsApis};
24
+ debug(`Updating index with ${path.join(dir, file)}`);
25
+ const indexFile = path.join(dir, 'index.ts');
26
+ if (!file.endsWith('.ts')) {
27
+ throw new Error(`${file} must be a TypeScript (.ts) file`);
28
+ }
29
+
30
+ let index = '';
31
+ const indexExists = await exists(indexFile);
32
+ if (indexExists) {
33
+ index = await read(indexFile);
34
+ }
35
+ const content = `export * from './${file.slice(0, -3)}';\n`;
36
+ if (!index.includes(content)) {
37
+ if (indexExists) {
38
+ await append(indexFile, content);
39
+ } else {
40
+ await fse.ensureDir(dir);
41
+ await write(indexFile, content);
42
+ }
43
+ }
44
+ };