编写一个基于webpack的React打包工具(3)

271 阅读2分钟

startapp.js

#!/usr/bin/env node

const path = require('path');
const { spawn } = require('child_process');
const { getEnv } = require('./util');

// 运行命令的路径
const runtimePath = process.cwd();
// startapp.js所在的路径
const codePath = __dirname;
// ctbuild.cmd或者ctbuild.sh所在路径
const commandPath = path.join(codePath, 'node_modules', '.bin', path.sep);

// 配置文件所在路径
let configPath;
let define;

/**
 * corssenvTask
 * @access private
 * @return {Promise}
 */
function corssenvTask() {
  return new Promise((resolve, reject) => {
    const command = process.platform === 'win32' ? `cross-env.cmd` : `cross-env`;
    const crossenvProcess = spawn(command, ['REAP_PATH=dev', 'NODE_ENV=development'], {
      cwd: codePath,
      encoding: 'utf-8',
      env: getEnv(commandPath),
    });

    crossenvProcess.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    crossenvProcess.stderr.on('data', (data) => {
      console.log(`stderr: ${data}`);
    });

    crossenvProcess.on('close', (code) => {
      console.log(`crossenvClose:${code}`);
      resolve();
    });
  });
}

/**
 * webpackServiceTask
 * @return {Promise}
 */
function webpackServiceTask() {
  return new Promise((resolve, reject) => {
    const command = process.platform === 'win32' ? `webpack-dev-server.cmd` : `webpack-dev-server`;
    const babelProcess = spawn(
      command,
      [
        '--open',
        '--config',
        path.join('webpackconfig', 'webpack.dev.js'),
        '--progress',
        '--colors',
        '--runtimepath',
        path.join(runtimePath, path.sep),
        '--customconfig',
        configPath,
        '--define',
        define.join(' '),
      ],
      {
        cwd: codePath,
        encoding: 'utf-8',
        env: getEnv(commandPath),
      },
    );

    babelProcess.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    babelProcess.stderr.on('data', (data) => {
      console.log(`stderr: ${data}`);
    });

    babelProcess.on('close', (code) => {
      console.log(`webpackServiceTaskClose:${code}`);
      resolve();
    });
  });
}

// startapp的tasks
const tasks = [corssenvTask, webpackServiceTask];
let index = 0;

/**
 * loopTask
 * @return {Promise}
 */
function loopTask() {
  return new Promise((resolve, reject) => {
    if (index >= tasks.length) {
      resolve();
    } else {
      const task = tasks[index++];
      if (task) {
        task()
          .then(() => {
            loopTask().then(() => {
              resolve();
            });
          })
          .catch((error) => {
            reject(error);
          });
      } else {
        reject();
      }
    }
  });
}

module.exports = {
  /**
   * build
   * @param {String} - ctbuildConfigPath
   * ctbuild.config.js配置文件的路径,如果没有指定则会寻找命令运行目录下的ctbuild.config.js文件
   */
  build: ({ config: ctbuildConfigPath = '', define: defineMap }) => {
    if (ctbuildConfigPath) {
      if (path.isAbsolute(ctbuildConfigPath)) {
        configPath = ctbuildConfigPath;
      } else {
        configPath = path.join(runtimePath, ctbuildConfigPath);
      }
    } else {
      configPath = path.join(runtimePath, 'ctbuild.config.js');
    }

    define = defineMap;

    loopTask()
      .then(() => {
        console.log('finish');
        process.exit();
      })
      .catch((error) => {
        console.log(error);
      });
  },
};

/**
 * 获取不同系统环境变量的分隔符
 */
function getEvnSplit() {
   return process.platform === 'win32' ? ';' : ':';
} 

/**
 * 获取env
 * @param commandPath
 */
getEnv(commandPath) {
   const obj = {};

   if (process.env && process.env.Path && process.env.Path.indexOf(commandPath) === -1) {
       obj.Path = process.env.Path + getEvnSplit() + commandPath;
   }

   if (process.env && process.env.PATH && process.env.PATH.indexOf(commandPath) === -1) {
      obj.PATH = process.env.PATH + getEvnSplit() + commandPath;
   }

   return Object.assign(process.env, obj);
}

主要实现的是build方法,主要执行2个操作,corssEvn和webpackService的操作,这里使用node的child_process的spawn方法启动2个子进程,cross-env和webpack-dev-server,需要注意的是,在win和linux上的命令不一样,需要使用process.platform进行判断,win上的命令是xxx.cmd,linux上是xxx,corssEvn定义了1个环境变量NODE_ENV=[development | production]来区分是dev还是prod环境,还有一个需要注意的地方是宿主工程使用不同终端安装此包后node_modules中目录的结构会有所不同,例如使用npm进项安装,会使用拉平的模式,把webpack-dev-server和cross-evn安装到宿主工程的node_modules下,而用cnpm则会安装到package的node_modules下,所以我们寻找webpack-dev-server和corss-env命令的时候就不知道去哪里寻找,这个时候我们应该将package/node_modules/.bin路径写到启动子进程的环境变量中,环境变量中会自带宿主工程/node_modules环境变量,而package/node_modules环境变量并不在其中,getEnv方法就是干着事的,还有一点需要注意的地方就是win和linux中环境变量的分隔符有所不同getEvnSplit方法就是获取不同操作系统上环境变量的分隔符。在调用webpack-dev-server的时候,配置文件是执行的配置文件是webpack.dev.js,同时还会把运行命令的路径和用户自定的配置文件路径传递过去。

   github