《JavaScript开发框架权威指南》——2.6 创建Grunt插件

2.6 创建Grunt插件

社区提供的丰富插件库是让Grunt真正变得闪耀的库,它能使你立即从Grunt中获益,而不是需要从头创建复杂的任务。如果你需要在项目中做自动构建,那么很可能某人已经为你做好这项“Grunt”工作。在这一节中,你可以懂得如何向社区回馈自己创建的Grunt插件。

2.6.1 开始
首先要做的事情之一是情创建一个公共的GitHub仓库,以存储你的新插件。下文中将要提及的示例包含在本书附带的源码中,本书附带了源码。一旦你准备好代码仓库,就把它克隆到你的电脑上。下一步,按照本章前面“将Grunt添加到项目中”一节所概述的步骤,在仓库目录中初始化Grunt。然后,你的新Grunt插件的文件模式将会类似清单中的示例。

清单2-31 新Grunt插件的文件模式

.
├──  Gruntfile.js
├──  README.md
├──  package.json
├──  tasks

注意:
 

这里提到的最重要一点是创建Grunt插件不需要任何额外的模式或知识(抛开本章已经涵盖的内容不说)。这个过程反映了把Grunt整合进一个现有的项目中,Gruntfile的建立用于加载任务,除此之外是任务本身。一旦插件发布至npm,其他的Grunt项目就能够像本章中到处提及的方式一样来加载你的插件。
2.6.2 创建任务
按照示例中的方式,让我们创建一个Grunt插件,该插件能够生成一份描述了项目中包含的文件类型、大小和数量的报告。该插件的配置示例如清单所示。

清单2-32 插件配置示例代码

// example-plugin/Gruntfile.js

module.exports = function(grunt) {

    grunt.config('file-report', {
        'options': {
        },
        'public': {
            'src': ['public/*/']
        },
        'images': {
            'src': ['public//.jpg', 'public/ /.png', 'public//*.gif']
        }
    });

    grunt.loadNpmTasks('grunt-file-reporter');
    grunt.registerTask('default', ['file-report']);
};

从清单中可以看到插件的源码,其中Grunt注册了一个多任务file-report。每当调用这个任务,清单中指定的目标文件会被迭代遍历。完成这个任务后,插件会编译得到一个报告展示出它发现的文件类型、数量和大小详情。

清单2-33 插件的源码

// example-plugin/node_modules/grunt-file-reporter/Gruntfile.js

var fs = require('fs');
var filesize = require('filesize');
var _ = require('lodash');
_.mixin(require('underscore.string'));

module.exports = function(grunt) {

    var mime = require('mime');
    var Table = require('cli-table');

    grunt.registerMultiTask('file-report', 'Generates a report of file types & sizes used
    within a project ', function() {

        var report = {
            'mimeTypes': {},
            'largest': null,
            'smallest': null
        };

        var table = new Table({
            'head': ['Content Type', 'Files Found', 'Total Size',
            'Average Size', 'Largest', 'Smallest']
        });
        var addFile = function(file) {
            if (grunt.file.isDir(file)) return;
            var mimeType = mime.lookup(file);
            if (!report.mimeTypes[mimeType]) {
                report.mimeTypes[mimeType] = {
                    'count': 0,
                    'sizes': [],
                    'largest': null,
                    'smallest': null,
                    'oldest': null,
                    'newest': null
                };
            }
            var details = report.mimeTypes[mimeType];
            details.count++;
            var stats = fs.statSync(file);
            details.sizes.push(stats.size);
            if (!details.largest || stats.size > details.largest.size) {
                details.largest = { 'file': file, 'size': stats.size };
            }
            if (!report.largest || stats.size > report.largest.size) {
                report.largest = { 'file': file, 'size': stats.size };
            }
            if (!details.smallest || stats.size < details.smallest.size) {
                details.smallest = { 'file': file, 'size': stats.size };
            }
            if (!report.smallest || stats.size < report.smallest.size) {
                report.smallest = { 'file': file, 'size': stats.size };
            }
       };
       var sum = function(arr) {
           return arr.reduce(function(a, b) {
               return a + b;
            });
        };

        var displayReport = function() {
            var totalSum = 0;
            var totalFiles = 0;
            var totalSizes = [];
            _.each(report.mimeTypes, function(data, mType) {
                var fileSum = sum(data.sizes);
                totalSum += fileSum;
                totalFiles += data.sizes.length;
                totalSizes = totalSizes.concat(data.sizes);
                table.push([mType, data.count, filesize(fileSum),
                    filesize(fileSum / data.sizes.length),
                    _.sprintf('%s (%s)', data.largest.file, filesize(data.largest.size)),
                    _.sprintf('%s (%s)', data.smallest.file, filesize(data.smallest.size)),
                ]);
            });
            table.push(['-', totalFiles, filesize(totalSum),
                filesize(totalSum / totalSizes.length),
                _.sprintf('%s (%s)', report.largest.file, filesize(report.largest.size)),
                _.sprintf('%s (%s)', report.smallest.file, filesize(report.smallest.size)),
            ]);
            console.log(table.toString());
        };
        this.files.forEach(function(files) {
            files.src.forEach(addFile);
        });

        displayReport();

    });

};

由file-report插件生成的输出如图所示。

图2-1 file-report任务所生成的输出

2.6.3 将任务发布到npm
一旦我们的插件已经就绪并且我们的Git仓库也更新到最新的代码,最后一步就是通过npm发布插件使他人也可用。

$ npm publish
注意:
 

