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