bootstrap.native icon indicating copy to clipboard operation
bootstrap.native copied to clipboard

How to enable tree shaking?

Open vdboor opened this issue 1 year ago • 11 comments

Somehow tree shaking isn't working for me. I end up with a JavaScript file of 40kB when I do:

import { Alert, Collapse } from 'bootstrap.native';

Doing the equivalent in Bootstrap 5 JS will give me a 13kB file:

import { Alert } from 'bootstrap/js/src/alert.js';
import { Collapse } from 'bootstrap/js/src/collapse.js';

WebPack is configured with:

{
  mode: 'production',

  optimization: {
    usedExports: true,  // tree shaking
    minimize: true,
    minimizer: [
      new TerserPlugin(),
    ]
  }
}

And my package.json has "sideEffects": false in it.

Is there anything I'm still doing wrong, or is this a bug in the package?

As a sidenote, I would have loved to use the old approach of importing from sub files directly. This is no longer possible due to the exports section in package.json.

vdboor avatar Mar 30 '24 20:03 vdboor

Yes, this happened during the Rollup => Vite migration. If I could find a solution, will let you know.

thednp avatar Mar 31 '24 05:03 thednp

Please test @5.0.12 and let me know.

thednp avatar Mar 31 '24 06:03 thednp

Tree shaking isn't working with esbuild also. Is there any workaround to import only selected components from library?

valsor avatar Jun 29 '24 13:06 valsor

I will have to redo the entire tooling for this, build individual components and export them separately in package.json

thednp avatar Jun 29 '24 16:06 thednp

Is this on schedule already?

valsor avatar Jun 29 '24 16:06 valsor

Not at this time, no.

thednp avatar Jun 29 '24 17:06 thednp

@vdboor @valsor please test @5.0.13 and let me know. I've updated the tooling to re-enable tree-shaking.

FYI: you have to do import Button from 'bootstrap.native/button'. I updated the wikies as well.

thednp avatar Jul 21 '24 11:07 thednp

@thednp This doesn't seem to work for me. This code worked before and still works:

import { Toast } from "bootstrap.native";

const toast = Toast.getInstance(toastEl);
// toast is initialized

However the treeshaken version does not work:

import Toast from "bootstrap.native/toast";

const toast = Toast.getInstance(toastEl);
// toast is null

I've verified that the code stepped through the Toast constructor first in both examples.

I've even combined them in one example (during an htmx load event):

import htmx from "htmx.org/dist/htmx.esm.js";
import { Toast as DestructuredToast } from "bootstrap.native";
import TreeShakenToast from "bootstrap.native/toast";

htmx.onLoad((rootEl) => {
  rootEl.querySelectorAll(".toast").forEach((toastEl) => {
    let destructuredToast = DestructuredToast.getInstance(toastEl);
    let treeShakenToast = TreeShakenToast.getInstance(toastEl);

    console.log({ destructuredToast, treeShakenToast });
  });
});

With this result: image

I can obviously do a null-check and construct it again but that shouldn't be necessary I guess.

Note: We use esbuild for bundling.

Another note: Since we're using HTMX, we use the initCallback method to initialise BSN on injected DOM content, like so:

import * as BSN from "bootstrap.native";

htmx.onLoad((el) => {
  // not necessary on initial load (DOMContentLoaded)
  if (el !== document.body) {
    BSN.initCallback(el);
  }
});

Since initCallback isn't "treeshakeable", I guess there's no real use for us to treeshake individual components in other places, since we'd still have the whole of BSN bundled by this import?

verheyenkoen avatar Jul 26 '24 07:07 verheyenkoen

Is your project a typescript based project? You might need to restart your Typescript server.

I tried playing around with tsup, might be a good solution for this multi exports tree-shake enabled feature.

Anyways, I'm open to any suggestion.

thednp avatar Jul 26 '24 14:07 thednp

@thednp No, it's JS (with htmx). Hope to convert it to TS soon but still to discuss with other team members. We added BSN to benefit for other additional features. Would that matter anyhow (apart from maybe the import * as BSN from ... statements)?

verheyenkoen avatar Jul 26 '24 14:07 verheyenkoen

You can try, but I believe using Vite and Typescript will ensure the file loader is in place to handle these separate exports. There could also be some properties missing in your package.json, you might have to check on that.

Just FYI: I installed BSN in a separate project, initialized with Tooltip and it worked out of the box, keep in mind that project is using an identical Typescript and Vite tooling setup.

thednp avatar Jul 26 '24 14:07 thednp