Skip to content

Instantly share code, notes, and snippets.

@nickjacob
Last active February 5, 2016 15:55
Show Gist options
  • Save nickjacob/5dbe83cc1365a85190a2 to your computer and use it in GitHub Desktop.
Save nickjacob/5dbe83cc1365a85190a2 to your computer and use it in GitHub Desktop.
How to inline third-party SDKs in Prebid.js

Inlining Bidder SDKs in Prebid

To improve performance in Prebid (or any other header bidding setup), you can inline third-party SDKs/resources that are required to make bids. This removes network requests/roundtrips which can have a significant impact (especially on mobile) — cutting down time to first bid request.

One way to inline the scripts as part of your build process is to replace calls to adloader.loadScript with a preprocessor directive that downloads the script and inlines its contents into the page. See below for instructions, with AOL/ADTECH as an example.

gulp-preprocess & request

Add gulp-preprocess & request: npm install --save-dev gulp-preprocess request, this will let you add preprocessing directives to the code, and add the request library to make HTTP requests.

Then add a gulp task to preprocess the code before it is bundled:

function preprocessFilter() {

    return preprocess({
        context: {
            scripts: {
                adtech: ['http://aka-cdn.adtechus.com/dt/common/DAC.js'],
                yieldbot: 'http://cdn.yldbt.com/js/yieldbot.intent.js'
            },
            script: function (args) {

                // it's an array, so get
                // the first argument
                var src = args[0],
                    exported = args[1],
                    callback = args[2];

                if (toString.call(src) !== '[object Array]') {
                    src = [src];
                }

                var scripts = src.map(function (scriptSrc) {

                    var res = request('GET', scriptSrc, {
                        headers: {
                            'User-Agent': CHROME_UA
                        }
                    }),
                    script = res.getBody();

                    if (!script.length) {
                        throw scriptSrc + ' returned error';
                    }
                    return script;
                }).join(';');

		/**
		 * Since this will be evaluated in an IIFE, we want to avoid situations where the script sets a global variable
		 * implicitly (e.g., via `var globalVar = value`), by attaching that variable to the `window` object:
		 */
                var exportCheck = 'try{ if (' + exported + ' && !window.' + exported + '){window.' + exported + '='+ exported + ';} }catch(e){};';

		/** 
		 * this wraps the script's contents in an IIFE that
		 * is deferred, which should reduce the impact on frame-rate
		 * from the browser evaluating such a large/continuous chunk of JS.
		 * because it's asynchronous, we let the user provide a callback
		 * which gets called when the script & its global variable are available.
		 */
                return [
                    "/** ",
                    src,
                    " @",
                    (new Date),
                    " */;setTimeout(function(){",
                    "(1,eval)('",
                    jsStringEscape(scripts.toString()),
                    "');",
                    exportCheck,
                    callback + "();",
                    "}, 1);"
                ].join('');
            }
        }
    });
}


gulp.task('preprocess', function () {
    return gulp.src(['src/*.js', 'src/**/*.js', 'src/*.json'])
        .pipe(preprocessFilter())
        .pipe(gulp.dest(buildDir));
});

Change the build-dev task to first preprocess:

gulp.task('build-dev', ['preprocess', 'clean-dist'], function () {
	return gulp.src([buildDir + '/prebid.js'])
            .pipe(browserify({
                debug: false,
                standalone: 'pbjs'
            }))
            .pipe(derequire())
            .pipe(header(banner, {
                    pkg: pkg
            }))
            .pipe(gulp.dest(releaseDir));
});

Add the directive to src/adapters/aol.js

The @exec script() directive works by downloading the script, putting its code into a string that will get eval'd, and then calling the specified function when the script is evaluated/available.

For aol, we add a _onScript function to the adapter scope:

var _scriptLoadQueue = [];
/**
 * When the script is available, call any waiting handlers
 * and then make further calls (pushes) immediate
 */
function _onScript() {
    function _call(fn){ fn(); }
    utils._each(_scriptLoadQueue, _call);
    _scriptLoadQueue.prototype.push = _call;
}
function _callBids(params) {
    bids = params.bids;
    if (!bids || !bids.length) return;
    _scriptLoadQueue.push(_reqBids);
}

/**
 * Inline the specified script (provided in the gulpfile)
 * @param {string} url
 * @param {string} varName - ensure that this variable is available on
 * the global scope.
 * @param {string} callbackName - call this function (in this scope)
 * when the script is ready/evaluated
 * @return {Function} inline eval IIFE
 */

/* @exec script(scripts.adtech, 'ADTECH', '_onScript') */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment