Sharing a Webpack vendor bundle across applications

If you find yourself maintaining standalone, yet related SPAs, and you would like your users to benefit from their browser's cache to download some third-party code they have in common only once, Webpack has you (pretty much) covered thanks to its DllPlugin, which is actually part of a duet along with DllReferencePlugin. To make a long story short, the former allows you to generate, through a separate build, a standalone bundle and an accompanying manifest file, whereas the latter takes these two as input and in consideration in the context of a full, regular build. Let's get started!

We will assume one of the apps will bear responsibility for building the common bundle. As I mentioned earlier, this implies creating a separate, specific Webpack config:

const webpack = require("webpack");
const path = require("path");

module.exports = {
entry: {
commonVendors: [/* list the names of your common dependencies here */]
},

output: {
path: path.join(__dirname, "vendor"),
filename: "[name].js",
publicPath: "/",
library: "Vendor"
},

plugins: [
new webpack.DllPlugin({
context: __dirname,
path: path.join(__dirname, "vendor", "manifest.json")
})
]
};

You might need extra stuff, like setting up loaders to support your framework's DSL, if any - I will leave that up to you. Let's build!

$ webpack --config path/to/specific/config.js

This should result in the creation of a vendor folder, containing commonVendors.js and manifest.json files. Now, on to our main config:

const webpack = require("webpack");

module.exports = {
// ...

plugins: [
// ...

new webpack.DllReferencePlugin({
context: __dirname,
manifest: require("./vendor/manifest.json"),
name: "Vendor" // this has to match output.library in the specific config
})
]
};

If you run your build as usual (and you ran the first one already), you should notice a decrease in your regular bundles' size! Of course, make sure to add a <script src="path/to/vendor/commonVendors.js"></script> tag to your HTML page.

OK, so we now have a separate bundle that we can use in this one application. How should we proceed to benefit from it in another build? Well, that is really up to you and your concrete use case: there is, to my knowledge, no perfect solution given it will always imply build coupling. Feel free to drop your knowledge in a comment below!

Just as a proof of concept, let's have it working in development though: have commonVendors.js and manifest.json available through a local server or, like me, just rely on the filesystem, and in your second app's Webpack config, make use of DllReferencePlugin as well:

const webpack = require("webpack");

module.exports = {
// ...

plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require("../path/to/manifest.json"),
name: "Vendor"
})
]
};

Once again, you should see your JavaScript footprint decrease, and no lack of code if you correctly import the common bundle file in your app!

Mock current time with Moment.js or Day.js

When writing unit tests for JavaScript code relying on Moment.js or Day.js (a lightweight alternative to the former, with pretty much the same API) to handle date-related shenanigans, one might find themselves in need to mock their library of choice when its main function is used with no parameters, to obtain a representation of current time.

The mock we actually need to set up in such a case is actually a partial one: we want it to act as a proxy for parameterized calls, passing them down to the original implementation, while returning a fixed value otherwise. Thankfully, Jest grants us the possibility to do so fairly easily:

jest.mock("dayjs", () => jest.fn((...args) => jest.requireActual("dayjs")(
args.filter(arg => arg).length > 0 ? ...args : "1989-03-21"
)));

As you can see, the aforementioned original implementation is available from within jest.mock's scope through the use of jest.requireActual, allowing us to check if it was called with or without parameters, and pass a fixed value in the latter case.

Congratulations, you are now able to freeze time!

Recursive function binding in a JavaScript object

Despite the rise of ES6 and especially the sacrosanct fat arrow syntax, JavaScript developers might still find themselves in need for Function.prototype.bind in some edge cases; binding all functions in an object to the same this value regardless of depth is one of them.

The example I have in mind is the building of a Vue.js plugin such as this one:

function install(Vue) {
Vue.mixin({
beforeCreate() {
this.$myPlugin = {
aMethod() {
console.log(this);
},

yet: {
anotherMethod() {
console.log(this);
}
}
};
}
});
}

export default { install };

Fairly logically, setting it up through Vue.use(MyPlugin) and calling this.$myPlugin.aMethod and this.$myPlugin.yet.anotherMethod yields a reference to the plugin object itself twice, which might not be what you want depending on what the plugin is supposed to do.

In such a situation, we therefore need to write a recursive binding function, which luckily is not all that hard:

function recursiveBind(obj, that) {
return Object.keys(obj).reduce((acc, key) => {
switch (typeof obj[key]) {
case "object":
return { ...acc, [key]: recursiveBind(obj[key], that) }; // recurse

case "function":
return { ...acc, [key]: obj[key].bind(that) }; // bind
}

return { ...acc, [key]: obj[key] }; // leave untouched
}, {});
}

All that remains to do then is to wrap our plugin object declaration with it:

function install(Vue) {
Vue.mixin({
beforeCreate() {
this.$myPlugin = recursiveBind({
aMethod() {
console.log(this);
},

yet: {
anotherMethod() {
console.log(this);
}
}
}, this);
}
});
}

And this now correctly refers to our Vue instance! Please share your plugin tips in the comment section, if any!

In-depth object prop validation in Vue.js

Vue.js offers a simple yet effective prop validation API:

export default {
props: {
config: {
type: Object,
required: true
}
}
};

For any given prop, you can specify the expected type, and whether the prop is required or has a default value. Doing so is actually best practice, just like defining data as a function in your components.

Regarding Object props, however, there is no built-in mechanism allowing one to validate the object's keys or structure; the closest thing we have is the ability to pass in a custom validation function, so let's build upon that!

The most simple implementation could be along the lines of the following:

const configKeys = ["some", "keys", "to", "check"];

export default {
props: {
config: {
type: Object,
required: true,
validator: config => configKeys.every(key => key in config)
}
}
};

That pretty much does it! A more thorough version could support deep key checking with a dotted notation, using this package for example.

But let's go further! If you find yourself using this pattern on a regular basis, why would you not write a mixin - actually, a mixin factory - to make it easier?

/**
* @param {String} propName
* @param {String[]} keys
*
* @return {Object}
*/
function getObjectPropMixin(propName, keys) {
return {
[propName]: {
type: Object,
required: true,
validator: value => keys.every(key => key in value)
}
};
}

export default {
mixins: [getObjectPropMixin("config", ["some", "keys", "to", "check"])]
};

You could even use it multiple times in the same component if you have more than one Object prop to check; or better yet:

/**
* @param {Object} propDefs
*
* @return {Object}
*/
function getObjectPropsMixin(propDefs) {
return Object.keys(propDefs).reduce((props, key) => {
return {
...props,
[key]: {
type: Object,
required: true,
validator: value => propDefs[key].every(propKey => propKey in value)
}
}
}, {});
}

export default {
mixins: [getObjectPropsMixin({
config: ["some", "keys", "to", "check"],
anotherObject: ["and", "here", "are", "more", "dummy", "values"]
})]
};

Using such stuff gives us the confidence to reference keys in Object props directly, without defensively checking for their presence in our code. If you have other ways to deal with this kind of thing, please let me know!