jaredpalmer / tsdx

Zero-config CLI for TypeScript package development
https://tsdx.io
MIT License
11.22k stars 507 forks source link

Publishing module for both Node.js and browser #946

Closed torkelrogstad closed 3 years ago

torkelrogstad commented 3 years ago

This question might be a bit misguided so please bear with me - I have never written a JS/TS module for other people to consume.


I'm trying to publish a library that will be consumed both in a Node.js context as well as browser context. I can't seem to figure out how to do that with Tsdx. Specifically, I'm trying to write a helper library that provides a nice integration with Storybook stories (browser context) and Jest (Node.js context).

It seems like all files are compiled into one big name-of-my-package.cjs.dev/prod.min.js file. This does not work for me, as that file contains an import of a library which in turn uses Node.js functionality. This then causes my browser code to crash.

I guess what I want to do here is to publish two versions of my library: one which exposes the browser methods, and one that exposes the Node.js methods. I want both to be part of the same package. Is this a very strange way of doing things?

Any help here is much appreciated.

agilgur5 commented 3 years ago

This is less a TSDX question and more a general library question.

There are two ways to go about doing this. One is to create two separate entrypoints, e.g. my-package/browser vs. my-package/node (or however you might want to organize those). You could also then use the browser package.json field to specify the browser entry. That would make this a duplicate of https://github.com/formium/tsdx/issues/751 which is effectively duplicating #175 and #367. A simplified example (that does not use TSDX) can be found in one of my libraries physijs-webpack which has separate entries for Browserify vs. Webpack (I've also been looking to add a Node version too, but lack of workers in Node made that difficult; now worker_threads exists, but it works a bit differently).

The other way of going about this is environment or feature detection. Basically you conditionally use certain functionality if you're in Node.js vs. in a browser by e.g. checking if window exists or not. That would make your code isomorphic/"universal". You can see an example of this in one of my libraries: https://github.com/agilgur5/mst-persist/pull/15

There are other techniques as well, for instance one can compile the same bundle a bit differently and replace variables (like IS_BROWSER) with rollup-plugin-replace or one can stub Node functionality, etc. For Jest one could also check for the test environment.

torkelrogstad commented 3 years ago

Thank you so much for a very detailed answer. Also, thank you for working on an excellent tool