263 lines
No EOL
6.6 KiB
JavaScript
263 lines
No EOL
6.6 KiB
JavaScript
'use strict';
|
|
|
|
const gulp = require('gulp');
|
|
const del = require('del');
|
|
const vfs = require('vinyl-fs');
|
|
const gutil = require('gulp-util');
|
|
const babel = require('gulp-babel');
|
|
const sass = require('gulp-sass');
|
|
const os = require('os');
|
|
const glob = require('glob');
|
|
const Worker = require('tiny-worker');
|
|
const merge = require('merge-stream');
|
|
const tap = require('gulp-tap');
|
|
const rename = require('gulp-rename');
|
|
const browserify = require('browserify');
|
|
const reactPatcher = require('./gulp/gulp-react-patcher');
|
|
const isWindows = /^win/.test(process.platform);
|
|
const NODE_ENV = process.env.NODE_ENV;
|
|
const workers = [];
|
|
var isExiting = false;
|
|
|
|
const formatDirsforMatcher = dirs => {
|
|
return dirs.length > 1 ? `{${dirs.join(',')}}` : dirs[0];
|
|
};
|
|
|
|
const killAllWorkers = () => {
|
|
for(let worker of workers) {
|
|
worker.terminate();
|
|
}
|
|
};
|
|
|
|
// list of folders from where .js files are compiled and non-js files are symlinked
|
|
const dirs = [
|
|
'chrome',
|
|
'components',
|
|
'defaults',
|
|
'resource',
|
|
'resource/web-library',
|
|
'test',
|
|
'test/resource/chai',
|
|
'test/resource/chai-as-promised',
|
|
'test/resource/mocha'
|
|
];
|
|
|
|
// list of folders from which all files are symlinked
|
|
const symlinkDirs = [
|
|
'styles',
|
|
'translators',
|
|
];
|
|
|
|
// list of folders which are copied to the build folder
|
|
const copyDirs = [
|
|
'test/tests/data' // browser follows symlinks when loading test data
|
|
// triggering false-positive test results with mismatched URIs
|
|
];
|
|
|
|
// list of files from root folder to symlink
|
|
const symlinkFiles = [
|
|
'chrome.manifest', 'install.rdf', 'update.rdf'
|
|
];
|
|
|
|
// these files will be browserified during the build
|
|
const browserifyConfigs = [
|
|
{
|
|
src: 'node_modules/sinon/lib/sinon.js',
|
|
dest: 'test/resource/sinon.js',
|
|
config: {
|
|
standalone: 'sinon'
|
|
}
|
|
},
|
|
{
|
|
src: 'node_modules/chai-as-promised/lib/chai-as-promised.js',
|
|
dest: 'test/resource/chai-as-promised.js',
|
|
config: {
|
|
standalone: 'chaiAsPromised'
|
|
}
|
|
}
|
|
];
|
|
|
|
const jsGlob = `./\{${dirs.join(',')}\}/**/*.js`;
|
|
const jsGlobIgnore = `./\{${symlinkDirs.concat(copyDirs).join(',')}\}/**/*.js`;
|
|
|
|
function onError(shouldExit, err) {
|
|
if(shouldExit) {
|
|
isExiting = true;
|
|
killAllWorkers();
|
|
throw new Error(err);
|
|
} else {
|
|
gutil.log(gutil.colors.red('Error:'), err);
|
|
this.emit('end');
|
|
}
|
|
}
|
|
|
|
function onSuccess(msg) {
|
|
if(!isExiting) {
|
|
gutil.log(gutil.colors.green('Build:'), msg);
|
|
}
|
|
}
|
|
|
|
function getBrowserify(exitOnError = true) {
|
|
const streams = browserifyConfigs.map(config => {
|
|
return gulp
|
|
.src(config.src)
|
|
.pipe(tap(file => {
|
|
file.contents = browserify(file.path, config.config).bundle();
|
|
}))
|
|
.pipe(rename(config.dest))
|
|
.on('error', function(err) { onError.bind(this)(exitOnError, err); })
|
|
.on('data', file => {
|
|
onSuccess(`[browserify] ${file.path}`);
|
|
})
|
|
.pipe(gulp.dest('build'));
|
|
});
|
|
|
|
return merge.apply(merge, streams);
|
|
}
|
|
|
|
function getJS(source, sourceIgnore, exitOnError = true) {
|
|
if (sourceIgnore) {
|
|
source = [source, '!' + sourceIgnore];
|
|
}
|
|
|
|
return gulp.src(source, { base: '.' })
|
|
.pipe(babel())
|
|
.on('error', function(err) { onError.bind(this)(exitOnError, err); })
|
|
.on('data', file => {
|
|
onSuccess(`[js] ${file.path}`);
|
|
})
|
|
.pipe(reactPatcher())
|
|
.pipe(gulp.dest('./build'));
|
|
}
|
|
|
|
function getJSParallel(source, sourceIgnore, exitOnError = true) {
|
|
const jsFiles = glob.sync(source, { ignore: sourceIgnore });
|
|
const cpuCount = os.cpus().length;
|
|
const threadCount = Math.min(cpuCount, jsFiles.length);
|
|
let threadsActive = threadCount;
|
|
let isError = false;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
for(let i = 0; i < threadCount; i++) {
|
|
let worker = new Worker('gulp/babel-worker.js');
|
|
workers[i] = worker;
|
|
worker.onmessage = ev => {
|
|
if(ev.data.error) {
|
|
isError = true;
|
|
let errorMsg = `Failed while processing ${ev.data.sourcefile}: ${ev.data.error}`;
|
|
NODE_ENV == 'debug' && console.log(`process ${i}: ${errorMsg}`);
|
|
onError(exitOnError, errorMsg);
|
|
reject(errorMsg);
|
|
}
|
|
|
|
NODE_ENV == 'debug' && console.log(`process ${i} took ${ev.data.processingTime} ms to process ${ev.data.sourcefile}`);
|
|
NODE_ENV != 'debug' && onSuccess(`[js] ${ev.data.sourcefile}`);
|
|
|
|
if(ev.data.isSkipped) {
|
|
NODE_ENV == 'debug' && console.log(`process ${i} SKIPPED ${ev.data.sourcefile}`);
|
|
}
|
|
let nextFile = jsFiles.pop();
|
|
|
|
if(!isError && nextFile) {
|
|
NODE_ENV == 'debug' && console.log(`process ${i} scheduled to process ${nextFile}`);
|
|
worker.postMessage(nextFile);
|
|
} else {
|
|
NODE_ENV == 'debug' && console.log(`process ${i} has terminated`);
|
|
worker.terminate();
|
|
workers.splice(i, 1);
|
|
if(!--threadsActive) {
|
|
resolve();
|
|
}
|
|
}
|
|
};
|
|
worker.postMessage(jsFiles.pop());
|
|
}
|
|
|
|
NODE_ENV == 'debug' && console.log(`Started ${threadCount} processes for processing JS`);
|
|
});
|
|
}
|
|
|
|
function getSymlinks(exitOnError = true) {
|
|
const match = symlinkFiles
|
|
.concat(dirs.map(d => `${d}/**`))
|
|
.concat(symlinkDirs.map(d => `${d}/**`))
|
|
.concat([`!./${formatDirsforMatcher(dirs)}/**/*.js`])
|
|
.concat([`!./${formatDirsforMatcher(copyDirs)}/**`]);
|
|
|
|
return gulp
|
|
.src(match, { nodir: true, base: '.', read: isWindows })
|
|
.on('error', function(err) { onError.bind(this)(exitOnError, err); })
|
|
.on('data', file => {
|
|
onSuccess(`[ln] ${file.path.substr(__dirname.length + 1)}`);
|
|
})
|
|
.pipe(isWindows ? gulp.dest('build/') : vfs.symlink('build/'));
|
|
}
|
|
|
|
function getCopy(exitOnError = true) {
|
|
return gulp
|
|
.src(copyDirs.map(d => `${d}/**`), { base: '.' })
|
|
.on('data', file => {
|
|
onSuccess(`[cp] ${file.path.substr(__dirname.length + 1)}`);
|
|
})
|
|
.on('error', function(err) { onError.bind(this)(exitOnError, err); })
|
|
.pipe(gulp.dest('build/'));
|
|
}
|
|
|
|
function getSass(exitOnError = true) {
|
|
return gulp
|
|
.src('scss/*.scss')
|
|
.on('error', function(err) { onError.bind(this)(exitOnError, err); })
|
|
.pipe(sass())
|
|
.pipe(gulp.dest('./build/chrome/skin/default/zotero/components/'));
|
|
}
|
|
|
|
|
|
gulp.task('clean', () => {
|
|
return del('build');
|
|
});
|
|
|
|
gulp.task('symlink', () => {
|
|
return getSymlinks();
|
|
});
|
|
|
|
gulp.task('js', done => {
|
|
getJSParallel(jsGlob, jsGlobIgnore)
|
|
.then(done)
|
|
.catch(errorMsg => {
|
|
onError(errorMsg);
|
|
});
|
|
});
|
|
|
|
gulp.task('browserify', () => {
|
|
getBrowserify();
|
|
});
|
|
|
|
gulp.task('copy', () => {
|
|
getCopy();
|
|
});
|
|
|
|
gulp.task('sass', () => {
|
|
return getSass();
|
|
});
|
|
|
|
gulp.task('build', ['js', 'sass', 'symlink', 'browserify', 'copy']);
|
|
|
|
gulp.task('build-clean', ['clean'], () => {
|
|
gulp.start('build');
|
|
});
|
|
|
|
gulp.task('dev', ['clean'], () => {
|
|
var interval = 750;
|
|
|
|
gulp.watch(jsGlob, { interval }).on('change', event => {
|
|
getJS(event.path, jsGlobIgnore, false);
|
|
});
|
|
|
|
gulp.watch('src/styles/*.scss', { interval }).on('change', () => {
|
|
getSass(false);
|
|
});
|
|
|
|
gulp.start('build');
|
|
});
|
|
|
|
gulp.task('default', ['dev']); |