# 前言

在公司里负责的前端项目每次去改通用的模块都需要一个个去打包,非常麻烦。在寻找解决方案时找到了 shelljs ,就尝试写了个打包脚本。

# 开始

项目目录

├─project					// 总文件夹
│  ├─projectA 			// 项目 A
│  ├─projectB 		    // 项目 B
│  ├─projectC  	        // 项目 C
│  ├─ ...               //......

在 project 目录下执行 npm init -y ,接着下载依赖包。
使用 npm

npm install shelljs

创建 build.js 文件,开始编写打包脚本

引入 shell.js fs

// build.js
const shell = require("shelljs");
const fs = require("fs");

获取当前目录下所有项目

const filesPath = fs.readdirSync(__dirname).filter(e => fs.statSync(__dirname + "/" + e).isDirectory());

打包函数

// 因为文档中没找到 exec 的同步方法,所以写了一个 Promise
shell.execSync = (command, options) => new Promise((resolve, reject) => {
  try {
    shell.exec(command, options, resolve);
  } catch (error) {
    reject(error);
  }
});
const buildFiles = async (files) => {
  const success = [], fail = [];
  for (const i in files) {
    const e = files[i];
    shell.cd(__dirname + "/" + e);
    try {
      await shell.execSync("npm run build", { async: true, slient: true });
      success.push(e);
    } catch (error) {
      fail.push(e);
    }
  }
  
  console.log(`打包任务执行完毕,打包成功数量:${success.length},打包失败数量:${fail.length}`);
  
  if (fail.length > 0) {
    console.log(`打包失败列表如下:\n${fail.join('\n')}`);
  }
};

在 project 目录的 package.json 中添加

"scripts": {
    "build": "node build.js",
}

在 buid.js 中调用方法

const [nodeEnv, dir, ...args] = process.argv;
if (args.length == 0) {
  // 打包所有文件
  buildFiles(filesPath);
} else {
  // 打包指定文件 可多个  npm run build 项目名称 1 项目名称 2......  
  const buildArgs = args.filter(v => {
    if (filesPath.includes(v)) return true;
    console.log(`err:文件${v}不存在`);
    return false;
  });
  buildFiles(buildArgs);
}

打包功能算是完成了,在 project 目录下执行 npm run build 就可以打包所有的项目了,也可以指定项目 npm run build projectA

# 压缩 dist

虽然批量打包已经实现了,但是把打包后的文件部署到服务器时还是要一个个目录去压缩上传。所以接下来使用 archiver 压缩打包后的 dist 文件并放在同一个目录。

下载 archiver

npm install archiver --save

最终 build.js

// build.js
const shell = require("shelljs");
const fs = require("fs");
const archiver = require('archiver');
shell.execSync = (command, options) => new Promise((resolve, reject) => {
  try {
    shell.exec(command, options, resolve);
  } catch (error) {
    reject(error);
  }
});
const clearDist = (name) => {
  console.log(`开始清理文件 distOut/${name}.zip`);
  try {
    if (name) {
      shell.rm('-rf', `distOut/${name}.zip`);
    } else {
      shell.rm('-rf', 'distOut/*');
    }
    console.log('清理完成');
  } catch (error) {
    console.log(error);
  }
};
const dirname = __dirname;
const creatZip = (name) => {
  try {
    console.log(`开始压缩文件${name}/dist`);
    const output = fs.createWriteStream(`${dirname}/distOut/${name}.zip`);
    const archive = archiver('zip', {
      zlib: { level: 9 }
    });
    output.on('close', () => {
      console.log(archive.pointer() + ' total bytes');
      console.log('压缩完成!');
    });
    archive.on('error', (err) => {
      throw err;
    });
    archive.pipe(output);
    archive.directory(`${dirname}/${name}/dist`, false);
    archive.finalize();
  } catch (error) {
    console.log(error);
  }
};
const filesPath = fs.readdirSync(__dirname).filter(e => fs.statSync(__dirname + "/" + e).isDirectory() && e != 'distOut');
const buildFiles = async (files) => {
  if (!fs.existsSync('./distOut')) {
    fs.mkdirSync(`distOut`);
  }
  console.log(`开始执行打包任务\n检测文件数量为${files.length}`);
  const success = [], fail = [];
  for (const i in files) {
    const e = files[i];
    console.log(`开始打包第${i * 1 + 1}个,${e}`);
    shell.cd(__dirname + "/" + e);
    try {
      await shell.execSync("npm run build", { async: true, slient: true });
      success.push(e);
      console.log(`${i * 1 + 1}${e}打包完成!`);
      clearDist(e);
      creatZip(e);
    } catch (error) {
      fail.push(e);
      console.log(`${i * 1 + 1}${e}打包失败!`);
    }
  }
  
  console.log(`打包任务执行完毕,打包成功数量:${success.length},打包失败数量:${fail.length}`);
  
  if (fail.length > 0) {
    console.log(`打包失败列表如下:\n${fail.join('\n')}`);
  }
};
const [nodeEnv, dir, ...args] = process.argv;
if (args.length == 0) {
  // 打包所有文件
  buildFiles(filesPath);
} else {
  // 打包指定文件 可多个  npm run build 项目名称 1 项目名称 2......  
  const buildArgs = args.filter(v => {
    if (filesPath.includes(v)) return true;
    console.log(`err:文件${v}不存在`);
    return false;
  });
  buildFiles(buildArgs);
}