如果这是你第一次向npm发布模块,你将被要求创建一个账号。

时间: 2024-08-30 16:47:52

《JavaScript开发框架权威指南》——2.6 创建Grunt插件的相关文章

《JavaScript开发框架权威指南》——2.2 Grunt是如何工作的

2.2 Grunt是如何工作的 Grunt为开发者提供了一个工具包,用于创建命令行程序来执行项目构建过程中的重复性任务,如压缩JavaScript代码.编译Sass样式表等.不过,Grunt的能力并不限于创建简单的任务(通常这些任务不会被分享或者复用),以解决特定工程遇到的特定需求,其真正的力量源于其将任务打包为可复用的插件的能力.这些插件可以被发布.分享.使用以及由其他人进行改进.本书写作之时已经有超过4 400个这样的插件. Grunt的运转依赖于四个核心组件,接下来逐一论述. 2.2.1

《JavaScript开发框架权威指南》——2.5 操作文件系统

2.5 操作文件系统 可想而知,作为构建工具,大部分Grunt插件都要以某种方式和文件系统交互.鉴于操作文件的重要性,Grunt提供了有益的抽象允许开发者与文件系统交互,并且仅仅使用很少数量的样板代码. 当然,我们不会把所有方法都列举在此,表显示了Grunt文件操作API中一些使用频率最高的方法.2.5.1 源-目标映射 许多Grunt任务与文件系统交互是依赖于源-目标映射的,该映射描述了要被处理的文件及各文件对应的目标.构建这样的映射会很冗长乏味,感谢Grunt为我们提供了解决此问题的有益捷径

《JavaScript开发框架权威指南》——2.4 处理任务

2.4 处理任务 对于配置来说,Grunt的config()方法既是"getter",也是"setter".在清单中,我们可以看到一个基本的Grunt任务是如何通过此方法来存取配置的. 清单2-9 管理一个基本Grunt任务的配置 module.exports = function(grunt) { grunt.config('basic-task', { 'message': 'Hello, world.' }); grunt.registerTask('basi

《JavaScript开发框架权威指南》——第2章 Grunt 2.1安装Grunt

第2章 Grunt 我是一个懒人.但正是懒人发明了轮子和自行车,因为他们既不愿意走路,也不愿意负重前行. --莱赫·瓦文萨,波兰前总统 拉里·沃尔(Larry Wall,著名的Perl语言创始人)在他的<Programming Perl>一书中提到,所有成功的程序员都有三个重要的品质:懒惰.急躁以及狂妄.乍一看,这些都是相当糟糕的品质,但是只是深挖一下,你就会发现其言外之意. 懒惰:懒惰的程序员讨厌重复自己.他们通常会花费大量的时间去创造有用的工具,代替自己完成重复性的工作.他们往往还会为这些

《JavaScript开发框架权威指南》——2.3 将Grunt添加到项目中

2.3 将Grunt添加到项目中 在本章前面,为了添加Grunt命令行工具,我们将npm包grunt-cli作为全局模块进行了安装.现在我们应该已经可以在命令行中使用Grunt命令,但是对于每个要使用Grunt的工程,仍然需要为其配置Grunt本地依赖.为此,只需在工程根目录下运行以下命令即可.本例假设npm已经针对示例所用项目进行了初始化,package.json文件也已经存在. $ npm install grunt --save-dev 现在,我们项目的package.json文件应该已经

《JavaScript开发框架权威指南》——1.7 创建Bower包

1.7 创建Bower包 截至目前,我们关注的焦点都在如何将Bower集成到项目中.我们先在项目中对Bower进行了初始化,然后探究了查找.添加以及移除软件包的方法.但是总有那么一天,你会希望将自己的软件包分享给其他人.要这么做的话,就必须遵守一些简单的规则,让我们从选择有效的包名开始. 1.7.1 选择有效的包名你需要为自己的包选定一个名字,该名字必须在整个Bower开放注册中心(public registry)唯一.通过Bower的search命令来检查你想要的名字是否可用.其他需要遵守的规

《JavaScript开发框架权威指南》——2.7 小结

2.7 小结 本章中,我们学习了功能强大的Grunt套件,让开发者对重复.枯燥冗长的任务进行自动化处理,还展示了: Grunt的工作原理(任务.插件和配置对象). 如何配置任务和插件. 如何利用Grunt内置的很多实用工具为用户提供反馈信息.与文件系统交互. 如何创建和分享你自己的插件.

《JavaScript开发框架权威指南》——1.5 语义化版本控制

1.5 语义化版本控制 如果安装了jQuery(见清单),那么请查看项目的Bower清单.你所看到的应当与清单相仿. 清单1-7 语义化版本号 "dependencies": { "jquery": "~2.1.3" } 清单中的版本号2.1.3(请暂时忽略-)就是所谓的语义化版本号(Semver,Semantic Version).语义化版本控制能够帮助开发者按照通用格式为其项目指定版本号.该格式如下所示. Version X.Y.Z(Majo

《JavaScript开发框架权威指南》——1.3 清单文件(Manifest)

1.3 清单文件(Manifest) Bower为开发者查找.添加.升级以及删除第三方库提供了唯一入口.这些操作被执行后,Bower会用最新的项目依赖项列表更新一个被称作"配置清单"的JSON文件.本章示例项目中的配置清单如清单所示.在此示例项目中,Bower维护着一个唯一的依赖项,即Bootstrap的CSS框架. 清单1-3 本章示例项目的Bower清单文件 // example-bootstrap/bower.json { "name": "exam