Lion Logo Lion Fundamentals Guides Components Blog Toggle darkmode

Rationales: Side Effects

All our packages are side effect free due to the fact that es modules are always side effect free even when changing other es modules. Only code which accesses browser globals like window, console in the root of the module will be treated a side effect and always included. Everything else can be tree shaken by rollup.

Example

In this example we will define a function to override an es module export.

👉 main.js

import { somethingElse, overlays } from './overlays.js';

console.log(somethingElse);

👉 overlays.js

class OverlayManager {}

export let overlays = new OverlayManager();

export function setOverlays(newOverlays) {
  overlays = newOverlays;
}

export const somethingElse = 'something else';

Example Result

However having this function alone does not mean a side effect as rollup can tree shake it.

See the code in rollup repl.

const somethingElse = 'something else';

console.log(somethingElse);

Example Override

Let's add an es module which uses that override.

👉 main.js

import { somethingElse, overlays } from './overlays.js';
import './override.js';

console.log(somethingElse);

// this will trigger loading of full overlays.js & override.js code
// console.log(overlays.list);

👉 override.js

import { setOverlays } from './overlays.js';

setOverlays(new Object());

Example Override Result

It will still be fully tree shaken.

See the code in rollup repl.

const somethingElse = 'something else';

console.log(somethingElse);

// this will trigger loading of full overlays.js & override.js code
// console.log(overlays.list);

Example Override Result Accessing

ONLY if you actually access overlays it will include overlays.js & override.js.

class OverlayManager {}

let overlays = new OverlayManager();

function setOverlays(newOverlays) {
  overlays = newOverlays;
}

const somethingElse = 'something else';

setOverlays(new Object());

console.log(somethingElse);

// this will trigger loading of full overlays.js & override.js code
console.log(overlays.list);

Comprehensive Example

This time we have a separate controller which also accesses the overlays.

👉 main.js

import { somethingElse, setOverlays, overlays } from './overlays.js';
import { OverlayController, somethingElse2 } from './OverlayController.js';

// the following code will tree shake overlays away hence overlays is side effect free

setOverlays(new Object());
console.log(somethingElse);
console.log(somethingElse2);

//** The following will toggle importing of overlays */

// 1. import overlays directly and access it
// console.log(overlays.list);

// 2. create an OverlayController which internally accesses overlays
// const ctrl = new OverlayController();
// console.log(ctrl.manager.list);

👉 overlays.js

class OverlayManager {
  constructor() {
    this.list = [];
  }
  add(a) {
    this.list.push(a);
  }
}

export let overlays = new OverlayManager();

export function setOverlays(newOverlays) {
  overlays = newOverlays;
}

export const somethingElse = 'something else';

// the following line is a side effect as it always will be included
console.log('overlays side effect');

👉 OverlayController.js

import { overlays } from './overlays.js';

export class OverlayController {
  constructor(config = {}, manager = overlays) {
    this.manager = manager;
    this.manager.add(this);
  }
}

export const somethingElse2 = 'something else';

Comprehensive Example Result

Even with all these intertwined code rollup can tree shake it fully away. Again if you access the overlays or you start instantiating an OverlaysController it will include all the needed code.

Be sure to see it for yourself in the rollup repl.

const somethingElse = 'something else';

// the following line is a side effect as it always will be included
console.log('overlays side effect');

const somethingElse2 = 'something else';

console.log(somethingElse);
console.log(somethingElse2);

//** The following will toggle importing of overlays */

// 1. import overlays directly and access it
// console.log(overlays.list);

// 2. create an OverlayController which internally accesses overlays
// const ctrl = new OverlayController();
// console.log(ctrl.manager.list);