question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Output tree-shakeable friendly classes when compiling ES5

See original GitHub issue

What happens and why it is wrong

I’ve been investigating why one of my React Typescript component libraries doesn’t treeshake nicely. An important fact is that I’m outputting ES5 target.

The cause I’ve come down to is because terser works best with es2015 modules and defaults to a more safe output. Since ES5 doesn’t have import/export type situations it’s hard terser to know what is safe to strip out. However with typescript I think there is a solution and I ran into it here specifically this custom plugin:

    plugins: [
        resolve({ jsnext: true, module: true }),
        {
            transform(code, id) {
                return code.replace(/\/\*\* @class \*\//g, "\/*@__PURE__*\/");
            }
        },
        uglify(uglify_options),
    ],

When typescript compiles, it adds a /** @class */ comment in front of every class object it transpiles. Simply replacing this class comment with Terser’s __PURE__ comment allows Terser to know it can tree shake these objects.

I further checked to make sure if prop-types is being used it wont remove the function. For example the following would be tree-shaked

export class Example1Component extends React.Component {
  render() {
    return 'hello1';
  }
}

but the following would not due to it having possible unintended consequences

export class Example1Component extends React.Component {
  render() {
    return 'hello1';
  }
}
Example1Component.defaultValues = {
  wheee: 123,
};

So the ask here is that rollup-plugin-typescript2 does the replacement of /** @class */ to /*@__PURE__*/ so that the ES5 outputs would be tree-shaken down stream.

Environment

N/A

Versions

  • typescript: 3.6.4
  • rollup: 1.23.1
  • rollup-plugin-typescript2: 0.24.3

rollup.config.js

import pkg from './package.json';
import typescript from 'rollup-plugin-typescript2';

export default {
  external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})],
  input: 'src/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
    },
    {
      file: pkg.module,
      format: 'es',
    },
  ],
  plugins: [
    typescript({ tsconfig: 'tsconfig.json' }),
  ],
};

tsconfig.json

{
  "compilerOptions": {
    "declaration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "lib": ["es5"],
    "module": "es2015",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": false,
    "outDir": "./dist/",
    "sourceMap": true,
    "strictNullChecks": true,
    "target": "es5"
  },
  "exclude": ["./src/**/*.test.ts", "./src/**/*.test.tsx", "./test/**/*.ts", "./test/**/*.tsx"],
  "include": ["./src/**/*.ts", "./src/**/*.tsx", "src/index.ts"]
}

package.json

Only relevant part is my build script

rollup -c rollup.config.js

plugin output with verbosity 3

N/A

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
ezolenkocommented, Oct 15, 2019

This looks like something a separate plugin should do, either rollup plugin like you had, or a typescript transformer. I’d rather not modify typescript output for everybody, too many possible unintended consequences.

0reactions
k2snowman69commented, Sep 9, 2020

Sigh, I hate replying when I know people are going to thumbs down it. Here are the changes I made to get this to work:

  1. Moved all dev-dependencies that I was including in my bundle into dependencies and let the consuming project deal with the bundling
  2. In the package.json mark the project as side-effect free - https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
  3. Remove all of rollup from my build and just use tsc. Most importantly, make sure each module you want to have be tree-shakeable is a separate dist file build output. When they are all one file, it’s hard for the tree shaker to split apart. As separate files, it’s much easier on the tree-shaking algorithm. For me, I moved from using out to outDir.
  4. Rely on the consuming app to add minification on their final output which would get applied to my libraries output as well

Again, not the answer a rollup repository is going to want to hear, however it did remove a ton of dependencies and complications for me which actually made maintenance easier.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Reduce JavaScript payloads with tree shaking - web.dev
A diagram illustrating the process of downloading, decompressing, parsing, compiling, and executing The process of downloading and running JavaScript. Note that ...
Read more >
Tree-shaking with webpack 2 and Babel 6 - 2ality
I think that the Typescript compiler doesn't currently remove dead code and you can't allow it to output ES5 code or CommonJS modules...
Read more >
Tree Shaking - webpack
Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module...
Read more >
How to do proper tree-shaking in Webpack 2 - Emarsys Craftlab
With tree-shaking in place we expect the output bundle to only include classes and functions we use. In our case it means the...
Read more >
How To Make Tree Shakeable Libraries - Theodo blog
The library is tree shaken! You may repeat this step but for multiple imports and look at the output code. The objective is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found