创建发布一个cli工具

1.初始化项目

npm init -y

2.创建目录bin,里面创建inde.js

#!/usr/bin/env node
// 上面的不是注释,非常重要,告诉命令使用node环境运行本文件
console.log("hellow cli...")

3.package.json中配置bin命令

...
"main": "index.js",
"bin": {
  "kun": "./bin/index.js"
},
...

4.执行命令,创建软链接,把bin目录的路径暴露出来

npm link

成功后,在命令行尝试执行

kun

控制台打印 hellow cli...

  1. 安装辅助的依赖 ,commander node.js命令行界面的完整解决方案
npm install commander -S

修改bin/index.js

#!/usr/bin/env node
// 上面的不是注释,非常重要,告诉命令使用node环境运行本文件
const program=require('commander');
program.version(require('./../package.json').version);
program.command("create <name>")

    .description("create a koa project")
    .action((name)=>{
        console.log("name: "+name)        
    })
// 这行代码是解析参数
program.parse(process.argv)

执行kun create hellow,打印name: hellow
现在支持 kun create <name>,kun -V,而且action中可以执行一下操作,并且能得到用户传入的参数。
6.安装 chalk.js 控制台粉笔工具,方便改变打印的颜色

npm install chalk -S

修改/bin/index.js,action中的function换成init导出的function

...
   .description("create a koa project")
   .action(require('./init'))
program.parse(process.argv)

根目录创建init.js ,主要功能就是创建一个文件夹,里面创建一个index.js和一个package.json。

const fs = require("fs");
const chalk = require("chalk");
const path = require("path");
const createLog = (type) => (content) => console.log(chalk[type](content));
const success = createLog("green");
const errorLog = createLog("red");
function getFilePath(name) {
  return `./${name}`;
}
// 删除目录
function delDir(path) {
  let files = [];

  if (fs.existsSync(path)) {
    files = fs.readdirSync(path);

    files.forEach((file) => {
      let curPath = path + "/" + file;

      if (fs.statSync(curPath).isDirectory()) {
        delDir(curPath); //递归删除文件夹
      } else {
        fs.unlinkSync(curPath); //删除文件
      }
    });

    fs.rmdirSync(path);
  }
}
// 这个name值是index.js中,`program.parse(argv)`解析的
module.exports = async (name) => {
  const basePath = getFilePath(name);
  if (fs.existsSync(basePath)) {
    errorLog("同名文件已经存在");
    return false;
  }
  //   创建目录
  fs.mkdirSync(basePath);
  try {
    fs.writeFileSync(`${basePath}/index.js`, "index");
    fs.writeFileSync(`${basePath}/package.json`,'package');
    success("创建成功");
  } catch (error) {
    //   删除目录及文件
    delDir(basePath);
    errorLog("创建失败!");
  }
};

7.有内味了。就是文件生成的确实low b点。安装ejs高效的嵌入式 JavaScript 模板引擎
首先要创建模板目录template。里面创建index.ejs和package.ejs

//index.ejs
const Koa = require('koa');

<% if (middleware.router) { %>    
const Router = require('koa-router');
<% } %>
<% if (middleware.static) { %>    
const serve = require("koa-static");
<% } %>
<% if (middleware.views) { %>    
const views = require("koa-views");
<% } %>
<% if (middleware.body) { %>    
const body = require('koa-body');
<% } %>

const app = new Koa();

<% if (middleware.static) { %>
app.use(serve(__dirname, '/static'));

<% } %>
<% if (middleware.views) { %>    
app.use(views(__dirname, '/views'), {
    extension: 'pug'
})
<% } %>

<% if (middleware.body) { %>    
app.use(body({
    multipart: true
}))
<% } %>

<% if (middleware.router) { %>    
const router = new Router();
router.get('/', ctx => {
    ctx.body = "hellow world"
})
app.use(router.routes());
<% } %>


app.listen(<%= port %>, () => {
    console.log("服务启动在localhost:<%= port %>")
})
//package.ejs
{
"name": "template",
"version": "1.0.0",
"description": "",
"main": "<%= packageName %>.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
    "koa": "^2.13.1"
    <% if (middleware.body) { %>
        ,"koa-body": "^4.2.0"
        <% } %>
            <% if (middleware.router) { %>
                , "koa-router": "^10.0.0"
                <% } %>
                    <% if (middleware.static) { %>
                        ,"koa-static": "^5.0.0"
                        <% } %>
                            <% if (middleware.views) { %>
                                ,"koa-views": "^7.0.1"
                                <% } %>
                                    }
                                    }

创建目录createCode ,里面创建文件 inde.jscreateIndexPage.jscreatePackagePage.js

//index.js
const createIndexPage =require('./createIndexPage')
const createPackagePage =require('./createPackagePage')
module.exports.createIndexPage=createIndexPage
module.exports.createPackagePage=createPackagePage
//createIndexPage.js
const ejs = require("ejs");
const fs = require("fs");
const path = require("path");
module.exports = () => {
 const content = fs
   .readFileSync(path.join(__dirname, "../template/index.ejs"))
   .toString();
 const code = ejs.render(content, { middleware: {}, port: 3000 });
 return code;
};

// createPackagePage.js
const ejs = require("ejs");
const fs = require("fs");
const path=require('path');
module.exports = () => {
  const content = fs
    .readFileSync(path.join(__dirname, "./../template/package.ejs"))
    .toString();
  const code = ejs.render(content, { middleware: {}, packageName: 3000 });
  return code;
};

init.js稍微修改一下,改变文件写入的字符

const { createIndexPage, createPackagePage } = require("./createCode");

...
  try {
    fs.writeFileSync(`${basePath}/index.js`, createIndexPage());
    fs.writeFileSync(`${basePath}/package.json`,createPackagePage());
    success("创建成功");
  } catch (error) {
...

8.来点交互更加炫酷
安装 inquirer 一个用户与命令行交互的工具

npm install inquirer -S

创建目录question,里面创建文件index.jspackageName.jsport.jsmiddleware.js

//index.js 
const inquirer = require("inquirer");
function getPromptList(name) {
  return [
    // 具体交互内容
    require("./packageName.js")(name),
    require("./port.js")(),
    require("./middleware.js")(),
  ];
}
module.exports = (name) => {
  return inquirer.prompt(getPromptList(name));
};

// packageName.js
module.exports = (name) => ({
  type: "input",
  name: "packageName",
  message: "设置项目名称",
  validate(val) {
    if (!val) {
      return "请输入项目名称";
    } else {
      return true;
    }
  },
  default() {
    return name;
  },
});

//middleware.js
module.exports = (name) => ({
  type: "checkbox",
  name: "middleware",
  message: "请选择使用的中间件",
  choices: [
    {
      name: "KoaBody",
    },
    {
      name: "Koaviews",
    },
    {
      name: "KoaStatic",
    },
    {
      name: "KoaRouter",
    },
  ],
});

// port.js
module.exports = () => ({
  type: "input",
  name: "port",
  message: "设置服务端口号",
  default() {
    return 8000;
  },
  validate: function (val) {
    if (val > 3000 && val < 65535) {
      // 校验位数
      return true;
    }
    return "端口号范围应为:3000-65535";
  },
});

init.js中执行交互,并且将用户的选择传入到生成代码的函数



const question = require("./question");


...
  const data = await question();
  console.log(data);
  var config = {
    packageName: data.packageName,
    port: data.port,
    middleware: {
      body: data.middleware.indexOf("KoaBody") !== -1,
      views: data.middleware.indexOf("KoaViews") !== -1,
      static: data.middleware.indexOf("KoaStatic") !== -1,
      router: data.middleware.indexOf("KoaRouter") !== -1,
    },
  };
   //   创建目录
  fs.mkdirSync(basePath);
  try {
    fs.writeFileSync(`${basePath}/index.js`, createIndexPage(config));
    fs.writeFileSync(`${basePath}/package.json`, createPackagePage(config));
....

  1. 项目创建完成,贴心的帮用户安装依赖。就是用代码执行npm install。安装 execa将process_child pipe到主进程
npm install execa -S

init.js

const fs = require("fs");
const chalk = require("chalk");
const execa = require("execa");
const { createIndexPage, createPackagePage } = require("./createCode");
const question = require("./question");
const createLog = (type) => (content) => console.log(chalk[type](content));
const success = createLog("green");
const errorLog = createLog("red");
function getFilePath(name) {
  return `./${name}`;
}
// 删除目录
function delDir(path) {
  let files = [];
  if (fs.existsSync(path)) {
    files = fs.readdirSync(path);
    files.forEach((file) => {
      let curPath = path + "/" + file;
      if (fs.statSync(curPath).isDirectory()) {
        delDir(curPath); //递归删除文件夹
      } else {
        fs.unlinkSync(curPath); //删除文件
      }
    });
    fs.rmdirSync(path);
  }
}

module.exports = async (name) => {
  const basePath = getFilePath(name);
  if (fs.existsSync(basePath)) {
    errorLog("同名文件已经存在");
    return false;
  }
  const data = await question(name);
  var config = {
    packageName: data.packageName,
    port: data.port,
    middleware: {
      body: data.middleware.indexOf("KoaBody") !== -1,
      views: data.middleware.indexOf("KoaViews") !== -1,
      static: data.middleware.indexOf("KoaStatic") !== -1,
      router: data.middleware.indexOf("KoaRouter") !== -1,
    },
  };
  //   创建目录
  fs.mkdirSync(basePath);
  try {
    fs.writeFileSync(`${basePath}/index.js`, createIndexPage(config));
    fs.writeFileSync(`${basePath}/package.json`, createPackagePage(config));
    await execa("npm", ["install"], { cwd: `./${name}`, stdio: [2, 2, 2] });
    success(`
恭喜!!!小项目创建完毕
-----------------------------------------------
***********************************************


cd ./${name}

npm run serve


===============================================
`);
  } catch (error) {
    //   删除目录及文件
    delDir(basePath);
    errorLog("创建失败!");
  }
};

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容