@loopback/cli 4.0.0-alpha.8 → 4.1.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 (160) hide show
  1. package/.yo-rc.json +1719 -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 +411 -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 +49 -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 +423 -0
  104. package/generators/rest-crud/templates/src/model-endpoints/model.rest-config-template.ts.ejs +11 -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 +137 -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 -75
  140. package/bin/cli.js +0 -66
  141. package/generators/.DS_Store +0 -0
  142. package/generators/app/templates/index.js +0 -14
  143. package/generators/app/templates/src/application.ts +0 -27
  144. package/generators/app/templates/src/controllers/ping-controller.ts +0 -25
  145. package/generators/app/templates/src/index.ts +0 -25
  146. package/generators/app/templates/test/ping-controller.test.ts +0 -46
  147. package/generators/extension/templates/index.js +0 -8
  148. package/generators/extension/templates/src/component.ts +0 -14
  149. package/generators/extension/templates/src/index.ts +0 -6
  150. package/generators/project/templates/.npmrc +0 -1
  151. package/generators/project/templates/.yo-rc.json +0 -1
  152. package/generators/project/templates/README.md +0 -4
  153. package/generators/project/templates/index.d.ts +0 -6
  154. package/generators/project/templates/index.ts +0 -11
  155. package/generators/project/templates/package.json +0 -79
  156. package/generators/project/templates/package.plain.json +0 -82
  157. package/generators/project/templates/test/mocha.opts +0 -1
  158. package/generators/project/templates/tsconfig.json +0 -29
  159. package/generators/project/templates/tslint.build.json +0 -17
  160. package/generators/project/templates/tslint.json +0 -33
