在命令行中输入kjl dev之后的具体执行流程

Posted by WWJ on July 26, 2018

在命令行中输入kjl dev之后的具体执行流程

首先,kjl是一个自定义的命令,那么如何自定义的命令呢?

自定义一个命令

  1. 在桌面上新建一个为lingwu的文件touch lingwu.js 在文件写写入
    console.log('lingwu');
    
  2. 再来查看一下文件的具体信息 Alt text

看用红框圈住的@符号之前的一串字符-rw-r--r-- 可以忽略掉第一个字符-(表示文件类型),然后就剩下9个字符rw-r--r--,其实这9个字符没3个是一个组,分别对应于当前用户、当前用户所在的组,其他用户对该文件的执行权限

  • r:表示对该文件有r (read)的权限
  • w:表示对该文件有w (weite)的权限
  • x:表示对文件有执行的权限

对于lingwu这个文:-rw-r--r--

  • 第一个字符表示文件类型
  • 接下来3个字符是rw-,表示该文件对于当前用户的权限是r/w,即当前用户能对111.txt读、写,但是不能执行,所以执行权限块是-,表示没有执行权限。
  • 再接下来3个字符是r–,表示该文件对于当前用户所在的组的成员来说,只能执行读,写和执行都是无权限的。
  • 最后3个字符是r–,表示该文件对于其他用户来说也是只能读,不能写和执行。

现在这个文件不是一个可执行文件,那么如何将它变成可执行的呢?

  1. 修改文件的权限
    • 修改权限的命令格式 chmod <权限范围><权限操作><具体权限> 具体文件
    • 权限范围 u:User,即文件或目录的拥有者。 g:Group,即文件或目录的所属群组。 o:Other,除了文件或目录拥有者或所属群组之外,其他用户皆属于这个范围。 a:All,即全部的用户,包含拥有者,所属群组以及其他用户。
    • . 权限操作 +:表示增加权限 -:表示取消权限 =:表示唯一设定权限
    • 具体权限 r:表示可读取 ` w:可写入 x :`表示可执行

执行一下修改文件的命令 Alt text

可以这个文件多了一个x权限

如何成为一个可执行命令

前提是在每个需要被执行文件的第一行加入#!/usr/bin/env node,即为文件提供可执行环境

#!/usr/bin/env node
console.log('lingwu');

(1)是否是绝对路径/相对路径 Alt text

第一个命令是个相对路径,即在当前目录下面有lingwu.js这个文件,故可以执行成功,第一个报错,找不到该文件,那么我们如何直接输入文件名就可以执行这个文件呢?答案就是这个命令是否是个环境变量 (2)是否是别名 我们在使用git的时候,一般可以用g来表示git,那么g就是git的别名 (3)是否是环境变量 如何将命令添加到环境变量里面呢? 建立软链接 Alt text

建立软链接之后,再输入lingwu就不会报错了 Alt text 到这里,我们就自定义了一个叫做lingwu的命令

代码流程

当我们在kjl-site根目录下面输入kjl dev时,首先会去找到kjl命令所对应的可执行文件,即/usr/local/bin/node_modules/@qunhe/def-cli/bin/kjl.js

kjl.js

#!/usr/bin/env node

require('../lib/main')();

该文件中只有2行代码

  • 第一行是为该文件指定编译环境,即node
  • 第二行是引入../lib/main导出的函数,并执行,所以核心代码都在../lib/main.js

核心步骤 (1)引入外部依赖、全局配置和工具函数utils

 // 引入外部依赖、全局配置、工具函数
const injects = require('./injects');
 
 let config = require('./config');

 const {
     pkgUp,
     program,
     path
 } = injects;

 const utils = await require('./prepareUtils')(config, injects);

 logger = utils.logger;

(2)在执行kjl命令的目录下面找到package.json,因为我是在kjl-site 的根目录下执行的kjl dev,所以找到的package.json就是kjl-site 根目录下面的package.json

