【javascript】wokoo脚手架搭建
前言支持下这位兄弟写的脚手架,这个脚手架是开发油猴脚本的。需要使用的直接去下wokoo-script 不用看下面的搭建。流程这是一个lerna项目,在lerna init 后,package.json增加:"workspaces": ["packages/*"],创建2个子项目:lerna create wokoo-scriptslerna create wokoo-template一个用来做脚手
·
前言
- 支持下这位兄弟写的脚手架,这个脚手架是开发油猴脚本的。
- 需要使用的直接去下wokoo-script 不用看下面的搭建。
流程
- 这是一个lerna项目,在lerna init 后,package.json增加:
"workspaces": [
"packages/*"
],
- 创建2个子项目:
lerna create wokoo-scripts
lerna create wokoo-template
- 一个用来做脚手架命令,一个用来存放模板。
- 在script的bin目录下建www
#! /usr/bin/env node
require('../index.js');
- 老样子,package.json里放执行语句:
"bin": {
"wokoo": "./bin/www"
},
- index.js:
const chalk = require("chalk");
const spawn = require("cross-spawn");
const { Command } = require("commander");
const fs = require("fs-extra");
const path = require("path");
const inquirer = require("inquirer");
const packageJson = require("./package.json");
const modifyTemplate = require("./modifyTemplate"); // 修改替换ejs模板内字段
let program = new Command();
init();
// 程序入口,读取命令行脚本,获得项目名称
async function init() {
let projectName;
program
.version(packageJson.version)
.arguments("<project-directory>") // 项目目录名 参数格式:<必选> [可选]
.usage(`${chalk.green(`<project-directory>`)}`)
.action((name) => {
projectName = name;
})
.parse(process.argv); // [node路径,脚本路径,参数]
await createApp(projectName);
}
/**
* 根据appName生成项目目录
* @param {*} appName
*/
async function createApp(appName) {
let root = path.resolve(appName); // 要生成的项目的绝对路径
fs.ensureDirSync(appName); // 没有则创建文件夹
console.log(`create a new app in ${chalk.green(root)}`);
// 初始化package.json
const packageJson = {
name: appName,
version: "0.0.1",
private: true,
scripts: {
start: "cross-env NODE_ENV=development webpack serve",
build: "webpack",
},
};
// 写入package.json
fs.writeFileSync(
path.join(root, "package.json"),
JSON.stringify(packageJson, null, 2)
);
// 改变工作目录,进入项目目录
process.chdir(root);
// 复制项目模板,安装项目依赖等
await run(root, appName);
}
/**
* 1、安装wokoo-template
* 2、复制模板文件到临时文件夹temp,并替换其中的ejs模板
* 3、删除临时文件夹temp
* 4、卸载wokoo-template
* @param {*} root 项目路径
* @param {*} appName 项目名
*/
async function run(root, appName) {
const templateName = "wokoo-template"; // 对应的wokoo模板
const allDependencies = [templateName];
// 安装wokoo-template包
console.log("Installing packages. This might take a couple of minutes");
console.log(`Installing ${chalk.cyan(templateName)} ...`);
try {
await doAction(root, allDependencies);
} catch (e) {
console.log(`Installing ${chalk.red(templateName)} failed ...`, e);
}
console.log(`Installing ${chalk.cyan(templateName)} succeed!`);
// 选择模板
const repos = ["vue", "react"];
const { targetTemplate } = await inquirer.prompt({
name: "targetTemplate",
type: "list",
message: "which template do you prefer?",
choices: repos, // 选择模式
});
const templatePath = path.dirname(
require.resolve(`${templateName}/package.json`, { paths: [root] })
);
// 复制文件到项目目录
const scriptsConfigDir = path.join(templatePath, "webpack.config.js");
const gitIgnoreDir = path.join(templatePath, ".npmignore");
const publicDir = path.join(templatePath, "public");
const tempDir = path.join(root, "temp"); // 临时模板路径
const templateDir = path.join(templatePath, `${targetTemplate}-template`);
// 从wokoo-template中拷贝模板到项目目录
if (fs.existsSync(templatePath)) {
// 将templateDir内模板拷贝到temp文件,并修改模板文件中的ejs配置项
await modifyTemplate(templateDir, "temp", {
projectName: appName,
basicProject: targetTemplate,
});
fs.copySync(tempDir, root); // 源 目标
fs.copySync(publicDir, root + "/public");
fs.copyFileSync(scriptsConfigDir, root + "/webpack.config.js");
fs.copyFileSync(gitIgnoreDir, root + "/.gitignore");
deleteFolder(tempDir);
} else {
console.error(
`Could not locate supplied template: ${chalk.green(templatePath)}`
);
return;
}
// 合并template.json和package.json
let tempPkg = fs.readFileSync(root + "/template.json").toString();
let pkg = fs.readFileSync(root + "/package.json").toString();
const tempPkgJson = JSON.parse(tempPkg);
const pkgJson = JSON.parse(pkg);
pkgJson.dependencies = {
...pkgJson.dependencies,
...tempPkgJson.package.dependencies,
};
pkgJson.devDependencies = {
...tempPkgJson.package.devDependencies,
};
// 编写package.json
fs.writeFileSync(
path.join(root, "package.json"),
JSON.stringify(pkgJson, null, 2)
);
fs.unlinkSync(path.join(root, "template.json")); // 删除template.json文件
// 再次根据dependenciesToInstall执行npm install
const dependenciesToInstall = Object.entries({
...pkgJson.dependencies,
...pkgJson.devDependencies,
});
let newDependencies = [];
if (dependenciesToInstall.length) {
newDependencies = newDependencies.concat(
dependenciesToInstall.map(([dependency, version]) => {
return `${dependency}@${version}`;
})
);
}
await doAction(root, newDependencies);
console.log(`${chalk.cyan("Installing succeed!")}`);
// 卸载wokoo-template
await doAction(root, "wokoo-template", "uninstall");
console.log(`🎉 Successfully created project ${appName}.`);
console.log("👉 Get started with the following commands:");
console.log(`${chalk.cyan(`cd ${appName}`)}`);
console.log(`${chalk.cyan("$ npm start")}`);
process.exit(0);
}
/**
* 使用npm安装或卸载项目依赖
* @param {*} root 项目路径
* @param {*} allDependencies 项目依赖
* @param {*} action npm install 或 npm uninstall
*/
async function doAction(root, allDependencies, action = "install") {
typeof allDependencies === "string"
? (allDependencies = [allDependencies])
: null;
return new Promise((resolve) => {
const command = "npm";
const args = [
action,
"--save",
"--save-exact",
"--loglevel",
"error",
...allDependencies,
"--cwd",
root,
];
const child = spawn(command, args, { stdio: "inherit" });
child.on("close", resolve); // 安装成功后触发resolve
});
}
/**
* 删除文件、文件夹
* @param {*} path 要删除资源的路径
*/
function deleteFolder(path) {
let files = [];
if (fs.existsSync(path)) {
if (!fs.statSync(path).isDirectory()) {
// path是文件,直接删除
fs.unlinkSync(path);
} else {
// 删除文件夹
files = fs.readdirSync(path);
files.forEach(function (file) {
let curPath = path + "/" + file;
deleteFolder(curPath);
});
fs.rmdirSync(path);
}
}
}
- 主要原理就是让用户去选择模板,然后写入package.json,npm安装,安装后,在nodemodule里就有了模板文件,再让用户选择模板,把对应的文件复制到项目里用ejs渲染即可。
- 我以前写的脚手架没发成npm包,直接调github地址拉,容易产生网络问题导致安装失败,发成npm包再去整就比较好。
- 最后发布时,改写package.json的file字段即可。
更多推荐
所有评论(0)