@@ -0,0 +1,509 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 2017,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 Generator = require('yeoman-generator');
9
+ const chalk = require('chalk');
10
+ const {StatusConflicter, readTextFromStdin} = require('./utils');
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const debug = require('./debug')('base-generator');
14
+ const updateIndex = require('./update-index');
15
+ const {checkLoopBackProject} = require('./version-helper');
16
+ const g = require('./globalize');
17
+
18
+ const supportedPackageManagers = ['npm', 'yarn'];
19
+
20
+ debug('Is stdin interactive (isTTY)?', process.stdin.isTTY);
21
+
22
+ const DEFAULT_COPY_OPTIONS = {
23
+ // See https://github.com/SBoudrias/mem-fs-editor/pull/147
24
+ // Don't remove .ejs from the file name to keep backward-compatibility
25
+ processDestinationPath: destPath => destPath,
26
+ // See https://github.com/mrmlnc/fast-glob#options-1
27
+ globOptions: {
28
+ // Allow patterns to match filenames starting with a period (files &
29
+ // directories), even if the pattern does not explicitly have a period
30
+ // in that spot.
31
+ dot: true,
32
+ // Disable expansion of brace patterns ({a,b}, {1..3}).
33
+ nobrace: true,
34
+ // Disable extglob support (patterns like +(a|b)), so that extglobs
35
+ // are regarded as literal characters. This flag allows us to support
36
+ // Windows paths such as
37
+ // `D:\Users\BKU\oliverkarst\AppData(Roaming)\npm\node_modules\@loopback\cli`
38
+ noext: true,
39
+ },
40
+ };
41
+
42
+ /**
43
+ * Base Generator for LoopBack 4
44
+ */
45
+ module.exports = class BaseGenerator extends Generator {
46
+ // Note: arguments and options should be defined in the constructor.
47
+ constructor(args, opts) {
48
+ super(args, opts);
49
+ debug('Initializing generator', this.constructor.name);
50
+ this.conflicter = new StatusConflicter(
51
+ this.env.adapter,
52
+ this.options.force,
53
+ );
54
+ this._setupGenerator();
55
+ }
56
+
57
+ /**
58
+ * Subclasses can extend _setupGenerator() to set up the generator
59
+ */
60
+ _setupGenerator() {
61
+ debug('Setting up generator', this.constructor.name);
62
+ // For the options coming from Yeoman,
63
+ // overwrite the whole option object so that they can get translated.
64
+ this._options['help'] = {
65
+ name: 'help',
66
+ type: Boolean,
67
+ alias: 'h',
68
+ description: g.f("Print the generator's options and usage"),
69
+ };
70
+ this._options['skip-cache'] = {
71
+ name: 'skip-cache',
72
+ type: Boolean,
73
+ description: g.f('Do not remember prompt answers'),
74
+ default: false,
75
+ };
76
+ this._options['skip-install'] = {
77
+ name: 'skip-install',
78
+ type: Boolean,
79
+ description: g.f('Do not automatically install dependencies'),
80
+ default: false,
81
+ };
82
+ this._options['force-install'] = {
83
+ name: 'force-install',
84
+ type: Boolean,
85
+ description: g.f('Fail on install dependencies error'),
86
+ default: false,
87
+ };
88
+ this._options['ask-answered'] = {
89
+ type: Boolean,
90
+ description: g.f('Show prompts for already configured options'),
91
+ default: false,
92
+ name: 'ask-answered',
93
+ hide: false,
94
+ };
95
+ debug(
96
+ 'Try overwrite yeoman messages globally',
97
+ this._options['help'].description,
98
+ );
99
+
100
+ this.option('config', {
101
+ type: String,
102
+ alias: 'c',
103
+ description: g.f('JSON file name or value to configure options'),
104
+ });
105
+
106
+ this.option('yes', {
107
+ type: Boolean,
108
+ alias: 'y',
109
+ description: g.f(
110
+ 'Skip all confirmation prompts with default or provided value',
111
+ ),
112
+ });
113
+
114
+ this.option('format', {
115
+ type: Boolean,
116
+ description: g.f('Format generated code using npm run lint:fix'),
117
+ });
118
+
119
+ this.option('packageManager', {
120
+ type: String,
121
+ description: g.f('Change the default package manager'),
122
+ alias: 'pm',
123
+ });
124
+
125
+ this.artifactInfo = this.artifactInfo || {
126
+ rootDir: 'src',
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Read a json document from stdin
132
+ */
133
+ async _readJSONFromStdin() {
134
+ debug('Reading JSON from stdin');
135
+ if (process.stdin.isTTY) {
136
+ this.log(
137
+ chalk.green(
138
+ 'Please type in a json object line by line ' +
139
+ '(Press <ctrl>-D or type EOF to end):',
140
+ ),
141
+ );
142
+ }
143
+
144
+ let jsonStr;
145
+ try {
146
+ jsonStr = await readTextFromStdin();
147
+ debug(
148
+ 'Result:',
149
+ jsonStr === undefined ? '(undefined)' : JSON.stringify(jsonStr),
150
+ );
151
+ return JSON.parse(jsonStr);
152
+ } catch (e) {
153
+ if (!process.stdin.isTTY) {
154
+ debug(e, jsonStr);
155
+ }
156
+ throw e;
157
+ }
158
+ }
159
+
160
+ async setOptions() {
161
+ let opts = {};
162
+ const jsonFileOrValue = this.options.config;
163
+ debug(
164
+ 'Loading generator options from CLI args and/or stdin.',
165
+ ...(this.option.config === undefined
166
+ ? ['(No config was provided.)']
167
+ : ['Config:', this.options.config]),
168
+ );
169
+ try {
170
+ if (
171
+ jsonFileOrValue === 'stdin' ||
172
+ (!jsonFileOrValue && !process.stdin.isTTY)
173
+ ) {
174
+ debug(' enabling --yes and reading config from stdin');
175
+ this.options['yes'] = true;
176
+ opts = await this._readJSONFromStdin();
177
+ } else if (typeof jsonFileOrValue === 'string') {
178
+ const jsonFile = path.resolve(process.cwd(), jsonFileOrValue);
179
+ if (fs.existsSync(jsonFile)) {
180
+ debug(' reading config from file', jsonFile);
181
+ opts = this.fs.readJSON(jsonFile);
182
+ } else {
183
+ debug(' parsing config from string', jsonFileOrValue);
184
+ // Try parse the config as stringified json
185
+ opts = JSON.parse(jsonFileOrValue);
186
+ }
187
+ }
188
+ } catch (e) {
189
+ this.exit(e);
190
+ return;
191
+ }
192
+ if (typeof opts !== 'object') {
193
+ this.exit('Invalid config file or value: ' + jsonFileOrValue);
194
+ return;
195
+ }
196
+ for (const o in opts) {
197
+ if (this.options[o] == null) {
198
+ this.options[o] = opts[o];
199
+ }
200
+ }
201
+
202
+ const packageManager =
203
+ this.options.packageManager || this.config.get('packageManager') || 'npm';
204
+ if (!supportedPackageManagers.includes(packageManager)) {
205
+ const supported = supportedPackageManagers.join(' or ');
206
+ this.exit(
207
+ `Package manager '${packageManager}' is not supported. Use ${supported}.`,
208
+ );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Check if a question can be skipped in `express` mode
214
+ * @param {object} question A yeoman prompt
215
+ */
216
+ _isQuestionOptional(question) {
217
+ return (
218
+ question.default != null || // Having a default value
219
+ this.options[question.name] != null || // Configured in options
220
+ question.type === 'list' || // A list
221
+ question.type === 'rawList' || // A raw list
222
+ question.type === 'checkbox' || // A checkbox
223
+ question.type === 'confirm'
224
+ ); // A confirmation
225
+ }
226
+
227
+ /**
228
+ * Get the default answer for a question
229
+ * @param {*} question
230
+ */
231
+ async _getDefaultAnswer(question, answers) {
232
+ // First check existing answers
233
+ let defaultVal = answers[question.name];
234
+ if (defaultVal != null) return defaultVal;
235
+
236
+ // Now check the `default` of the prompt
237
+ let def = question.default;
238
+ if (typeof question.default === 'function') {
239
+ def = await question.default(answers);
240
+ }
241
+ defaultVal = def;
242
+
243
+ if (question.type === 'confirm') {
244
+ return defaultVal != null ? defaultVal : true;
245
+ }
246
+ if (question.type === 'list' || question.type === 'rawList') {
247
+ // Default to 1st item
248
+ if (def == null) def = 0;
249
+ if (typeof def === 'number') {
250
+ // The `default` is an index
251
+ const choice = question.choices[def];
252
+ if (choice) {
253
+ defaultVal = choice.value || choice.name;
254
+ }
255
+ } else {
256
+ // The default is a value
257
+ if (question.choices.map(c => c.value || c.name).includes(def)) {
258
+ defaultVal = def;
259
+ }
260
+ }
261
+ } else if (question.type === 'checkbox') {
262
+ if (def == null) {
263
+ defaultVal = question.choices
264
+ .filter(c => c.checked && !c.disabled)
265
+ .map(c => c.value || c.name);
266
+ } else {
267
+ defaultVal = def
268
+ .map(d => {
269
+ if (typeof d === 'number') {
270
+ const choice = question.choices[d];
271
+ if (choice && !choice.disabled) {
272
+ return choice.value || choice.name;
273
+ }
274
+ } else {
275
+ if (
276
+ question.choices.find(
277
+ c => !c.disabled && d === (c.value || c.name),
278
+ )
279
+ ) {
280
+ return d;
281
+ }
282
+ }
283
+ return undefined;
284
+ })
285
+ .filter(v => v != null);
286
+ }
287
+ }
288
+ return defaultVal;
289
+ }
290
+
291
+ /**
292
+ * Override the base prompt to skip prompts with default answers
293
+ * @param questions - One or more questions
294
+ */
295
+ async prompt(questions) {
296
+ // Normalize the questions to be an array
297
+ if (!Array.isArray(questions)) {
298
+ questions = [questions];
299
+ }
300
+ if (!this.options['yes']) {
301
+ if (!process.stdin.isTTY) {
302
+ const msg =
303
+ 'The stdin is not a terminal. No prompt is allowed. ' +
304
+ 'Use --config to provide answers to required prompts and ' +
305
+ '--yes to skip optional prompts with default answers';
306
+ this.log(chalk.red(msg));
307
+ this.exit(new Error(msg));
308
+ return;
309
+ }
310
+ // Non-express mode, continue to prompt
311
+ debug('Questions', questions);
312
+ const answers = await super.prompt(questions);
313
+ debug('Answers', answers);
314
+ return answers;
315
+ }
316
+
317
+ const answers = Object.assign({}, this.options);
318
+
319
+ for (const q of questions) {
320
+ let when = q.when;
321
+ if (typeof when === 'function') {
322
+ when = await q.when(answers);
323
+ }
324
+ if (when === false) continue;
325
+ if (this._isQuestionOptional(q)) {
326
+ const answer = await this._getDefaultAnswer(q, answers);
327
+ debug('%s: %j', q.name, answer);
328
+ answers[q.name] = answer;
329
+ } else {
330
+ if (!process.stdin.isTTY) {
331
+ const msg =
332
+ 'The stdin is not a terminal. No prompt is allowed. ' +
333
+ `(While resolving a required prompt ${JSON.stringify(q.name)}.)`;
334
+ this.log(chalk.red(msg));
335
+ this.exit(new Error(msg));
336
+ return;
337
+ }
338
+ // Only prompt for non-skipped questions
339
+ const props = await super.prompt([q]);
340
+ Object.assign(answers, props);
341
+ }
342
+ }
343
+ return answers;
344
+ }
345
+
346
+ /**
347
+ * Override the usage text by replacing `yo loopback4:` with `lb4 `.
348
+ */
349
+ usage() {
350
+ const text = super.usage();
351
+ return text.replace(/^yo loopback4:/g, 'lb4 ');
352
+ }
353
+
354
+ /**
355
+ * Tell this generator to exit with the given reason
356
+ * @param {string|Error} reason
357
+ */
358
+ exit(reason) {
359
+ // exit(false) should not exit
360
+ if (reason === false) return;
361
+ // exit(), exit(undefined), exit('') should exit
362
+ if (!reason) reason = true;
363
+ this.exitGeneration = reason;
364
+ }
365
+
366
+ /**
367
+ * Select pkgManager and install packages
368
+ * @param {String|Array} pkgs
369
+ * @param {Object} options
370
+ * @param {Object} spawnOpts
371
+ */
372
+ pkgManagerInstall(pkgs, options = {}, spawnOpts) {
373
+ const pm = this.config.get('packageManager') || this.options.packageManager;
374
+ if (pm === 'yarn') {
375
+ return this.yarnInstall(pkgs, options.yarn, spawnOpts);
376
+ }
377
+ this.npmInstall(pkgs, options.npm, spawnOpts);
378
+ }
379
+
380
+ /**
381
+ * Run `[pkgManager] install` in the project
382
+ */
383
+ install() {
384
+ if (this.shouldExit()) return false;
385
+ const opts = {
386
+ npm: this.options.npmInstall,
387
+ yarn: this.options.yarnInstall,
388
+ };
389
+ const spawnOpts = Object.assign({}, this.options.spawn, {
390
+ cwd: this.destinationRoot(),
391
+ });
392
+ this.pkgManagerInstall(null, opts, spawnOpts);
393
+ }
394
+
395
+ /**
396
+ * Wrapper for mem-fs-editor.copyTpl() to ensure consistent options
397
+ *
398
+ * See https://github.com/SBoudrias/mem-fs-editor/blob/master/lib/actions/copy-tpl.js
399
+ *
400
+ * @param {string} from
401
+ * @param {string} to
402
+ * @param {object} context
403
+ * @param {object} templateOptions
404
+ * @param {object} copyOptions
405
+ */
406
+ copyTemplatedFiles(
407
+ from,
408
+ to,
409
+ context,
410
+ templateOptions = {},
411
+ copyOptions = DEFAULT_COPY_OPTIONS,
412
+ ) {
413
+ return this.fs.copyTpl(from, to, context, templateOptions, copyOptions);
414
+ }
415
+
416
+ /**
417
+ * Checks if current directory is a LoopBack project by checking for
418
+ * "@loopback/core" package in the dependencies section of the
419
+ * package.json.
420
+ */
421
+ checkLoopBackProject() {
422
+ debug('Checking for loopback project');
423
+ return checkLoopBackProject(this);
424
+ }
425
+
426
+ _runNpmScript(projectDir, args) {
427
+ return new Promise((resolve, reject) => {
428
+ this.spawnCommand('npm', args, {
429
+ // Disable stdout
430
+ stdio: [process.stdin, 'ignore', process.stderr],
431
+ cwd: projectDir,
432
+ }).on('close', code => {
433
+ if (code === 0) resolve();
434
+ else reject(new Error('npm exit code: ' + code));
435
+ });
436
+ });
437
+ }
438
+
439
+ /**
440
+ * Check if the generator should exit
441
+ */
442
+ shouldExit() {
443
+ return !!this.exitGeneration;
444
+ }
445
+
446
+ async _runLintFix() {
447
+ if (this.options.format) {
448
+ const pkg = this.packageJson || {};
449
+ if (pkg.scripts && pkg.scripts['lint:fix']) {
450
+ this.log(g.f("Running 'npm run lint:fix' to format the code..."));
451
+ await this._runNpmScript(this.destinationRoot(), [
452
+ 'run',
453
+ '-s',
454
+ 'lint:fix',
455
+ ]);
456
+ } else {
457
+ this.log(
458
+ chalk.red(g.f("No 'lint:fix' script is configured in package.json.")),
459
+ );
460
+ }
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Print out the exit reason if this generator is told to exit before it ends
466
+ */
467
+ async end() {
468
+ if (this.shouldExit()) {
469
+ debug(this.exitGeneration);
470
+ this.log(
471
+ chalk.red(g.f('Generation is aborted: %s', this.exitGeneration)),
472
+ );
473
+ // Fail the process
474
+ process.exitCode = 1;
475
+ return;
476
+ }
477
+ await this._runLintFix();
478
+ }
479
+
480
+ // Check all files being generated to ensure they succeeded
481
+ _isGenerationSuccessful() {
482
+ const generationStatus = !!Object.entries(
483
+ this.conflicter.generationStatus,
484
+ ).find(([key, val]) => {
485
+ // If a file was modified, update the indexes and say stuff about it
486
+ return val !== 'skip' && val !== 'identical';
487
+ });
488
+ debug(`Generation status: ${generationStatus}`);
489
+ return generationStatus;
490
+ }
491
+
492
+ async _updateIndexFile(dir, file) {
493
+ await updateIndex(dir, file, this.fs);
494
+
495
+ // Output for users
496
+ const updateDirRelPath = path.relative(
497
+ this.artifactInfo.relPath,
498
+ this.artifactInfo.outDir,
499
+ );
500
+
501
+ const outPath = path.join(
502
+ this.artifactInfo.relPath,
503
+ updateDirRelPath,
504
+ 'index.ts',
505
+ );
506
+
507
+ this.log(chalk.green(' update'), `${outPath}`);
508
+ }
509
+ };