App: [$500] ExpensiMark: large bundle size

Problem A simple script that uses ExpensiMark library to parse Markdown into HTML has a bundle size of 336 kB (or 184 kB minified) which is quite big.

Details The file is large because it contains mappings of all HTML entities (from _.escape) and countries, states, currencies etc. from CONST.jsx.

Solution Investigate if it’s possible to minimize the bundle size.

Steps to reproduce

index.js

import ExpensiMark from "expensify-common/lib/ExpensiMark";

const input = "Hello, *world*!";
const parser = new ExpensiMark();
const output = parser.replace(input);

console.log(JSON.stringify(input));
console.log(JSON.stringify(output));
esbuild index.js --bundle --minify --outfile=out.js

out.js: https://pastebin.swmansion.com/?9798f33833cc33b6#DRgiwxs8ujteamHaGDioSzZVZ83nuWUim3QabJ5PUYed

Issue OwnerCurrent Issue Owner: @Skalakid
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~0155a63860a646579c
  • Upwork Job ID: 1740457868647858176
  • Last Price Increase: 2024-01-04
  • Automatic offers:
    • fedirjh | Reviewer | 28090032
    • aswin-s | Contributor | 28099676

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Comments: 46 (36 by maintainers)

Most upvoted comments

Found another opportunity to reduce bundle size. ExpensifyMark.js and str.js uses below methods from underscore.

union 
isEmpty
filter
contains
map
difference
isFunction
first
last   

These could be replaced with pure ES6 methods. That shaves off another 20kb, bringing down the minified bundle size to approximately 129kb.

Based on bundle analysis 50% of the bundle is html-entities library. named-references to be exact. CONST.jsx contributes just 12kb to the whole bundle. So shouldn’t we focus more on how to optimise the usage of html-entities?

image

However a polyfill of String.prototype.replaceAll is embedded in Str file. I guess this was for Node 14 compatibility and no longer needed. Removing this polyfill itself reduces 30kb from entire bundle resulting in a 16% savings.

PRs for this issue were reviewed internally

@roryabraham I want to make sure I’m looking at the Correct PR. The one above has not merged to production yet, correct? The payment countdown starts after it’s merged to production and there are no regressions.

@aswin-s The PR above https://github.com/Expensify/expensify-common/pull/632 is not showing merged to prod yet.

once that happens the payment title for this GH will auto-update with the payment date

I agree with @aswin-s’s suggestion regarding the removal of the String.prototype.replaceAll polyfill. This change is straightforward and indeed offers a significant reduction in the bundle size

Removing the polyfill impacts the backward compatibility and potential technical debt for legacy systems. It might be beneficial to conduct a quick survey or analysis to determine how many such legacy projects are dependent on expensify-common and their current Node.js versions.

I am generally in favor of removing the polyfill, as it primarily affects projects running on very old versions of Node.js (older than 15), which are likely to be a minority if any

@roryabraham Also not sure why we overlooked the removal of String.prototype.replaceAll. That amounts to 30KB (16% savings). Seems like that’s more relevant than underscore since it is being shared with other packages.

https://bundlephobia.com/package/string.prototype.replaceall@1.0.8

The hyperlink validation logic is very complex, is this necessary?

It evolved as the result of fixing many different bugs people found over the years, so my hunch is it would be very high-risk for a low reward to try and update that.

Hi contributors 👋🏼

I’ve left some suggestions for how to improve this, but the approach can be open-ended. If multiple contributors have different ideas too, we could potentially hire more than one (in that case, it would probably be simplest to create separate issues to track the work separately).

Also note that the expensify-common codebase must remain compatible with vanilla (non-typescript) js code so that it can continue to be used in our legacy javascript codebases.

Moving this back to weekly. We are hoping to launch this feature in January, so we should start making moves here too. @Skalakid is going to be focused on inline video, so we should find someone else to tag in on this.

No update from me, but @Skalakid keep me posted if you’re starting to work on this

Maybe the first and easiest steps here are to:

  1. Port Expensimark to typescript and stop using underscore – that would likely help reduce the file size
  2. Split up the big CONST export and separately export/import only the pieces that we need in Expensimark