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

A typescript package that supports both commonjs and esmodule should generate .d.ts and .d.cts type files, now only .d.ts #1030

Open
yuntian001 opened this issue Mar 3, 2023 · 7 comments

Comments

@yuntian001
Copy link

yuntian001 commented Mar 3, 2023

typescript 文档请参考:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#packagejson-exports-imports-and-self-referencing

package.json

"type": "module",
    "main": "dist/index.cjs",
    "module": "dist/index.js",
    "types": "dist/index.d.ts",
    "exports": {
      ".": {
        "import": {
          "types": "./dist/index.d.ts",
          "default":"./dist/index.js"
        },
        "require":  {
          "types": "./dist/index.d.cts",
          "default":"./dist/index.cjs"
        }
      }      
    },
    "files": [
      "dist"
    ],
    "scripts": {
      "test": "npm run build && cd ./test && npm run dev",
      "clean": "rimraf dist",
      "build": "rimraf dist && microbundle --target node  -f esm,cjs"
    },
@yuntian001 yuntian001 changed the title A typescript package that supports both commonjs and esmodule should generate .d.ts and .d.cts life files, now only .d.ts A typescript package that supports both commonjs and esmodule should generate .d.ts and .d.cts type files, now only .d.ts Mar 3, 2023
@JohnAlbin
Copy link

Specifically, the TypeScript docs say:

It’s important to note that the CommonJS entrypoint and the ES module entrypoint each needs its own declaration file, even if the contents are the same between them. Every declaration file is interpreted either as a CommonJS module or as an ES module, based on its file extension and the "type" field of the package.json, and this detected module kind must match the module kind that Node will detect for the corresponding JavaScript file for type checking to be correct. Attempting to use a single .d.ts file to type both an ES module entrypoint and a CommonJS entrypoint will cause TypeScript to think only one of those entrypoints exists, causing compiler errors for users of the package.

@rschristian
Copy link
Collaborator

While that's indeed the position of the TS team now, for the past 6+ years, ESM .d.ts files were all that existed and were pretty much without issue. I don't think this is going to be a priority to address here, and likely won't be addressed at all until TS provides a dual-compilation method.

@brandonmcconnell
Copy link

brandonmcconnell commented Apr 23, 2024

I just switched over to microbundle, but I'm not likely to stay long if this issue persists. Very simple to use, which is great, but this is a bit too risky.

From attw:

tailwindcss-default-shades v0.0.19

Build tools:

  • typescript@^5.1.6
  • microbundle@^0.15.1

👺 Import resolved to an ESM type declaration file, but a CommonJS JavaScript file. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseESM.md

"tailwindcss-default-shades"

node10: 🟢
node16 (from CJS): 👺 Masquerading as ESM
node16 (from ESM): 🟢 (ESM)
bundler: 🟢


The instructions to resolve this are to output both .d.ts and .d.cts files, which is appears microbundle does not support.

@rschristian
Copy link
Collaborator

rschristian commented Apr 23, 2024

Then you'll probably be better served elsewhere -- I don't find TS's changes here to be particularly compelling (as it causes no real issue anyhow) and no one else has invested time to land this, so this issue will probably persist.

Edit: This is OSS ran by people volunteering free time; you're not a customer we're trying to keep. To start off a reply saying you'll go elsewhere unless maintainers support your feature request is incredibly demanding and rude.

If this tool doesn't fit your needs, please, there's a world of great software out there.

@brandonmcconnell
Copy link

@rschristian Hi there! Sorry, in no way did I mean what I said to be rude or demanding. I was attempting to provide an example of how this might be seen as important.

As a new adopter of the tool, I'm finding it genuinely challenging to use, as it conflicts with other common build tools that expect both .d.ts and .d.cts.

This is not meant in any sort of demanding way. I thought it would be more constructive to the team to mention my observation and experience here rather than silently switch to an alternative, in case there is an opportunity to meet this need, perhaps due to my new shared information (the conflicts with other build tools). If there's not, I completely understand.

OSS is hard work, and I truly respect and appreciate the team working on microbundle. I was just introduced to the tool today, and I found it exceptionally simple to use in comparison to other tools. That's why I'm so bummed it conflicts with my other build tools.

Thanks again 🙂

@brandonmcconnell
Copy link

@rschristian I also wonder if there could be a simple workaround, even in user land, such as duplicating the .d.ts file and text-replacing certain things that change between the .d.ts and .d.cts files.

@rschristian
Copy link
Collaborator

a simple workaround, such as duplicating the .d.ts file and text-replacing certain things that change between the .d.ts and .d.cts files.

Unfortunately, this is unlikely. To copy/paste from MDN:

// Exporting declarations
export let name1, name2/*, … */; // also var
export const name1 = 1, name2 = 2/*, … */; // also var, let
export function functionName() { /* … */ }
export class ClassName { /* … */ }
export function* generatorFunctionName() { /* … */ }
export const { name1, name2: bar } = o;
export const [ name1, name2 ] = array;

// Export list
export { name1, /* …, */ nameN };
export { variable1 as name1, variable2 as name2, /* …, */ nameN };
export { variable1 as "string name" };
export { name1 as default /*, … */ };

// Default exports
export default expression;
export default function functionName() { /* … */ }
export default class ClassName { /* … */ }
export default function* generatorFunctionName() { /* … */ }
export default function () { /* … */ }
export default class { /* … */ }
export default function* () { /* … */ }

// Aggregating modules
export * from "module-name";
export * as name1 from "module-name";
export { name1, /* …, */ nameN } from "module-name";
export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name";
export { default, /* …, */ } from "module-name";
export { default as name1 } from "module-name";

There's a lot of ways to export values w/ ESM, and translating them to CJS would be pretty tricky. A few of these are probably irrelevant, but as we do use TS (through rollup-plugin-typescript2), which is an eternally moving target, I don't think there's any way to expect only a certain subset of the syntax to convert. It'd be a pretty complicated plugin to convert this all over to CJS correctly for all cases I'd think.

Now, we likely could override the user's tsconfig.json settings to output the correct .[cm]ts during compilation, but I have no idea if that'll introduce other issues. I had hoped TS would walk back on this / provide better tooling by now, as introducing .cts years after being ESM-only is quite frustrating.

As a new adopter of the tool, I'm finding it genuinely challenging to use, as it conflicts with other common build tools that expect both .d.ts and .d.cts.

Honest question, what build tools are these? I've never seen a build tool that would take .d.ts files into account at all, short of (say) declaration merging.

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

No branches or pull requests

4 participants