gulpfile.babel.js (274 lines of code) (raw):
import config from './config.json'
import gulp from 'gulp'
import file from 'gulp-file'
import gutil from 'gulp-util'
import s3 from 'gulp-s3-upload';
import sass from 'gulp-sass'
import size from 'gulp-size'
import sourcemaps from 'gulp-sourcemaps'
import template from 'gulp-template'
import browserSync from 'browser-sync'
import del from 'del'
import fs from 'fs'
import inquirer from 'inquirer'
import rp from 'request-promise-native'
import runSequence from 'run-sequence'
import source from 'vinyl-source-stream'
import named from 'vinyl-named'
import buffer from 'vinyl-buffer'
import replace from 'gulp-replace'
import webpack from 'webpack'
import ws from 'webpack-stream'
const debug = require('gulp-debug');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const browser = browserSync.create();
const buildDir = '.build';
const cdnUrl = 'https://interactive.guim.co.uk';
const isDeploy = gutil.env._.indexOf('deploy') > -1 || gutil.env._.indexOf('deploylive') > -1 || gutil.env._.indexOf('deploypreview') > -1;
const version = `html`;
const s3Path = `embed/iframeable/${config.path}`;
const s3VersionPath = `${s3Path}/${version}`;
const path = isDeploy ? `${cdnUrl}/${s3VersionPath}` : '.';
// hack to use .babelrc environments without env var, would be nice to
// be able to pass "client" env through to babel
const babelrc = JSON.parse(fs.readFileSync('.babelrc'));
const presets = (babelrc.presets || []).concat(babelrc.env.client.presets);
const plugins = (babelrc.plugins || []).concat(babelrc.env.client.plugins);
function logError(plugin, err) {
console.error(new gutil.PluginError(plugin, err.message).toString());
if (err instanceof SyntaxError) {
console.error(err.codeFrame);
}
}
let webpackPlugins = [
new webpack.LoaderOptionsPlugin({
options: {
babel: {
presets,
plugins
}
}
})
];
if (isDeploy) webpackPlugins.push(new UglifyJSPlugin({ sourceMap : true }));
function buildJS(filename) {
return () => {
return gulp.src(`./src/js/${filename}`)
.pipe(named())
.pipe(ws({
watch: false,
mode: isDeploy ? 'production' : 'development',
module: {
rules: [
{
test: /\.css$/,
loader: 'style!css'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.html$/,
use: 'raw-loader'
},
{
test: /\.mjs$/,
type: "javascript/auto",
}
]
},
devtool: 'source-map',
plugins: webpackPlugins
}, webpack))
.on('error', function handleError(e) {
this.emit('end'); // Recover from errors
})
.pipe(replace('<%= path %>', path))
.pipe(gulp.dest(buildDir));
}
}
function s3Upload(cacheControl, keyPrefix) {
return s3()({
'Bucket': 'gdn-cdn',
'ACL': 'public-read',
'CacheControl': cacheControl,
'keyTransform': fn => `${keyPrefix}/${fn}`
});
}
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
function readOpt(fn) {
try {
return fs.readFileSync(fn);
} catch (err) {
return '';
}
}
gulp.task('clean', () => del(buildDir));
gulp.task('build:css', () => {
return gulp.src('src/css/*.scss')
.pipe(sourcemaps.init())
.pipe(sass({
'outputStyle': isDeploy ? 'compressed' : 'expanded'
}).on('error', sass.logError))
.pipe(sourcemaps.write('.'))
// .pipe(template({
// path
// }))
.pipe(replace('<%= path %>', path))
.pipe(gulp.dest(buildDir))
.pipe(browser.stream({
'match': '**/*.css'
}));
});
gulp.task('build:js.main', buildJS('main.js'));
gulp.task('build:js.app', buildJS('app.js'));
gulp.task('build:js', ['build:js.main', 'build:js.app']);
gulp.task('build:html', cb => {
try {
let render = requireUncached('./src/render.js').render;
Promise.resolve(render()).then(html => {
file('main.html', html, {
'src': true
})
.pipe(replace('<%= path %>', path))
.pipe(gulp.dest(buildDir))
.on('end', cb);
}).catch(err => {
logError('render.js', err);
cb();
});
} catch (err) {
logError('render.js', err);
cb();
}
});
gulp.task('build:assets', () => {
return gulp.src('src/assets/**/*').pipe(gulp.dest(`${buildDir}/assets`));
});
gulp.task('_build', ['clean'], cb => {
runSequence(['build:css', 'build:js', 'build:html', 'build:assets'], cb);
});
// TODO: less hacky build/_build?
gulp.task('build', ['_build'], () => {
return gulp.src('iframe/*')
.pipe(template({
'css': readOpt(`${buildDir}/main.css`),
'html': readOpt(`${buildDir}/main.html`),
'js': readOpt(`${buildDir}/main.js`)
}))
.pipe(gulp.dest(buildDir));
});
gulp.task('deploy', ['build'], cb => {
if (s3Path === "embed/iframeable/2018/10/iTest") {
console.error("ERROR: You need to change the deploy path from its default value")
return;
}
inquirer.prompt({
type: 'list',
name: 'env',
message: 'Where would you like to deploy to?',
choices: ['preview', 'live']
}).then(res => {
let isLive = res.env === 'live';
gulp.src(`${buildDir}/**/*`)
.pipe(s3Upload('max-age=30', s3VersionPath))
.on('end', () => {
gulp.src('config.json')
.pipe(file('preview', version))
.pipe(isLive ? file('live', version) : gutil.noop())
.pipe(s3Upload('max-age=30', s3Path))
.on('end', cb);
});
});
});
gulp.task('local', ['build'], () => {
return gulp.src('harness/*')
.pipe(template({
'css': readOpt(`${buildDir}/main.css`),
'html': readOpt(`${buildDir}/main.html`),
'js': readOpt(`${buildDir}/main.js`)
}))
.pipe(replace('<%= path %>', path))
.pipe(gulp.dest(buildDir));
});
gulp.task('local:html', ['build:html'], () => {
return gulp.src('harness/*')
.pipe(template({
'css': readOpt(`${buildDir}/main.css`),
'html': readOpt(`${buildDir}/main.html`),
'js': readOpt(`${buildDir}/main.js`)
}))
.pipe(replace('<%= path %>', path))
.pipe(gulp.dest(buildDir));
});
gulp.task('default', ['local'], () => {
gulp.watch(['src/**/*', '!src/css/*', '!src/js/app.js', '!src/render.js', '!src/assets/*'], ['local']).on('change', evt => {
gutil.log(gutil.colors.yellow(`${evt.path} was ${evt.type}`));
});
gulp.watch(['src/css/*'], ['build:css']).on('change', evt => {
gutil.log(gutil.colors.yellow(`${evt.path} was ${evt.type}`));
});
gulp.watch(['src/js/app.js'], ['build:js']).on('change', evt => {
gutil.log(gutil.colors.yellow(`${evt.path} was ${evt.type}`));
});
gulp.watch(['src/render.js'], ['local:html']).on('change', evt => {
gutil.log(gutil.colors.yellow(`${evt.path} was ${evt.type}`));
});
gulp.watch(['src/assets/*'], ['build:assets']).on('change', evt => {
gutil.log(gutil.colors.yellow(`${evt.path} was ${evt.type}`));
});
browser.init({
'server': {
'baseDir': buildDir
},
'port': 8000
});
});
gulp.task('deploylive', ['build'], cb => {
if (s3Path === "embed/iframeable/2018/10/iTest") {
console.error("ERROR: You need to change the deploy path from its default value")
return;
}
gulp.src(`${buildDir}/**/*`)
.pipe(s3Upload('max-age=30', s3VersionPath))
.on('end', () => {
gulp.src('config.json')
.pipe(file('preview', version))
.pipe(file('live', version))
.pipe(s3Upload('max-age=30', s3Path))
.on('end', cb);
});
});
gulp.task('deploypreview', ['build'], cb => {
if (s3Path === "embed/iframeable/2018/10/iTest") {
console.error("ERROR: You need to change the deploy path from its default value")
return;
}
gulp.src(`${buildDir}/**/*`)
.pipe(s3Upload('max-age=30', s3VersionPath))
.on('end', () => {
gulp.src('config.json')
.pipe(file('preview', version))
.pipe(s3Upload('max-age=30', s3Path))
.on('end', cb);
});
});
gulp.task('url', () => {
let url = `${cdnUrl}/embed/iframeable/${config.path}/${version}/index.html`;
gutil.log(gutil.colors.green(`iframeable URL: ${url}`));
});
gulp.task('log', () => {
function log(type) {
let url = `${cdnUrl}/embed/iframeable/${config.path}/${type}.log?${Date.now()}`;
return rp(url).then(log => {
gutil.log(gutil.colors.green(`Got ${type} log:`));
console.log(log);
}).catch(err => {
if (err.statusCode === 404) {
gutil.log(gutil.colors.yellow(`No ${type} log, have you ever deployed?`));
} else {
throw err;
}
});
}
return Promise.all([log('live'), log('preview')]);
});