Using webpack code splitting in Nextcloud

Webpack can be a great tool when creating Nextcloud apps, bundling all your javascript and 3rdparty libraries together into a single transpiled and minified bundle for the browser to load.

However bundling everything together into one file doesn’t always lead to the best user experience, having to load large amounts of (3rdparty) javascript every time the page is loaded even though large portions of the code might not always be needed.

This is especially so with apps that get loading into file Files app or public share page, an app for viewing pdf files or rendering markdown previews in the text editor will sit around unused by the user most of the time, while loading large 3rdparty libraries every page load.

Luckily webpack provides an answer in cases like this, “code splitting”.
Code splitting allows us to split up the bundle into multiple parts, only loading pieces of code on demand when they are needed.

For example for our pdf viewer, we can only load a small piece of code on page load that registers the viewer and then load the full viewer from there only when a pdf file is opened.

OCA.Files.fileActions.registerAction({
	name: 'Open',
	mime: 'application/pdf',
	actionHandler: (filename, context) => {
		import('./viewer').then(viewer => {
			viewer.open(filename, context);
		})
	},
	permissions: OC.PERMISSION_READ
});

However if we try to use code splitting like this we’ll run into two problems.

  • The location of the scripts files that we want to load on the server wont be the same on each instance.
  • Nextcloud’s CSP will block webpack’s attempt to load script files.

Luckily with a couple lines of code we can configure webpack’s script loading to properly load our script files.

__webpack_require__.p = OC.filePath('files_pdfviewer', 'js', '../build/'); // put your own app id and build path here.
const script = document.querySelector('[nonce]');
__webpack_require__.nc = script.nonce || script.getAttribute('nonce');

We tell webpack where the bundles are located and give it the nonce that it needs to bypass the CSP (Chrome and Firefox both requiring their own method).

Finally when using code we want to ensure that there are no conflicts between our app and any other app that might use code splitting, this is done by specifying a unique jsonpFunction in the webpack configuration:

 module.exports = {
		...
		output: {
			...
			jsonpFunction: 'webpackJsonpPdfViewer'
		},
	...

That way any chunks loaded will be handled by the correct instance of webpack.