Aren Hovsepyan
Aren Hovsepyan

Reputation: 2047

Versioning of webpack bundle in EJS

I am creating application with Express and ReactJS. For splitting big components I use lazy loading and in webpack I am adding chunkhash to query to creating versioning in browsers and serving new versions. But unfortunately my bundle file served in EJS template since I am using Express as backend and it simple script tag.

<script src="/build/bundle.js" type="text/javascript" defer></script>

So how it is possible to provide versioning in EJS templates? what is best practices in this case?

Hope I did not ask stupid question.

Thanks in advance!

Upvotes: 0

Views: 495

Answers (1)

Swaraj Giri
Swaraj Giri

Reputation: 4037

Well its totally doable. At the end of the day script tags are strings, right?

Here is what i do, Use assets-webpack-plugin to create a json file with the outputs of webpack bundling.

Lets say it creates a assets.json file.

While setting up the express server, require that file and put that in locals. req.locals.assets = require('path/to/assets.json')

Now all you need is to build the string tag and include in the template. For this, you can write a ejs helper that takes an array of js files that you want to be in your template and return script tags.

Something like

function renderJSTags(jstags) {
    let out = '';

    _.each(jstags, function (js) {
        if (typeof js === 'string') {
            out = out + renderTag({
                'name': 'script',
                'opts': objToKeyval({
                    'src': js,
                    'nonce': `${_res.locals.nonce}` // you should be using nonce along with good CSP
                })
            });
        } else {
            out = out + renderTag({
                'name': 'script',
                'opts': objToKeyval(js)
            });
        }
    });

    return out;
}

function objToKeyval(obj) {
    return _.map(obj, function (val, key) {
        return {'key': key, 'val': val};
    });
}

function renderTag(obj) {
    let tag = '<' + obj.name + ' ';

    _.each(obj.opts, function (opt) {
        if ((obj.name === 'link' || obj.name === 'script') &&
            (opt.key === 'href' || opt.key === 'src') &&
            !url.parse(opt.val, false, true).host) {
            opt.val = opt.val;
        }

        tag += opt.key + '="' + opt.val + '" ';
    });

    if (obj.selfclose) {
        tag += '/>';
        return tag;
    }

    tag += '></' + obj.name + '>';

    return tag;
}

This brings us to the final step In the route handlers, pass an array of js tags to the view along with what ever data you are passing.

router.get('/', (req, res, next) => {
    const assets = req.locals.assets;

    const payload = {
        // your data
        'name' : 'John Doe',
        'js': [
            assets.webpackEntryPoint1.js,
            assets.webpackEntryPoint2.js,
        ]
    }

    res.render('/path/to/template', payload);
});

Upvotes: 2

Related Questions