关于zhen-cli的介绍

开发目的

  • 首先是方便自己快速创建项目工程的初始化模板;
  • 其次真正实现日常维护的工程模板价值;
  • 再次实现一个脚手架同时管理两种技术栈的目的:管理vue技术栈和react技术栈。

zhen-cli是一个脚手架。其功能类似vue-cli,相当于vue-cli的简化版,但功能又不全是vue-cli的子集。

目前发布在npm上的版本1.0.14实现的功能:

  • 项目工程、版本号以及作者可以自定义;
  • 可以选择是否安装vuex;
  • 可以同时生成react技术栈的项目初始化工程模板。 最后这点算是区别于vue-cli的。

工作原理

整体工作流程如下。

整体上,zhen-cli是一个nodejs的应用,归属于命令行工具应用。

依赖的主要功能库:

  • commander 负责命令行输入和参数解解析

以下是对想要注册命令的定义。

#!/usr/bin/env node

// commander是终端命令行命令注册库
// #!/usr/bin/env node 这行代表的是按照node的环境处理
const program = require("commander");
program
  .version(require("../package").version)
  .usage("<command> [options]")
  .option('--no-sauce', 'Remove sauce')
  .command("vue", "generate a new vue project from a template") // 注册vue命令
  .command("react", "generate a new react project from a template") // 注意react命令
program.parse(process.argv); // 去掉后不管作用
  • download-git-repo 负责下载gitlab或者github的仓库代码
   const download = require("download-git-repo"); // 下载github下的仓库

    /**
     * 从模板仓库下载模板,并生成项目
     *
     * @param {String} template
     */
    function downloadAndGenerate(template) {
        const spinner = ora("模板下载中,请稍等···");
        spinner.start();

        // 如果存在本地模板,先删除
        if (exists(tmp)) rm(tmp);
        download(template, tmp, {
            clone: false
        }, err => {
            spinner.stop();
            if (err) {
                logger.fatal("模板" + template + "下载失败" + ": " + err.message.trim());
            }
            // 生产项目方法 核心方法
            // name为文件名 tmp数临时目录 生成的项目的目录
            generate(name, tmp, to, err => {
                if (err) logger.fatal(err);
                logger.success('"%s" 创建成功.', name);
            });
        });
    }
}
  • metalsmith 串行执行各个方法直到下载项目依赖

读取文件之后链式调用中间件方法。

// Read all the files in a source directory.
// Invoke a series of plugins that manipulate the files.
// Write the results to a destination directory!
  const metalsmith = Metalsmith(path.join(src, "template"));
  // metalsmith.metadata() 这个默认是{}
  // console.log(metalsmith.metadata(), 'metalsmith.metadata()')
  const data = Object.assign(metalsmith.metadata(), {
    destDirName: name,
    inPlace: dest === process.cwd(),
    noEscape: true
  });

  // 将模板中自定义的helper注册到handlebars中。
  // helper实际就是模板可以自动获取到helper中的数据
  // opts.helpers &&
  //   Object.keys(opts.helpers).map(key => {
  //     Handlebars.registerHelper(key, opts.helpers[key]);
  //   });

  // 给metalsmith绑定插件,1.收集用户交互信息 2. 过滤需要渲染的文件 3. 渲染文件
  // metalsmith Read all the files in a source directory.
  // 将数据咋转为以路径为key的键值对形式
  // 输出到一个指定的目录
  metalsmith
    .use(askQuestions(opts.prompts))
    .use(filterFiles(opts.filters))
    .use(renderTemplateFiles)
    .clean(false)
    .source(".")
    .destination(dest)
    .build(err => {
      done(err);
      console.log(' 正在下载依赖... ')
      executeCommand('npm install', path.join(process.cwd(), name)).then(()=>{
        logMessage(opts.completeMessage, data);
      })
    });
  • handlebars 负责渲染字符串为文件
const Handlebars = require("handlebars");
const async = require("async");  // async这是一个异步处理的库
// 这里是用的handlebars模板 也可以使用ejs模板语法
const render = require("consolidate").handlebars.render; //Template engine consolidation library. 模板引擎库 有多种模板渲染引擎

 const metalsmithMetadata = metalsmith.metadata();
  // metalsmithMetadata是meta里面的数据
  // console.log(metalsmithMetadata, 'metalsmithMetadata')
  async.each(
    keys,
    (file, next) => {
      const str = files[file].contents.toString();
      // 如果文件中没有模板语法,则不对该文件进行渲染,直接输出文件内容。
      if (!/{{([^{}]+)}}/g.test(str) || file.indexOf('.vue') > -1) {
        return next();
      }
      // 使用数据对象对模板进行渲染
      render(str, metalsmithMetadata, (err, res) => {
        if (err) {
          err.message = `[${file}] ${err.message}`;
          return next(err);
        }
        files[file].contents = Buffer.from(res);
        next();
      });
    },
    done
  );
  • inquirer 负责用户与命令行交互
inquirer
    .prompt([
      {
        type: prompt.type,
        name: key,
        message: prompt.message || key,
        default: promptDefault,
        choices: prompt.choices || [],
        validate: prompt.validate || (() => true)
      }
    ])
    .then(answers => {
      if (Array.isArray(answers[key])) {
        data[key] = {};
        answers[key].forEach(multiChoiceAnswer => {
          data[key][multiChoiceAnswer] = true;
        });
      } else if (typeof answers[key] === "string") {
        data[key] = answers[key].replace(/"/g, '\"');
      } else {
        data[key] = answers[key];
      }
      done();
    })
    .catch(done);

使用方法

执行命令npm i -g zhen-cli 安装

使用zhen vue/react project-name 创建项目

如果想研究源代码可以直接查看node modules里面的zhen-cli

后续工作

后续将继续优化原始模板。尤其react模板目前比较简单,后期将做出更加针对性的优化方案。

我站在山顶看风景!下面是我的家乡!
原文地址:https://www.cnblogs.com/zhensg123/p/15459100.html