1 | import * as path from 'path'
|
2 | import * as fs from 'fs-extra'
|
3 | import * as inquirer from 'inquirer'
|
4 | import * as semver from 'semver'
|
5 | import { createApp } from './init'
|
6 | import fetchTemplate from './fetchTemplate'
|
7 | import Creator from './creator'
|
8 | import CONFIG from '../config'
|
9 | import { DEFAULT_TEMPLATE_SRC, TARO_CONFIG_FLODER, TARO_BASE_CONFIG, getUserHomeDir, chalk } from '@tarojs/helper'
|
10 |
|
11 | export 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 |
|
27 | interface AskMethods {
|
28 | (conf: IProjectConf, prompts: object[], choices?: string[]): void
|
29 | }
|
30 |
|
31 | export 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 | }
|