Logo

dev-resources.site

for different kinds of informations.

How I optimized Carousel for EditorJS 2x in size.

Published at
3/29/2024
Categories
babel
webpack
esbuild
esm
Author
appqui
Categories
4 categories in total
babel
open
webpack
open
esbuild
open
esm
open
Author
6 person written this
appqui
open
How I optimized Carousel for EditorJS 2x in size.

Why are we doing this?

I am supporting a project where lawyers, advisors can publish articles or news, to get more attention and clients. Initially it was made through TinyMCE, but they wanted not just HTML, but also a way of showing many pictures with interactions.
I was seeking UI editor like Medium, and I guess best what I found was EditorJS and during checking its Awesome List there were several carousel plugins and I stopped on this.

carousel look like this

  • The pros: it had already generated bundle with it, which I could easily add to my project
  • The cons: there were no demo page with it, it was only a screenshot, which later happens already outdated 😬

In current version there were buttons to move order, but for some reason instead of "delete" ❎ button as on screenshot it was blank, and also inside toolbox it was also blank.

initial view

No other way: I need to fork, clone it, πŸ”§ bugs, build myself. About fixing part maybe will write separate post, but I would like to share process of optimizing bundle, because it looked a bit heavy for such kind of code.

What with bundle size?

Let's see initial size of build we have 35.4k.

    Asset      Size
bundle.js  35.4 KiB
Enter fullscreen mode Exit fullscreen mode

After fixes size become even smaller:

    Asset      Size
bundle.js  35.1 KiB
Enter fullscreen mode Exit fullscreen mode

Even without tools it looked a bit weird, all files in source without minification costs us 27k, its something not needed inside, and if you unpack it with something like https://beautifier.io/ you will see:

strange stuff inside

What the hell process.chdir doing inside browser bundle?

What bundle consists of?

So as always we add webpack-bundle-analyzer:

"webpack": "^4.29.5",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^3.2.3"
Enter fullscreen mode Exit fullscreen mode

Adding --profile flag to build command:

"build": "webpack --mode production --profile",
Enter fullscreen mode Exit fullscreen mode

Bundle inside looks like this:
Initial build size 35.11k

What we have here, lets show it in form of json:

{
  "src": {
    "index.js + uploader.js": "8.64k",
    "index.css": "3.64k",
    "button-icon.svg": "439b",

    "all": "13kb",
  },
  "styles": {
    "style-loader": "4.67k",
    "css-loader": "808b"
  },
  "babel": "1.42k",
  "ajax": "14kb"
}
Enter fullscreen mode Exit fullscreen mode
  • src is fine, its meat we need to deliver.
  • babel hard to say, why it was used. There is no target or browserlist left. Not sure if author wanted to support IE11. Even when this component was created in 2021, es6 was on 97% adoption through browsers.
  • styles is interesting, we have index.css of 3.64k and delivery mechanism that costs 5.4k, which more than css itself 😺.
  • ajax is a blackbox for us for now, its another UMD bundle, need to build it separately, and check what inside.

Remove babel and styles

Remove babel from webpack.config.js
remove babel

Remove styles is trickier. In fact what we could do, is just remove it from bundle, to separate file. We could put it as it is, but author decided to use a bit of scss-stuff, and we might want to leave it. For webpack4 I didn't find a solution for this, so I updated it to webpack5.

Packages now looks like this:

"mini-css-extract-plugin": "^2.8.1",
"svg-inline-loader": "^0.8.0",
"webpack": "^5.91.0",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^3.2.3"
Enter fullscreen mode Exit fullscreen mode

Adding MiniCssExtractPlugin:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
module.exports = {
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'bundle.css',
    })
  ],

...

  {
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          plugins: [
            require('postcss-nested-ancestors'),
            require('postcss-nested')
          ]
        }
      }
    ]
  },
Enter fullscreen mode Exit fullscreen mode

Let's build it, and see what we get:

    Asset      Size 
bundle.js  22.8 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

As you see bundle.css is even smaller than css inside bundle, this is because to be in js, css need a bit of escaping and module instantiation. After we remove utils for loading, now instead of 9k we have 3.1k.

Intermediate result: 25.9k vs 35.1k

Checking ajax library

Library used here: https://github.com/codex-team/ajax. As stated in readme it is Just another AJAX requests helper. Main reason why it was used, I guess a simple helper for submitFiles(). Lets see what dist have inside:

