Skip to content

code not being tree shaked in ESM build when nullifying object #3162

@ghiscoding

Description

@ghiscoding

We have this old open source project SlickGrid used by thousand of users, I'm slowly rewriting the code to support 3 build formats ifie, cjs and esm and I'm using this plugin esbuild-plugin-ifdef to execute conditional code while building for ESM. The project was originally built as iife and I still want to support that standalone format, by having a global name of Slick on the window object, while also produce ESM build

Repro

Here's a https://esbuild.github.io/try repro

Code repro

// index.ts
import { Utils as Utils_ } from './utils.ts'; // dropped in iife by custom plugin of mine

//#ifdef ESM_ONLY
window.Slick = null; // enabled only for ESM build
//#endif

// use this ternary as a trick so that I can build for both iife and esm
// for iife, this is great I got no dead code because I have a custom plugin that removes all imports
// but for esm build I get window.Slick dead code not being tree shaked 
const Utils = window.Slick ? Slick.Utils : Utils_; 

// use utils
Utils.createDomElement('div', { className: 'header' }); 
// utils.ts
export function createDomElement(tagName, elementOptions) {
    const elm = document.createElement(tagName);

    if (elementOptions) {
      Object.keys(elementOptions).forEach((elmOptionKey) => {
        const elmValue = elementOptions[elmOptionKey];
        if (typeof elmValue === 'object') {
          Object.assign(elm[elmOptionKey], elmValue);
        } else {
          elm[elmOptionKey] = (elementOptions)[elmOptionKey];
        }
      });
    }
    return elm;
}

export const Utils = {
    createDomElement
}

when running with this config

{
  bundle: true,
  minifySyntax: true,
  format: 'esm',
  treeShaking: true,
  outfile: 'index.js'
}

Expectation

I was expecting all window.Slick code to be dropped with tree skaking process (even with /* @__PURE__ */ it makes no difference)

So I was expecting this code

import { Utils as Utils_ } from './utils.ts'; // dropped in iife by custom plugin of mine
window.Slick = null; // for ESM only
const Utils = window.Slick ? Slick.Utils : Utils_;
Utils.createDomElement('div', { className: 'header' }); 

to be transformed to this code

import { Utils } from './utils.ts';
Utils.createDomElement('div', { className: 'header' }); 

but in reality the output is

window.Slick = null;
var Utils2 = (
  window.Slick ? Slick.Utils : Utils
);
Utils2.createDomElement("div", { className: "header" });

Technically speaking window.Slick is null so the first ternary case window.Slick ? Slick.Utils will never be reached, so why isn't it removed? Is there another way to get it removed somehow?

For now I can live with what esbuild creates but it would be nice if I could somehow delete this unreachable code for my ESM output and lower my build size a little. If it's not possible, I can live with what I have about 100 lines like this, and everything is working fine (it's just dead code that's all).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions