thednp / bootstrap.native

Bootstrap components build with Typescript
http://thednp.github.io/bootstrap.native/
MIT License
1.7k stars 178 forks source link

How to enable tree shaking? #476

Open vdboor opened 5 months ago

vdboor commented 5 months ago

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.

thednp commented 5 months ago

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

thednp commented 5 months ago

Please test @5.0.12 and let me know.

valsor commented 2 months ago

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

thednp commented 2 months ago

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

valsor commented 2 months ago

Is this on schedule already?

thednp commented 2 months ago

Not at this time, no.

thednp commented 1 month ago

@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.

verheyenkoen commented 1 month ago

@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?

thednp commented 1 month ago

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.

verheyenkoen commented 1 month ago

@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)?

thednp commented 1 month ago

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.