1 | 'use strict'
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | process.on('unhandledRejection', err => {
|
7 | throw err
|
8 | })
|
9 |
|
10 | const fs = require('fs-extra')
|
11 | const path = require('path')
|
12 | const chalk = require('chalk')
|
13 | const execSync = require('child_process').execSync
|
14 | const { execa } = require('@mara/devkit')
|
15 | const os = require('os')
|
16 | const { sortObject } = require('../lib/utils')
|
17 |
|
18 |
|
19 | const TMPL_VUE = 'project-vue'
|
20 | const TMPL_REACT = 'project-react'
|
21 | const TMPL_GENERAL = 'project-general'
|
22 | const BASE_DIR = 'base'
|
23 | const JS_DIR = 'js'
|
24 | const TS_DIR = 'ts'
|
25 |
|
26 | function isInGitRepository() {
|
27 | try {
|
28 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' })
|
29 | return true
|
30 | } catch (e) {
|
31 | return false
|
32 | }
|
33 | }
|
34 |
|
35 | function tryGitInit(appPath) {
|
36 | let didInit = false
|
37 |
|
38 | try {
|
39 | execSync('git --version', { stdio: 'ignore' })
|
40 |
|
41 | if (isInGitRepository()) return false
|
42 |
|
43 | execSync('git init', { stdio: 'ignore' })
|
44 | didInit = true
|
45 |
|
46 | execSync('git add -A', { stdio: 'ignore' })
|
47 | execSync('git commit -m "Initial commit from Marauder"', {
|
48 | stdio: 'ignore'
|
49 | })
|
50 |
|
51 | return true
|
52 | } catch (e) {
|
53 | if (didInit) {
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | try {
|
60 |
|
61 | fs.removeSync(path.join(appPath, '.git'))
|
62 | } catch (removeErr) {
|
63 |
|
64 | }
|
65 | }
|
66 |
|
67 | return false
|
68 | }
|
69 | }
|
70 |
|
71 |
|
72 | function getCdPath(appName, appPath, originalDirectory) {
|
73 | return path.join(originalDirectory, appName) === appPath ? appName : appPath
|
74 | }
|
75 |
|
76 | function resetGitignore(appPath) {
|
77 |
|
78 |
|
79 | try {
|
80 | fs.moveSync(
|
81 | path.join(appPath, 'gitignore'),
|
82 | path.join(appPath, '.gitignore'),
|
83 | []
|
84 | )
|
85 | } catch (err) {
|
86 |
|
87 | if (err.code === 'EEXIST') {
|
88 | const data = fs.readFileSync(path.join(appPath, 'gitignore'))
|
89 |
|
90 | fs.appendFileSync(path.join(appPath, '.gitignore'), data)
|
91 | fs.unlinkSync(path.join(appPath, 'gitignore'))
|
92 | } else {
|
93 | throw err
|
94 | }
|
95 | }
|
96 | }
|
97 |
|
98 | function copyRootFiles(tmplPath, appPath, filterFn = () => true) {
|
99 | fs.copySync(tmplPath, appPath, { filter: filterFn })
|
100 | resetGitignore(appPath)
|
101 | }
|
102 |
|
103 | function sortPackageJsonField(pkg) {
|
104 |
|
105 | pkg.dependencies = sortObject(pkg.dependencies)
|
106 | pkg.devDependencies = sortObject(pkg.devDependencies)
|
107 | pkg.scripts = sortObject(pkg.scripts, [
|
108 | 'serve',
|
109 | 'build',
|
110 | 'test',
|
111 | 'e2e',
|
112 | 'lint',
|
113 | 'deploy'
|
114 | ])
|
115 | pkg = sortObject(pkg, [
|
116 | 'name',
|
117 | 'version',
|
118 | 'private',
|
119 | 'description',
|
120 | 'author',
|
121 | 'scripts',
|
122 | 'dependencies',
|
123 | 'devDependencies',
|
124 | 'vue',
|
125 | 'babel',
|
126 | 'eslintConfig',
|
127 | 'prettier',
|
128 | 'postcss',
|
129 | 'browserslist',
|
130 | 'jest'
|
131 | ])
|
132 |
|
133 | return pkg
|
134 | }
|
135 |
|
136 | function getTmplName(type, depName) {
|
137 | const isComp = type === 'component'
|
138 |
|
139 | switch (depName) {
|
140 | case 'react':
|
141 | return TMPL_REACT
|
142 | case 'vue':
|
143 | return TMPL_VUE
|
144 | default:
|
145 | return isComp ? 'component' : TMPL_GENERAL
|
146 | }
|
147 | }
|
148 |
|
149 | function copyFiles(src, dest, pathname = '') {
|
150 | fs.copySync(path.join(src, pathname), dest)
|
151 | }
|
152 |
|
153 |
|
154 | function fillProjectContent({ tmplType, preset, useTs, ownPath, appPath }) {
|
155 | const templateName = getTmplName(tmplType, preset)
|
156 | const templatePath = path.join(ownPath, 'templates', templateName)
|
157 | const rootConfigPath = path.join(ownPath, 'templates/root-files')
|
158 | const copyProjectFiles = copyFiles.bind(null, templatePath, appPath)
|
159 |
|
160 |
|
161 |
|
162 | if (!fs.existsSync(templatePath)) {
|
163 | throw new Error(
|
164 | `Could not locate supplied template: ${chalk.green(templatePath)}`
|
165 | )
|
166 | }
|
167 |
|
168 |
|
169 | copyRootFiles(rootConfigPath, appPath, src =>
|
170 | useTs ? true : !src.includes('tsconfig.json')
|
171 | )
|
172 |
|
173 |
|
174 | copyProjectFiles(BASE_DIR)
|
175 |
|
176 |
|
177 | copyProjectFiles(useTs ? TS_DIR : JS_DIR)
|
178 | }
|
179 |
|
180 | function getGitUserInfo() {
|
181 | try {
|
182 | const { stdout: name } = execa.sync('git', ['config', 'user.name'])
|
183 | const { stdout: email } = execa.sync('git', ['config', 'user.email'])
|
184 |
|
185 | return `${name} <${email}>`
|
186 | } catch (e) {
|
187 | return ''
|
188 | }
|
189 | }
|
190 |
|
191 | module.exports = function({
|
192 | appPath,
|
193 | appName,
|
194 | preset,
|
195 | useTs,
|
196 | originalDirectory,
|
197 | tmplType
|
198 | }) {
|
199 | const ownPath = path.dirname(
|
200 | require.resolve(path.join(__dirname, '..', 'package.json'))
|
201 | )
|
202 | let appPackage = require(path.join(appPath, 'package.json'))
|
203 | const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock'))
|
204 |
|
205 |
|
206 | appPackage.dependencies = appPackage.dependencies || {}
|
207 |
|
208 |
|
209 | appPackage.scripts = {
|
210 | dev: 'marax dev',
|
211 | build: 'marax build',
|
212 | test: 'marax test'
|
213 | }
|
214 |
|
215 |
|
216 | appPackage.eslintConfig = {
|
217 | extends: 'eslint-config-sinamfe'
|
218 | }
|
219 |
|
220 | appPackage.author = getGitUserInfo()
|
221 |
|
222 | appPackage = sortPackageJsonField(appPackage)
|
223 |
|
224 |
|
225 | fs.writeFileSync(
|
226 | path.join(appPath, 'package.json'),
|
227 | JSON.stringify(appPackage, null, 2) + os.EOL
|
228 | )
|
229 |
|
230 | const readmeExists = fs.existsSync(path.join(appPath, 'README.md'))
|
231 |
|
232 | if (readmeExists) {
|
233 | fs.renameSync(
|
234 | path.join(appPath, 'README.md'),
|
235 | path.join(appPath, 'README.old.md')
|
236 | )
|
237 | }
|
238 |
|
239 | fillProjectContent({
|
240 | tmplType: 'project',
|
241 | useTs,
|
242 | preset,
|
243 | ownPath,
|
244 | appPath
|
245 | })
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 | if (tryGitInit(appPath)) {
|
252 | console.log()
|
253 | console.log('Initialized a git repository.')
|
254 | }
|
255 |
|
256 |
|
257 | const runScript = useYarn ? 'yarn' : 'npm run'
|
258 |
|
259 | console.log()
|
260 | console.log(`🎉 Successfully created project ${chalk.yellow(appName)}.`)
|
261 | console.log('👉 Inside that directory, you can run several commands:\n')
|
262 |
|
263 | console.log(chalk.cyan(` ${runScript} dev`))
|
264 | console.log(' Starts the development server.\n')
|
265 |
|
266 | console.log(chalk.cyan(` ${runScript} build`))
|
267 | console.log(' Bundles the app into static files for production.\n')
|
268 |
|
269 | console.log(chalk.cyan(` ${runScript} test`))
|
270 | console.log(' Starts the test runner.\n')
|
271 |
|
272 | console.log('👉 Get started with the following commands:\n')
|
273 |
|
274 | console.log(
|
275 | chalk.cyan(` cd ${getCdPath(appName, appPath, originalDirectory)}`)
|
276 | )
|
277 | console.log(` ${chalk.cyan(`${runScript} dev`)}\n`)
|
278 |
|
279 | if (readmeExists) {
|
280 | console.log(
|
281 | chalk.yellow(
|
282 | 'You had a `README.md` file, we renamed it to `README.old.md`\n'
|
283 | )
|
284 | )
|
285 | }
|
286 |
|
287 | console.log('😎 Happy hacking!')
|
288 | }
|