typescriptwebpackdependenciesbundletree-shaking

Produce a tree-shakeable ESM library with Webpack


I have a TypeScript CJS library (common-fe), that I use in a Next.js app (app-fe).

I started working on a v2, with ESM output instead, hoping that the library was completely modular and tree-shakeable.

This library treats every dependency as external, so you must install them yourself, to make it work.

Some of these are @aws-sdk/client-dynamodb, ts-luxon, rxjs, firebase, mylogger.

Importantly, app-fe doesn't use any part involving either AWS SDK or RxJS.

Basically what changed in the WebpackBundleAnalyzer output is this.

So what I understand is that at least now the dependencies are linked to common-fe and that's good. Now ts-luxon only uses the umd (who knows why, but that's not a problem, since I'll convert that to esm as well to avoid the problem entirely). What's not good is having @aws and rxjs, that aren't used in app-fe.

What I tried so far is using named top-level exports, so I went from this index.ts:

export const VERSION = "__BUILD_VRS__" as const; // REPLACED WITH WEBPACK

export * from "./shared/api";
export * from "./shared/common";
export * from "./shared/giftcard";
export * from "./shared/marketing-cloud";
export * from "./shared/utils";
export * from "./shared/user";
export * from "./services";

to this index.ts:

export const VERSION = "__BUILD_VRS__" as const; // REPLACED WITH WEBPACK

export * from "./shared/api";
export * from "./shared/common";
export * from "./shared/giftcard";
export * from "./shared/marketing-cloud";
export * from "./shared/utils";
export * from "./shared/user";
export * from "./services/logger";
export * from "./services/http";
export {KikFirebaseServiceConfig} from "./services/firebase";
export {KikFirebaseService} from "./services/firebase/kik-firebase.service";
export {KikDynamoService} from "./services/kik-dynamo.service";

But this didn't help. I even added CircularDependencyPlugin to check for circular dependencies, but there are none.

What did help was removing entirely KikDynamoService.

But what I was unable to do, was accessing it afterwards, for problems with typings and modules (app-fe uses node moduleResolution).

What might be a good way to address this, that may avoid creating a separate library (alghough the idea is starting to grow stronger)?

EDIT 1

So what I discovered is that basically by running

tsc -p tsconfig.esm.json

I obtain an oputput that works the way I intend.

node_modules down to 3.5 MB

A this point what I'm missing is how to produce such output with webpack and still take advantage of the other plugins: Terser, CircularDependencies, etc.

I still need to work on running the tests on this, because for now I'm building an extra cjs for the tests (Jest was another pain in the ass, but probably with the new output it might work just fine)...

So for know webpack checks at least circular dependencies.

And maybe I can go back to rollup, which I used at the very beginning...and that should be able to compile and minify the way I intend...


Solution

  • basically what you want to do cannot be done with Webpack. This is because Webpack is intended for bundling a web application.

    So despite you might have some luck separating your modules manually (e.g. you're talking about that KikDynamoService: if you remove all references and make it stand on its own as entry in the webpack config), you might have better luck changing approach.

    So rollup is an option, but you might want to explore esbuild instead, which is significally faster and can work on transformations, target lowering and minification.

    Good luck buddy.