Reputation: 2047
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
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