TypeScript NPM Packages Done Right
blog.liblab.com
blog.liblab.com
I had to recheck this was indeed an article from 2023, because this part surprised me greatly. To my understanding, es2016 only added Array.prototype.includes, the exponentiation operator (**), and preventing generator functions from being constructed. Even the slowest adopters already had these in 2017. Is the author assuming IE11 support?
The OP recommends to additionally package src/*.ts along with sourceMaps and declarationMaps.
What's actually the best practice here?
Some, but not all of these, are transpilable/polyfillable (refer to compat-table).
Edit: Those numbers are weighted by global usage.
[1] https://caniuse.com/?feats=mdn-javascript_builtins_array_at,...
Most modern browsers are evergreen, so targeting es2022 would be fine for most users. The exception is Safari, which is slower to incorporate new features and doesn't roll out its updates nearly as quickly as Chromium-based or Firefox, but even they have had es2016 support since 2016.
You can then use that to make your own decisions; if you're working on a high-tech video streaming platform for kids you can probably safely ignore the 5%, but if you're providing information on behalf of the government you might need to support much more than that.
To clarify - the goal is if you have a project that uses ES7, why would you want to use a package written in ES2020 that is transpiled to, i.e. ES2016 using non-native async / await.
[0] https://stackoverflow.com/questions/76558023/whts-the-ideal-...
Which doesn't support const enum correctly and god knows what else without erroring.
Also TS enums are flawed: https://blog.logrocket.com/why-typescript-enums-suck/
> Enums in TypeScript are a very useful addition to the JavaScript language when used properly.
So, just like any language feature.
I like Typescript enums generally, my only complaint is that they are kept in the final build as big objects, especially in the scenario where you don't enumerate them. I use a replace function with terser to replace them with inlined constants to make my fine build smaller.
That’s because its main job is not transpiling to JS, but as a compiler it actually checks the correctness of the code, which is probably the main reason typescript exists in the first place.
You can actually run tsc without emitting JS at all in particular configurations.
TS module resolution looks almost like ESM, but neither the syntax nor the semantics are the same.
And or course you need a compiler to transpile TS, even if you wish to ignore the typings (often wrapped in a bundler like Vite which in turn wraps swc or ESBuild... because tsc is not as practical for large projects)
Really, having dealt with this kind of problem on Friday, it can make you go crazy.
E.g. having to deal with CJS-specific settings for some tool in a project using TS and exporting to ESM JS...
Libraries especially don't and should be published to npm unbundled. Bundling is purely an application concern.
> Bundling is purely an application concern
by this logic all compiled languages' repos should just be source, no precompiled binaries.
[1] https://github.com/arethetypeswrong/arethetypeswrong.github....
Nope.
So we just use CommonJS with project references and call it a day. Everything works and we don’t need to change any of our code. The way ESM was introduced into Node has been the dumbest decision. And they’re doubling down on it. Excited for Bun to be compatible with my project so that I never have to deal with any of this again.
[1] https://nodejs.org/api/packages.html#dual-commonjses-module-...
Any modules that didn't work (e.g. mocha) were switched over to more modern equivalents (vitest). This is probably a good thing.
We also export ESM and CommonJS source in our packages by first transpiling to ESM then using vite to convert to CommonJS. It works really well and allows us to use our packages in both environments.
I started using Rust for quick and dirty programs, web services, small frontend apps - and I noticed: - slower to prototype simple things compared to node CJS - equal or faster when compared to TypeScript - faster to get complex things right
I have been that vocal minority that keeps complaining about CommonJS support to library authors. But I have given it up for the greater good.
we have a standard now, CommonJS needs to go, why keep pushing non standard stuff in a 2023 article.
Basically we will have to move the library to ESM and then use a bundler like esbuild to compile it to CJS as well for compatibility. It’s a mess
I think everyone agrees about this.
What people can’t agree about is how to fix it.
Maybe an aversion to bundlers exists, but I think tsup and vite are great and simple. We have turbopack on the horizon too.
For small projects I don’t even need a configuration file for tsup to target both formats. For larger more complex projects vite configs tend to be under 50 LoC.
Everything should be published as standard modules at this point.
> ▲ Module 'some-library' used by 'src/app/some.component.ts' is not ESM
> CommonJS or AMD dependencies can cause optimization bailouts.
> For more information see: https://angular.io/guide/build#configuring-commonjs-dependen...
I got round this with some funky step where I copy the package.json into the dist folder and rewrite some paths. Is there a better way to do this?
{
"exports": "dist/index.js"
}
or {
"exports": {
".": "dist/index.js",
"./*": "dist/*.js"
}
}
https://nodejs.org/api/packages.html#subpath-exportsMany packages do this.
I.e. if you're releasing a package today, you probably don't have to worry about support.
We simply chose to mark Jest 27 as unsupported in this case, but it did cost some debugging time.
For some projects I just configure publishConfig.directory directly at the dist folder.
While this may not look as clean, it is a lot easier for me to manage everything that’s published under a single directory.
It doesn't have to be `index.js` in the root of your package. That's the fallback if no `main` or subpath exports exist in `package.json`
So that you can test without doing increments which are not required
It's so frustrating that in 2023 this is still a concern. It's probably a bad idea when we adopt the latest JS language standards before they are implemented in the runtime env. Transpiling sucks.
On top of that, being implemented in at least two different environments is a prerequisite to get finalized as a standard per the tc39 process.
I agree with you when it comes to things that aren't far along- the decorator goat rodeo is a good example, but on the whole transpiling is essentially a part of the process now.
If you’re not using Yarn, there seems to be a similar thing on npm, `patch-package`. [2] I never had to use that though.