1 | import * as fs from 'fs-extra'
|
2 | import * as path from 'path'
|
3 | import { exec } from 'child_process'
|
4 | import * as ora from 'ora'
|
5 | import { shouldUseYarn, shouldUseCnpm, chalk } from '@tarojs/helper'
|
6 |
|
7 | import { getAllFilesInFloder, getPkgVersion } from '../util'
|
8 | import { IProjectConf } from './project'
|
9 | import { IPageConf } from './page'
|
10 | import Creator from './creator'
|
11 |
|
12 | const CONFIG_DIR_NAME = 'config'
|
13 | export const TEMPLATE_CREATOR = 'template_creator.js'
|
14 |
|
15 | const styleExtMap = {
|
16 | sass: 'scss',
|
17 | less: 'less',
|
18 | stylus: 'styl',
|
19 | none: 'css'
|
20 | }
|
21 |
|
22 | const doNotCopyFiles = ['.DS_Store', '.npmrc', TEMPLATE_CREATOR]
|
23 |
|
24 | function createFiles (
|
25 | creater: Creator,
|
26 | files: string[],
|
27 | handler,
|
28 | options: (IProjectConf | IPageConf) & {
|
29 | templatePath: string;
|
30 | projectPath: string;
|
31 | pageName: string;
|
32 | period: string;
|
33 | version?: string;
|
34 | }
|
35 | ): string[] {
|
36 | const {
|
37 | description,
|
38 | projectName,
|
39 | version,
|
40 | css,
|
41 | date,
|
42 | typescript,
|
43 | template,
|
44 | templatePath,
|
45 | projectPath,
|
46 | pageName,
|
47 | framework
|
48 | } = options
|
49 | const logs: string[] = []
|
50 |
|
51 | const globalChangeExt = Boolean(handler)
|
52 | const currentStyleExt = styleExtMap[css] || 'css'
|
53 |
|
54 | files.forEach(file => {
|
55 |
|
56 | const fileRePath = file.replace(templatePath, '').replace(new RegExp(`\\${path.sep}`, 'g'), '/')
|
57 | let externalConfig: any = null
|
58 |
|
59 | const isVueFramework = /^vue/.test(framework)
|
60 | if (isVueFramework && file.endsWith('.jsx')) {
|
61 | return
|
62 | }
|
63 |
|
64 | if (!isVueFramework && file.endsWith('.vue')) {
|
65 | return
|
66 | }
|
67 |
|
68 |
|
69 | if (handler && typeof handler[fileRePath] === 'function') {
|
70 | externalConfig = handler[fileRePath](options)
|
71 | if (!externalConfig) return
|
72 | }
|
73 |
|
74 | let changeExt = globalChangeExt
|
75 | if (externalConfig && typeof externalConfig === 'object') {
|
76 | if (externalConfig.changeExt === false) {
|
77 | changeExt = false
|
78 | }
|
79 | }
|
80 |
|
81 |
|
82 | const config = Object.assign(
|
83 | {},
|
84 | {
|
85 | description,
|
86 | projectName,
|
87 | version,
|
88 | css,
|
89 | cssExt: currentStyleExt,
|
90 | date,
|
91 | typescript,
|
92 | template,
|
93 | pageName,
|
94 | framework
|
95 | },
|
96 | externalConfig
|
97 | )
|
98 |
|
99 | let destRePath = fileRePath
|
100 |
|
101 |
|
102 | if (config.setPageName) {
|
103 | destRePath = config.setPageName
|
104 | }
|
105 | destRePath = destRePath.replace(/^\//, '')
|
106 |
|
107 | if (
|
108 | typescript &&
|
109 | changeExt &&
|
110 | !destRePath.startsWith(`${CONFIG_DIR_NAME}`) &&
|
111 | (path.extname(destRePath) === '.js' || path.extname(destRePath) === '.jsx') &&
|
112 | !(destRePath.endsWith('babel.config.js') || destRePath.endsWith('.eslintrc.js'))
|
113 | ) {
|
114 | destRePath = destRePath.replace('.js', '.ts')
|
115 | }
|
116 | if (changeExt && path.extname(destRePath).includes('.css')) {
|
117 | destRePath = destRePath.replace('.css', `.${currentStyleExt}`)
|
118 | }
|
119 |
|
120 |
|
121 | creater.template(template, fileRePath, path.join(projectPath, destRePath), config)
|
122 |
|
123 | const destinationPath = creater.destinationPath(path.join(projectPath, destRePath))
|
124 | logs.push(`${chalk.green('✔ ')}${chalk.grey(`创建文件: ${destinationPath}`)}`)
|
125 | })
|
126 | return logs
|
127 | }
|
128 |
|
129 | export async function createPage (creater: Creator, params: IPageConf, cb) {
|
130 | const { projectDir, template, pageName } = params
|
131 | // path
|
132 | const templatePath = creater.templatePath(template)
|
133 |
|
134 | if (!fs.existsSync(templatePath)) return console.log(chalk.red(`创建页面错误:找不到模板${templatePath}`))
|
135 |
|
136 | // 引入模板编写者的自定义逻辑
|
137 | const handlerPath = path.join(templatePath, TEMPLATE_CREATOR)
|
138 | const basePageFiles = fs.existsSync(handlerPath) ? require(handlerPath).basePageFiles : []
|
139 | const files = Array.isArray(basePageFiles) ? basePageFiles : []
|
140 | const handler = fs.existsSync(handlerPath) ? require(handlerPath).handler : null
|
141 |
|
142 | const logs = createFiles(creater, files, handler, {
|
143 | ...params,
|
144 | templatePath,
|
145 | projectPath: projectDir,
|
146 | pageName,
|
147 | period: 'createPage'
|
148 | })
|
149 |
|
150 | creater.fs.commit(() => {
|
151 | // logs
|
152 | console.log()
|
153 | logs.forEach(log => console.log(log))
|
154 | console.log()
|
155 | typeof cb === 'function' && cb()
|
156 | })
|
157 | }
|
158 |
|
159 | export async function createApp (creater: Creator, params: IProjectConf, cb) {
|
160 | const { projectName, projectDir, template, autoInstall = true, framework } = params
|
161 | const logs: string[] = []
|
162 | // path
|
163 | const templatePath = creater.templatePath(template)
|
164 | const projectPath = path.join(projectDir, projectName)
|
165 |
|
166 | // npm & yarn
|
167 | const version = getPkgVersion()
|
168 | const isShouldUseYarn = shouldUseYarn()
|
169 | const useNpmrc = !isShouldUseYarn
|
170 | const yarnLockfilePath = path.join('yarn-lockfiles', `${version}-yarn.lock`)
|
171 | const useYarnLock = isShouldUseYarn && fs.existsSync(creater.templatePath(template, yarnLockfilePath))
|
172 |
|
173 | if (useNpmrc) {
|
174 | creater.template(template, '.npmrc', path.join(projectPath, '.npmrc'))
|
175 | logs.push(`${chalk.green('✔ ')}${chalk.grey(`创建文件: ${projectName}${path.sep}.npmrc`)}`)
|
176 | }
|
177 | if (useYarnLock) {
|
178 | creater.template(template, yarnLockfilePath, path.join(projectPath, 'yarn.lock'))
|
179 | logs.push(`${chalk.green('✔ ')}${chalk.grey(`创建文件: ${projectName}${path.sep}yarn.lock`)}`)
|
180 | }
|
181 |
|
182 | // 遍历出模板中所有文件
|
183 | const files = await getAllFilesInFloder(templatePath, doNotCopyFiles)
|
184 |
|
185 | // 引入模板编写者的自定义逻辑
|
186 | const handlerPath = path.join(templatePath, TEMPLATE_CREATOR)
|
187 | const handler = fs.existsSync(handlerPath) ? require(handlerPath).handler : null
|
188 |
|
189 | // 为所有文件进行创建
|
190 | logs.push(
|
191 | ...createFiles(creater, files, handler, {
|
192 | ...params,
|
193 | framework,
|
194 | version,
|
195 | templatePath,
|
196 | projectPath,
|
197 | pageName: 'index',
|
198 | period: 'createApp'
|
199 | })
|
200 | )
|
201 |
|
202 | // fs commit
|
203 | creater.fs.commit(() => {
|
204 | // logs
|
205 | console.log()
|
206 | console.log(`${chalk.green('✔ ')}${chalk.grey(`创建项目: ${chalk.grey.bold(projectName)}`)}`)
|
207 | logs.forEach(log => console.log(log))
|
208 | console.log()
|
209 |
|
210 | // git init
|
211 | const gitInitSpinner = ora(`cd ${chalk.cyan.bold(projectName)}, 执行 ${chalk.cyan.bold('git init')}`).start()
|
212 | process.chdir(projectPath)
|
213 | const gitInit = exec('git init')
|
214 | gitInit.on('close', code => {
|
215 | if (code === 0) {
|
216 | gitInitSpinner.color = 'green'
|
217 | gitInitSpinner.succeed(gitInit.stdout!.read())
|
218 | } else {
|
219 | gitInitSpinner.color = 'red'
|
220 | gitInitSpinner.fail(gitInit.stderr!.read())
|
221 | }
|
222 | })
|
223 |
|
224 | const callSuccess = () => {
|
225 | console.log(chalk.green(`创建项目 ${chalk.green.bold(projectName)} 成功!`))
|
226 | console.log(chalk.green(`请进入项目目录 ${chalk.green.bold(projectName)} 开始工作吧!😝`))
|
227 | if (typeof cb === 'function') {
|
228 | cb()
|
229 | }
|
230 | }
|
231 |
|
232 | if (autoInstall) {
|
233 | // packages install
|
234 | let command: string
|
235 | if (isShouldUseYarn) {
|
236 | command = 'yarn install'
|
237 | } else if (shouldUseCnpm()) {
|
238 | command = 'cnpm install'
|
239 | } else {
|
240 | command = 'npm install'
|
241 | }
|
242 | const installSpinner = ora(`执行安装项目依赖 ${chalk.cyan.bold(command)}, 需要一会儿...`).start()
|
243 | exec(command, (error, stdout, stderr) => {
|
244 | if (error) {
|
245 | installSpinner.color = 'red'
|
246 | installSpinner.fail(chalk.red('安装项目依赖失败,请自行重新安装!'))
|
247 | console.log(error)
|
248 | } else {
|
249 | installSpinner.color = 'green'
|
250 | installSpinner.succeed('安装成功')
|
251 | console.log(`${stderr}${stdout}`)
|
252 | }
|
253 | callSuccess()
|
254 | })
|
255 | } else {
|
256 | callSuccess()
|
257 | }
|
258 | })
|
259 | }
|
260 |
|
\ | No newline at end of file |