When generating the SVG sprite (using imagemin and svstore) the imagemin step needs to NOT cleanup/remove the viewBox attributes from the individual svg files. fixes #1306
498 lines
14 KiB
JavaScript
498 lines
14 KiB
JavaScript
// ==========================================================================
|
|
// Gulp build script
|
|
// ==========================================================================
|
|
/* global require, __dirname */
|
|
/* eslint no-console: "off" */
|
|
|
|
const path = require('path');
|
|
const gulp = require('gulp');
|
|
// ------------------------------------
|
|
// JavaScript
|
|
// ------------------------------------
|
|
const terser = require('gulp-terser');
|
|
const rollup = require('gulp-better-rollup');
|
|
const babel = require('rollup-plugin-babel');
|
|
const commonjs = require('rollup-plugin-commonjs');
|
|
const resolve = require('rollup-plugin-node-resolve');
|
|
// ------------------------------------
|
|
// CSS
|
|
// ------------------------------------
|
|
const sass = require('gulp-sass');
|
|
const clean = require('gulp-clean-css');
|
|
const prefix = require('gulp-autoprefixer');
|
|
// ------------------------------------
|
|
// Images
|
|
// ------------------------------------
|
|
const svgstore = require('gulp-svgstore');
|
|
const imagemin = require('gulp-imagemin');
|
|
// ------------------------------------
|
|
// Utils
|
|
// ------------------------------------
|
|
const del = require('del');
|
|
const filter = require('gulp-filter');
|
|
const header = require('gulp-header');
|
|
const gitbranch = require('git-branch');
|
|
const rename = require('gulp-rename');
|
|
const replace = require('gulp-replace');
|
|
const ansi = require('ansi-colors');
|
|
const log = require('fancy-log');
|
|
const open = require('gulp-open');
|
|
const plumber = require('gulp-plumber');
|
|
const size = require('gulp-size');
|
|
const sourcemaps = require('gulp-sourcemaps');
|
|
const through = require('through2');
|
|
// ------------------------------------
|
|
// Deployment
|
|
// ------------------------------------
|
|
const aws = require('aws-sdk');
|
|
const publish = require('gulp-awspublish');
|
|
const FastlyPurge = require('fastly-purge');
|
|
// ------------------------------------
|
|
// Configs
|
|
// ------------------------------------
|
|
const pkg = require('./package.json');
|
|
const build = require('./build.json');
|
|
const deploy = require('./deploy.json');
|
|
// ------------------------------------
|
|
// Info from package
|
|
// ------------------------------------
|
|
const { browserslist: browsers, version } = pkg;
|
|
const minSuffix = '.min';
|
|
|
|
// Get AWS config
|
|
Object.values(deploy).forEach(target => {
|
|
Object.assign(target, {
|
|
publisher: publish.create({
|
|
region: target.region,
|
|
params: {
|
|
Bucket: target.bucket,
|
|
},
|
|
credentials: new aws.SharedIniFileCredentials({ profile: 'plyr' }),
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Paths
|
|
const paths = {
|
|
plyr: {
|
|
// Source paths
|
|
src: {
|
|
sass: path.join(__dirname, 'src/sass/**/*.scss'),
|
|
js: path.join(__dirname, 'src/js/**/*.js'),
|
|
sprite: path.join(__dirname, 'src/sprite/*.svg'),
|
|
},
|
|
|
|
// Output paths
|
|
output: path.join(__dirname, 'dist/'),
|
|
},
|
|
demo: {
|
|
// Source paths
|
|
src: {
|
|
sass: path.join(__dirname, 'demo/src/sass/**/*.scss'),
|
|
js: path.join(__dirname, 'demo/src/js/**/*.js'),
|
|
},
|
|
|
|
// Output paths
|
|
output: path.join(__dirname, 'demo/dist/'),
|
|
|
|
// Demo
|
|
root: path.join(__dirname, 'demo/'),
|
|
},
|
|
upload: [
|
|
path.join(__dirname, `dist/*${minSuffix}.*`),
|
|
path.join(__dirname, 'dist/*.css'),
|
|
path.join(__dirname, 'dist/*.svg'),
|
|
path.join(__dirname, `demo/dist/*${minSuffix}.*`),
|
|
path.join(__dirname, 'demo/dist/*.css'),
|
|
path.join(__dirname, 'demo/dist/*.svg'),
|
|
],
|
|
};
|
|
|
|
// Task arrays
|
|
const tasks = {
|
|
css: [],
|
|
js: [],
|
|
sprite: [],
|
|
clean: 'clean',
|
|
};
|
|
|
|
// Size plugin
|
|
const sizeOptions = { showFiles: true, gzip: true };
|
|
|
|
// Clean out /dist
|
|
gulp.task(tasks.clean, done => {
|
|
const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*'));
|
|
|
|
// Don't delete the mp4
|
|
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
|
|
|
|
del(dirs);
|
|
|
|
done();
|
|
});
|
|
|
|
// JavaScript
|
|
Object.entries(build.js).forEach(([filename, entry]) => {
|
|
const { dist, formats, namespace, polyfill, src } = entry;
|
|
|
|
formats.forEach(format => {
|
|
const name = `js:${filename}:${format}`;
|
|
const extension = format === 'es' ? 'mjs' : 'js';
|
|
tasks.js.push(name);
|
|
|
|
gulp.task(name, () =>
|
|
gulp
|
|
.src(src)
|
|
.pipe(plumber())
|
|
.pipe(sourcemaps.init())
|
|
.pipe(
|
|
rollup(
|
|
{
|
|
plugins: [
|
|
resolve(),
|
|
commonjs(),
|
|
babel({
|
|
presets: [
|
|
[
|
|
'@babel/env',
|
|
{
|
|
// debug: true,
|
|
useBuiltIns: polyfill ? 'usage' : false,
|
|
corejs: polyfill ? 3 : undefined,
|
|
},
|
|
],
|
|
],
|
|
babelrc: false,
|
|
exclude: [/\/core-js\//],
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: namespace,
|
|
format,
|
|
},
|
|
),
|
|
)
|
|
.pipe(header('typeof navigator === "object" && ')) // "Support" SSR (#935)
|
|
.pipe(
|
|
rename({
|
|
extname: `.${extension}`,
|
|
}),
|
|
)
|
|
.pipe(gulp.dest(dist))
|
|
.pipe(filter(`**/*.${extension}`))
|
|
.pipe(terser())
|
|
.pipe(rename({ suffix: minSuffix }))
|
|
.pipe(size(sizeOptions))
|
|
.pipe(sourcemaps.write(''))
|
|
.pipe(gulp.dest(dist)),
|
|
);
|
|
});
|
|
});
|
|
|
|
// CSS
|
|
Object.entries(build.css).forEach(([filename, entry]) => {
|
|
const { dist, src } = entry;
|
|
const name = `css:${filename}`;
|
|
tasks.css.push(name);
|
|
|
|
gulp.task(name, () =>
|
|
gulp
|
|
.src(src)
|
|
.pipe(plumber())
|
|
.pipe(sass())
|
|
.pipe(
|
|
prefix(browsers, {
|
|
cascade: false,
|
|
}),
|
|
)
|
|
.pipe(clean())
|
|
.pipe(size(sizeOptions))
|
|
.pipe(gulp.dest(dist)),
|
|
);
|
|
});
|
|
|
|
// SVG Sprites
|
|
Object.entries(build.sprite).forEach(([filename, entry]) => {
|
|
const { dist, src } = entry;
|
|
const name = `sprite:${filename}`;
|
|
tasks.sprite.push(name);
|
|
|
|
gulp.task(name, () =>
|
|
gulp
|
|
.src(src)
|
|
.pipe(plumber())
|
|
.pipe(imagemin([
|
|
imagemin.svgo({
|
|
plugins: [{ removeViewBox: false }]
|
|
})
|
|
]))
|
|
.pipe(svgstore())
|
|
.pipe(rename({ basename: path.parse(filename).name }))
|
|
.pipe(size(sizeOptions))
|
|
.pipe(gulp.dest(dist)),
|
|
);
|
|
});
|
|
|
|
// Build all JS
|
|
gulp.task('js', () => gulp.parallel(...tasks.js));
|
|
|
|
// Watch for file changes
|
|
gulp.task('watch', () => {
|
|
// Plyr core
|
|
gulp.watch(paths.plyr.src.js, gulp.parallel(...tasks.js));
|
|
gulp.watch(paths.plyr.src.sass, gulp.parallel(...tasks.css));
|
|
gulp.watch(paths.plyr.src.sprite, gulp.parallel(...tasks.sprite));
|
|
|
|
// Demo
|
|
gulp.watch(paths.demo.src.js, gulp.parallel(...tasks.js));
|
|
gulp.watch(paths.demo.src.sass, gulp.parallel(...tasks.css));
|
|
});
|
|
|
|
// Build distribution
|
|
gulp.task('build', gulp.series(tasks.clean, gulp.parallel(...tasks.js, ...tasks.css, ...tasks.sprite)));
|
|
|
|
// Default gulp task
|
|
gulp.task('default', gulp.series('build', 'watch'));
|
|
|
|
// Publish a version to CDN and demo
|
|
// --------------------------------------------
|
|
// Get deployment config
|
|
let credentials = {};
|
|
try {
|
|
credentials = require('./credentials.json'); //eslint-disable-line
|
|
} catch (e) {
|
|
// Do nothing
|
|
}
|
|
|
|
// Get branch info
|
|
const branch = {
|
|
current: gitbranch.sync(),
|
|
master: 'master',
|
|
beta: 'beta',
|
|
};
|
|
|
|
const maxAge = 31536000; // 1 year
|
|
const options = {
|
|
cdn: {
|
|
headers: {
|
|
'Cache-Control': `max-age=${maxAge}`,
|
|
},
|
|
},
|
|
demo: {
|
|
uploadPath: branch.current === branch.beta ? 'beta' : null,
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
|
|
},
|
|
},
|
|
symlinks(ver, filename) {
|
|
return {
|
|
headers: {
|
|
// http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
|
|
'x-amz-website-redirect-location': `/${ver}/${filename}`,
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
const regex =
|
|
'(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?';
|
|
const semver = new RegExp(`v${regex}`, 'gi');
|
|
const localPath = new RegExp('(../)?dist', 'gi');
|
|
const versionPath = `https://${deploy.cdn.domain}/${version}`;
|
|
const cdnpath = new RegExp(`${deploy.cdn.domain}/${regex}/`, 'gi');
|
|
|
|
const renameFile = rename(p => {
|
|
p.basename = p.basename.replace(minSuffix, ''); // eslint-disable-line
|
|
p.dirname = p.dirname.replace('.', version); // eslint-disable-line
|
|
});
|
|
|
|
// Check we're on the correct branch to deploy
|
|
const canDeploy = () => {
|
|
const allowed = [branch.master, branch.beta];
|
|
|
|
if (!allowed.includes(branch.current)) {
|
|
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
gulp.task('version', done => {
|
|
if (!canDeploy()) {
|
|
done();
|
|
return null;
|
|
}
|
|
|
|
const { domain } = deploy.cdn;
|
|
|
|
log(`Uploading ${ansi.green.bold(version)} to ${ansi.cyan(domain)}...`);
|
|
|
|
// Replace versioned URLs in source
|
|
const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js'];
|
|
|
|
return gulp
|
|
.src(files.map(file => path.join(__dirname, `src/js/${file}`)), { base: '.' })
|
|
.pipe(replace(semver, `v${version}`))
|
|
.pipe(replace(cdnpath, `${domain}/${version}/`))
|
|
.pipe(gulp.dest('./'));
|
|
});
|
|
|
|
// Publish version to CDN bucket
|
|
gulp.task('cdn', done => {
|
|
if (!canDeploy()) {
|
|
done();
|
|
return null;
|
|
}
|
|
|
|
const { domain, publisher } = deploy.cdn;
|
|
|
|
if (!publisher) {
|
|
throw new Error('No publisher instance. Check AWS configuration.');
|
|
}
|
|
|
|
log(`Uploading ${ansi.green.bold(pkg.version)} to ${ansi.cyan(domain)}...`);
|
|
|
|
// Upload to CDN
|
|
return (
|
|
gulp
|
|
.src(paths.upload)
|
|
.pipe(renameFile)
|
|
// Remove min suffix from source map URL
|
|
.pipe(
|
|
replace(
|
|
/sourceMappingURL=([\w-?.]+)/,
|
|
(match, filename) => `sourceMappingURL=${filename.replace(minSuffix, '')}`,
|
|
),
|
|
)
|
|
.pipe(size(sizeOptions))
|
|
.pipe(replace(localPath, versionPath))
|
|
.pipe(publisher.publish(options.cdn.headers))
|
|
.pipe(publish.reporter())
|
|
);
|
|
});
|
|
|
|
// Purge the fastly cache incase any 403/404 are cached
|
|
gulp.task('purge', () => {
|
|
if (!Object.keys(credentials).includes('fastly')) {
|
|
throw new Error('Fastly credentials required to purge cache.');
|
|
}
|
|
|
|
const { fastly } = credentials;
|
|
const list = [];
|
|
|
|
return gulp
|
|
.src(paths.upload)
|
|
.pipe(
|
|
through.obj((file, enc, cb) => {
|
|
const filename = file.path.split('/').pop();
|
|
list.push(`${versionPath}/${filename.replace(minSuffix, '')}`);
|
|
cb(null);
|
|
}),
|
|
)
|
|
.on('end', () => {
|
|
const purge = new FastlyPurge(fastly.token);
|
|
|
|
list.forEach(url => {
|
|
log(`Purging ${ansi.cyan(url)}...`);
|
|
|
|
purge.url(url, (error, result) => {
|
|
if (error) {
|
|
log.error(error);
|
|
} else if (result) {
|
|
log(result);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// Publish to demo bucket
|
|
gulp.task('demo', done => {
|
|
if (!canDeploy()) {
|
|
done();
|
|
return null;
|
|
}
|
|
|
|
const { publisher } = deploy.demo;
|
|
const { domain } = deploy.cdn;
|
|
|
|
if (!publisher) {
|
|
throw new Error('No publisher instance. Check AWS configuration.');
|
|
}
|
|
|
|
log(`Uploading ${ansi.green.bold(pkg.version)} to ${ansi.cyan(domain)}...`);
|
|
|
|
// Replace versioned files in readme.md
|
|
gulp.src([`${__dirname}/readme.md`])
|
|
.pipe(replace(cdnpath, `${domain}/${version}/`))
|
|
.pipe(gulp.dest(__dirname));
|
|
|
|
// Replace local file paths with remote paths in demo HTML
|
|
// e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
|
|
const index = `${paths.demo.root}index.html`;
|
|
const error = `${paths.demo.root}error.html`;
|
|
const pages = [index];
|
|
|
|
if (branch.current === branch.master) {
|
|
pages.push(error);
|
|
}
|
|
|
|
return gulp
|
|
.src(pages)
|
|
.pipe(replace(localPath, versionPath))
|
|
.pipe(publisher.publish(options.demo.headers))
|
|
.pipe(publish.reporter());
|
|
});
|
|
|
|
gulp.task('error', done => {
|
|
// Only update CDN for master (prod)
|
|
if (!canDeploy() || branch.current !== branch.master) {
|
|
done();
|
|
return null;
|
|
}
|
|
|
|
const { publisher } = deploy.cdn;
|
|
|
|
if (!publisher) {
|
|
throw new Error('No publisher instance. Check AWS configuration.');
|
|
}
|
|
|
|
// Replace local file paths with remote paths in demo HTML
|
|
// e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
|
|
// Upload error.html to cdn
|
|
return gulp
|
|
.src(`${paths.demo.root}error.html`)
|
|
.pipe(replace(localPath, versionPath))
|
|
.pipe(publisher.publish(options.demo.headers))
|
|
.pipe(publish.reporter());
|
|
});
|
|
|
|
// Open the demo site to check it's ok
|
|
gulp.task('open', () => {
|
|
const { domain } = deploy.demo;
|
|
|
|
return gulp.src(__filename).pipe(
|
|
open({
|
|
uri: `https://${domain}/${branch.current === branch.beta ? 'beta' : ''}`,
|
|
}),
|
|
);
|
|
});
|
|
|
|
// Do everything
|
|
gulp.task(
|
|
'deploy',
|
|
gulp.series(
|
|
'version',
|
|
tasks.clean,
|
|
gulp.parallel(...tasks.js, ...tasks.css, ...tasks.sprite),
|
|
'cdn',
|
|
'demo',
|
|
'purge',
|
|
'open',
|
|
),
|
|
);
|