Repository navigation
Design Meeting Notes, 5/14/2021 #44097
Copy link
Copy link
Closed
Labels
Design NotesNotes from our design meetingsNotes from our design meetings
Description
ES Modules in Node
https://github.com/proxy/gist.github.com/weswigham/22a064ffa961d5921077132ae2f8da78
-
exportmaps and.d.tsfiles-
The same way we let you file a
typesfield to reflect themain, we should have a "types" condition in your export maps.{ "exports": { "./": { "import+types": "./ts/esm/index.d.ts", // <- this reflects the types you get for an import "require+types": "./ts/cjs/index.d.ts", // <- this reflects the type you get for a require() "import": "./esm/index.js", "require": "./cjs/index.js", } } }
-
Alternatively, "nested conditions" approach
{ "exports": { "./": { "types": { "import": "./ts/esm/index.d.ts", // <- this reflects the types you get for an import "require": "./ts/cjs/index.d.ts", // <- this reflects the type you get for a require() }, "import": "./esm/index.js", "require": "./cjs/index.js", } } }
-
-
typesVersions?-
Version specifier in the
exportsmap?{ "exports": { "./": { "import+types>4.4": "./ts/esm/index.d.ts", "require+types>4.4": "./ts/cjs/index.d.ts", "import+types": "./ts4.4/esm/index.d.ts", "require+types": "./ts4.4/cjs/index.d.ts", "import": "./esm/index.js", "require": "./cjs/index.js", } }, }
- Keeping it like this has the benefit of keeping things up to date.
-
-
Do we expect these
exportsto be written by hand?- Generally, yes.
-
This is all quite a lot of complexity.
- Most people won't need multiple entry points and the like.
-
typesVersionswas explicit, this isn't. -
It's not necessarily just about ESM imports vs CJS require.
- It's also about Node vs. the browser.
- Also maybe about ES5 vs ESNext
- There's value in the fact that you can nest these in flexible ways.
import+types>4.4seems a little bit confusing, maybe "abusive" of the export map syntax.- Spec kind of expected identifier-like strings.
- This behavior is relied on specially - for example, anything starting with a
.is a path, anything not is "special".
- This behavior is relied on specially - for example, anything starting with a
- Make sure people involved in Node take a look.
- (side note: be cognicent of people importing
.d.tsfiles like assets?)
- It's also about Node vs. the browser.
-
Seems like team slightly prefers nesting, not concatenating (e.g.
yadda+types){ "exports": { "./": { "types": { "import": "./ts/esm/index.d.ts", "require": "./ts/cjs/index.d.ts", }, "import": "./esm/index.js", "require": "./cjs/index.js", } } }
- But still need to check how that works for versioning.
- Versioning?
-
typescript@>=4.4 -
types>=4.4 -
What about
{ "exports": { "./": { "types": { "import": "./ts/esm/index.d.ts", "require": "./ts/cjs/index.d.ts", }, "typesVersions": { ">=4.4": "..." }, "import": "./esm/index.js", "require": "./cjs/index.js", } } }
- Doesn't make sense because there's multiple ways to specify versioning in this manner.
-
- If you care about versions older than whatever TypeScript supports this, you'll still need a top-level
typesVersion. - Can't omit support for
typesVersionsfrom the MVP, otherwise users have to specially support the MVP in a weird way. - Leaning towards
{ "exports": { "./": { "types": { "import": "./ts/esm/index.d.ts", "require": "./ts/cjs/index.d.ts", }, "types<=4.5": { "import": "./ts/esm/index.d.ts", "require": "./ts/cjs/index.d.ts", }, "import": "./esm/index.js", "require": "./cjs/index.js", } } }
"types<=4.5"or"types@<=4.5"?types@*vstypes*?- You'd just write
types. - But it's usually a semver pattern.
- Need something that separates the semver pattern.
- You'd just write
Breaking Changes
- We'll look into this.
- Change in inference.
- We'll look into this.
- Issue is that we look for all things that
numberis assignable to, reduce everything. - In theory, should reject the assignment.
- So inclined to say "won't fix" - weirdness is caused by a bad assignment.
- Bloomberg found the cause internally.
- Without a minimal repro, can't improve anything.
unknown in catch
- People liked
unknownin a survey. - But it sucks to turn on in the TypeScript codebase.
- What do you mean by it "sucks in the TS codebase?"
- Lots of well-working code that doesn't improve.
- "I'm willing to do the feature, I'm not willing to be the one who converts the TypeScript codebase to use it."
- Like all good strictness flags.
- Seems wrong that we don't complain about that loose
anyunderstrict. - Can always do a refactoring to migrate an existing codebase to use
: anyincatchclauses.- That helps us feel okay with putting this under
--strict.
- That helps us feel okay with putting this under
- Would be great to have a way to say "the only thing this throws is
Error".- Agreed, but no way to do that today.
- Could totally imagine a thing that asserts the type of the error thrown.
- Seems like we're okay with this flag going under
--strict.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Design NotesNotes from our design meetingsNotes from our design meetings
Activity
milesj commentedon May 14, 2021
In regards to
exports, is theimport/requiredifferences based on the source code? With TypeScript, people are almost always using import/export, so wouldn't it always just beimport+types? What situation wouldrequire+typespop up?Also, for
catchclauses. I've always felt like it should infer toErrorif no type is provided, otherwise useunknownif explicitly provided. I'm not sure how sound that would be though.DanielRosenwasser commentedon May 14, 2021
Wesley Wigham (@weswigham) might have opinions here, but my understanding is that you would potentially want to have different behavior between
and
Additionally, I think that we would disallow mixing these import forms outside of declaration files.
weswigham commentedon May 14, 2021
Yes. An es6 like import or dynamic import sets the
importcondition, arequiresets therequirecondition. This means, notably,await import("whatever")andrequire("whatever")in the same file, can resolve to different things with different shapes.chyzwar commentedon May 19, 2021
Do you plan to support conditional exports based on target env: browser/node and runtime development/production?
https://webpack.js.org/guides/package-exports/
cspotcode commentedon May 24, 2021
In the examples,
./esmand./cjssubdirectories are used for 2x versions of the module, but the contents of both use.jsfile extension. Does this imply that one has apackage.jsonin it?For example:
If not, I believe node will throw an
ERR_REQUIRE_ESMwhen attempting torequire('libfoo')because./cjs/index.jswill be loaded as ESM. If yes, and if./cjs/package.jsondoes not declare dependencies, then I think imports will fail in yarn2 PnP.weswigham commentedon May 24, 2021
Yes.
arcanis commentedon May 28, 2021
I think it should work. We always get dependencies from the "true" package root (you can't declare dependencies from the package's subfolders), and only use nested
package.jsonfor the purpose of resolving imported paths extensions orexportsfields (just like Node).