Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a class-based custom element side-effect-free so webpack only bundles the explicitly imported components

I have a set of spec v1 custom elements which I'm using webpack 4 to bundle (and babel-loader to transpile).

The components all look similar to this:

export class CompDiv extends HTMLDivElement {
  constructor(...args) {
    const self = super(...args);
    self.property = null;
    return self;
  }
  connectedCallback() {
    console.log('connected CompDiv');
  }
}

customElements.define('comp-div', CompDiv, { extends: 'div' });

Now to be able to create custom packages from these components using selective, named imports I need to mark these files as side-effect-free.

The component registration, though, takes place in the module itself:

customElements.define('comp-div', CompDiv, { extends: 'div' });

As far as I understand, that is a sideeffect.

Now I have an index.js that basically looks like this:

export { CompDiv } from './components/comp-div/comp-div';
...
export { CompBtn } from './components/comp-btn/comp-btn';

My webpack entry point looks like this:

import 'document-register-element';
import 'babel-polyfill';
import { CompDiv } from './index';

Now when I do this, CompBtn (and all other exports in index.js) ends up being part of the bundle even though it's not imported in my webpack entry point.

What would be the recommended way of allowing for treeshaking in webpack with these web components?

like image 483
connexo Avatar asked Aug 28 '18 11:08

connexo


People also ask

What are webpack side effects?

From Webpack docs. The "sideEffects": false flag in big-module's package. json indicates that the package's modules have no side effects (on evaluation) and only expose exports. This allows tools like webpack to optimize re-exports.

What is chunking in webpack?

Chunk: This webpack-specific term is used internally to manage the bundling process. Bundles are composed out of chunks, of which there are several types (e.g. entry and child).

Does webpack automatically tree shake?

In Node. js, for example, you can conditionally run require with a variable to load a random script. Webpack can't possibly know all of your imports and exports at build time, so it will attempt to tree shake a handful of constructs and bail as soon as things get too dynamic.

How do you bundle on a webpack?

You can bundle your JavaScript using the CLI command by providing an entry file and output path. Webpack will automatically resolve all dependencies from import and require and bundle them into a single output together with your app's script.


1 Answers

From webpack guide - Mark the file as side-effect-free:

All the code noted above does not contain side effects, so we can simply mark the property as false to inform webpack that it can safely prune unused exports.

So, setting "sideEffects": false in package.json tells webpack that your modules are side effect free. So that it can prune unused exports (in your case, unused re-exports). This is generally used by library authors.

But that's just one side of the equation.

From webpack configuration docs - optimization.sideEffects:

Tells webpack to recognise the sideEffects flag in package.json or rules to skip over modules which are flagged to contain no side effects when exports are not used.

So, in order to leverage that previously mentioned option, the library consumer will have to set the optimization.sideEffects option to true in their webpack config file:

// webpack.config.js
module.exports = {
  ...
  optimization: {
    sideEffects: true
  }
  ...
}

Note that, in production mode, this option is enabled by default. So, you'll only need to set it for development mode.

N.B.: In this case, you are both the author and the consumer of your modules.

Lastly, let's look at your webpack entrypoint:

// webpack entrypoint
import 'document-register-element';
import 'babel-polyfill';
import { CompDiv } from './index';

If you don't use your imported CompDiv later in this file, webpack will prune it - assuming you've set "sideEffects": false in package.json and optimization.sideEffects to true in your webpack config.

But, for example, even if you only imported 'babel-polyfill' and won't explicitly use anything from it later in this file, webpack will not prune it, because the package.json for babel-polyfill library doesn't contain "sideEffects": false.

I hope that clears things up.

like image 191
Munif Tanjim Avatar answered Oct 19 '22 17:10

Munif Tanjim