UNPKG

5.98 kBPlain TextView Raw
1import * as path from 'path'
2import * as fs from 'fs-extra'
3import * as inquirer from 'inquirer'
4import * as semver from 'semver'
5import { createApp } from './init'
6import fetchTemplate from './fetchTemplate'
7import Creator from './creator'
8import CONFIG from '../config'
9import { DEFAULT_TEMPLATE_SRC, TARO_CONFIG_FLODER, TARO_BASE_CONFIG, getUserHomeDir, chalk } from '@tarojs/helper'
10
11export interface IProjectConf {
12 projectName: string,
13 projectDir: string,
14 templateSource: string,
15 clone?: boolean,
16 template: string,
17 description?: string,
18 typescript?: boolean,
19 css: 'none' | 'sass' | 'stylus' | 'less',
20 date?: string,
21 src?: string,
22 sourceRoot?: string,
23 env?: string,
24 autoInstall?: boolean
25}
26
27interface AskMethods {
28 (conf: IProjectConf, prompts: object[], choices?: string[]): void
29}
30
31export default class Project extends Creator {
32 public rootPath: string
33 public conf: IProjectConf
34
35 constructor (options: IProjectConf) {
36 super(options.sourceRoot)
37 const unSupportedVer = semver.lt(process.version, 'v7.6.0')
38 if (unSupportedVer) {
39 throw new Error('Node.js 版本过低,推荐升级 Node.js 至 v8.0.0+')
40 }
41 this.rootPath = this._rootPath
42
43 this.conf = Object.assign({
44 projectName: '',
45 projectDir: '',
46 template: '',
47 description: ''
48 }, options)
49 }
50
51 init () {
52 console.log(chalk.green(`Taro 即将创建一个新项目!`))
53 console.log('Need help? Go and open issue: https://github.com/NervJS/taro/issues/new')
54 console.log()
55 }
56
57 create () {
58 this.fetchTemplates()
59 .then((templateChoices: string[]) => this.ask(templateChoices))
60 .then(answers => {
61 const date = new Date()
62 this.conf = Object.assign(this.conf, answers)
63 this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}`
64 this.write()
65 })
66 .catch(err => console.log(chalk.red('创建项目失败: ', err)))
67 }
68
69 async fetchTemplates (): Promise<string[]> {
70 const conf = this.conf
71 // 使用默认模版
72 if (conf.template && conf.template === 'default') {
73 return Promise.resolve([])
74 }
75
76 // 处理模版源取值
77 if (!conf.templateSource) {
78 const homedir = getUserHomeDir()
79 if (!homedir) {
80 chalk.yellow('找不到用户根目录,使用默认模版源!')
81 conf.templateSource = DEFAULT_TEMPLATE_SRC
82 }
83
84 const taroConfigPath = path.join(homedir, TARO_CONFIG_FLODER)
85 const taroConfig = path.join(taroConfigPath, TARO_BASE_CONFIG)
86
87 if (fs.existsSync(taroConfig)) {
88 const config = await fs.readJSON(taroConfig)
89 conf.templateSource = config && config.templateSource ? config.templateSource : DEFAULT_TEMPLATE_SRC
90 } else {
91 await fs.createFile(taroConfig)
92 await fs.writeJSON(taroConfig, { templateSource: DEFAULT_TEMPLATE_SRC })
93 conf.templateSource = DEFAULT_TEMPLATE_SRC
94 }
95 }
96
97 // 从模板源下载模板
98 return fetchTemplate(this.conf.templateSource, this.templatePath(''), this.conf.clone)
99 }
100
101 ask (templateChoices: string[]) {
102 const prompts: object[] = []
103 const conf = this.conf
104
105 this.askProjectName(conf, prompts)
106 this.askDescription(conf, prompts)
107 this.askTypescript(conf, prompts)
108 this.askCSS(conf, prompts)
109 this.askTemplate(conf, prompts, templateChoices)
110
111 return inquirer.prompt(prompts)
112 }
113
114 askProjectName: AskMethods = function (conf, prompts) {
115 if (typeof conf.projectName as string | undefined !== 'string') {
116 prompts.push({
117 type: 'input',
118 name: 'projectName',
119 message: '请输入项目名称!',
120 validate (input) {
121 if (!input) {
122 return '项目名不能为空!'
123 }
124 if (fs.existsSync(input)) {
125 return '当前目录已经存在同名项目,请换一个项目名!'
126 }
127 return true
128 }
129 })
130 } else if (fs.existsSync(conf.projectName)) {
131 prompts.push({
132 type: 'input',
133 name: 'projectName',
134 message: '当前目录已经存在同名项目,请换一个项目名!',
135 validate (input) {
136 if (!input) {
137 return '项目名不能为空!'
138 }
139 if (fs.existsSync(input)) {
140 return '项目名依然重复!'
141 }
142 return true
143 }
144 })
145 }
146 }
147
148 askDescription: AskMethods = function (conf, prompts) {
149 if (typeof conf.description !== 'string') {
150 prompts.push({
151 type: 'input',
152 name: 'description',
153 message: '请输入项目介绍!'
154 })
155 }
156 }
157
158 askTypescript: AskMethods = function (conf, prompts) {
159 if (typeof conf.typescript !== 'boolean') {
160 prompts.push({
161 type: 'confirm',
162 name: 'typescript',
163 message: '是否需要使用 TypeScript ?'
164 })
165 }
166 }
167
168 askCSS: AskMethods = function (conf, prompts) {
169 const cssChoices = [{
170 name: 'Sass',
171 value: 'sass'
172 }, {
173 name: 'Less',
174 value: 'less'
175 }, {
176 name: 'Stylus',
177 value: 'stylus'
178 }, {
179 name: '无',
180 value: 'none'
181 }]
182
183 if (typeof conf.css as string | undefined !== 'string') {
184 prompts.push({
185 type: 'list',
186 name: 'css',
187 message: '请选择 CSS 预处理器(Sass/Less/Stylus)',
188 choices: cssChoices
189 })
190 }
191 }
192
193 askTemplate: AskMethods = function (conf, prompts, list = []) {
194 const choices = [{
195 name: '默认模板',
196 value: 'default'
197 }, ...list.map(item => ({ name: item, value: item }))]
198
199 if (typeof conf.template as 'string' | undefined !== 'string') {
200 prompts.push({
201 type: 'list',
202 name: 'template',
203 message: '请选择模板',
204 choices
205 })
206 }
207 }
208
209 write (cb?: () => void) {
210 this.conf.src = CONFIG.SOURCE_DIR
211 createApp(this, this.conf, cb)
212 .catch(err => console.log(err))
213 }
214}