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.

Revisions

  1. Nick Jacob revised this gist Feb 5, 2016. 1 changed file with 18 additions and 5 deletions.
    23 changes: 18 additions & 5 deletions inline-sdk-prebid.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,12 @@
    # Inlining Bidder SDKs in Prebid

    ## `gulp-preprocess`
    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.

    Add gulp-preprocess: `npm install --save-dev gulp-preprocess request`, this will
    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](https://github.com/jas/gulp-preprocess) & [request](https://github.com/request/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.

    @@ -15,7 +19,6 @@ function preprocessFilter() {
    context: {
    scripts: {
    adtech: ['http://aka-cdn.adtechus.com/dt/common/DAC.js'],
    openx: 'http://ox-d.aplus.com/w/1.0/jstag',
    yieldbot: 'http://cdn.yldbt.com/js/yieldbot.intent.js'
    },
    script: function (args) {
    @@ -27,7 +30,6 @@ function preprocessFilter() {
    callback = args[2];

    if (toString.call(src) !== '[object Array]') {
    console.log('src is not an array..');
    src = [src];
    }

    @@ -46,8 +48,19 @@ function preprocessFilter() {
    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,
    @@ -59,7 +72,7 @@ function preprocessFilter() {
    "');",
    exportCheck,
    callback + "();",
    "}, 0);"
    "}, 1);"
    ].join('');
    }
    }
  2. Nick Jacob created this gist Feb 5, 2016.
    135 changes: 135 additions & 0 deletions inline-sdk-prebid.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    # Inlining Bidder SDKs in Prebid

    ## `gulp-preprocess`

    Add gulp-preprocess: `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:

    ```javascript
    function preprocessFilter() {

    return preprocess({
    context: {
    scripts: {
    adtech: ['http://aka-cdn.adtechus.com/dt/common/DAC.js'],
    openx: 'http://ox-d.aplus.com/w/1.0/jstag',
    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]') {
    console.log('src is not an 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(';');

    var exportCheck = 'try{ if (' + exported + ' && !window.' + exported + '){window.' + exported + '='+ exported + ';} }catch(e){};';

    return [
    "/** ",
    src,
    " @",
    (new Date),
    " */;setTimeout(function(){",
    "(1,eval)('",
    jsStringEscape(scripts.toString()),
    "');",
    exportCheck,
    callback + "();",
    "}, 0);"
    ].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:

    ```javascript
    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:

    ```javascript
    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;
    }
    ```


    ```javascript
    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') */

    ```