-
Notifications
You must be signed in to change notification settings - Fork 74
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
fix: enable compatibility with node16/nodenext module resolution #1803
Conversation
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.
As a stop gap measure, this approach seems fine.
Should the types field be used at all?
I am still of the opinion that the answer is no, and that we should migrate away from these manual type definition files that shadow the runtime entrained definitions. We don't have to use JSDoc @typedefs
to migrate away, just need to "runtime" re-export the types file from the entrypoint, but that does require generating type definition files in a prepublish build step, which these packages do not do yet.
@mhofman I don't follow. Say we wanted to expose |
e160ccf
to
e1baa2d
Compare
|
@mhofman I'll try that. But it begs the question: why use a In this case, |
(btw, |
@mhofman By "I'll try that", I mean I will try that in another forthcoming PR. Care to approve? FWIW, I just tried this in
Mind you, within a |
Because that requires the generation of the output not just for publish but also for authoring inside the repo, which I am against.
That is exactly the behavior I find unacceptable. There should be no generation step needed when working within the repo (which effectively also uses links through yarn workspace) |
It doesn't. To be clear, the changes to
There isn't! It's only when working outside of the repo. By my own project I mean "a package somewhere in the lavamoat monorepo which links I'm confident this all works the way you think it should. |
if there is an |
@mhofman Ah, you're right. Anyway: the code in question doesn't exist. I am trying to get this PR merged, though. ;) |
e1baa2d
to
9231d05
Compare
Much like endojs#1803, this adds `types` conditional exports where appropriate.
Much like endojs#1803, this adds `types` conditional exports where appropriate.
Much like endojs#1803, this adds `types` conditional exports where appropriate.
Much like endojs#1803, this adds `types` conditional exports where appropriate.
Much like endojs#1803, this adds `types` conditional exports where appropriate.
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.
improvement. no risks. let's merge
TypeScript's `node16` and `nodenext` module resolution algorithms attempt to adhere more closely to the behavior of the `exports` field. To that end, if an `exports` field is present in a package, the `types` field is ignored; instead, a `types` conditional export is expected. Without this change, any consumer using one of these newer--_officially recommended_--module resolution algorithms will be unable to load any `types.d.ts` file from any package. We retain the `types` field because of backwards compatibility with the legacy `node` module resolution algorithm. ### Should `types.d.ts` exist at all? _It depends._ While this file is not _truly_ needed for type support if type declarations are shipped alongside each `.js` source file, it's awkward to export any types created via `@typedef` without it. For instance, say we have a function `foo(opts: FooOpts)`. `foo` lives in `a.js`, and `FooOpts` is a `@typedef` in `b.js`. The parameter `opts` is tagged as `@param {import('./b.js').FooOpts} opts`, as it should. Entry point `index.js` re-exports `foo()` from `a.js`. However, the definition for `FooOpts` _will not be available to consumers_--which will cause compilation errors. We must, then, re-export this type from the entry point. To avoid this, we have precious few tools at our disposal: 1. Create a reference of `FooOpts` in `index.js` via `@typedef`, which will export the `FooOpts` type. If `FooOpts` is generic, this becomes more of a headache, as we have to _copy_ the generics from the original. This is a hazard, because the generics could become out-of-sync. 2. Create a `.d.ts` (it could also be a `.ts`, which we can assume will generate the associated `.d.ts`; this doesn't matter for our purposes) which re-exports `FooOpts` from `b.js`. This file will be the titular `types.d.ts`. The question we need to ask, then, is "do we need to export any `@typedef`s?" If the answer is _yes_, then a `types.d.ts` makes sense. I didn't actually check to see if `types.d.ts` is appropriate in all of these packages; I just fixed the problem I was encountering. For `@endo/compartment-mapper`, some work I'm doing will mean re-exporting some `@typedef`s, even if that's not happening currently. Furthermore, I ignored the packages with `"types": null`, since I don't understand what that's supposed to mean.
9231d05
to
f54bea6
Compare
Closes #1802
Description
TypeScript's
node16
andnodenext
module resolution algorithms attempt to adhere more closely to the behavior of theexports
field. To that end, if anexports
field is present in a package, thetypes
field is ignored; instead, atypes
conditional export is expected.Without this change, any consumer using one of these newer--officially recommended--module resolution algorithms will be unable to load any
types.d.ts
file from any package. We retain thetypes
field because of backwards compatibility with the legacynode
module resolution algorithm.Read more about how I came to this conclusion
Should the
types
field be used at all?It depends. While this file is not truly needed for type support if type declarations are shipped alongside each
.js
source file, it's awkward to export any types created via@typedef
without it.For instance, say we have a function
foo(opts: FooOpts)
.foo
lives ina.js
, andFooOpts
is a@typedef
inb.js
. The parameteropts
is tagged as@param {import('./b.js').FooOpts} opts
, as it should. Entry pointindex.js
re-exportsfoo()
froma.js
. However, the definition forFooOpts
will not be available to consumers--which will cause compilation errors. We must, then, re-export this type from the entry point. To avoid this, we have precious few tools at our disposal:FooOpts
inindex.js
via@typedef
, which will export theFooOpts
type. IfFooOpts
is generic, this becomes more of a headache, as we have to copy the generics from the original. This is a hazard, because the generics could become out-of-sync..d.ts
(it could also be a.ts
, which we can assume will generate the associated.d.ts
; this doesn't matter for our purposes) which re-exportsFooOpts
fromb.js
. This file will be the titulartypes.d.ts
.The question we need to ask, then, is "do we need to export any
@typedef
s?" If the answer is yes, then atypes.d.ts
makes sense.I didn't actually check to see if
types.d.ts
is appropriate in all of these packages; I just fixed the problem I was encountering. For@endo/compartment-mapper
, some work I'm doing will mean re-exporting some@typedef
s, even if that's not happening currently. Furthermore, I ignored the packages with"types": null
, since I don't understand what that's supposed to mean.Testing Considerations
I did not add any tests, as the test needs to happen on the result of
npm pack
, which is more of a fixture than I wanted to build out. However, I am working on a tool which will (hopefully soon) automatically check for problems like this. Once that's ready, I can open a new PR to add the checks and you can see what you think.