项目脚手架

构建流程

  • 检查目录文件
  • 获取初始化参数(包括项目名称、版本等信息)
  • 获取项目模板(包括本地模板和 github 模板)
  • 项目中填充参数生成项目

检查目录

递归获取对应文件夹下的文件列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function traverseDirectory(pathname) {
const realPath = resolveRootPath(pathname)
if (isDirectory(realPath)) {
const result = []
const list = fs.readdirSync(realPath)
const len = list.length
for (let i = 0; i < len; i++) {
const tempPath = path.resolve(pathname, list[i])
if (isDirectory(list[i])) {
const tempArr = traverseDirectory(tempPath)
result.push(...tempArr)
} else {
result.push(tempPath)
}
}
return result
} else {
return []
}
}

初始化参数

使用 inquirer 通过提问的方式获取对应的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
async function getProjectConfig(pathname) {
const defaultProjectName = (pathname || process.cwd())
.split(/(\/|\\)/)
.reverse()
.filter(Boolean)[0]

const answers = await inquirer.prompt([
{
name: 'name',
message: '请输入项目名称',
default: defaultProjectName,
},
{
name: 'version',
message: '请输入版本',
default: '1.0.0',
},
{
name: 'author',
message: '请输入创建人',
},
{
name: 'template',
message: '请选择模板',
type: 'list',
choices: Object.keys(templateMap).map((key) => ({
key,
value: key,
name: templateMap[key].label,
})),
},
])
return answers
}

获取项目模板

通过初始化参数判断使用的模板类型,若为本地模板则返回文件路径,若为 git 仓库模板则先下载保存在临时文件夹下,返回文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function getTemplate(key) {
const temp = templateMap[key]
if (temp && temp.isGit) {
try {
rm(downloadTempRootPath)
const target = await downloadTemplate(temp.path, downloadTempRootPath, {
clone: true,
})
return target
} catch (e) {
error('\ngit 拉取模板失败,失败原因:', e)
return ''
}
}
return temp.path
}

项目中填充参数生成项目

读取模板文件填充模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async function generateProject(source, destination, config) {
return new Promise((resolve, reject) => {
metalsmith(process.cwd())
.metadata(config)
.clean(false)
.source(source)
.destination(destination)
.use((files, metalsmith, done) => {
const meta = metalsmith.metadata()
Object.keys(files).forEach((fileName) => {
const t = files[fileName].contents.toString()
files[fileName].contents = Buffer.from(Handlebars.compile(t)(meta))
})
done()
})
.build((err) => {
rm(process.cwd())
if (err) {
reject(err)
} else {
resolve()
}
})
})
}

相关库

todo

  • 更多模板
  • 优化模板功能
  • package.json 字段详解