从 gulp-minify 看怎么写 gulp 插件


// through2 Node stream 的一个小包装器。是一个对 node 的 transform streams 简单封装
const through = require("through2");
// terser 的插件 处理文件用的
const uglify = require('terser');
// 就是正则匹配
const minimatch = require('minimatch');
const path = require('path');
// gulp提供的一个包 Vinyl can be used to describe files from all of these sources.
const Vinyl = require('vinyl');
// 用于gulp插件报错的
const PluginError = require('plugin-error');
// 给命令行加颜色的 chalk 也可以
const colors = require('ansi-colors');
// sm的注释 用户gulp的watch模式
const reSourceMapComment = /\n\/\/# sourceMappingURL=.+?$/;
const pathSeparatorRe = /[\/\\]/g;
// 产出的文件格式
const jsExtensions = /\.m?js$/;

// 一个解析后缀的方法
function parseExt(ext, byDefault = ".js") {
  let _ext = {};
  if (!ext) {
    _ext = {
      min: "-min" + byDefault,
      src: byDefault
    }
  } else if (typeof ext === "string") {
    _ext = {
      min: ext,
      src: byDefault
    }
  } else {
    _ext = {
      min: ext.min || "-min" + byDefault,
      src: ext.src || byDefault
    }
  }
  return _ext;
}
// 把错误信息格式化 以后命令行的工具可以使用这个
function formatError(error, file) {
  let filePath = error.file === 'stdin' ? file.path : error.file;
  let message = '';
  filePath = filePath ? filePath : file.path;
  let relativePath = path.relative(process.cwd(), filePath);
  message += colors.underline(relativePath) + '\n';
  message += error.message + ' (line: ' + error.line  + ', col: ' + error.col + ', pos: ' + error.pos;
  error.message = colors.red(message);
  return error;
}

// 要导出的 gulp 插件
module.exports = function(opt = {}) {
  
  let options = typeof opt === 'object' ? opt || {};

  //Set options output to itself, or, if null an empty object.
  options.output =  options.output ||  {};

  function minify(file, encoding, callback) {
    // file是 gulp 在 pipe 过程中的一个 vinyl File 对象 https://github.com/gulpjs/vinyl
    // 如果是空的直接返回
    if (file.isNull()) {
      this.push(file);
      return callback();
    }
    // 如果是 Stream 不处理直接抛出异常然后结束任务
    if (file.isStream()) {
      this.emit('end');
      return new callback(PluginError('gulp-minify', 'Streaming not supported:' + file.path));
    }
    let ignore = false;
    // 对哪些文件进行 minify 操作
    if (options.exclude) {
      ignore = options.exclude.some(function(item) {
        return path.dirname(file.path).split(pathSeparatorRe).some(function(pathName) {
          return minimatch(pathName, item);
        });
      });
    }
    // 同上
    if (!ignore && options.ignoreFiles) {
      ignore = options.ignoreFiles.some(function(item) {
        return minimatch(path.basename(file.path), item);
      });
    }
    // 同上
    if (ignore || !path.extname(file.path).match(jsExtensions)) {
      this.push(file);
      return callback();
    }
    let mangled, originalSourceMap;
    // 源文件有 sourceMap 的话进行 sourceMap 操作
    if (file.sourceMap) {
      options.outSourceMap = file.relative;
      if (file.sourceMap.mappings !== '') {
        options.inSourceMap = file.sourceMap;
      }
      originalSourceMap = file.sourceMap;
    }
    // 是否保留注释
    if (options.preserveComments === 'all') {
      options.output.comments = true;
    } else if (options.preserveComments === 'some') {
      options.output.comments = /^!|@preserve|@license|@cc_on/i;
    } else if (typeof options.preserveComments === 'function') {
      options.output.comments = options.preserveComments;
    }
    options.fromString = options.hasOwnProperty("fromString") ? options.fromString : true;
    //Parse the extensions form the options.
    let ext = parseExt(options.ext, path.extname(file.path));
    // 创建一个 Vinyl 实例
    let min_file = new Vinyl({
      base: file.base,
      path: Array.isArray(ext.min) ? file.path.replace(ext.min[0], ext.min[1]) : file.path.replace(jsExtensions, ext.min),
    });
    // uglify 的配置
    const uglifyOptions = {
      mangle   : options.mangle   !== undefined ? options.mangle : true,
      output   : options.output   !== undefined ? options.output : null,
      compress : options.compress !== undefined ? options.compress : {},
      sourceMap: !!file.sourceMap
    };
    // 使用插件进行处理js文件
    try {
      mangled = uglify.minify(String(file.contents), uglifyOptions);
      min_file.contents = new Buffer(mangled.code.replace(reSourceMapComment, ''));
    } catch (e) {
      this.emit('end');
      return callback(new PluginError('gulp-minify', formatError(e, file)));
    }
    // 配置 sourceMap
    if (file.sourceMap) {
      min_file.sourceMap = JSON.parse(mangled.map);
      min_file.sourceMap.sourcesContent = originalSourceMap.sourcesContent;
      min_file.sourceMap.sources = originalSourceMap.sources;
    }
    // 确保文件进去下一个插件
    this.push(min_file);
    // ms 模式下把源文件返回方便 debug
    if (options.noSource !== true) {
      file.path = file.path.replace(jsExtensions, ext.src);
      this.push(file);
    }
    // 告诉 stream 引擎,我们已经处理完了这个文件
    callback();
  }
  // 返回一个 object streams  through2.obj(fn) is a convenience wrapper around through2({ objectMode: true }, fn)
  return through.obj(minify);
};
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值