1 | const resolveFrom = require('resolve-from')
|
2 | const loadConfig = require('./loadConfig')
|
3 | const BaseGeneratorContext = require('./GeneratorContext')
|
4 | const parseGenerator = require('./parseGenerator')
|
5 | const updateCheck = require('./updateCheck')
|
6 | const PluginAPI = require('./PluginAPI')
|
7 | const paths = require('./paths')
|
8 | const defaultOptions = require('./defaultOptions')
|
9 | const { setGenerator } = require('./cache')
|
10 | const {
|
11 | fs,
|
12 | path,
|
13 | chalk,
|
14 | logger,
|
15 | spinner,
|
16 | isLocalPath,
|
17 | downloadGitRepo,
|
18 | installPackages,
|
19 | SherryError,
|
20 | npmClient
|
21 | } = require('sherry-utils')
|
22 |
|
23 | module.exports = class Sherry {
|
24 | |
25 |
|
26 |
|
27 |
|
28 | constructor(opts) {
|
29 | this.logger = logger
|
30 | logger.setOptions(
|
31 | process.argv.includes('--debug')
|
32 | ? 4
|
33 | : 1
|
34 | )
|
35 | if (opts) {
|
36 | this.setOptions(opts)
|
37 | }
|
38 | }
|
39 |
|
40 | setCLI(cli) {
|
41 | this.cli = cli
|
42 | this.pluginAPI = new PluginAPI(this)
|
43 | }
|
44 |
|
45 | setOptions(opts) {
|
46 | this.opts = Object.assign({}, defaultOptions, opts)
|
47 | logger.setOptions({
|
48 | logLevel:
|
49 | this.opts.debug
|
50 | ? 4
|
51 | : this.opts.quiet
|
52 | ? 1
|
53 | : 3
|
54 | })
|
55 |
|
56 | this.parsedGenerator = parseGenerator(this.opts.generator, this.opts)
|
57 | logger.debug('parsedGenerator', this.parsedGenerator)
|
58 |
|
59 | if (this.parsedGenerator.subGenerator) {
|
60 | logger.debug(
|
61 | `Setting out directory to process.cwd() since it's a sub generator`
|
62 | )
|
63 | this.opts.outDir = process.cwd()
|
64 | }
|
65 |
|
66 |
|
67 | npmClient.set(this.opts.npmClient)
|
68 |
|
69 | return this
|
70 | }
|
71 |
|
72 | |
73 |
|
74 |
|
75 | async run(generator, parent) {
|
76 | generator = generator || this.parsedGenerator
|
77 |
|
78 | if (generator.type === 'repo') {
|
79 | await ensureRepo(generator, this.opts)
|
80 | } else if (generator.type === 'npm') {
|
81 | await ensurePackage(generator, this.opts)
|
82 | } else if (generator.type === 'local') {
|
83 | await ensureLocal(generator)
|
84 | }
|
85 |
|
86 | const loaded = await loadConfig(generator.path)
|
87 | const config = loaded.path ?
|
88 | loaded.data :
|
89 | require(path.join(__dirname, 'sherry-config.fallback.js'))
|
90 |
|
91 |
|
92 | if (!parent) {
|
93 | if (this.opts.updateCheck) {
|
94 | updateCheck({
|
95 | generator,
|
96 | checkGenerator:
|
97 | config.updateCheck !== false && generator.type === 'npm',
|
98 |
|
99 |
|
100 | showNotifier: !this.opts.update
|
101 | })
|
102 | }
|
103 |
|
104 | setGenerator(generator)
|
105 | }
|
106 |
|
107 | if (generator.subGenerator) {
|
108 |
|
109 | const subGenerators = config.subGenerators || config.generators
|
110 | const subGenerator =
|
111 | subGenerators &&
|
112 | subGenerators.find(g => g.name === generator.subGenerator)
|
113 | if (subGenerator) {
|
114 |
|
115 | let generatorPath = subGenerator.generator || subGenerator.from
|
116 | generatorPath = isLocalPath(generatorPath) ?
|
117 | path.resolve(generator.path, generatorPath) :
|
118 | resolveFrom(generator.path, generatorPath)
|
119 | return this.run(parseGenerator(generatorPath, this.opts), generator)
|
120 | }
|
121 | throw new SherryError(
|
122 | `No such sub generator in generator ${generator.path}`
|
123 | )
|
124 | }
|
125 |
|
126 | await this.runGenerator(generator, config)
|
127 | }
|
128 |
|
129 | async runGenerator(generator, config) {
|
130 | if (config.description) {
|
131 | logger.status('green', 'Generator', config.description)
|
132 | }
|
133 |
|
134 | const GeneratorContext = this.opts.getContext ?
|
135 | this.opts.getContext(BaseGeneratorContext) :
|
136 | BaseGeneratorContext
|
137 | const generatorContext = new GeneratorContext(this, generator)
|
138 | this.generatorContext = generatorContext
|
139 |
|
140 | if (typeof config.prepare === 'function') {
|
141 | await config.prepare.call(generatorContext, generatorContext)
|
142 | }
|
143 |
|
144 | if (config.prompts) {
|
145 | await require('./runPrompts')(config, generatorContext)
|
146 | }
|
147 |
|
148 | generatorContext.data = typeof config.data === 'function' ?
|
149 | config.data.call(generatorContext, generatorContext) :
|
150 | (config.data || {})
|
151 |
|
152 | if (config.actions) {
|
153 | await require('./runActions')(config, generatorContext)
|
154 | }
|
155 |
|
156 | if (!this.opts.mock && config.completed) {
|
157 | await config.completed.call(generatorContext, generatorContext)
|
158 | }
|
159 | }
|
160 |
|
161 | applyPlugins(plugins) {
|
162 | for (const { name, path, options = {} } of plugins) {
|
163 | logger.debug(`Apply plugin: ${name}`)
|
164 | const pluginFn = require(path)
|
165 | pluginFn(this.pluginAPI, options)
|
166 | }
|
167 | }
|
168 | }
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | async function ensureRepo(generator, { update, gitProtocol, registry, gitOrigin }) {
|
177 | if (!update && (await fs.pathExists(generator.path))) {
|
178 | return
|
179 | }
|
180 |
|
181 |
|
182 | spinner.start('Downloading repo')
|
183 | try {
|
184 | await downloadGitRepo(generator.slug, generator.path, { protocol: gitProtocol, gitOrigin })
|
185 |
|
186 | spinner.stop()
|
187 | logger.success('Downloaded repo')
|
188 | } catch (err) {
|
189 | if (err) {
|
190 | const { statusCode } = err
|
191 | if (typeof statusCode === 'number' && statusCode > 500) {
|
192 | console.log()
|
193 | console.log(err)
|
194 | throw new SherryError(`Please check if you have the access to ${chalk.cyan(generator.slug)}`)
|
195 | }
|
196 | }
|
197 |
|
198 | let message = err.message
|
199 | if (err.host && err.path) {
|
200 | message += '\n' + err.host + err.path
|
201 | }
|
202 | throw new SherryError(message)
|
203 | }
|
204 |
|
205 | const [hasConfig, hasPackageJson] = await Promise.all([
|
206 | loadConfig.hasConfig(generator.path),
|
207 | fs.pathExists(path.join(generator.path, 'package.json'))
|
208 | ])
|
209 |
|
210 | if (hasConfig && hasPackageJson) {
|
211 | await installPackages({
|
212 | cwd: generator.path,
|
213 | registry,
|
214 | installArgs: ['--production']
|
215 | })
|
216 | }
|
217 | }
|
218 |
|
219 | async function ensureLocal(generator) {
|
220 | const exists = await fs.pathExists(generator.path)
|
221 |
|
222 | if (!exists) {
|
223 | throw new SherryError(
|
224 | `Directory ${chalk.underline(generator.path)} does not exist`
|
225 | )
|
226 | }
|
227 | }
|
228 |
|
229 | async function ensurePackage(generator, { update, registry }) {
|
230 | const installPath = path.join(paths.packagePath, generator.hash)
|
231 |
|
232 | if (update || !(await fs.pathExists(generator.path))) {
|
233 | await fs.ensureDir(installPath)
|
234 | await fs.writeFile(
|
235 | path.join(installPath, 'package.json'),
|
236 | JSON.stringify({
|
237 | private: true
|
238 | }),
|
239 | 'utf8'
|
240 | )
|
241 | logger.debug('Installing generator at', installPath)
|
242 | await installPackages({
|
243 | cwd: installPath,
|
244 | registry,
|
245 | packages: [`${generator.name}@${generator.version || 'latest'}`]
|
246 | })
|
247 | }
|
248 | }
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|