This repository has been archived by the owner on Feb 5, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gulpfile.js
396 lines (319 loc) · 9.84 KB
/
gulpfile.js
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
'use strict';
let
_ = require('lodash'),
chalk = require('chalk'),
colors = require('colors'),
del = require('del'),
glob = require('glob'),
gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
path = require('path'),
runSequence = require('run-sequence'),
argv = require('yargs').argv,
webpack = require('webpack'),
webpackDevServer = require('webpack-dev-server'),
pkg = require('./package.json'),
plugins = gulpLoadPlugins(),
assets = require(path.posix.resolve('./config/assets.js'));
// Patch chalk to use colors
chalk.enabled = true;
colors.enabled = true;
/**
* --------------------------
* Watch/Reload Tasks
* --------------------------
*/
/**
* Nodemon helper function. This function will start nodemon
* using the provided configuration. It also registers handlers
* for the crash and restart events to log the events.
*
* @param nodemonConfig The config to pass to nodemon
* @returns {*}
*/
function nodemon(nodemonConfig) {
let config = require('./src/server/config');
let nodeArgs = [ `--inspect=${config.devPorts.debug}` ];
if (plugins.util.env.debugBrk) {
nodeArgs.push('--debug-brk');
}
nodemonConfig.nodeArgs = nodeArgs;
let stream = plugins.nodemon(nodemonConfig);
stream
.on('restart', () => {
plugins.util.log('Restarting server...');
})
.on('crash', () => {
plugins.util.log('Application crashed, restarting...');
stream.emit('restart', 1);
});
return stream;
}
gulp.task('watch-server', () => {
return nodemon({
script: 'src/server/server.js',
ext: 'js json html',
watch: _.union(
assets.build,
assets.server.views,
assets.server.allJS,
assets.server.config),
tasks: [ 'build-server' ]
});
});
gulp.task('webpack-dev-server', (done) => {
// Start a webpack-dev-server
let webpackConfig = require(path.posix.resolve('./config/build/webpack.conf.js'))('develop');
let compiler = webpack(webpackConfig);
let config = require(path.posix.resolve('./src/server/config'));
new webpackDevServer(compiler, {
publicPath: `${config.app.url.protocol}://${config.app.url.host}:${config.devPorts.webpack}/dev/`,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: {
chunks: false,
children: false,
colors: true
},
watchOptions: {
aggregateTimeout: 1000,
poll: 1000
}
}).listen(config.devPorts.webpack, 'localhost', (err) => {
if(err) throw new plugins.util.PluginError('webpack', err);
// Server listening
plugins.util.log('[webpack]', `http://localhost:${config.devPorts.webpack}/webpack-dev-server/index.html`);
});
});
gulp.task('watch-client', () => {
var config = require(path.posix.resolve('./src/server/config'));
// Start livereload
plugins.livereload.listen(config.liveReload.port);
/*
* Add watch rules to trigger livereload
*/
// On changes to compiled stuff, recompile
gulp.watch(assets.client.app.src.sass, [ 'build-client-style' ]);
// In dev mode, we just want to re-lint ts code
gulp.watch(assets.client.app.src.ts, [ 'lint-client-code' ])
.on('change', (d) => { setTimeout(() => { plugins.livereload.changed(d); }, 1000); });
// When generated css changes, let livereload handle the changes
gulp.watch(assets.client.app.dist.development.css).on('change', plugins.livereload.changed);
// When views or content change, force livereload to reload the whole page (we had issues with changes getting missed)
gulp.watch(assets.client.app.views).on('change', () => { setTimeout(plugins.livereload.reload, 1000); });
gulp.watch(assets.client.app.content).on('change', () => { setTimeout(plugins.livereload.reload, 1000); });
});
/**
* --------------------------
* Server Build Tasks
* --------------------------
*/
gulp.task('lint-server', () => {
return gulp.src(_.union(assets.server.allJS, assets.tests.server, assets.build))
// ESLint
.pipe(plugins.eslint())
.pipe(plugins.eslint.format())
.pipe(plugins.eslint.failAfterError());
});
gulp.task('build-server', ['lint-server']);
/**
* --------------------------
* Client Build Tasks
* --------------------------
*/
gulp.task('lint-client-code', () => {
return gulp.src(assets.client.app.src.ts)
// Lint the Typescript
.pipe(plugins.tslint({
formatter: 'prose'
}))
.pipe(plugins.tslint.report({
summarizeFailureOutput: true,
emitError: true
}));
});
gulp.task('build-client', (done) => {
runSequence('clean-client', [ 'build-client-code', 'build-client-style' ], done);
});
gulp.task('clean-client', () => {
return del([ 'public/**/*' ]);
});
gulp.task('build-client-code', [ 'lint-client-code' ], (done) => {
let webpackConfig = require(path.posix.resolve('./config/build/webpack.conf.js'));
webpack(webpackConfig('build'), (err, stats) => {
// Fail if there were errors initiating webpack
if(err) return done(new plugins.util.PluginError('webpack', err));
// log the stats from webpack
plugins.util.log('[webpack]', stats.toString({
assets: true,
children: false,
chunks: false,
colors: true
}));
let buildErrors;
if (stats.hasErrors()) {
buildErrors = new plugins.util.PluginError('webpack', 'Errors during webpack build. See webpack output for details.');
}
done(buildErrors);
});
});
gulp.task('build-client-style', [ 'clean-client-style' ], () => {
// Generate a list of the sources in a deterministic manner
let sourceArr = [];
assets.client.app.src.sass.forEach((f) => {
sourceArr = sourceArr.concat(glob.sync(f).sort());
});
return gulp.src(sourceArr)
// Lint the Sass
.pipe(plugins.sassLint({
formatter: 'stylish',
rules: require(path.posix.resolve('./config/build/sasslint.conf.js'))
}))
.pipe(plugins.sassLint.format())
.pipe(plugins.sassLint.failOnError())
// Compile and concat the sass (w/sourcemaps)
.pipe(plugins.sourcemaps.init())
.pipe(plugins.sass())
.pipe(plugins.concat('application.css'))
.pipe(plugins.insert.prepend(assets.bannerString))
.pipe(plugins.sourcemaps.write('.', { sourceRoot: null }))
.pipe(gulp.dest('public/dev'))
// Clean the CSS
.pipe(plugins.filter('public/dev/application.css'))
.pipe(plugins.cleanCss())
.pipe(plugins.insert.prepend(assets.bannerString))
.pipe(plugins.hash({
template: 'application.<%= hash %>.min.css',
hashLength: 16
}))
.pipe(gulp.dest('public'));
});
gulp.task('clean-client-style', () => {
return del([
'public/application*.css',
'public/dev/application.css',
'public/dev/application.css.map'
]);
});
/**
* --------------------------
* Testing Tasks
* --------------------------
*/
gulp.task('env:test', () => {
// Set the environment to test
process.env.NODE_ENV = 'test';
});
gulp.task('test-server', [ 'env:test' ], () => {
// Gather some args for custom testing
let args = [];
// --bail will cause mocha to stop on first error
if(argv.bail) { args.push('--bail'); }
// --filter will filter the test files using the glob pattern
if(null != argv.filter) { args.push(`--filter='${argv.filter}'`); }
// Run mocha tests with nodemon
return nodemon({
script: './config/build/test-server.js',
ext: 'js json',
env: { 'NODE_ENV': 'test' },
args: args,
watch: _.union(
assets.tests.server,
assets.server.allJS,
assets.server.config,
assets.build)
});
});
function runKarmaTest(additionalEnvironmentVariables, callback) {
const clientEnv = _.merge({}, process.env, additionalEnvironmentVariables);
const spawn = require('child_process').spawn;
const karma = spawn('node', [ './config/build/test-client.js' ], {
env: clientEnv
});
karma.stdout.pipe(process.stdout);
karma.stderr.pipe(process.stderr);
karma.on('close', callback);
}
gulp.task('test-client', [ 'env:test' ], () => {
runKarmaTest({}, _.noop);
});
gulp.task('test-client-ci', [ 'env:test' ], (done) => {
runKarmaTest({
KARMA_CODE_COVERAGE_ENABLED: '1',
KARMA_CI_MODE: '1'
}, done);
});
gulp.task('coverage-init', () => {
// Covering all server code minus routes
return gulp.src([
'src/server/**/*.js',
'!src/server/**/routes/**/*.js'
])
// Covering files
.pipe(plugins.istanbul({
includeUntested: true
}))
// Force `require` to return covered files
.pipe(plugins.istanbul.hookRequire());
});
gulp.task('test-server-ci', [ 'env:test', 'coverage-init' ], (done) => {
// Run mocha tests with coverage and without nodemon
// Open mongoose connections
let mongoose = require(path.posix.resolve('./src/server/lib/mongoose.js'));
let error = null;
mongoose.connect().then(() => {
let sources = assets.tests.server;
gulp.src(sources)
.pipe(plugins.mocha({
reporter: 'mochawesome',
reporterOptions: {
reportDir: 'reports/tests',
reportName: 'index',
reportTitle: pkg.name + ' Server Test Results',
inlineAssets: true
}
}))
.on('error', (err) => {
error = err; // save the error to a local variable, then let it fall through to the 'end' event
})
.on('end', () => {
return mongoose.disconnect().done();
})
.pipe(plugins.istanbul.writeReports({
dir: './reports/coverage/server',
reporters: ['text-summary', 'lcov']
}))
.on('end', () => {
done(error); // complete or fail the gulp pipeline by handling the rejected promise
});
}).done();
});
/**
* --------------------------
* Main Tasks
* --------------------------
*/
/**
* dev - main development task to run and watch server
*/
gulp.task('dev', (done) => {
runSequence(
[ 'build-server', 'build-client-style', 'lint-client-code' ],
[ 'watch-server', 'watch-client', 'webpack-dev-server' ],
done);
});
/**
* build - Builds production assets into ./public dir
*/
gulp.task('build', [ 'build-client', 'build-server' ]);
/**
* test - Run tests in dev mode with nodemon
*/
gulp.task('test', (done) => { runSequence([ 'test-server', 'test-client' ], done); });
/**
* Run tests in CI mode with coverage and no nodemon
*/
gulp.task('test-ci', (done) => { runSequence([ 'test-server-ci', 'test-client-ci' ], done); });
/**
* Sets the default task
*/
gulp.task('default', [ 'build' ]);