ajax dist initially

What we really need is in red border.

  Asset      Size  
main.js  14.3 KiB  
Enter fullscreen mode Exit fullscreen mode
{
  "src": {
    "index.js": "3.74kb",
    "utils.js": "1.74kb",

    "all": "5.5kb",
  },
  "http-build-query": "569b",
  "promise-polyfill": "7kb"
}
Enter fullscreen mode Exit fullscreen mode

Again this library is from 2020.

support promise

Support of Promise in major browsers goes from 2014. Yes we supported IE11 for many years, and I guess in 2018 many companies let it go. Problem here that polyfill always here, npm distribute this bundle, and no way to tell, if you need it for your build or not.

After removing promise-polyfill:

  Asset      Size 
main.js  7.09 KiB 
Enter fullscreen mode Exit fullscreen mode

After removing babel for es6 classes emulation πŸ‘½

  Asset      Size 
main.js  6.09 KiB 
Enter fullscreen mode Exit fullscreen mode

If we copy this main.js to our carousel project as ajax.js, and build it, we will get:

    Asset      Size 
bundle.js  14.7 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

It's already much better, but what I don't like about this, is that ajax.js is still UMD style webpack bundle, that means array of modules inside connected by something called (webpack)/buildin/global.js which takes 472b.

(webpack)/buildin/global.js

Why use it, if we have such a great thing like ESM πŸ˜ƒ.

Enabling ESM for ajax project

I didn't follow long story of dealing with ESM inside webpack during last 5 years, but looks like now it works good, and in fact my build from webpack was even more compact, than I could make from esbuild.

To enable it you just need to:

  • upgrade webpack to latest 5, for me its ^5.91.0
  • change "type": "module" in package.json
  • rewrite fully webpack.config.js

initial vs module

Result is nice πŸ”₯ and one more useless kilobyte gone.

  Asset      Size 
main.js  5.06 KiB 
Enter fullscreen mode Exit fullscreen mode

Lets see final result πŸŽ‰: 16.9k vs 35.1k

    Asset      Size 
bundle.js  13.8 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

Resume

  • Don't treat bundles as binary, it is JavaScript
  • JavaScript is a high-level language
  • It is easily readable πŸ“š, even when minified
  • Can be formatted πŸ“ by vscode
  • It is a pleasure to feel full control of it

Thanks for reading. 🀘
Open to collaboration. πŸ‘‹
If you have full-time or part-time projects.
Usually work as full-stack: .net + js.
Write me πŸ“«: [email protected]

esm Article's
30 articles in total
Favicon
Bundling without a bundler with esm.sh
Favicon
Building NPM packages for CommonJS with ESM dependencies
Favicon
Web Development Without (Build) Tooling
Favicon
Dual Node TypeScript Packages - The Easy Way
Favicon
Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS
Favicon
The Ongoing War Between CJS & ESM: A Tale of Two Module Systems
Favicon
How I optimized Carousel for EditorJS 2x in size.
Favicon
Transitioning from CommonJS to ESM
Favicon
Node.js, TypeScript and ESM: it doesn't have to be painful
Favicon
Set up Hot Reload for Typescript ESM projects
Favicon
Set up a Node.js project + TypeScript + Jest using ES Modules
Favicon
ESM & CJS: The subtle shift in bundlejs' behaviour
Favicon
Mastering the Art of ESM and CJS Package Handling
Favicon
Modules & Modules & Modules, Oh My!
Favicon
How to build TypeScript to ESM and CommonJS
Favicon
ES Modules & Import Maps: Back to the Future
Favicon
How to use ESM on the web and in Node.js
Favicon
Custom ESM loaders: Who, what, when, where, why, how
Favicon
Fix NX Node executor ERR_REQUIRE_ESM Error
Favicon
Creating a Node.js module for both CommonJS & ESM consumption
Favicon
STOP using require() in node backend
Favicon
JavaScript Module Ecosystem
Favicon
Declarative database modelling
Favicon
Expressjs: Javascript written in ECMAScript 2015 (ES6)
Favicon
How to use ES Modules with Node.js
Favicon
What does it take to support Node.js ESM?
Favicon
Build modular app with Alpine.js
Favicon
TS and ts-jest meet β€œtype”: β€œmodule”
Favicon
ESM doesn't need to break the ecosystem
Favicon
constructor() dynamic import()

Featured ones: