isaacs / tshy

Other
890 stars 18 forks source link

Incompatible with "verbatimModuleSyntax": true in tsconfig.json #17

Closed mattpocock closed 1 year ago

mattpocock commented 1 year ago

This is an interesting wrinkle. verbatimModuleSyntax enforces the correct usage of export default and export = in your code.

But since tshy runs tsc in both cjs and esm mode on your code, then verbatimModuleSyntax can never be resolved correctly. Either your export default is incompatible with cjs, or export = is incompatible with esm.

Interested in your thoughts - you could:

  1. Fail fast if you detect verbatimModuleSyntax is set to true, with a nice warning.
  2. If the package.json has type: module, set verbatimModuleSyntax to false when emitting cjs. And vice versa.
isaacs commented 1 year ago

It does fail fairly fast if you set it true today, albeit without a nice warning, since VMS is always dialect specific. (Unless you polyfill literally every file in src, I guess, but then why even use this module? :joy:)

In general, default exports are kind of a huge pita for hybrid builds. You pretty much must use a cjs polyfill in that case, or else you get the weird const blah = require('pkg').default wart, and if you use export = in the CJS polyfill, you can't export types along with it (and so these have to go in a global namespace).

For example:

The bigger hazard, though, is non-default exports, I think.

$ cat src/index.ts 
import x = require("url");
export { x };

$ tsc -p .tshy/commonjs.json 
src/index.ts:2:10 - error TS1286: ESM syntax is not allowed in a CommonJS module when 'verbatimModuleSyntax' is enabled.

2 export { x };
           ~

Found 1 error in src/index.ts:2

To be honest, I'm not sure how it's possible/reasonable to use verbatimModuleSyntax with a hybrid package. It ties the input code to the emit format in a way that is fundamentally inflexible, effectively turning "ts->commonjs" and "ts->esm" into two different source dialects.

So, the best solution seems to be: detect VMS early, and fail immediately if tshy.dialects.length > 1.

ggoodman commented 10 months ago

@isaacs would you be open to supporting an opt-out of this behaviour for module authors who are (thoughtfully) avoiding default exports?

My understanding is that for such modules, verbatimModuleSyntax results in a more well-defined authoring experience and wouldn't be problematic for the CJS / ESM interop nightmare.

isaacs commented 10 months ago

@ggoodman Unfortunately, the syntax for importing and non-default exports are also different between CJS and ESM when VMS is enabled. verbatimModuleSyntax does avoid the challenges of write-once and shipping to both esm and cjs, by making it 100% impossible to do that ("doc, it hurts when I do this" style).

If you're using verbatimModuleSyntax, then every build must be either CJS or ESM, and the code has to be aware of where it's going, because you can't use ESM-style syntax for a CJS build if you're using verbatimModuleSyntax, so tshy is irrelevant in that case.

My recommendation: If you are shipping one dialect, and find verbatimModuleSyntax style to be clearer and helpful for your program (personally, I think it is, but it's subjective style opinion of course), then do not use tshy, because it is not really offering you much. Just use tsc.