const pkgPath = pkgUp.sync();
const lg = logger.getLogger();

  let workspace;
  if (pkgPath) {
      workspace = path.dirname(pkgPath);
      lg.info(`当前工作空间为:${workspace}`);
  } else {
      workspace = process.cwd();
      lg.info('没有在工程目录下执行命令, package.json 文件不存在');
  }

(3)初始化cli

const ctx = await require(path.join(toolRoot, 'lib/initCli'))(config, injects, utils);
  • 检查版本 lib/middlewares/checkNodeVersion
  • def-cli的版本和套件信息添加到全局配置中 lib/middlewares/initial
    const path = require('path');
    module.exports = async (config, injects, utils) => {
      return {
          async value (ctx, next) {
              const {
                  toolRoot
              } = config;
    
              const {
                  program,
                  getTemplateInfo
              } = injects;
    
              ctx.commands = [];
    
              const pkg = require(path.join(toolRoot, 'package.json'));
              program.version(pkg.version);
    
              config.version = pkg.version;
    
              config.templateInfo = getTemplateInfo(config);
    
              ctx.toolInfo = require(path.join(toolRoot, 'package.json'));
    
              await next();
          }
      };
    };
    

    getTemplateInfo.js ```javascript const path = require(‘path’);

module.exports = ({ workspace, // 工作区间,在你成功执行kjl dev命令的目录 templatesRoot //套件所在的目录,在home目录下面的kjl-templates }) => { let templateInfo; try { templateInfo = require(path.join(workspace, ‘package.json’))[‘kjl-template’]; } catch (e) { templateInfo = undefined; }

// 如果套件存在的话,那套件所在的目录就是在/kjl-templates/node_modules/@qunhe/kjl-template_${templateInfo.name}
if (templateInfo && templateInfo.name) {
    const templateRoot = path.join(templatesRoot, `node_modules/@qunhe/kjl-template_${templateInfo.name}`);
    templateInfo.templateRoot = templateRoot;
}

return templateInfo; }; ``` * 判断套件是否已经安装 `lib/middlewares/ensureTemplate`,就是判断全局配置中的templateInfo && templateInfo.name是否存在,temolateInfo在上一步已经被添加到了全局配置中 * 初始化命令:`lib/middlewares/getCommands`收集当前工程所使用的套件的所有生命周期和插件,和全局插件一起合并到default commands上形成一份命令配置 ```javascript ctx.commands = mergeCommands(defaultCommands, lifecycles, plugins); ``` * 注册并执行命令:`lib/middlewares/registerCommands`把上面的配置注册到一个Commander实例上面,解析当前命令行参数并执行命令 ```javascript const {
commands } = ctx; Object.keys(commands).forEach((name) => {
const command = commands[name];
const {
    command: commandName,
    description,
    before = noop,
    __wrapper = defalutAsyncMiddleware,
    middlewares,
    after = noop,
    __confirm = defalutAsyncMiddleware,
    config: mwConfig,
    injects: mwInjects,
    alias,
    allowUnknownOption,
    options = []
} = command;

const p = program.command(commandName || name).description(description);

options.forEach(op => {
    p.option(op.flags, op.description, op.handleOption || (a => a), op.defaultValue);
}); }); ``` * 检查更新`/lib/middlewares/checkForUpdates`

(4)解析执行命令行参数

program.parse(process.argv);

现在命令行参数是dev (1)dev命令存在于套件的生命周期目录下,执行该目录下的index.js文件

module.exports = async (config, injects) => ({
    async middlewares ({ meta }) {
        return [
            require('../../common/middleware/yarn-install'),
            require('./middleware/handle-options'),
            require('./middleware/sync-pr-common'),
            require('./middleware/dev-server')
        ];
    }
});

核心代码就是执行4个函数,即安装依赖、判断当前启动服务的模式、同步pr-common、启动服务 ** **完成