@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.
- package/.yo-rc.json +1697 -0
- package/{generators/project/templates/LICENSE → LICENSE} +2 -1
- package/README.md +44 -43
- package/bin/cli-main.js +61 -0
- package/generators/app/index.js +109 -15
- package/generators/app/templates/.dockerignore +5 -0
- package/generators/app/templates/Dockerfile +28 -0
- package/generators/app/templates/README.md.ejs +130 -0
- package/generators/app/templates/public/index.html.ejs +88 -0
- package/generators/app/templates/{test → src/__tests__}/README.md +0 -1
- package/generators/app/templates/src/__tests__/acceptance/home-page.acceptance.ts.ejs +31 -0
- package/generators/app/templates/src/__tests__/acceptance/ping.controller.acceptance.ts.ejs +21 -0
- package/generators/app/templates/src/__tests__/acceptance/test-helper.ts.ejs +32 -0
- package/generators/app/templates/src/application.ts.ejs +70 -0
- package/generators/app/templates/src/controllers/README.md +6 -0
- package/generators/app/templates/src/controllers/index.ts.ejs +1 -0
- package/generators/app/templates/src/controllers/ping.controller.ts.ejs +55 -0
- package/generators/app/templates/src/datasources/README.md +3 -0
- package/generators/app/templates/src/index.ts.ejs +39 -0
- package/generators/app/templates/src/migrate.ts.ejs +20 -0
- package/generators/app/templates/src/models/README.md +3 -0
- package/generators/app/templates/src/openapi-spec.ts.ejs +23 -0
- package/generators/app/templates/src/sequence.ts.ejs +3 -0
- package/generators/controller/index.js +240 -23
- package/generators/controller/templates/src/controllers/controller-rest-template.ts.ejs +150 -0
- package/generators/controller/templates/src/controllers/{controller-template.ts → controller-template.ts.ejs} +2 -2
- package/generators/copyright/fs.js +46 -0
- package/generators/copyright/git.js +78 -0
- package/generators/copyright/header.js +306 -0
- package/generators/copyright/index.js +230 -0
- package/generators/copyright/license.js +105 -0
- package/generators/datasource/index.js +341 -0
- package/generators/datasource/templates/datasource.ts.ejs +22 -0
- package/generators/discover/import-discovered-model.js +70 -0
- package/generators/discover/index.js +349 -0
- package/generators/example/downloader.js +16 -0
- package/generators/example/index.js +176 -0
- package/generators/extension/index.js +34 -5
- package/generators/extension/templates/README.md.ejs +32 -0
- package/generators/extension/templates/{test → src/__tests__}/acceptance/README.md +0 -0
- package/generators/extension/templates/{test → src/__tests__}/integration/README.md +0 -0
- package/generators/extension/templates/{test → src/__tests__}/unit/README.md +0 -0
- package/generators/extension/templates/src/component.ts.ejs +22 -0
- package/generators/extension/templates/src/controllers/README.md +3 -2
- package/generators/extension/templates/src/decorators/README.md +10 -4
- package/generators/extension/templates/src/index.ts.ejs +3 -0
- package/generators/extension/templates/src/keys.ts.ejs +11 -0
- package/generators/extension/templates/src/mixins/README.md +77 -21
- package/generators/extension/templates/src/providers/README.md +51 -25
- package/generators/extension/templates/src/repositories/README.md +1 -1
- package/generators/extension/templates/src/types.ts.ejs +15 -0
- package/generators/import-lb3-models/index.js +197 -0
- package/generators/import-lb3-models/lb3app-loader.js +31 -0
- package/generators/import-lb3-models/migrate-model.js +249 -0
- package/generators/import-lb3-models/model-names.js +32 -0
- package/generators/interceptor/index.js +178 -0
- package/generators/interceptor/templates/interceptor-template.ts.ejs +62 -0
- package/generators/model/index.js +536 -0
- package/generators/model/property-definition.js +85 -0
- package/generators/model/templates/model.ts.ejs +42 -0
- package/generators/observer/index.js +132 -0
- package/generators/observer/templates/observer-template.ts.ejs +40 -0
- package/generators/openapi/README.md +211 -0
- package/generators/openapi/index.js +535 -0
- package/generators/openapi/schema-helper.js +447 -0
- package/generators/openapi/spec-helper.js +484 -0
- package/generators/openapi/spec-loader.js +75 -0
- package/generators/openapi/templates/src/controllers/controller-template.ts.ejs +43 -0
- package/generators/openapi/templates/src/datasources/datasource.ts.ejs +42 -0
- package/generators/openapi/templates/src/models/model-template.ts.ejs +71 -0
- package/generators/openapi/templates/src/models/type-template.ts.ejs +13 -0
- package/generators/openapi/templates/src/services/service-proxy-template.ts.ejs +55 -0
- package/generators/openapi/utils.js +322 -0
- package/generators/project/templates/.eslintignore +4 -0
- package/generators/project/templates/.eslintrc.js.ejs +3 -0
- package/generators/project/templates/.mocharc.json +5 -0
- package/generators/project/templates/.prettierignore +0 -2
- package/generators/project/templates/.prettierrc +2 -1
- package/generators/project/templates/.vscode/launch.json +38 -0
- package/generators/project/templates/.vscode/settings.json +32 -0
- package/generators/project/templates/.vscode/tasks.json +29 -0
- package/generators/project/templates/DEVELOPING.md +36 -0
- package/generators/project/templates/_.gitignore +3 -5
- package/generators/project/templates/package.json.ejs +175 -0
- package/generators/project/templates/package.plain.json.ejs +176 -0
- package/generators/project/templates/tsconfig.json.ejs +39 -0
- package/generators/relation/base-relation.generator.js +220 -0
- package/generators/relation/belongs-to-relation.generator.js +196 -0
- package/generators/relation/has-many-relation.generator.js +200 -0
- package/generators/relation/has-many-through-relation.generator.js +331 -0
- package/generators/relation/has-one-relation.generator.js +200 -0
- package/generators/relation/index.js +795 -0
- package/generators/relation/references-many-relation.generator.js +142 -0
- package/generators/relation/templates/controller-relation-template-belongs-to.ts.ejs +38 -0
- package/generators/relation/templates/controller-relation-template-has-many-through.ts.ejs +110 -0
- package/generators/relation/templates/controller-relation-template-has-many.ts.ejs +110 -0
- package/generators/relation/templates/controller-relation-template-has-one.ts.ejs +110 -0
- package/generators/relation/utils.generator.js +260 -0
- package/generators/repository/index.js +576 -0
- package/generators/repository/templates/src/repositories/repository-crud-default-template.ts.ejs +21 -0
- package/generators/repository/templates/src/repositories/repository-kv-template.ts.ejs +19 -0
- package/generators/rest-crud/crud-rest-component.js +63 -0
- package/generators/rest-crud/index.js +401 -0
- package/generators/rest-crud/templates/src/model-endpoints/model.rest-config-template.ts.ejs +10 -0
- package/generators/service/index.js +351 -0
- package/generators/service/templates/local-service-class-template.ts.ejs +10 -0
- package/generators/service/templates/local-service-provider-template.ts.ejs +19 -0
- package/generators/service/templates/remote-service-proxy-template.ts.ejs +21 -0
- package/generators/update/index.js +55 -0
- package/intl/cs/messages.json +204 -0
- package/intl/de/messages.json +204 -0
- package/intl/en/messages.json +204 -0
- package/intl/es/messages.json +204 -0
- package/intl/fr/messages.json +204 -0
- package/intl/it/messages.json +204 -0
- package/intl/ja/messages.json +204 -0
- package/intl/ko/messages.json +204 -0
- package/intl/nl/messages.json +204 -0
- package/intl/pl/messages.json +204 -0
- package/intl/pt/messages.json +204 -0
- package/intl/ru/messages.json +204 -0
- package/intl/tr/messages.json +204 -0
- package/intl/zh-Hans/messages.json +204 -0
- package/intl/zh-Hant/messages.json +204 -0
- package/lib/artifact-generator.js +138 -39
- package/lib/ast-helper.js +214 -0
- package/lib/base-generator.js +509 -0
- package/lib/cli.js +233 -0
- package/lib/connectors.json +894 -0
- package/lib/debug.js +16 -0
- package/lib/globalize.js +12 -0
- package/lib/model-discoverer.js +118 -0
- package/lib/project-generator.js +154 -57
- package/lib/tab-completion.js +127 -0
- package/lib/update-index.js +44 -0
- package/lib/utils.js +689 -20
- package/lib/version-helper.js +299 -0
- package/package.json +183 -39
- package/CHANGELOG.md +0 -86
- package/bin/cli.js +0 -66
- package/generators/app/templates/index.js +0 -14
- package/generators/app/templates/src/application.ts +0 -27
- package/generators/app/templates/src/controllers/ping-controller.ts +0 -25
- package/generators/app/templates/src/index.ts +0 -25
- package/generators/app/templates/test/ping-controller.test.ts +0 -46
- package/generators/extension/templates/index.js +0 -8
- package/generators/extension/templates/src/component.ts +0 -14
- package/generators/extension/templates/src/index.ts +0 -6
- package/generators/project/templates/.npmrc +0 -1
- package/generators/project/templates/.yo-rc.json +0 -1
- package/generators/project/templates/README.md +0 -4
- package/generators/project/templates/index.d.ts +0 -6
- package/generators/project/templates/index.ts +0 -11
- package/generators/project/templates/package.json +0 -79
- package/generators/project/templates/package.plain.json +0 -82
- package/generators/project/templates/test/mocha.opts +0 -1
- package/generators/project/templates/tsconfig.json +0 -29
- package/generators/project/templates/tslint.build.json +0 -17
- package/generators/project/templates/tslint.json +0 -33
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
// Copyright IBM Corp. 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
|
+
};
|