Skip to content

WIP: test esbuild and rollup as bundlers for backend#742

Draft
gmaclennan wants to merge 1 commit intodevelopfrom
chore/backend-bundle
Draft

WIP: test esbuild and rollup as bundlers for backend#742
gmaclennan wants to merge 1 commit intodevelopfrom
chore/backend-bundle

Conversation

@gmaclennan
Copy link
Member

We currently bundle the backend JS code into a single file using noderify. We do this because loading the unbundled code takes time, especially on low-powered devices like phones.

This PR is an experiment to use a different bundler for backend code. It currently includes build scripts to use esbuild and rollup. The JS bundle has not yet been tested, and probably needs some changes for native modules that use node-gyp or bindings e.g. see https://github.com/staltz/bindings-noderify-nodejs-mobile

We do not need to change the build tool, but there are some reasons to consider it:

  1. ESM module support: An growing number of npm modules are supporting ESM only e.g. no CommonJS support, and noderify only works with CommonJS.
  2. Typescript support: Currently the backend uses JSDoc comments for Typescript typing. If we want to switch to writing directly in Typescript we will need a compile step, which could be done separately, but can be included in the bundle step with esbuild and rollup.
  3. Greater contol: Modern bundlers give more control and analysis over what and how things are bundled. E.g. running esbuild analysis shows that the debug and readable-stream modules are duplicated multiple times in the bundle, which adds to the bundle size (and parse time). They are duplicated because npm will only dedupe the most recent version of a dependency - sub-dependencies which are older versions are not deduped e.g. Mapeo dependencies use debug v4, v3 and v2, but only v4 is deduped.
  4. Tree shaking: If we switch to ESM we could potentially reduce the bundle size thanks to tree-shaking to remove unused code.
  5. Bundle speed: Using esbuild in particular reduces the bundle time. Although of all the build steps for Mapeo, the backend bundle is relatively insignificant.

Based on this experiment with esbuild and rollup, I found esbuild to be easier to learn and implement. Rollup required learning about the plugin ecosystem and reading the docs for each plugin, whereas I found the esbuild config easier to learn from the docs. Esbuild was much faster than Rollup, and produced a smaller bundle. This needs more investigation though and some quantitative analysis.

In parallel to this work, we should consider a bundler for the backend code for Mapeo Desktop. Currently we ship the Desktop backend code unbundled, and the shipped node_modules folder is a significant proportion of the app download size. Bundling could significantly reduce the app size, and potentially increase startup time. It would be simpler if we use the same bundler for mobile and desktop.

@gmaclennan
Copy link
Member Author

Just adding another reason for this: noderify does not support source maps, which limits the utility of error reporting through bugsnag etc. When debugging locally I've ended up scrolling through the bundle to figure out which module the error is in. I think this would be worth picking up again, and shifting to ES modules for the backend, but I think sticking with JSDoc for typescript is working well, particularly as it simplifies the test runner setup for backend tests.

@gmaclennan
Copy link
Member Author

On a recent long train journey I did some further hacking on this using ESBuild. ESBuild is very fast, but it does not give as much control over code transforms as I think Rollup might, and seems to have fewer examples of bundling Node code. Resolving __dirname, Worker code, and native (.node) bindings looks like quite a bit of work in ESBuild. I have not yet looked at how to do this in Rollup, but I believe it might be easier, since I think you have access to the full AST in Rollup, where-as with ESBuild you can basically do search and replace of source code.

@gmaclennan
Copy link
Member Author

As a side-note, bundle analysis shows up a lot of duplicate modules (caused by multiple dependencies requiring the same module but slightly different versions). This is significant for modules such as AJV, which is large, and is duplicated 5-6 times in the bundle. Some of this might be resolved by updating npm or using pnpm, and updating the package-lock to attempt to deduplicate deps. A thorough bundle review and cleanup (perhaps after the Mapeo Core update) could reduce the bundle size (and hence APK size) and also reduce the initial parse time at app load.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant