UNPKG

10.4 kBJavaScriptView Raw
1'use strict';
2
3var gutil = require('gulp-util'),
4 jshint = require('gulp-jshint'),
5 stylish = require('jshint-stylish'),
6 mocha = require('gulp-mocha'),
7 istanbul = require('gulp-istanbul'),
8 coverageEnforcer = require('gulp-istanbul-enforcer'),
9 size,
10 _ = require('lodash'),
11 plato = require('gulp-plato'),
12 fs = require('fs'),
13 open = require('gulp-open'),
14 path = require('path'),
15 spawn = require('child_process').spawn,
16 mapstream = require('map-stream'),
17 nicePackage = require('gulp-nice-package'),
18 shrinkwrap = require('gulp-shrinkwrap');
19
20/**
21 * Assigns default tasks to your gulp instance
22 * @param {Gulp} gulp
23 * @param {Object} [options] custom options
24 */
25module.exports = function (gulp, options) {
26
27 // we need to track total errors and exit code manually since gulp doesn't have a good way to do this internally
28 var exitCode = 0,
29 totalLintErrors = 0,
30 totalFelintErrors = 0;
31
32 // defaults
33 gulp.options = {
34 istanbul: {
35 includeUntested: true
36 },
37 istanbulWriteReports: {
38 dir: './target/coverage'
39 },
40 istanbulEnforcer: {
41 thresholds: {
42 statements: 80,
43 branches: 70,
44 lines: 80,
45 functions: 80
46 },
47 coverageDirectory: './target/coverage',
48 rootDirectory: ''
49 },
50 paths: {
51 lint: [
52 './*.js',
53 './lib/**/*.js',
54 './test/**/*.js'
55 ],
56 felint: [
57 './content/**/*.js'
58 ],
59 cover: [
60 './lib/**/*.js'
61 ],
62 test: [
63 './test/**/*.js'
64 ]
65 },
66 jshintrc: {
67 server: path.join(__dirname, 'lint/.jshintrc'),
68 client: path.join(__dirname, 'felint/.jshintrc')
69 },
70 showStreamSize: false,
71 complexity: {
72 destDir: './target/complexity',
73 options: {} // https://github.com/philbooth/complexity-report#command-line-options
74 },
75 nicePackage: {
76 spec: 'npm',
77 options: {
78 warnings: false,
79 recommendations: false
80 }
81 }
82 };
83
84 _.merge(gulp.options, options, function (a, b) {
85 return _.isArray(a) ? b : undefined;
86 });
87
88 // support backwards compatibility to v0.3.2
89 if (options && options.coverageSettings) {
90 _.merge(gulp.options.istanbulEnforcer, options.coverageSettings);
91 if (options.coverageSettings.coverageDirectory) {
92 gulp.options.istanbulWriteReports.dir = options.coverageSettings.coverageDirectory;
93 }
94 }
95
96 size = (gulp.options.showStreamSize) ? require('gulp-size') : require('./size-fake/index.js');
97
98 require('gulp-help')(gulp, { aliases: ['h', '?']});
99
100 process.on('exit', function () {
101 process.nextTick(function () {
102 var msg = "gulp '" + gulp.seq + "' failed";
103 console.log(gutil.colors.red(msg));
104 process.exit(exitCode);
105 });
106 });
107
108 function taskPassed(taskName) {
109 var msg = "gulp '" + taskName + "' passed";
110 console.log(gutil.colors.green(msg));
111 }
112
113 // cleanup all variables since, if we're running 'watch', they'll stick around in memory
114 function beforeEach() {
115 totalLintErrors = 0;
116 totalFelintErrors = 0;
117 exitCode = 0;
118 }
119
120 // ----------------
121 // lint
122 // ----------------
123
124 function lint() {
125 beforeEach();
126 return gulp.src(gulp.options.paths.lint)
127 .pipe(jshint(gulp.options.jshintrc.server))
128 .pipe(jshint.reporter(stylish, { verbose: true }))
129 .pipe(mapstream(function (file, cb) {
130 if (!file.jshint.success) {
131 totalLintErrors += file.jshint.results.length;
132 exitCode = 1;
133 }
134 cb(null, file);
135 }));
136 }
137
138 function lintOnEnd() {
139 var errString = totalLintErrors + '';
140 if (exitCode) {
141 console.log(gutil.colors.magenta(errString), 'errors\n');
142 gutil.beep();
143 } else {
144 taskPassed('lint');
145 }
146 }
147
148 gulp.task('lint', 'Lint server side js', function () {
149 return lint()
150 .on('end', function () {
151 lintOnEnd();
152 if (exitCode) {
153 process.emit('exit');
154 }
155 })
156 .pipe(size({
157 title: 'lint'
158 }));
159 });
160
161 gulp.task('lint-watch', false, function () {
162 return lint()
163 .on('end', lintOnEnd)
164 .pipe(size({
165 title: 'lint'
166 }));
167 });
168
169 // ----------------
170 // felint
171 // ----------------
172
173 function felint() {
174 beforeEach();
175 return gulp.src(gulp.options.paths.felint)
176 .pipe(jshint(gulp.options.jshintrc.client))
177 .pipe(jshint.reporter(stylish))
178 .pipe(mapstream(function (file, cb) {
179 if (!file.jshint.success) {
180 totalFelintErrors += file.jshint.results.length;
181 exitCode = 1;
182 }
183 cb(null, file);
184 }));
185 }
186
187 function felintOnEnd() {
188 var errString = totalFelintErrors + '';
189 if (exitCode) {
190 console.log(gutil.colors.magenta(errString), 'errors\n');
191 gutil.beep();
192 } else {
193 taskPassed('felint');
194 }
195 }
196
197 gulp.task('felint', 'Lint client side js', function () {
198 return felint()
199 .on('end', function () {
200 felintOnEnd();
201 if (exitCode) {
202 process.emit('exit');
203 }
204 })
205 .pipe(size({
206 title: 'felint'
207 }));
208 });
209
210 gulp.task('felint-watch', false, function () {
211 return felint()
212 .on('end', felintOnEnd)
213 .pipe(size({
214 title: 'felint'
215 }));
216 });
217
218 // ----------------
219 // test, cover
220 // ----------------
221
222 function testErrorHandler(err) {
223 gutil.beep();
224 gutil.log(err.message);
225 exitCode = 1;
226 }
227
228 function cover(cb) {
229 beforeEach();
230 return gulp.src(gulp.options.paths.cover)
231 .pipe(istanbul(gulp.options.istanbul))
232 .on('finish', cb)
233 .pipe(size({
234 title: 'cover'
235 }));
236 }
237
238 function coverOnEnd() {
239 gutil.log('Wrote coverage reports to', gutil.colors.magenta(gulp.options.istanbulWriteReports.dir));
240 // not calling cb() due to bug https://github.com/SBoudrias/gulp-istanbul/issues/22
241 }
242
243 gulp.task('test-cover', 'Unit tests and coverage', function () {
244 return cover(function () {
245 return gulp.src(gulp.options.paths.test)
246 .pipe(mocha({reporter: 'dot'}))
247 .on('error', function (err) { // handler for mocha error
248 testErrorHandler(err);
249 process.emit('exit');
250 })
251 .pipe(size({
252 title: 'test-cover'
253 }))
254 .pipe(istanbul.writeReports(gulp.options.istanbulWriteReports))
255 .pipe(coverageEnforcer(gulp.options.istanbulEnforcer))
256 .on('error', function (err) { // handler for istanbul error
257 testErrorHandler(err);
258 process.emit('exit');
259 })
260 .on('end', coverOnEnd);
261 });
262 });
263
264 gulp.task('test-cover-watch', false, function () {
265 return cover(function () {
266 return gulp.src(gulp.options.paths.test)
267 .pipe(mocha({reporter: 'dot'}))
268 .on('error', testErrorHandler) // handler for mocha error
269 .pipe(size({
270 title: 'test-cover'
271 }))
272 .pipe(istanbul.writeReports(gulp.options.istanbulWriteReports))
273 .pipe(coverageEnforcer(gulp.options.istanbulEnforcer))
274 .on('error', testErrorHandler) // handler for istanbul error
275 .on('end', coverOnEnd);
276 });
277 });
278
279 gulp.task('test', 'Unit tests only', function () {
280 return gulp.src(gulp.options.paths.test)
281 .pipe(mocha({reporter: 'dot'}))
282 .on('error', function (err) { // handler for mocha error
283 testErrorHandler(err);
284 process.emit('exit');
285 })
286 .pipe(size({
287 title: 'test'
288 }));
289 });
290
291 gulp.task('test-watch', false, function (cb) {
292 return gulp.src(gulp.options.paths.test)
293 .pipe(mocha({
294 reporter: 'min',
295 growl: true
296 }))
297 .on('error', testErrorHandler) // handler for mocha error
298 .pipe(size({
299 title: 'test-watch'
300 }));
301 });
302
303 gulp.task('test-debug', 'Run unit tests in debug mode', function (cb) {
304 spawn('node', [
305 '--debug-brk',
306 path.join(__dirname, 'node_modules/gulp/bin/gulp.js'),
307 'test'
308 ], { stdio: 'inherit' });
309 });
310
311 // ----------------
312 // complexity
313 // ----------------
314
315 gulp.task('plato', 'Generate complexity analysis reports with plato', function (cb) {
316
317 // http://james.padolsey.com/javascript/removing-comments-in-javascript/
318 var commentRemovalRegex = /\/\*.+?\*\/|\/\/.*(?=[\n\r])/g,
319 jshintJSON;
320
321 fs.readFile(gulp.options.jshintrc.server, 'utf8', function (err, data) {
322 if (err) {
323 throw err;
324 }
325 jshintJSON = JSON.parse(data.replace(commentRemovalRegex, ''));
326
327 gulp.src(gulp.options.paths.cover)
328 .pipe(plato(gulp.options.complexity.destDir, {
329 jshint: {
330 options: jshintJSON
331 },
332 complexity: gulp.options.complexity.options
333 }));
334
335 gulp.src(gulp.options.complexity.destDir + '/index.html')
336 .pipe(open());
337
338 cb();
339 });
340
341 });
342
343 // ----------------
344 // shrinkwrap
345 // ----------------
346
347 function validatePackageJson() {
348 return gulp.src('package.json')
349 .pipe(nicePackage(gulp.options.nicePackage.spec, gulp.options.nicePackage.options));
350 }
351
352 gulp.task('nice-package', 'Validates package.json', function () {
353 var isValid = true;
354 return validatePackageJson()
355 .pipe(mapstream(function (file, cb) {
356 isValid = file.nicePackage.valid;
357 cb(null, file);
358 }))
359 .on('end', function () {
360 if (!isValid) {
361 process.emit('exit');
362 }
363 });
364 });
365
366 gulp.task('shrinkwrap', 'Cleans package.json deps and generates npm-shrinkwrap.json', function () {
367 return validatePackageJson()
368 .pipe(shrinkwrap())
369 .pipe(gulp.dest('./'));
370 });
371
372 // ----------------
373 // combo tasks
374 // ----------------
375
376 gulp.task('ci', 'Lint, tests and test coverage', ['lint', 'felint', 'test-cover', 'nice-package']);
377
378 function getTestAndLintPaths() {
379 var paths = gulp.options.paths.lint.concat(gulp.options.paths.test);
380 return _.uniq(paths);
381 }
382
383 // separate task so "watch" can easily be overridden
384 gulp.task('ci-watch', false, function () {
385 gulp.watch(getTestAndLintPaths(), ['lint-watch', 'test-cover-watch']);
386 gulp.watch(gulp.options.paths.felint, ['felint-watch']);
387 });
388
389 gulp.task('watch', 'Watch files and run all ci validation on change', ['ci-watch']);
390
391 gulp.task('watch-test', 'Watch files and run tests on change', function () {
392 gulp.watch(getTestAndLintPaths(), ['test-watch']);
393 });
394
395 return gulp;
396};