-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgulpfile.js
More file actions
380 lines (275 loc) · 13.2 KB
/
Copy pathgulpfile.js
File metadata and controls
380 lines (275 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// Gulp ----------------------------------
// Gulp is a build system/task automator written for nodejs. It’s useful
// for simplifying and speeding up your build process, automating tasks
// such as compiling code, minification, and concatenation.
// ---------------------------------------
// Gulp utility
var gulp = require('gulp'),
gutil = require('gulp-util'),
gulpif = require('gulp-if');
// Requires.
var nodemon = require('gulp-nodemon'),
plumber = require('gulp-plumber'),
del = require('del'),
runsequence = require('run-sequence'),
importOnce = require('node-sass-import-once');
// Plugin requires.
var concat = require('gulp-concat'),
eslint = require('gulp-eslint'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
sass = require('gulp-sass'),
autoprefixer = require('gulp-autoprefixer'),
nano = require('gulp-cssnano'),
sourcemaps = require('gulp-sourcemaps'),
livereload = require('gulp-livereload'),
sasslint = require('gulp-sass-lint'),
imagemin = require('gulp-imagemin'),
newer = require('gulp-newer'),
notify = require('gulp-notify');
// Load external config.
var config = require('./gulp-config.json'),
publicDirectory = config.publicDirectory;
// Error Handling and Notification -------
// Make error handling easier by outputting the error message
// to the console and sending a notification.
// ---------------------------------------
// 1. Setup the error notification.
// 2. Log error to console.
// 3. Let gulp know to end the task that errors and not to break
// the run (forcing you to restart gulp).
function errorAlert(err) {
notify.onError({
title: 'Error', message: '<%= error.message %>', sound: 'Sosumi'
})(err);
console.log(err.toString());
this.emit('end');
}
// Script build task ---------------------
// Combines and uglifies JS, producing both a minified and non-minified
// version in public/js.
// ---------------------------------------
// 1. Assign our output directory to a variable.
// 2. Use all files defined by files.scripts within config.
// 3. Pipe the readable stream through gulp-plumber, which prevents pipe
// breaking caused by errors from gulp plugins (replaces pipe method
// and removes standard onerror handler on errors event, which unpipes
// streams on error by default). Pass in our errorAlert function
// to the onerror handler.
// 4. Run ESLint and report the output.
// 5. Combine into main.js
// 6. Output combined but non-minified version to public/js.
// 7. Rename to main.min.js
// 8. Uglify to minify.
// 9. Output minified version to public/js.
gulp.task('scripts', function() {
var outputDirectory = publicDirectory + 'js/'; // [1]
return gulp.src(config.files.scripts) // [2]
.pipe(plumber({errorHandler: errorAlert})) // [3]
.pipe(eslint()) // [4]
.pipe(eslint.format())
.pipe(eslint.failAfterError())
.pipe(concat('main.js')) // [5]
.pipe(gulp.dest(outputDirectory)) // [6]
.pipe(rename({ suffix: '.min' })) // [7]
.pipe(uglify()) // [8}
.pipe(gulp.dest(outputDirectory)); // [9]
});
// Styles build task ---------------------
// Compiles CSS from Sass, auto-prefixes and optionally outputs a source map,
// which allows you to edit your Sass directly within DevTools.
// Output both a minified and non-minified version into /public/css.
// ---------------------------------------
// 1. Assign our output directory to a variable.
// 2. Using all files defined by files.styles within config.
// 3. Pipe the readable stream through gulp-plumber, which prevents pipe
// breaking caused by errors from gulp plugins (replaces pipe method
// and removes standard onerror handler on errors event, which unpipes
// streams on error by default). Pass in our errorAlert function
// to the onerror handler.
// 4. Conditionally initialise sourcemaps (if sourceMaps == true within config).
// 5. Pipe stream through sasslint.
// 5. Compile using Sass, expanded style.
// 6. Include and compile any Sass partials defined by files.nodeModules
// within config.
// 7. Prevent styles from being duplicated if a Sass partial declares it's
// own dependencies (encapsulation) using the @import directive.
// 8. Auto-prefix (e.g. -moz-) using last 2 browser versions.
// 9. Conditionally pipe stream through sourcemaps (if sourceMaps == true within config)
// and write out a source map to the directory defined by files.styleMap
// within config.
// 10.Output prefixed but non-minifed CSS to public/css
// 11.Rename to .min.css
// 12.Minify the CSS.
// 13.Conditionally write sourcemaps (if sourceMaps == true within config)
// to the directory defined by files.styleMap within config.
// Bug in DevTools that prevents it from using the source map:
// https://github.com/terinjokes/gulp-uglify/issues/105#issuecomment-160292080
// 14.Output prefixed, minified CSS to public/css.
gulp.task('styles', function() {
var outputDirectory = publicDirectory + 'css/'; // [1]
return gulp.src(config.files.styles) // [2]
.pipe(plumber({errorHandler: errorAlert})) // [3]
.pipe(gulpif(config.sourceMaps, sourcemaps.init())) // [4]
.pipe(sasslint({ // [5]
'config': '.sass-lint.yml'
}))
.pipe(sasslint.format())
.pipe(sasslint.failOnError())
.pipe(sass({ // [6]
style: 'expanded',
includePaths: [config.files.nodeModules], // [7]
importer: importOnce // [8]
}))
.pipe(autoprefixer('last 2 versions')) // [9]
.pipe(gulp.dest(outputDirectory)) // [10]
.pipe(rename({ suffix: '.min' })) // [11]
.pipe(nano()) // [12]
.pipe(gulpif(
config.sourceMaps, sourcemaps.write(
config.files.stylesMap
))) // [13]
.pipe(gulp.dest(outputDirectory)); // [14]
});
// Image optimisation task ---------------
// Minify PNG, JPEG, GIF and SVG images.
// Outputs a minified version into /public/images.
// ---------------------------------------
// 1. Assign our output directory to a variable.
// 2. Use files defined in files.images config.
// 3. Pipe the readable stream through gulp-plumber, which prevents pipe
// breaking caused by errors from gulp plugins (replaces pipe method
// and removes standard onerror handler on errors event, which unpipes
// streams on error by default). Pass in our errorAlert function
// to the onerror handler.
// 4. Conditionally pipe stream through imagemin (if minifyImages == true
// within config).
// 5. Filter to only images that are newer than within public/images.
// 6. Output optimised images to public/images.
gulp.task('images', function() {
var outputDirectory = publicDirectory + 'images/'; // [1]
return gulp.src(config.files.images) // [2]
.pipe(plumber({errorHandler: errorAlert})) // [3]
.pipe(gulpif(config.minifyImages, imagemin({ // [4]
optimizationLevel: 3,
progressive: true,
interlaced: true
})))
.pipe(newer(outputDirectory)) // [5]
.pipe(gulp.dest(outputDirectory)); // [6]
});
// Watch task ----------------------------
// Sets up several watchers. Using different config for styles and
// templates as they have partials that need watching but not compiling.
// ---------------------------------------
// 1. Any changes to any files defined by files.watchStyles within config
// starts styles task.
// 2. Any changes to any files defined by files.scripts within config
// starts scripts task.
// 3. Any changes to any files defined by files.images within config
// starts images task.
gulp.task('watch', function() {
gulp.watch(config.files.watchStyles, ['styles']); // [1]
gulp.watch(config.files.scripts, ['scripts']); // [2]
gulp.watch(config.files.images, ['images']); // [3]
});
// Clean task ----------------------------
// Deletes the /public directory
// ---------------------------------------
gulp.task('clean', function() {
return del(publicDirectory);
});
// Copy task ----------------------------
// Copies over any files that are not part of other tasks
// (e.g. HTML templates, JS libraries) to the public directory
// ---------------------------------------
// 1. Change the base path to avoid copying top-level directory.
gulp.task('copy', function() {
return gulp.src(config.files.copy, { base: config.copyBase }) // [1]
.pipe(gulp.dest(publicDirectory));
});
// Develop task --------------------------
// Runs build task and sets up watches.
// ---------------------------------------
// 1. Control the shutdown of the nodemon process within gulp. Let's capture
// the kill signal and handle it (otherwise we have to crtl + c twice).
// 2. Assign our output directory to variable.
// 3. Start a LiveReload server.
// 4. Watch for any changes in public/ and conditionally check to see if we
// want LiveReload to automatically refresh the browser from our config.
// 4. Any changes to template files will automatically restart the node app.
gulp.task('develop', ['build', 'watch'], function () {
function exitHandler() { // [1]
process.kill(process.pid, 'SIGINT');
}
process.once('SIGINT', exitHandler);
var outputDirectory = publicDirectory + '**/*'; // [2]
livereload.listen(); // [3]
gulp.watch(outputDirectory
).on(gulpif(config.autoReload, 'change'), livereload.changed
); // [4]
// Setup nodemon -------------------------
// nodemon will watch the files in the directory in which nodemon was
// started, and if any files change, nodemon will automatically restart
// your node application.
// ---------------------------------------
// 1. Pass in the the app script to start/restart during the task.
// 2. Watch for any changes to nunjucks files in the directory.
// 3. Tell nodemon not to output to console.
// 4. Invoking a livereload server at the start of gulp develop task,
// (before we start the nodemon child process—which starts the app.js
// through the app.listen method), means we'll need to run a function
// to monitor the stdout stream of the nodemon child process, so we can
// test for an output, and then tell LiveReload there's been a change
// to reload.
// If we don't the gulp task will refresh the LiveReload server
// before the nodemon child process has invoked the app.listen method
// from the parent process, causing the browser to refresh without the
// server being ready.
// The `readable` event indicates that data is ready to pick up in the
// stdout and stderr streams.
// Conditionally check to see if we want LiveReload to automatically
// refresh the browser from our config. If true…
// 5. …let's listen for a data event to the stdout stream from the
// parent process…
// 6. …and use the test method to search for a match of the specified
// string and if this returns true…
// 7. …tell live reload there's been a change.
// 8. Pipe the nodemon child process stdout/stderr to the parent process
// stdout/stderr streams.
nodemon({
script: 'app.js', // [1]
ext: 'nunjucks', // [2]
stdout: false // [3]
}).on(gulpif(config.autoReload, 'readable'), function () { // [4]
this.stdout.on('data', function (chunk) { // [5]
if(/^Express server listening on/.test(chunk)){ // [6]
livereload.changed(__dirname); // [7]
}
});
this.stdout.pipe(process.stdout); // [8]
this.stderr.pipe(process.stderr);
});
});
// Build task ----------------------------
// Runs other tasks that produce a built project in the public directory.
// ---------------------------------------
gulp.task('build', function(callback) {
runsequence('clean', ['scripts', 'styles', 'images', 'copy'], callback);
});
// Default task --------------------------
// Lists out available tasks.
// ---------------------------------------
gulp.task('default', function() {
var cyan = gutil.colors.cyan,
green = gutil.colors.green;
gutil.log(green('----------'));
gutil.log(('The following main ') + cyan('tasks') + (' are available:'));
gutil.log(cyan('build'
) + ': builds the contents to the public directory.'
);
gutil.log(cyan('develop'
) + ': performs an initial build then sets up watches.'
);
gutil.log(green('----------'));
});