nodeca / pako

high speed zlib port to javascript, works in browser & node.js

Home Page:http://nodeca.github.io/pako/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use classes to improve tree-shaking

ggrossetie opened this issue · comments

I've noticed that, currently, it makes little to no difference to use import {deflate} from 'pako' rather than import pako from 'pako'.

import pako from 'pako'

kroki.js → kroki.bundled.js...
Build summary for kroki.bundled.js - es
┌──────────────────┬───────────┐
│ File name        │ Size      │
│ ---------------- │ --------- │
│ kroki.bundled.js │ 247.11 kB │
│ ---------------- │ --------- │
│ Totals           │ 247.11 kB │
└──────────────────┴───────────┘
created kroki.bundled.js in 851ms

import {deflate} from 'pako'

kroki.js → kroki.bundled.js...
Build summary for kroki.bundled.js - es
┌──────────────────┬───────────┐
│ File name        │ Size      │
│ ---------------- │ --------- │
│ kroki.bundled.js │ 244.26 kB │
│ ---------------- │ --------- │
│ Totals           │ 244.26 kB │
└──────────────────┴───────────┘
created kroki.bundled.js in 854ms

While investigating, I saw that Inflate was not removed from the bundle but if I convert function Inflate(options) to a class class Inflate then it's working as expected:

kroki.js → kroki.bundled.js...
Build summary for kroki.bundled.js - es
┌──────────────────┬───────────┐
│ File name        │ Size      │
│ ---------------- │ --------- │
│ kroki.bundled.js │ 156.18 kB │
│ ---------------- │ --------- │
│ Totals           │ 156.18 kB │
└──────────────────┴───────────┘
created kroki.bundled.js in 662ms

Is there a particular reason for not using classes? Will you consider a pull request with that change?

This package was done to be compatible with ancient browsers, probably not actual now.

But you can import pre-build files from dist https://github.com/nodeca/pako/tree/master/dist. Will it solve your problem right now, without changes?

Not really because I'm creating an ESM-first web component so I cannot import deflate from pako/dist/pako_deflate since it's a CommonJS file.

If we don't want to update the code, an alternative solution would be to produce ESM files for pako_deflate.js and pako_inflate.js but it feels counter productive since ESM was designed to produce compact bundle with a static structure. In other words, import deflate from 'pako/dist/pako_deflate' (instead of import {deflate} from 'pako') would be contrary to the ESM philosophy (in my opinion).

Since we are already using Babel, I believe that class will be transpiled/transformed when the target is ES5. So this change will only affect the ESM file.

@puzrin I too only need to use deflate and it would be very nice to have inflate tree-shooken out.

commented

@ggrossetie @rlidwka is this a change that you would be interested in supporting? This library has like 10m+ users tree shaking would be a really good add especially since that is far more modern. @ggrossetie @puzrin what changes would be needed to add that support is it really just a simple as adding class?

what changes would be needed to add that support is it really just a simple as adding class?

Yes.

From my understanding, classes don't tree-shake as well as ESM Modules, even static. That's because you can always reference a method by name:

class MyClass {
  myMethod() { }

  myMethodINeverUse() {}
}

const instance = new MyClass();

instance.myMethod();
let methodName = 'myMethodINeverUse'
instance[methodName]();

ESM Modules don't really exhibit this issue and are better for tree-shaking:

export function myMethod() {}
export function myMethodINeverUse() {};

Babel, Webpack, and Rollup will all tree-shake when you do:

import {deflate, inflate} from 'pako'

And even if you were to import inflate and never use it, those bundlers will remove them anyway during minification.

You can even do import * as pako from 'pako' and bundlers will remove what you don't use. But then you have to be conscious of the sideEffects: true if you try to do something like:

import * as pako from 'pako';

const pakoFn = 'import';
pako[pakoFn]()

That's a side-effect where you don't actually support clean tree-shaking of imports.

See: https://webpack.js.org/guides/tree-shaking/

TL;DR: Drop require and use native ESM modules, not classes, for better tree-shaking.

Also, I'm pretty sure you can do

import { deflate } from 'pako/lib/deflate.js';
import { inflate } from 'pako/lib/inflate.js';

And you're basically tree-shaking yourself. Though because the files are using require, they both might import utils/string.js completely, instead of tree-shaking only what it needs.