• 搞一个自己用的node-cli


    我们都用过 vue 的cli ,或者 react的cli,  亦或是其他的cli 如 vite 等。他们都是提供了一个全局命令,然后在终端执行这个全局命令就可以创建出模板项目。今天我们就自己做一个,给自己用的脚手架项目,帮助自己开发一些项目。

    现在我们来造点需求。

    背景:我们要在nextjs 项目中,添加页面路由,用过nextjs 的同学应该知道,这个pages 下面的文件就是页面路由。我们一般添加页面都在pages文件夹下新建文件 ,有时候还会在pages同级文件夹下新建components 文件夹,来放我们页面的组件。

    要求:

      1. 可以单独创建一个页面路由
      2. 如果页面复杂,那我需要在pages同级目录下创建一个components文件夹下创建同名的组件文件夹
      3. 可以控制是否生成style 文件

        分析:我们回忆下@vue/cli 这样的脚手架是如何使用的

    npm install -g @vue/cli
    
    vue create my-project

    全局安装之后 开始使用这个全局命名。

    那么我们就按照这个思路倒着打。

    因此我们也需要搞一个全局命令,然后执行我们全局命令后,就可以执行我们想要的动作,创建文件夹,写文件等。

    1. 创建空项目,next-project-add-page
    2. npm init 
    3. 修改 package.json, 添加 bin 字段
    4. 本地调试,先生成软连接 来让我们可以使用 next-add-page 命名, npm  link 
    5. 核心代码编写
    {
      "name": "next-project-add-page",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "bin": {
        "next-add-page": "index.js"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "commander": "^10.0.0"
      }
    }

    先上一个能走完大概流程的核心代码,里面一些细节的代码逻辑,我们后面再补充。

    // index.js
    // console.log(process.argv)
    // [
    //     'C:\\Program Files\\nodejs\\node.exe',
    //     'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\next-project-add-page\\index.js',
    //     'add', // 想要执行的命令
    //     'list', // 新增的页面
    //     '-components', // 是否添加到pages同级的components 文件夹下为 组件
    //     '-style', // 是否添加样式文件
    // ]
    const { program } = require('commander');
    const child_process = require('child_process');
    const { clearInterval } = require('timers');
    const fs = require('fs');
    const path = require('path');
    program
      .option('-components')
      .option('-style');
    
    program.parse();
    console.log(program.opts()) // { Components: true, Style: true }
    
    ;
    
    
    function intervalProgress() {
        const readline = require('readline');
        const unloadChar='-';
        const loadedChar='=';
    
        let i = 0;
        let time = setInterval(()=>{
            if(i>10){
                clearInterval(time);
                console.log(`创建完成,请查收`);
                process.exit(0);
            }
            readline.cursorTo(process.stdout,0,1);
            readline.clearScreenDown(process.stdout);
            renderProgress('文件创建中',i);
            i++;
        },200);
    
        function renderProgress(text,step){
            const PERCENT = Math.round(step*10);
            const COUNT = 2;
            const unloadStr = new Array(COUNT*(10-step)).fill(unloadChar).join('');
            const loadedStr = new Array(COUNT*(step)).fill(loadedChar).join('');
            process.stdout.write(`${text}:【${loadedStr}${unloadStr}|${PERCENT}%】`);
        }
    
    }
    
    
    
    
    function main(){
        if(process.argv[2] && process.argv[2] == 'add') {
            if(process.argv[3]) {
                // 页面名称
                // 判断pages 文件夹是否存在
                if(fs.existsSync(`./pages`) && !fs.existsSync(`./pages/${process.argv[3]}`)) {
                    let subProcess= child_process.exec("cd ./pages && mkdir " + process.argv[3],  function(err,stdout){
                        if(err)console.log(err);
                            // 读取 tempalte/index.tsx 的文件
                            let tempalteStr = fs.readFileSync( path.resolve(__dirname, './template/index.tsx'));
                            fs.writeFile(`./pages/${process.argv[3]}/index.tsx`, tempalteStr, (err) =>{
                                console.log(err);
                            });
                       // 创建style less
                        if(program.opts().Style && process.argv[3]) {
                            fs.writeFile(`./pages/${process.argv[3]}/index.less`,'', (err) =>{
                                console.log(err);
                            });
                        }
                        subProcess.kill();
                    });
                }
              
            }
        }
        
         if(program.opts().Components && process.argv[3]) {
             // 添加component组件
             // 页面名称
             // 判断components 文件夹是否存在
             if(!fs.existsSync(`./components`)) {
                fs.mkdirSync('./components');
             }
             if(!fs.existsSync(`./components/${process.argv[3]}`)){
                let subProcess=child_process.exec("cd ./components && mkdir " + process.argv[3], function(err,stdout){
                    if(err)console.log(err);
                    subProcess.kill();
               });
             }
         }
    
         // 展示进度条
         intervalProgress();
         // 精确点: 实时查询新建的几个文件(文件夹)是否创建成功,如果创建完毕,应该提前结束进度条
       
    }
    
    main();

     代码大概解说:

    • 使用nodejs 的fs 模块 api,对文件的读写以及文件存在的判断。
    • commander 对命令行的解析,得到命令行具体的值后 继续做文件的读写
    • 使用 child_process 子进程 对文件的读写
    • process.stdout.write 来写终端的输出内容
    • 用template 文件夹 放 具体的模板文件,具体生成文件的时候,可以将模板的内容给到相应的文件中
     
     
    上述代码不是很复杂,一些比较细节的内容我没有做细节处理,比如进度条的展示,这里是用一个 定时器 来模拟进度的一个过程,如果需要精确点可以监听文件的生成进度,从而调优进度条的展示。
     

    现在可以nextjs 项目下 执行命令:next-add-page add list -components -style

    next-add-page:是全局的一个命令

    add: 是添加页面的 一个命令

    list: 是添加页面的名称 

    -components: 在 components 文件夹下创建同名的组件 (可选)

    -style:  创建样式文件 (可选)

    是否可选,取决与代码对这个命令行参数的解析以及操作。

     

    现在执行这个命令行

    就可以看到项目中 生成了对应的文件。

    到这里基本的需求已经完成了。

    接下里是发布,

    具体发布到npm 的步骤就不多说了,没有发布过的可以参考下这里,https://cnodejs.org/topic/5823c4411120be9438b02a31

     

    好了,到这里应该知道怎木去开发自己的一个cli 脚手架了。

     

    参考

    https://juejin.cn/post/6844903702453551111

    https://juejin.cn/post/6857842033084760071

    https://www.runoob.com/nodejs/nodejs-fs.html

     

  • 相关阅读:
    js 数组根据某个字段去重
    Git常用命令
    【爬虫教程】2023最详细的爬虫入门教程~
    垃圾分类查询管理系统
    ELK学习(一)
    WEB跨平台桌面程序构建工具对比(Electron、Tauri、Wails)
    深度学习算法
    手动引入jar包,解决Dependency ‘XXX‘ not found的两种方式
    inno Setup 打包Java exe可执行文件和MySQL数据库,无需额外配置实现一键傻瓜式安装
    【Docker】五分钟完成Docker部署Java应用,你也可以的!!!
  • 原文地址:https://www.cnblogs.com/adouwt/p/17254413.html