-
Notifications
You must be signed in to change notification settings - Fork 46
feat: add dual package support (ESM + CJS) #96
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
base: main
Are you sure you want to change the base?
Conversation
|
@codex review |
verytactical
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consult the newly added CONTRIBUTING.md.
| "ton3-core": "^0.0.20", | ||
| "ts-jest": "^29.1.2", | ||
| "ts-node": "^10.9.2", | ||
| "tsdown": "^0.15.12", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tsdown is not stable enough to be used in a security- and safety-critical project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@verytactical, I understand your concern, and I will not defend this decision (I used it as the simplest option here to get a result). What do you think about bundling using Rollup with rollup-plugin-dts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about sticking to plain old tsc, and running it twice.
Do we really need anything that advanced?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to use bare tsc myself, but at the build stage, renaming files to .mjs for ESM is mandatory.
You could use the rewriteRelativeImportExtensions option from TypeScript 5.7, but then all imports must include the .ts extension:
import { BitString } from './boc/BitString.ts';
In other words, if building for CommonJS and ESM were possible using bare tsc, projects such as tsup, rolldown, and rollup-plugin-dts would never have been created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should also mention that there is another way to use .js files for both CommonJS and ESM in a bundle. This is "Subpath exports with multiple package.json". It's a bit old-fashioned, but it's still well supported by bundlers and native to Node.js. I will prepare the changes later and show what happens. The setup will be more complicated, but it will be possible to leave only tsc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I forgot to put meeting notes from the call with @radist2s.
The main problem here is that this change is inherently backwards incompatible: users would have to configure their build tools to select the exact desired build of the package and its transitive dependencies. It's very likely someone will just update the library, and get into a tarpit of debugging their build.
Worse, we create two separate versions of the library, albeit in the same version of the same npm package. This means that even if there is a single version of the @ton/core in a dependency tree, we might load both ESM and CJS versions at the same time, and suffer from instanceof problems.
So, even if we're ready to ignore this kind of DX problems that ensue from ESM/CJS hybrid libraries, we'd still have to release a new major version. And that version is 1, which means we're publicly promising to keep backward compatibility on all the interfaces, and pretty much all of them are really not good at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to semver we can break stuff in minor versions prior to 1.0.0
Probably we should ditch all the instanceof checks and replace it with Symbol.for tags. I've seen the approach to fix the same problem from @preact/signals guys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to semver we can break stuff in minor versions prior to 1.0.0
Probably we should ditch all the instanceof checks and replace it with Symbol.for tags. I've seen the approach to fix the same problem from @preact/signals guys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I wish it was our instanceof, not in dependents. That'd make it much easier :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@verytactical, unfortunately, I have not yet completed the compatibility study we agreed upon. I was urgently transferred to another project and asked not to be distracted until its release. I will return to this issue any day now.
| "./package.json": "./package.json" | ||
| }, | ||
| "sideEffects": [ | ||
| "./src/boc/Cell.ts", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why these files have to be in sideEffects?
Neither Cell nor Bitstring should have any global state in them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we strictly follow the rules, then the structures of the form
static readonly EMPTY = new BitString(Buffer.alloc(0), 0, 0);
are side effects. But for the cases mentioned, I must agree that there will be no side effects. I will delete it if it raises questions, and change sideEffects to false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This declaration is actually side effect free. It just creates dependency chain from the other file, but the file itself can be totally removed to optimize bundle size
The main motivation for this PR is to achieve better tree-shaking and optimize bundle size for consumers. To accomplish this, the package now supports both ESM and CommonJS formats, exporting
.mjsand.jsfiles as well as explicit type-only exports for maximum compatibility with modern bundlers and tooling.What's Changed
tsdownfor building both ESM and CommonJS formatspackage.jsonwith properexportsfield for conditional exportssideEffectsfor optimal tree-shaking supportexport typesyntaxBenefits
Testing
tscpasses from downstream code.mjsand.jsfiles are generated and work as expected when imported in a real projectBreaking ChangesNone. This change is fully backward compatible. Existing CommonJS imports will continue to work as before, and the package now additionally supports ESM imports for modern tooling.