如何编写Grunt插件

参考这里: http://javascriptplayground.com/blog/2014/01/creating-your-first-grunt-plugin/

一,准备环境


Grunt团队提供了一个yeoman generator 可以自动生成一个插件模板,使用yeoman之前需要先安装:

npm install -g yo

然后我们需要安装对应的 generator:

npm install -g generator-gruntplugin

然后就可以创建一个目录来放你的插件,并且使用yeoman来生成模板比如:

mkdir ~/my-grunt-plugin
cd ~/my-grunt-plugin
yo gruntplugin

执行上述最后一条命令的时候会有一个交互式问答,填好所有信息之后就会生成一个如下的文件结构:

- Gruntfile.js
- README.md
- node_modules/
- package.json
- tasks/
- test/
- tmp/

其中我们插件的代码在 tasks/ my_test.js文件中,这里的文件名是你在之前填写的插件名。

这里的Gruntfile主要定义的是测试任务,默认情况下有两个测试,并且执行grunt不会出错。


二,模板代码结构


这个默认的插件功能非常简单,就是把几个文件内容合并,并且可以在后面追加一段文字。

在Grunt.file中定义了my_test任务,有两个分别如下:

my_test: {
      default_options: {
        options: {
        },
        files: {
          'tmp/default_options': ['test/fixtures/testing', 'test/fixtures/123']
        }
      },
      custom_options: {
        options: {
          placeholder: '?'
        },
        files: {
          'tmp/custom_options': ['test/fixtures/testing', 'test/fixtures/123']
        }
      }
    },

    // Unit tests.
    nodeunit: {
      tests: ['test/*_test.js']
    }



当执行grunt的时候会先执行my_test任务,然后执行nodeunit来测试任务是否正常输出结果。
可以看到,my_test任务是把 test/fixtures 下的两个文件处理并输出到 tmp/custom_options中,而且定义了不同的配置。
在test/expected目录下定义了希望的输出结果,在test.js中做测试的时候,只需要比较 /tmp下的文件内容和test/expected 中的内容是否相同即可。

这里是默认的 my_test.js 中的代码,可以看到他就是比较了tmp下的文件内容和 test/expected下的文件内容是否相同:

exports.my_test = {
  setUp: function (done) {
    // setup here if necessary
    done();
  },
  default_options: function (test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/default_options');
    var expected = grunt.file.read('test/expected/default_options');
    test.equal(actual, expected, 'should describe what the default behavior is.');

    test.done();
  },
  custom_options: function (test) {
    test.expect(1);

    var actual = grunt.file.read('tmp/custom_options');
    var expected = grunt.file.read('test/expected/custom_options');
    test.equal(actual, expected, 'should describe what the custom option(s) behavior is.');

    test.done();
  }
};




下面是my_test.js中的代码,它的主体部分逻辑非常简单,就是把所有的文件内容连接起来并写到指定的文件中。

/*
 * my_test
 *
 *
 * Copyright (c) 2014
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('my_test', 'The best Grunt plugin ever.', function () {

    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      punctuation: '.',
      separator: ', '
    });

    // Iterate over all specified file groups.
    this.files.forEach(function (file) {
      // Concat specified files.
      var src = file.src.filter(function (filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function (filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).join(grunt.util.normalizelf(options.separator));

      // Handle options.
      src += options.punctuation;

      // Write the destination file.
      grunt.file.write(file.dest, src);

      // Print a success message.
      grunt.log.writeln('File "' + file.dest + '" created.');
    });
  });

};



三,编写自己的代码


知道在哪里写代码和如何进行单元测试之后,就可以动手写自己的代码了。
我们先定义自己的配置项:

    
var options = this.options({
      words: ['xxoo', 'ooxx'],
      placeholder: '*'
    });



这里用户可以配置一个关键词列表,然后可以定义被屏蔽关键词的占位符。默认配置是把 xxoo 和 ooxx 替换成 *。

然后我们的只需要用关键词列表生成一个正则式并替换到原文件中的对应单词即可:

      
var r = new RegExp('(' + options.words.join("|") + ')', 'g');
      src = src.replace(r, options.placeholder);


把上述代码放到文件操作的那几行代码中,如下:

this.files.forEach(function (file) {
      // Concat specified files.
      var src = file.src.filter(function (filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function (filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).join('');

      var r = new RegExp('(' + options.words.join("|") + ')', 'g');
      src = src.replace(r, options.placeholder);

      // Write the destination file.
      grunt.file.write(file.dest, src);

      // Print a success message.
      grunt.log.writeln('File "' + file.dest + '" created.');
    });


显然如果执行grunt,会发现有两个测试错误。优秀的代码一定要有详尽的测试用例,我们来修改一下现有的测试文件就可以。

把 test/fixtures/testing 的内容改成 ‘abcxxoo’
然后把 test/fixtures/123的内容改成 '1 2 3 xxoo’

打开Gruntfile.js 把配置稍微改一下:

my_test: {
      default_options: {
        options: {
        },
        files: {
          'tmp/default_options': ['test/fixtures/testing', 'test/fixtures/123']
        }
      },
      custom_options: {
        options: {
          placeholder: '?'
        },
        files: {
          'tmp/custom_options': ['test/fixtures/testing', 'test/fixtures/123']
        }
      }
    },



然后再执行 grunt 依然会报错,因为我们的任务和测试文件都改过了,所以需要把 测试的预期结果也改一下,看看插件的输出结果,是很容易知道应该怎么改 test/expected目录下的文件内容的。

到此为止,我们的插件代码和测试用例都写完了,npm publish 发布一下试试吧。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值