UNPKG

7.77 kBPlain TextView Raw
1import * as fs from 'fs-extra'
2import * as path from 'path'
3import { exec } from 'child_process'
4import * as ora from 'ora'
5import { shouldUseYarn, shouldUseCnpm, chalk } from '@tarojs/helper'
6
7import { getAllFilesInFloder, getPkgVersion } from '../util'
8import { IProjectConf } from './project'
9import { IPageConf } from './page'
10import Creator from './creator'
11
12const CONFIG_DIR_NAME = 'config'
13export const TEMPLATE_CREATOR = 'template_creator.js'
14
15const styleExtMap = {
16 sass: 'scss',
17 less: 'less',
18 stylus: 'styl',
19 none: 'css'
20}
21
22const doNotCopyFiles = ['.DS_Store', '.npmrc', TEMPLATE_CREATOR]
23
24function 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 // fileRePath startsWith '/'
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 // 合并自定义 config
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 // createPage 创建页面模式
102 if (config.setPageName) {
103 destRePath = config.setPageName
104 }
105 destRePath = destRePath.replace(/^\//, '')
106 // 处理 .js 和 .css 的后缀
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
129export 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
159export 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