npm指令执行前执行自定义代码

259 阅读2分钟

1、基本逻辑

npm start执行前执行node ./bin/wb-handle

"scripts": {
    "wb": "node ./bin/wb-handle; npm start"
},

wb-handle源码,删除deleteNodeModules配置的最后一级目录文件

#!/usr/bin/env node --max-old-space-size=4096 --optimize-for-size --max_old_space_size=4096 --optimize_for_size

"use strict";

var deleteNodeModules = "abab/efg";

var fs = require("fs");

function deleteFolderRecursive(path) {
    if( fs.existsSync(path) ) {
        fs.readdirSync(path).forEach(function(file) {
            var curPath = path + "/" + file;
            if(fs.statSync(curPath).isDirectory()) { // recurse
                deleteFolderRecursive(curPath);
            } else { // delete file
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
};

deleteFolderRecursive(`./node_modules/${deleteNodeModules}`)


2、推包到npm

接下来我们代码推包到npm使用

{
  "name": "wb-handle",
  "version": "1.0.2",
  "description": "delete mode_modules file",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "wbhandle": "./bin/wb-handle.js"
  },
  "keywords": [
    "node"
  ],
  "author": "wangb",
  "license": "ISC"
}

wb-handle.js改进代码,可以在项目package.json配置deleteModule自动删除nodel_modules中的包

#!/usr/bin/env node --max-old-space-size=4096 --optimize-for-size --max_old_space_size=4096 --optimize_for_size

"use strict";


const path = require('path');
var fs = require("fs");


const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
var packagePath = resolveApp("package.json")
var packageJson = require(packagePath);
var deleteModule = packageJson && packageJson.deleteModule;
if (!deleteModule) {
    console.log("请在package.json中输入deleteModule");
    return;
} 
function deleteFolderRecursive(path) {
    if( fs.existsSync(path) ) {
        fs.readdirSync(path).forEach(function(file) {
            var curPath = path + "/" + file;
            if(fs.statSync(curPath).isDirectory()) { // recurse
                deleteFolderRecursive(curPath);
            } else { // delete file
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
};

deleteFolderRecursive(`./node_modules/${deleteModule}`)


1

3、使用

yarn add wb-handle -D

配置项目命令

"scripts": {
  "start": "wbhandle;react-scripts start --port 3000",
},

4、继续改进可以读取命令行参数执行指令

#!/usr/bin/env node --max-old-space-size=4096 --optimize-for-size --max_old_space_size=4096 --optimize_for_size

"use strict";

const path = require("path");
var fs = require("fs");
const args = process.argv.slice(2);
const scriptIndex = args.findIndex((x) => x === "delete");
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];

if (script.indexOf("delete") !== -1) {
  const appDirectory = fs.realpathSync(process.cwd());
  const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
  var packagePath = resolveApp("package.json");
  var packageJson = require(packagePath);
  var deleteModule = packageJson && packageJson.deleteModule;
  if (!deleteModule) {
    console.log("请在package.json中输入deleteModule");
    return;
  }
  function deleteFolderRecursive(path) {
    if (fs.existsSync(path)) {
      fs.readdirSync(path).forEach(function (file) {
        var curPath = path + "/" + file;
        if (fs.statSync(curPath).isDirectory()) {
          // recurse
          deleteFolderRecursive(curPath);
        } else {
          // delete file
          fs.unlinkSync(curPath);
        }
      });
      fs.rmdirSync(path);
    }
  }

  deleteFolderRecursive(`./${deleteModule}`);
}

5、创建delete和copy命令

Wb-handle.js代码

#!/usr/bin/env node --max-old-space-size=4096 --optimize-for-size --max_old_space_size=4096 --optimize_for_size

"use strict";
const path = require("path");
var fs = require("fs");

const args = process.argv.slice(2);
const scriptIndex = args.findIndex((x) => x === "delete");
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
var packagePath = resolveApp("package.json");
var packageJson = require(packagePath);

if (script.indexOf("delete") !== -1) {
  var deleteModule = packageJson && packageJson.deleteModule;
  if (!deleteModule) {
    console.error("请在package.json中输入deleteModule");
    return;
  }
  if (Array.isArray(deleteModule)) {
    deleteModule.forEach((item) => {
      require("../script/delete")(`./${item}`);
    });
  } else {
    require("../script/delete")(`./${deleteModule}`);
  }
} else if (script.indexOf("copy") !== -1) {
  var copy = packageJson && packageJson.copyModule;
  if (Array.isArray(copy)) {
    copy.forEach((item) => {
      // 复制文件
      require("../script/copy")(`./${item.from}`, `./${item.to}`);
    });
  } else {
    console.error("copy必须是数组格式");
  }
}


script/copy代码如下

const path = require("path");
var fs = require("fs");
var copyFile = function (srcPath, tarPath, cb) {
  var rs = fs.createReadStream(srcPath);
  rs.on("error", function (err) {
    if (err) {
      console.error("read error", srcPath);
    }
    cb && cb(err);
  });

  var ws = fs.createWriteStream(tarPath);
  ws.on("error", function (err) {
    if (err) {
      console.error("write error", tarPath);
    }
    cb && cb(err);
  });
  ws.on("close", function (ex) {
    cb && cb(ex);
  });

  rs.pipe(ws);
};
var copyFolder = function (srcDir, tarDir, cb) {
  fs.readdir(srcDir, function (err, files) {
    var count = 0;
    var checkEnd = function () {
      ++count == files.length && cb && cb();
    };

    if (err) {
      checkEnd();
      return;
    }
    if (!fs.existsSync(tarDir)) {
      fs.mkdirSync(tarDir);
    }

    files.forEach(function (file) {
      var srcPath = path.join(srcDir, file);
      var tarPath = path.join(tarDir, file);

      fs.stat(srcPath, function (err, stats) {
        if (stats.isDirectory()) {
          fs.mkdir(tarPath, function (err) {
            if (err) {
              console.error(err);
              return;
            }

            copyFolder(srcPath, tarPath, checkEnd);
          });
        } else {
          copyFile(srcPath, tarPath, checkEnd);
        }
      });
    });

    //为空时直接回调
    files.length === 0 && cb && cb();
  });
};
 // 复制文件
 function wbCopyFile(orgfilepath, desdirpath) {
  if (fs.lstatSync(orgfilepath).isDirectory()) {
    // 目录到指定目录
    copyFolder(orgfilepath, desdirpath);
  } else {
    // 文件到指定目录
    if (fs.existsSync(orgfilepath)) {
      if (!fs.existsSync(desdirpath)) {
        fs.mkdirSync(desdirpath);
        fs.copyFileSync(
          orgfilepath,
          `${desdirpath}/${path.basename(orgfilepath)}`
        );
      } else {
        fs.copyFileSync(
          orgfilepath,
          `${desdirpath}/${path.basename(orgfilepath)}`
        );
      }
    } else {
      console.error(
        Date().toString() +
          "FolderAndFileOperation_copyFile: org file not existed." +
          " org path: " +
          orgfilepath.toString()
      );
    }
  }
}
module.exports = wbCopyFile;

script/delete代码如下

var fs = require("fs");

function deleteFolderRecursive(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach(function (file) {
      var curPath = path + "/" + file;
      if (fs.statSync(curPath).isDirectory()) {
        // recurse
        deleteFolderRecursive(curPath);
      } else {
        // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
}
module.exports = deleteFolderRecursive;

6、支持创建模版文件

wb-handle代码修改为下面

#!/usr/bin/env node --max-old-space-size=4096 --optimize-for-size --max_old_space_size=4096 --optimize_for_size

"use strict";
const path = require("path");
var fs = require("fs");

const args = process.argv.slice(2);
const scriptIndex = args.findIndex(
  (x) => x === "delete" || x === "copy"  || x === "tem"
);
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
var packagePath = resolveApp("package.json");
var packageJson = require(packagePath);

if (script.indexOf("delete") !== -1) {
  var deleteModule = packageJson && packageJson.deleteModule;
  if (!deleteModule) {
    console.error("请在package.json中输入deleteModule");
    return;
  }
  if (Array.isArray(deleteModule)) {
    deleteModule.forEach((item) => {
      require("../script/delete")(`./${item}`);
    });
  } else {
    require("../script/delete")(`./${deleteModule}`);
  }
} else if (script.indexOf("copy") !== -1) {
  var copy = packageJson && packageJson.copyModule;
  if (Array.isArray(copy)) {
    copy.forEach((item) => {
      // 复制文件
      require("../script/copy")(`./${item.from}`, `./${item.to}`);
    });
  } else {
    console.error("copy必须是数组格式");
  }
} else if (script.indexOf("tem") !== -1) {
  const list = args.findIndex((x) => x === "list");
  const nameIndex = args.findIndex((x) => x.indexOf("name=") !== -1);
  if (nameIndex === -1) {
    console.error("template必须输入name=");
    return;
  }
  const nameArr = args[nameIndex].split("=");
  const name = nameArr[1];
  if (list !== -1) {
    require("../script/template")(name, true); // 列表
  } else {
    require("../script/template")(name, false); // 视图
  }
}

新增template.js

var fs = require("fs");
const path = require("path");

function firstToUpper(str) {
  return str.trim().toLowerCase().replace(str[0], str[0].toUpperCase());
}
function create(name, list) {
  var template;
  var templateModel;
  var templateDir = path.resolve(__dirname, '../template');
  if (list) {
    template = fs.readFileSync(templateDir +"/template.list.tsx");
    templateModel = fs.readFileSync(templateDir + "/template.model.ts");
  } else {
    template = fs.readFileSync(templateDir +"/template.tsx");
    templateModel = fs.readFileSync(templateDir +"/template.model.ts");
  }

  var templateStr = template
    .toString()
    .replace(/ModuleName/g, firstToUpper(name))
    .replace(/moduleName/g, name);
  var templateModelStr = templateModel
    .toString()
    .replace(/ModuleName/g, firstToUpper(name))
    .replace(/moduleName/g, name);

  if (list) {
    fs.writeFileSync("./" + name + ".list.tsx", templateStr);
  } else {
    fs.writeFileSync("./" + name + ".tsx", templateStr);
  }
  fs.writeFileSync("./" + name + ".model.tsx", templateModelStr);
}
module.exports = create;

7、最终版本使用

使用在项目package.json配置如下

"scripts": {
    "start": "wbhandle delete;react-scripts start --port 3000",
    "tem": "wbhandle tem"
},
"deleteModule": [
    "node_modules/busynessbill1",
    "node_modules/busynessbill2",
    "node_modules/busynessbill3"
]
// 或者
"deleteModule": "test/package.json" // 删除指定文件夹
"copyModule": [
  {
    "from": "package.json", // 复制文件到指定目录
    "to": "test"
  },
  {
    "from": "image", // 复制文件夹到指定目录
    "to": "test"
  }
],

创建模版文件执行

npm run tem name=ceshiwenjian
npm run tem name=ceshiwenjian list // 列表模版