npm init vite
Need to install the following packages:
create-vite
Ok to proceed? (y) y
√ Project name: ... vite-project
√ Select a framework: » react
√ Select a variant: » react
Scaffolding project in C:\aprepare\t1\vite-project...
Done. Now run:
cd vite-project
npm install
npm run dev
git clone https://github.com/vitejs/vite.git
cd vite
yarn install
packages\create-vite\index.js
.vscode\launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\packages\\create-vite\\index.js",
"args": ["create","vite-project"]
}
]
}
node
命令 create-react-appgitub
和gitee
仓库动态读取mkdir vite2
cd vite2
lerna init
node_modules
下,节省磁盘空间,且给了yarn更大的依赖优化空间{
"packages": [
"packages/*"
],
"version": "0.0.0",
+ "npmClient": "yarn",
+ "useWorkspaces": true
}
{
"name": "root",
"private": true,
"devDependencies": {
"lerna": "^4.0.0"
},
+ "workspaces": [
+ "packages/*"
+ ]
}
lerna create @vite2/config -y //配置项
lerna create @vite2/create -y //创建项目
lerna create vite2 -y //核心命令
lerna create @vite2/settings -y //常量定义
lerna create @vite2/utils -y //工具方法
packages\config\package.json
{
"dependencies": {
"@vite2/settings": "^0.0.0",
"@vite2/utils": "^0.0.0",
"fs-extra": "^10.0.0",
"userhome": "^1.0.0"
}
}
packages\create\package.json
{
"dependencies": {
"@vite2/settings": "^0.0.0",
"@vite2/utils": "^0.0.0",
"chalk": "^4.1.2",
"clone-git-repo": "^0.0.2",
"ejs": "^3.1.6",
"execa": "^5.1.1",
"fs-extra": "^10.0.0",
"glob": "^7.1.7",
"inquirer": "^8.1.2"
},
}
packages\utils\package.json
{
"dependencies": {
"@vite2/settings": "^0.0.0",
"axios": "^0.21.2",
"cross-spawn": "^7.0.3",
"userhome": "^1.0.0"
},
}
packages\vite2\package.json
{
"dependencies": {
"@vite2/config": "^0.0.0",
"@vite2/create": "^0.0.0"
}
}
{
"publishConfig": {
"access": "public",
"registry": "http://registry.npmjs.org"
}
}
packages\vite2\package.json
{
"name": "vite2",
"version": "0.0.0",
"dependencies": {
"@vite2/config":"^0.0.0",
"@vite2/create":"^0.0.0"
},
+ "bin":{
+ "vite2": "index.js"
+ }
}
packages\vite2\index.js
#!/usr/bin/env node
async function main() {
let argv = process.argv.slice(2);
console.log(argv);
}
main().catch((err) => {
console.error(err);
});
#!/usr/bin/env node
再link,否则会用文本编辑器打开vite2\packages\vite2
目录中执行yarn unlink
,再重新linkyarn link
yarn global bin
C:\Users\zhangrenyang\AppData\Local\Yarn\bin
C:\Users\zhangrenyang\AppData\Local\Yarn\Data\link\vite2
npm bin -g
C:\Users\zhangrenyang\AppData\Roaming\npm
vite2 create vite-project
yarn workspace @vite2/config add userhome fs-extra
yarn workspace @vite2/utils add cross-spawn userhome fs-extra
packages\settings\index.js
//执行命令脚本
exports.COMMAND_SOURCE = `
const args = JSON.parse(process.argv[1]);
const factory = require('.');
factory(args);
`
//配置文件名称
exports.RC_NAME = ".vite3rc";
packages\utils\config.js
const userhome = require("userhome");
const fs = require("fs-extra");
const { RC_NAME } = require("@vite5/settings");
const configPath = userhome(RC_NAME);
let config = {};
if (fs.existsSync(configPath)) {
config = fs.readJSONSync(configPath);
}
config.configPath=configPath;
module.exports = config;
packages\utils\executeNodeScript.js
const spawn = require("cross-spawn");
async function executeNodeScript({ cwd }, source, args) {
return new Promise((resolve) => {
const childProcess = spawn(
process.execPath,
["-e", source, "--", JSON.stringify(args)],
{ cwd, stdio: "inherit" }
);
childProcess.on("close", resolve);
});
}
module.exports = executeNodeScript;
packages\utils\log.js
const log = require('npmlog');
log.heading = 'vite2';
module.exports = log;
packages\utils\index.js
exports.log = require('./log');
exports.executeNodeScript = require('./executeNodeScript');
exports.config = require('./config');
packages\config\command.js
const {executeNodeScript} = require('@vite2/utils');
const {COMMAND_SOURCE} = require('@vite2/settings');
const command = {
command: "config [key] [value]",
describe: "设置或查看配置项,比如GIT_TYPE设置仓库类型,ORG_NAME设置组织名",
builder: (yargs) => {},
handler:async function(argv){
await executeNodeScript({ cwd: __dirname }, COMMAND_SOURCE,argv);
},
};
module.exports = command;
packages\config\index.js
const fs = require("fs-extra");
const { log ,config} = require("@vite2/utils");
async function factory(argv) {
const { key, value } = argv;
console.log('config',config);
if (key && value) {
config[key] = value;
await fs.writeJSON(config.configPath, config, { spaces: 2 });
log.info("vite3","(%s=%s)配置成功保存至%s", key, value, config.configPath);
}else if(key){
console.log('%s=%s',key, config[key]);
}else{
console.log(config);
}
}
module.exports = factory;
packages\vite2\index.js
#!/usr/bin/env node
const yargs = require("yargs/yargs");
const configCmd = require("@vite2/config/command");
async function main() {
const cli = yargs();
cli
.usage(`Usage: vite2 <command> [options]`)
.demandCommand(1, "至少需要一个命令")
.strict()
.recommendCommands()
+ .command(configCmd)
.parse(process.argv.slice(2));
}
main().catch((err) => {
console.error(err);
});
packages\create\command.js
const {COMMAND_SOURCE} = require('@vite2/settings');
const {executeNodeScript} = require('@vite2/utils');
const command = {
command: "create <name>",
describe: "创建项目",
builder: (yargs) => {
yargs.positional("name", {
type: "string",
describe: "项目名称",
});
},
handler:async function(argv){
let args = {name:argv.name,cwd:process.cwd()};
await executeNodeScript({ cwd: __dirname }, COMMAND_SOURCE,args);
}
};
module.exports = command;
packages\create\index.js
async function factory(argv) {
console.log('create',argv);
}
module.exports = factory;
packages\vite2\index.js
#!/usr/bin/env node
const yargs = require("yargs/yargs");
const configCmd = require("@vite2/config/command");
+const createCmd = require("@vite2/create/command");
async function main() {
const cli = yargs();
cli
.usage(`Usage: vite2 <command> [options]`)
.demandCommand(1, "至少需要一个命令")
.strict()
.recommendCommands()
.command(configCmd)
+ .command(createCmd)
.parse(process.argv.slice(2));
}
main().catch((err) => {
console.error(err);
});
yarn workspace @vite2/create add chalk fs-extra inquirer
packages\create\index.js
+const path = require("path");
+const { red } = require("chalk");
+const { prompt } = require("inquirer");
+const fs = require("fs-extra");
+const {config, log } = require("@vite2/utils");
async function factory(argv) {
+ const { cwd, name } = argv;
+ process.chdir(cwd);//切换为当前命令执行的工作目录
+ const { ORG_NAME } = config;
+ if (!ORG_NAME) {
+ throw new Error(red("X") + " 尚未配置组织名称!");
+ }
+ const targetDir = path.join(process.cwd(), name);
+ log.info("vite2", "创建的项目目录为%s", targetDir);
+ try {
+ await fs.access(targetDir);
+ const files = await fs.readdir(targetDir);
+ if (files.length > 0) {
+ const { overwrite } = await prompt({
+ type: "confirm",
+ name: "overwrite",
+ message: `目标目录非空,是否要移除存在的文件并继续?`,
+ });
+ if (overwrite) {
+ await fs.emptyDir(targetDir);
+ } else {
+ throw new Error(red("X") + " 操作被取消");
+ }
+ }
+ } catch (error) {
+ await fs.mkdirp(targetDir);
+ }
+ log.info("vite3", "%s目录已经准备就绪", targetDir);
}
module.exports = factory;
yarn workspace @vite2/create add clone-git-repo
yarn workspace @vite2/utils add axios
packages\settings\index.js
//执行命令脚本
exports.COMMAND_SOURCE = `
const args = JSON.parse(process.argv[1]);
const factory = require('.');
factory(args);
`
+//配置文件名称
+exports.RC_NAME = ".vite3rc";
+//组织的名称
+exports.ORG_NAME = "ORG_NAME";
+//git仓库类型
+exports.GIT_TYPE = "GIT_TYPE";
+//模板存放名称
+exports.TEMPLATES = ".vite3_templates";
packages\utils\request.js
const axios = require("axios");
const { GIT_TYPE } = require("./config");
const GITEE = "https://gitee.com/api/v5";
const GITHUB = "https://api.github.com";
const BASE_URL = GIT_TYPE === "gitee" ? GITEE : GITHUB;
const request = axios.create({
baseURL: BASE_URL,
timeout: 5000,
});
request.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
module.exports = request;
packages\utils\withLoading.js
async function withLoading(message, fn, ...args) {
const ora = await import("ora");
const spinner = ora.default(message);
spinner.start();
const result = await fn(...args);
spinner.succeed();
return result;
}
module.exports = withLoading;
packages\utils\index.js
exports.log = require('./log');
exports.executeNodeScript = require('./executeNodeScript');
exports.config = require('./config');
+exports.withLoading = require('./withLoading');
+exports.request = require('./request');
packages\create\index.js
const path = require("path");
+const { promisify } = require("util");
const { red } = require("chalk");
const fs = require("fs-extra");
+const { prompt } = require("inquirer");
+const userhome = require("userhome");
+const clone = promisify(require('clone-git-repo'));
+const { TEMPLATES } = require("@vite2/settings");
+const { config, log,withLoading, request} = require("@vite2/utils");
async function factory(argv) {
const { cwd, name } = argv;
process.chdir(cwd);//切换为当前命令执行的工作目录
+ const { GIT_TYPE,ORG_NAME } = config;
if (!ORG_NAME) {
throw new Error(red("X") + " 尚未配置组织名称!");
}
const targetDir = path.join(process.cwd(), name);
log.info("vite2", "创建的项目目录为%s", targetDir);
try {
await fs.access(targetDir);
const files = await fs.readdir(targetDir);
if (files.length > 0) {
const { overwrite } = await prompt({
type: "confirm",
name: "overwrite",
message: `目标目录非空,是否要移除存在的文件并继续?`,
});
if (overwrite) {
await fs.emptyDir(targetDir);
} else {
throw new Error(red("X") + " 操作被取消");
}
}
} catch (error) {
await fs.mkdirp(targetDir);
}
log.info("vite3", "%s目录已经准备就绪", targetDir);
+ let repos = await withLoading("读取模板列表", async () =>
+ request.get(`/orgs/${ORG_NAME}/repos`)
+ );
+ let { repo } = await prompt({
+ name: "repo",
+ type: "list",
+ message: "请选择模板",
+ choices: repos.map((repo) => repo.name)
+ });
+ let tags = await withLoading("读取标签列表", async () =>
+ request.get(`/repos/${ORG_NAME}/${repo}/tags`)
+ );
+ let { tag } = await prompt({
+ name: "tag",
+ type: "list",
+ message: "请选择版本",
+ choices: tags,
+ });
+ let repository = GIT_TYPE + `:${ORG_NAME}/${repo}`;
+ if(tag)repository+=`#${tag}`;
+ const downloadDirectory = userhome(TEMPLATES);
+ const repoDirectory = `${downloadDirectory}/${repo}/${tag}`;
+ log.info("vite3", "准备下载模板到%s", repoDirectory);
+ try {
+ await fs.access(repoDirectory);
+ } catch (error) {
+ log.info("vite2", "从仓库下载%s", repository);
+ await clone(repository,repoDirectory, {clone:true});
+ }
}
module.exports = factory;
yarn workspace @vite2/create add ejs glob execa
ask.json
[
{
"name": "projectName",
"type":"text",
"message":"请输入项目名称"
},
{
"name": "projectVersion",
"type": "text",
"message": "请输入项目版本"
}
]
packages\settings\index.js
//执行命令脚本
exports.COMMAND_SOURCE = `
const args = JSON.parse(process.argv[1]);
const factory = require('.');
factory(args);
`
//配置文件名称
exports.RC_NAME = ".vite3rc";
//组织的名称
exports.ORG_NAME = "ORG_NAME";
//git仓库类型
exports.GIT_TYPE = "GIT_TYPE";
//模板存放名称
exports.TEMPLATES = ".vite3_templates";
+//重命名文件配置
+exports.RENAME_FILES = {
+ _gitignore: '.gitignore'
+}
packages\create\index.js
const path = require("path");
const { promisify } = require("util");
const { red } = require("chalk");
const fs = require("fs-extra");
const { prompt } = require("inquirer");
const userhome = require("userhome");
const clone = promisify(require('clone-git-repo'));
+const glob = promisify(require("glob"));
+const { render } = require("ejs");
+const execa = require("execa");
+const { TEMPLATES,RENAME_FILES } = require("@vite2/settings");
const { config, log, withLoading, request } = require("@vite2/utils");
async function factory(argv) {
const { cwd, name } = argv;
process.chdir(cwd);//切换为当前命令执行的工作目录
const { GIT_TYPE, ORG_NAME } = config;
if (!ORG_NAME) {
throw new Error(red("X") + " 尚未配置组织名称!");
}
const targetDir = path.join(process.cwd(), name);
log.info("vite2", "创建的项目目录为%s", targetDir);
try {
await fs.access(targetDir);
const files = await fs.readdir(targetDir);
if (files.length > 0) {
const { overwrite } = await prompt({
type: "confirm",
name: "overwrite",
message: `目标目录非空,是否要移除存在的文件并继续?`,
});
if (overwrite) {
await fs.emptyDir(targetDir);
} else {
throw new Error(red("X") + " 操作被取消");
}
}
} catch (error) {
await fs.mkdirp(targetDir);
}
log.info("vite3", "%s目录已经准备就绪", targetDir);
let repos = await withLoading("读取模板列表", async () =>
request.get(`/orgs/${ORG_NAME}/repos`)
);
let { repo } = await prompt({
name: "repo",
type: "list",
message: "请选择模板",
choices: repos.map((repo) => repo.name)
});
let tags = await withLoading("读取标签列表", async () =>
request.get(`/repos/${ORG_NAME}/${repo}/tags`)
);
let { tag } = await prompt({
name: "tag",
type: "list",
message: "请选择版本",
choices: tags,
});
let repository = GIT_TYPE + `:${ORG_NAME}/${repo}`;
if (tag) repository += `#${tag}`;
const downloadDirectory = userhome(TEMPLATES);
const repoDirectory = `${downloadDirectory}/${repo}/${tag}`;
log.info("vite3", "准备下载模板到%s", repoDirectory);
try {
await fs.access(repoDirectory);
} catch (error) {
log.info("vite2", "从仓库下载%s", repository);
await clone(repository, repoDirectory, { clone: true });
}
+ let ask = path.join(repoDirectory, "ask.json");
+ if (fs.existsSync(ask)) {
+ const askOptions = await fs.readJSON(ask);
+ const result = await prompt(askOptions);
+ const files = await glob(`**/*`, {
+ cwd: repoDirectory,
+ ignore: ['ask.json'],
+ nodir: true
+ });
+ await Promise.all(files.map(file => new Promise(async function (resolve) {
+ let content = await fs.readFile(path.join(repoDirectory, file), 'utf8');
+ let renderContent = await render(content, result);
+ let targetName = RENAME_FILES[file] || file;
+ let targetFile = path.join(targetDir, targetName);
+ await fs.ensureDir(path.dirname(targetFile));
+ await fs.writeFile(targetFile, renderContent, 'utf8');
+ resolve();
+ })));
+ } else {
+ await fs.copy(repoDirectory, targetDir);
+ }
+ process.chdir(targetDir);
+ log.info("vite3", "在%s初始化 Git 仓库", targetDir);
+ await execa("git", ["init"], { stdio: "inherit" });
+ log.info("vite3", "在%s安装依赖", targetDir);
+ await execa("npm", ["install"], { stdio: "inherit" });
+ log.info("vite3", "启动服务");
+ await execa("node", ["./node_modules/esbuild/install.js"], {
+ stdio: "inherit",
+ });
+ await execa("npm", ["run", "dev"], { stdio: "inherit" });
}
module.exports = factory;
package.json
{
"publishConfig": {
"access": "public",
"registry": "http://registry.npmjs.org"
}
}
npm whoami
npm login
zhangrenyang2000
lerna publish
命令 | 功能 |
---|---|
lerna bootstrap | 安装依赖 |
lerna clean | 删除各个包下的node_modules |
lerna init | 创建新的lerna库 |
lerna list | 查看本地包列表 |
lerna changed | 显示自上次release tag以来有修改的包, 选项通 list |
lerna diff | 显示自上次release tag以来有修改的包的差异, 执行 git diff |
lerna exec | 在每个包目录下执行任意命令 |
lerna run | 执行每个包package.json中的脚本命令 |
lerna add | 添加一个包的版本为各个包的依赖 |
lerna import | 引入package |
lerna link | 链接互相引用的库 |
lerna create | 新建package |
lerna publish | 发布 |
命令 | 说明 |
---|---|
yarn -v | 查看yarn版本 |
yarn config list | 查看yarn的所有配置 |
yarn config set registry https://registry.npm.taobao.org/ | 修改yarn的源镜像为淘宝源 |
yarn config set global-folder "D:\RTE\Yarn\global" | 修改全局安装目录, 先创建好目录(global), 我放在了Yarn安装目录下(D:\RTE\Yarn\global) |
yarn config set prefix "D:\RTE\Yarn\global\" | 修改全局安装目录的bin目录位置 |
yarn config set cache-folder "D:\RTE\Yarn\cache" | 修改全局缓存目录, 先创建好目录(cache), 和global放在同一层目录下 |
yarn config list | 查看所有配置 |
yarn global bin | 查看当前yarn的bin的位置 |
yarn global dir | 查看当前yarn的全局安装位置 |
作用 | 命令 |
---|---|
查看工作空间信息 | yarn workspaces info |
给所有的空间添加依赖 | yarn workspaces run add lodash |
给根空间添加依赖 | yarn add -W -D typescript jest |
给某个项目添加依赖 | yarn workspace create-react-app3 add commander |
删除所有的 node_modules | lerna clean 等于 yarn workspaces run clean |
安装和link所有的名 | yarn install 等于 lerna bootstrap --npm-client yarn --use-workspaces |
重新获取所有的 node_modules | yarn install --force |
查看缓存目录 | yarn cache dir |
清除本地缓存 | yarn cache clean |
在所有package中运行指定的命令 | yarn workspaces run |
const yargs = require("yargs/yargs");
const cli = yargs();
cli
.usage(`Usage: vite2 <command> [options]`)
.demandCommand(1, "至少需要一个命令")
.strict()
.recommendCommands()
.command({
command: "create <name>",
describe: "创建项目",
builder: (yargs) => {
yargs.positional("name", {
type: "string",
describe: "项目名称",
});
},
handler: async function (argv) {
console.log(argv);
//{ _: [ 'create' ], '$0': 'doc\\1.yargs.js', name: 'p1' }
}
})
.parse(process.argv.slice(2));
stdion: 'inherit'
,当执行代码时,子进程将会继承主进程的stdin、stdout和stderrnode -e "console.log(process.argv)" -- a b
node -e "console.log(JSON.parse(process.argv[1]))" -- "{\"name\":\"zhufeng\"}"
node -e "console.log(process.cwd())"
const spawn = require("cross-spawn");
async function executeNodeScript({ cwd }, source, args) {
return new Promise((resolve) => {
const childProcess = spawn(
process.execPath,
["-e", source, "--", JSON.stringify(args)],
{ cwd, stdio: "inherit" }
);
childProcess.on("close", resolve);
});
}
module.exports = executeNodeScript;
const clone = require('clone-git-repo');
let repository = 'gitee:zhufengtemplate/template-react#v1.0';
clone(repository,'./output', {clone:true},function (err) {
console.log(err);
})