UNPKG

4.5 kBJavaScriptView Raw
1const path = require('path')
2const inquirer = require('inquirer')
3const hbs = require('../lib/hbs')
4const util = require('../lib/util')
5const fetch = require('../lib/fetch')
6const prompt = require('../lib/prompt')
7const generate = require('../lib/generate')
8
9/**
10 * 1. Confirm destination path exists
11 *
12 * @uses
13 * - context.dest - Destination path
14 * - context.inPlace - Destination path in place
15 * @mounts
16 * - context.exists - Destination path exists
17 */
18const confirmExists = context => {
19 const { dest, inPlace } = context
20
21 context.exists = util.existsSync(dest)
22
23 if (!context.exists) return context
24
25 // padding
26 console.log()
27 const questions = {
28 name: 'ok',
29 type: 'confirm',
30 default: false,
31 message: inPlace
32 ? 'Generate project in current directory?'
33 : 'Target directory exists. Continue?'
34 }
35 // prompt when dest exist
36 return inquirer.prompt(questions).then(answers => {
37 if (!answers.ok) throw new Error('You have to cancel the init task.')
38 return context
39 })
40}
41
42/**
43 * 2. Resolve template path from local or remote
44 *
45 * @uses
46 * - context.template - Template name
47 * - context.offline - Offline mode
48 * @mounts
49 * - context.src - Template path
50 */
51const resolveTemplate = context => {
52 const { template, offline } = context
53
54 if (util.isLocalPath(template)) {
55 // local
56 context.src = path.resolve(template)
57 return context
58 }
59
60 // remote
61 return fetch(template, offline).then(source => {
62 context.src = source
63 return context
64 })
65}
66
67/**
68 * 3. Load template options
69 *
70 * @uses
71 * - context.src - Template path
72 * @mounts
73 * - context.options - Template options
74 */
75const loadTemplate = context => {
76 const { src } = context
77
78 try {
79 context.options = require(src)
80 } catch (e) {
81 // TODO: template validate && docs tips
82 if (e.code !== 'MODULE_NOT_FOUND') throw e
83 // throw new Error('This template is invalid.')
84 context.options = {}
85 }
86
87 return context
88}
89
90/**
91 * 4. Ask Questions
92 *
93 * @uses
94 * - context.dest - Destination path
95 * - context.name - Default name
96 * - context.exists - Destination path exists
97 * - context.options - Template options
98 * @mounts
99 * - context.answers - User answers
100 */
101const askQuestions = context => {
102 const { dest, name, exists, options } = context
103 const { prompts } = options
104
105 // TODO: params
106 return prompt(prompts, dest, exists, name).then(answers => {
107 context.answers = answers
108 return context
109 })
110}
111
112/**
113 * 5. Generate files from template
114 *
115 * @uses
116 * - context.src - Template path
117 * - context.dest - Destination path
118 * - context.answers - User answers
119 * - context.options - Template options
120 * @mounts
121 * - context.files - Generated files
122 */
123const generateFiles = context => {
124 const { src, dest, answers, options } = context
125 return generate(src, dest, answers, options)
126 .then(files => {
127 context.files = files
128 return context
129 })
130}
131
132/**
133 * 6. Response message to console
134 *
135 * @uses
136 * - context.options - Template options
137 * - context.answers - User answers
138 */
139const responseConsole = context => {
140 const { options, answers } = context
141 const { complete } = options
142
143 if (typeof complete === 'function') {
144 console.log()
145 complete(context)
146 } else if (typeof complete === 'string') {
147 console.log()
148 console.log(hbs.render(complete, answers))
149 }
150
151 console.log()
152 return context
153}
154
155/**
156 * Error handler
157 */
158const onError = context => err => {
159 console.error(`\n💀 ${err.message}`)
160
161 context.debug && console.error('\n', err)
162
163 console.log()
164 /* istanbul ignore else */
165 if (process.env.NODE_ENV === 'testing') {
166 throw err
167 } else {
168 process.exit()
169 }
170}
171
172/**
173 * Initial command
174 * @param {String} template Template name
175 * @param {String} target Target name
176 * @param {Boolean} offline Offline mode
177 * @param {Boolean} debug Debug mode
178 * @return {Promise} Initial promise
179 */
180module.exports = (template, target, offline, debug) => {
181 const dest = path.resolve(target || '.')
182 const inPlace = dest === process.cwd()
183 const name = path.basename(dest)
184
185 const context = { template, dest, inPlace, name, offline, debug }
186
187 // set hbs options
188 hbs.setOptions(context)
189
190 return Promise.resolve(context)
191 .then(confirmExists)
192 .then(resolveTemplate)
193 .then(loadTemplate)
194 .then(askQuestions)
195 .then(generateFiles)
196 .then(responseConsole)
197 .catch(onError(context))
198}