mk9-prebid
Version:
Header Bidding Management Library
425 lines (359 loc) • 13.6 kB
JavaScript
/* eslint-disable no-console */
;
var _ = require('lodash');
var argv = require('yargs').argv;
var gulp = require('gulp');
var gutil = require('gulp-util');
var connect = require('gulp-connect');
var webpack = require('webpack');
var webpackStream = require('webpack-stream');
var terser = require('gulp-terser');
var gulpClean = require('gulp-clean');
var KarmaServer = require('karma').Server;
var karmaConfMaker = require('./karma.conf.maker.js');
var opens = require('opn');
var webpackConfig = require('./webpack.conf.js');
var helpers = require('./gulpHelpers.js');
var concat = require('gulp-concat');
var header = require('gulp-header');
var footer = require('gulp-footer');
var replace = require('gulp-replace');
var shell = require('gulp-shell');
var eslint = require('gulp-eslint');
var gulpif = require('gulp-if');
var sourcemaps = require('gulp-sourcemaps');
var through = require('through2');
var fs = require('fs');
var jsEscape = require('gulp-js-escape');
const path = require('path');
const execa = require('execa');
var prebid = require('./package.json');
var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10);
var banner = '/* <%= prebid.name %> v<%= prebid.version %>\n' + dateString + '*/\n';
var port = 9999;
const FAKE_SERVER_HOST = argv.host ? argv.host : 'localhost';
const FAKE_SERVER_PORT = 4444;
const { spawn } = require('child_process');
// these modules must be explicitly listed in --modules to be included in the build, won't be part of "all" modules
var explicitModules = [
'pre1api'
];
// all the following functions are task functions
function bundleToStdout() {
nodeBundle().then(file => console.log(file));
}
bundleToStdout.displayName = 'bundle-to-stdout';
function clean() {
return gulp.src(['build'], {
read: false,
allowEmpty: true
})
.pipe(gulpClean());
}
// Dependant task for building postbid. It escapes postbid-config file.
function escapePostbidConfig() {
gulp.src('./integrationExamples/postbid/oas/postbid-config.js')
.pipe(jsEscape())
.pipe(gulp.dest('build/postbid/'));
};
escapePostbidConfig.displayName = 'escape-postbid-config';
function lint(done) {
if (argv.nolint) {
return done();
}
const isFixed = function (file) {
return file.eslint != null && file.eslint.fixed;
}
return gulp.src([
'src/**/*.js',
'modules/**/*.js',
'test/**/*.js',
'plugins/**/*.js',
'./*.js'
], { base: './' })
.pipe(gulpif(argv.nolintfix, eslint(), eslint({ fix: true })))
.pipe(eslint.format('stylish'))
.pipe(eslint.failAfterError())
.pipe(gulpif(isFixed, gulp.dest('./')));
};
// View the code coverage report in the browser.
function viewCoverage(done) {
var coveragePort = 1999;
var mylocalhost = (argv.host) ? argv.host : 'localhost';
connect.server({
port: coveragePort,
root: 'build/coverage/lcov-report',
livereload: false,
debug: true
});
opens('http://' + mylocalhost + ':' + coveragePort);
done();
};
viewCoverage.displayName = 'view-coverage';
// View the reviewer tools page
function viewReview(done) {
var mylocalhost = (argv.host) ? argv.host : 'localhost';
var reviewUrl = 'http://' + mylocalhost + ':' + port + '/integrationExamples/reviewerTools/index.html'; // reuse the main port from 9999
// console.log(`stdout: opening` + reviewUrl);
opens(reviewUrl);
done();
};
viewReview.displayName = 'view-review';
// Watch Task with Live Reload
function watch(done) {
var mainWatcher = gulp.watch([
'src/**/*.js',
'modules/**/*.js',
'test/spec/**/*.js',
'!test/spec/loaders/**/*.js'
]);
var loaderWatcher = gulp.watch([
'loaders/**/*.js',
'test/spec/loaders/**/*.js'
]);
connect.server({
https: argv.https,
port: port,
host: FAKE_SERVER_HOST,
root: './',
livereload: true
});
mainWatcher.on('all', gulp.series(clean, gulp.parallel(lint, 'build-bundle-dev', test)));
loaderWatcher.on('all', gulp.series(lint));
done();
};
function makeModuleList(modules) {
return modules.map(module => {
return '"' + module + '"'
});
}
function makeDevpackPkg() {
var cloned = _.cloneDeep(webpackConfig);
cloned.devtool = 'source-map';
var externalModules = helpers.getArgModules();
const analyticsSources = helpers.getAnalyticsSources();
const moduleSources = helpers.getModulePaths(externalModules);
return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js'))
.pipe(helpers.nameModules(externalModules))
.pipe(webpackStream(cloned, webpack))
.pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules)))
.pipe(gulp.dest('build/dev'))
.pipe(connect.reload());
}
function makeWebpackPkg() {
var cloned = _.cloneDeep(webpackConfig);
delete cloned.devtool;
var externalModules = helpers.getArgModules();
const analyticsSources = helpers.getAnalyticsSources();
const moduleSources = helpers.getModulePaths(externalModules);
return gulp.src([].concat(moduleSources, analyticsSources, 'src/prebid.js'))
.pipe(helpers.nameModules(externalModules))
.pipe(webpackStream(cloned, webpack))
.pipe(terser())
.pipe(replace(/('|")v\$prebid\.modulesList\$('|")/g, makeModuleList(externalModules)))
.pipe(gulpif(file => file.basename === 'prebid-core.js', header(banner, { prebid: prebid })))
.pipe(gulp.dest('build/dist'));
}
function getModulesListToAddInBanner(modules) {
return (modules.length > 0) ? modules.join(', ') : 'All available modules in current version.';
}
function gulpBundle(dev) {
return bundle(dev).pipe(gulp.dest('build/' + (dev ? 'dev' : 'dist')));
}
function nodeBundle(modules) {
return new Promise((resolve, reject) => {
bundle(false, modules)
.on('error', (err) => {
reject(err);
})
.pipe(through.obj(function (file, enc, done) {
resolve(file.contents.toString(enc));
done();
}));
});
}
function bundle(dev, moduleArr) {
var modules = moduleArr || helpers.getArgModules();
var allModules = helpers.getModuleNames(modules);
if (modules.length === 0) {
modules = allModules.filter(module => explicitModules.indexOf(module) === -1);
} else {
var diff = _.difference(modules, allModules);
if (diff.length !== 0) {
throw new gutil.PluginError({
plugin: 'bundle',
message: 'invalid modules: ' + diff.join(', ')
});
}
}
var entries = [helpers.getBuiltPrebidCoreFile(dev)].concat(helpers.getBuiltModules(dev, modules));
var outputFileName = argv.bundleName ? argv.bundleName : 'prebid.js';
// change output filename if argument --tag given
if (argv.tag && argv.tag.length) {
outputFileName = outputFileName.replace(/\.js$/, `.${argv.tag}.js`);
}
gutil.log('Concatenating files:\n', entries);
gutil.log('Appending ' + prebid.globalVarName + '.processQueue();');
gutil.log('Generating bundle:', outputFileName);
return gulp.src(
entries
)
// Need to uodate the "Modules: ..." section in comment with the current modules list
.pipe(replace(/(Modules: )(.*?)(\*\/)/, ('$1' + getModulesListToAddInBanner(helpers.getArgModules()) + ' $3')))
.pipe(gulpif(dev, sourcemaps.init({ loadMaps: true })))
.pipe(concat(outputFileName))
.pipe(gulpif(!argv.manualEnable, footer('\n<%= global %>.processQueue();', {
global: prebid.globalVarName
}
)))
.pipe(gulpif(dev, sourcemaps.write('.')));
}
// Run the unit tests.
//
// By default, this runs in headless chrome.
//
// If --watch is given, the task will re-run unit tests whenever the source code changes
// If --file "<path-to-test-file>" is given, the task will only run tests in the specified file.
// If --browserstack is given, it will run the full suite of currently supported browsers.
// If --browsers is given, browsers can be chosen explicitly. e.g. --browsers=chrome,firefox,ie9
// If --notest is given, it will immediately skip the test task (useful for developing changes with `gulp serve --notest`)
function test(done) {
if (argv.notest) {
done();
} else if (argv.e2e) {
let wdioCmd = path.join(__dirname, 'node_modules/.bin/wdio');
let wdioConf = path.join(__dirname, 'wdio.conf.js');
let wdioOpts;
if (argv.file) {
wdioOpts = [
wdioConf,
`--spec`,
`${argv.file}`
]
} else {
wdioOpts = [
wdioConf
];
}
// run fake-server
const fakeServer = spawn('node', ['./test/fake-server/index.js', `--port=${FAKE_SERVER_PORT}`]);
fakeServer.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
fakeServer.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
execa(wdioCmd, wdioOpts, { stdio: 'inherit' })
.then(stdout => {
// kill fake server
fakeServer.kill('SIGINT');
done();
process.exit(0);
})
.catch(err => {
// kill fake server
fakeServer.kill('SIGINT');
done(new Error(`Tests failed with error: ${err}`));
process.exit(1);
});
} else {
var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file);
var browserOverride = helpers.parseBrowserArgs(argv);
if (browserOverride.length > 0) {
karmaConf.browsers = browserOverride;
}
new KarmaServer(karmaConf, newKarmaCallback(done)).start();
}
}
function newKarmaCallback(done) {
return function (exitCode) {
if (exitCode) {
done(new Error('Karma tests failed with exit code ' + exitCode));
if (argv.browserstack) {
process.exit(exitCode);
}
} else {
done();
if (argv.browserstack) {
process.exit(exitCode);
}
}
}
}
// If --file "<path-to-test-file>" is given, the task will only run tests in the specified file.
function testCoverage(done) {
new KarmaServer(karmaConfMaker(true, false, false, argv.file), newKarmaCallback(done)).start();
}
function coveralls() { // 2nd arg is a dependency: 'test' must be finished
// first send results of istanbul's test coverage to coveralls.io.
return gulp.src('gulpfile.js', { read: false }) // You have to give it a file, but you don't
// have to read it.
.pipe(shell('cat build/coverage/lcov.info | node_modules/coveralls/bin/coveralls.js'));
}
// This task creates postbid.js. Postbid setup is different from prebid.js
// More info can be found here http://prebid.org/overview/what-is-post-bid.html
function buildPostbid() {
var fileContent = fs.readFileSync('./build/postbid/postbid-config.js', 'utf8');
return gulp.src('./integrationExamples/postbid/oas/postbid.js')
.pipe(replace('\[%%postbid%%\]', fileContent))
.pipe(gulp.dest('build/postbid/'));
}
function setupE2e(done) {
if (!argv.host) {
throw new gutil.PluginError({
plugin: 'E2E test',
message: gutil.colors.red('Host should be defined e.g. ap.localhost, anlocalhost. localhost cannot be used as safari browserstack is not able to connect to localhost')
});
}
process.env.TEST_SERVER_HOST = argv.host;
if (argv.https) {
process.env.TEST_SERVER_PROTOCOL = argv.https;
}
argv.e2e = true;
done();
}
function injectFakeServerEndpoint() {
return gulp.src(['build/dist/*.js'])
.pipe(replace('https://ib.adnxs.com/ut/v3/prebid', `http://${FAKE_SERVER_HOST}:${FAKE_SERVER_PORT}`))
.pipe(gulp.dest('build/dist'));
}
function injectFakeServerEndpointDev() {
return gulp.src(['build/dev/*.js'])
.pipe(replace('https://ib.adnxs.com/ut/v3/prebid', `http://${FAKE_SERVER_HOST}:${FAKE_SERVER_PORT}`))
.pipe(gulp.dest('build/dev'));
}
function startFakeServer() {
const fakeServer = spawn('node', ['./test/fake-server/index.js', `--port=${FAKE_SERVER_PORT}`]);
fakeServer.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
fakeServer.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
}
// support tasks
gulp.task(lint);
gulp.task(watch);
gulp.task(clean);
gulp.task(escapePostbidConfig);
gulp.task('build-bundle-dev', gulp.series(makeDevpackPkg, gulpBundle.bind(null, true)));
gulp.task('build-bundle-prod', gulp.series(makeWebpackPkg, gulpBundle.bind(null, false)));
// public tasks (dependencies are needed for each task since they can be ran on their own)
gulp.task('test', gulp.series(clean, lint, test));
gulp.task('test-coverage', gulp.series(clean, testCoverage));
gulp.task(viewCoverage);
gulp.task('coveralls', gulp.series('test-coverage', coveralls));
gulp.task('build', gulp.series(clean, 'build-bundle-prod'));
gulp.task('build-postbid', gulp.series(escapePostbidConfig, buildPostbid));
gulp.task('serve', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, test)));
gulp.task('serve-fast', gulp.series(clean, gulp.parallel('build-bundle-dev', watch)));
gulp.task('serve-fake', gulp.series(clean, gulp.parallel('build-bundle-dev', watch), injectFakeServerEndpointDev, test, startFakeServer));
gulp.task('default', gulp.series(clean, makeWebpackPkg));
gulp.task('e2e-test', gulp.series(clean, setupE2e, gulp.parallel('build-bundle-prod', watch), injectFakeServerEndpoint, test));
// other tasks
gulp.task(bundleToStdout);
gulp.task('bundle', gulpBundle.bind(null, false)); // used for just concatenating pre-built files with no build step
// build task for reviewers, runs test-coverage, serves, without watching
gulp.task(viewReview);
gulp.task('review-start', gulp.series(clean, lint, gulp.parallel('build-bundle-dev', watch, testCoverage), viewReview));
module.exports = nodeBundle;