如何写一个gulp插件?

1、基础储备

  • Node常用的三种流:Readable、Writable、Transform
  • 流之间用pipe来传递,pipe是流的实例方法
  • Transform流通常用第三方模块through2来创建
  • 常用Node的fs模块中fs.createReadStream(路径)创建Readable流,fs.createWriteStream(路径)创建Writable流
  • Buffer类数据buffer可通过buffer.toString('utf-8')转为字符串String类型

2、基本结构

// 1、引入模块
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
const PLUGIN_NAME = 'gulp-prefixer';

function prefixStream(prefixText) {
  var stream = through();
  stream.write(prefixText);
  return stream;
}

// 2、编写插件主函数 (处理文件)
function gulpPrefixer(prefixText) {
  if (!prefixText) {
    throw new PluginError(PLUGIN_NAME, 'Missing prefix text!');
  }
  prefixText = new Buffer(prefixText); // 将prefixText以二进制形式存储

  // 创建一个让每个文件通过的 stream 通道
  var stream = through.obj(function(file, enc, cb) {
    if (file.isBuffer()) {
      this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!'));
      return cb();
    }
    if (file.isStream()) {
      // 定义转换内容的 streamer
      var streamer = prefixStream(prefixText);
      // 从 streamer 中捕获错误,并发出一个 gulp的错误
      streamer.on('error', this.emit.bind(this, 'error'));
      // 开始转换
      file.contents = file.contents.pipe(streamer);
    }

    // 确保文件进去下一个插件
    this.push(file);
    // 告诉 stream 转换工作完成
    cb();
  });

  // 返回文件 stream
  return stream;
}

// 3、暴露(export)插件的主函数
module.exports = gulpPrefixer;

3、主函数编写

  • 判断插件的输入类型:Buffer Or Stream
    Buffer类一般指该插件接收的数据是Buffer;Stream类该插件接收的数据是Stream,在使用gulp时,由gulp.src读取的文件传递给后续插件时默认都是Buffer,若想支持Stream,需显示调用gulp.src(路径,{buffer:false})。从大部分gulp插件使用方式来看,处理Buffer的情形占大多数。
file.isStream()
  • file对象
    保存读取的文件信息,file.contents是主要内容,也是一个流对象
  • through2
    通过through或through.obj创建的transform对象起着承前启后的作用。
    承前:通过file对象获取上一个pipe来的数据
    启后:对file对象进行改造,并通过this.push(file)和cb()向后一个pipe传递数据

4、插件实例

var through = require('through2');
var gutil = require('gulp-util');
var applySourceMap = require('vinyl-sourcemaps-apply');
var path = require('path');
var merge = require('merge');

var PluginError = gutil.PluginError;

module.exports = function (opt) {
  function replaceExtension(path) {
    path = path.replace(/\.coffee\.md$/, '.litcoffee');
    return gutil.replaceExtension(path, '.js');
  }

  function transform(file, enc, cb) {
    if (file.isNull()) return cb(null, file);
    if (file.isStream()) return cb(new PluginError('gulp-coffee', 'Streaming not supported'));

    var data;
    var str = file.contents.toString('utf8');  // 1、这里开始,str就都是字符串了,可以采用正则进行处理了!
    var dest = replaceExtension(file.path);

    var options = merge({  // 2、合并选项
      bare: false,
      coffee: require('coffeescript'),
      header: false,
      sourceMap: !!file.sourceMap,
      sourceRoot: false,
      literate: /\.(litcoffee|coffee\.md)$/.test(file.path),
      filename: file.path,
      sourceFiles: [file.relative],
      generatedFile: replaceExtension(file.relative)
    }, opt);

    try {
      data = options.coffee.compile(str, options);
    } catch (err) {
      return cb(new PluginError('gulp-coffee', err));
    }

    if (data && data.v3SourceMap && file.sourceMap) {
      applySourceMap(file, data.v3SourceMap);
      file.contents = new Buffer(data.js);   
    } else {
      file.contents = new Buffer(data);      // 3、将处理后的字符串再转回buffer类型
    }

    file.path = dest;
    cb(null, file);                              //  4、将处理后的文件向下一级传输
  }

  return through.obj(transform);
};

你可能感兴趣的:(如何写一个gulp插件?)