Skip to content
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

Generate .d.cts and .d.mts files #725

Merged
merged 2 commits into from
Aug 23, 2023
Merged

Generate .d.cts and .d.mts files #725

merged 2 commits into from
Aug 23, 2023

Conversation

calebeby
Copy link
Member

When a project that uses pleasantest uses TS with moduleResolution: nodenext, TS becomes unhappy with pleasantest because it interprets the .d.ts as a non-ESM file, even though it was imported as an ESM.

This is all a big headache. I'm going to do my best to explain it but I might be a little bit wrong:

If a .ts file is authored as a module (e.g. it has import statements), it may be either compiled to commonjs or compiled to a module.

If a file with import statements is compiled to commonjs, there is potentially a problem where that file may have had import 'some-esm-file' which would get transpiled to require('some-esm-file'). But, importing an ESM file from a commonjs file is not allowed. So even though the file was authored in ESM (with import statements), since it was transpiled to commonjs, it would no longer be valid.

To fix this real problem (ignoring typescript), pleasantest has an export map in package.json, so that a user can require('pleasantest') or import 'pleasantest'. This export map says "when you require me, load this CJS file, when you import me, load this ESM file".

This all works fine at runtime but TS doesn't know about it and thinks there is still a problem. When I create a project with these TS settings:

    "module": "Node16",
    "moduleResolution": "Node16",

then I get the following error:

The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("pleasantest")' call instead.
  To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/Users/calebeby/Projects/tmp-ts-pt-modules/package.json'.ts(1479)

This TS error is not helpful because at runtime everything is fine (the export map handles all the CJS/ESM interop as necessary).

To work around this, I added separate types fields for each of the parts of the export map, pointing to .d.cts and .d.mts files. types.d.cts, types.d.mts, and types.d.ts all have exactly the same contents.

I wish we'd gotten ES modules sooner so that we wouldn't have ever had to deal with pretending that import and export were syntax sugar for require and module.exports. It causes so many headaches now.

@calebeby calebeby marked this pull request as draft August 22, 2023 19:15
@calebeby calebeby marked this pull request as ready for review August 22, 2023 19:44
Copy link
Member

@Paul-Hebert Paul-Hebert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@calebeby calebeby merged commit 24b347d into main Aug 23, 2023
5 checks passed
@calebeby calebeby deleted the dts-modules-and-cjs branch August 23, 2023 16:41
@github-actions github-actions bot mentioned this pull request Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants