Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make tslib imports shakable #16999

Closed
tommytroylin opened this issue Jul 7, 2017 · 12 comments · Fixed by #32742
Closed

make tslib imports shakable #16999

tommytroylin opened this issue Jul 7, 2017 · 12 comments · Fixed by #32742
Assignees
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript

Comments

@tommytroylin
Copy link

TypeScript Version: 2.4.1
when I use ts with config like:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "importHelpers": true,
    "module": "esnext",
    "noEmitHelpers": true,
    "target": "esnext"
  }
}

I notice that it come out with code like this

import * as tslib_1 from "tslib";

It is not good enough for tree-shaking by webpack.
Webpack actually bundle with the entire tslib.es6.js

And it's a bit heavy in my bundle
image

Expected behavior:

import { __decorate } from "tslib"; 
// depends on funcitons that actually used with in this file
@DanielRosenwasser DanielRosenwasser added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jul 8, 2017
@bfricka
Copy link

bfricka commented Jul 12, 2017

It should be shakable, as the package.json defines "module": "tslib.es6.js", and uses es6 exports rather than exporting an object. From this perspective, it seems more like a Webpack issue. Have you tried reproducing with Rollup?

@RichiCoder1
Copy link

@bfricka I think the issue is that import * as tslib_1 from "tslib"; is what is being generated by tsc. Webpack (currently) doesn't know how to shake this. Typescript should emit an import that only includes what that module is actually using.

@bfricka
Copy link

bfricka commented Jul 12, 2017

Totally. I should have read the ticket closer.

@TheLarkInn
Copy link
Member

That is correct!!! import * is very hard for webpack to try and analyze.

However something like import foo from "tslib/lib/foo" would be far easier to analyze.

@RikkiGibson
Copy link
Member

Is there anything that would make this unfeasible? It would really be ideal to have only the actually used helpers get bundled by tools like webpack.

@mhegazy mhegazy added this to the TypeScript 3.0 milestone Jun 6, 2018
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jun 6, 2018

@TheLarkInn, just curious, where can users track better tree-shaking for namespace imports in Webpack?

@fazouane-marouane
Copy link

Hi there 👋

We faced this issue this week after using generators with typescript while trying not to blow our size budget.
Below is the workaround I tried. I'll definitely go to hell for this 🔥

Configuration

{
  "compilerOptions": {
    // ...

    // We will need typescript not to inline implementation for __generator
    "noEmitHelpers": true
    // We will need typescript not to import 'tslib' either
    "importHelpers": false,
  }
}

Module using GeneratorFunctions

// import __generator manually
import { __generator as tslib__generator } from 'tslib'
// This is necessary so the above import won't get remove on the compiled version
export const __generator = tslib__generator

export function* helloWorld() {
  yield* "Hello, World!";
}

Output

esm + es2015

import { __generator as tslib__generator } from 'tslib';
export const __generator = tslib__generator;
export function* helloWorld() {
    yield* Array.from('Hello, World!');
}
//# sourceMappingURL=hello_world.js.map

✅ Using any bundler that does support dead code elimination (a.k.a TreeShaking™), it will ignore __generator and tslib since it has not been imported/used.

esm + es5

import { __generator as tslib__generator } from 'tslib';
export var __generator = tslib__generator;
export function helloWorld() {
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0: return [5, __values(Array.from('Hello, World!'))];
            case 1:
                _a.sent();
                return [2];
        }
    });
}
//# sourceMappingURL=hello_world.js.map

✅ Any bundler will see that __generator is in use and will keep it while ignoring the rest of tslib.

I hope that this will help some of you until we have proper support for tree shaking of importHelpers.

@theKashey
Copy link

Not sure how tslib could be tree shackable, as long as it does not marked as a sideEffect:false, thus should not be a subject for optimization.

@theKashey
Copy link

theKashey commented Jun 13, 2019

There is a less hacky(in comparison to @fazouane-marouane solution) way to solve the problem

  1. disable importHelpers, let ts inline everything (not doable to libraries*)
  2. add runtime-compress-loader loader to your webpack configuration.
  3. it will scan files for ts-helpers signatures, and replace them with corresponding import {helper} from 'tslib', making import tree shakable.
  • for libraries one might look for tslib_1.__assign and replace these signatures, but that's not done yet.

@rbuckton
Copy link
Member

rbuckton commented Aug 6, 2019

I'd like to clarify some of the requirements for tree-shakable imports:

  • Which output module kinds should be tree shakable? WebPack seems to only support tree shaking via ES module syntax (import/export), so it seems like this should only be --module es2015 and --module esnext.
  • Is import { __helper } from "tslib"; enough to be tree shakable, or do there need to be changes made to tslib as well?
  • The tslib package main export introduces global declarations for each helper to be able to leverage tslib via <script> tags as well as via import. Is that a problem for tree-shakability?

@RikkiGibson
Copy link
Member

  1. Agreed.
  2. Yes, because tslib has an esm distribution that the bundler will find and use https://unpkg.com/tslib@1.10.0/tslib.es6.js
  3. No, because the esm distribution doesn't include the global declarations (as far as I can see).

It actually looks like it should be enough to change to the import { __helper } from 'tslib' syntax to start tree shaking the tslib usages. You might have to add a "sideEffects": false but that can be good to do anyway if your esm distribution doesn't cause observable side effects when it is evaluated.

@RichiCoder1
Copy link

Ditto the above. An tslib build using ESM style imports and exports + "sideEffects": false should be enough for both Webpack 4+ and Rollup.

Webpack:
https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free

Rollup:
https://rollupjs.org/guide/en/#tree-shaking

^ The difference between the two being Rollup will try and determine sideEffects itself while Webpack needs a signal to trigger it's tree-shaking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.