Open kraenhansen opened 1 year ago
I did a bit of digging and I believe this is the piece of code responsible for determining the extension of the output file:
Related, I found these minutes from a design meeting on the feature to support .mjs
and .cjs
as input files.
Also this issue seem tangental, although I understand it more about the file extension for .ts
source files.
I think that #35148 (and #35589) is related; however, I don't recall whether that also did path rewriting - and we didn't have the context of #44442 at the time.
As you pointed out, in #44442 we discussed the issue of .mjs
as an input file under "module": "commonjs"
being a weird "undefined behavior" of the compiler. I'm still not sure if the right thing to do here is to convert to .cjs
in the output. I could kind of see that, but I don't really know if we have the appetite for a new compiler option to solve this case (as in your PR at #54583).
Is there any reason you don't want to just use .js
as an input file extension?
I agree the behavior is bad, but I think the solution we want is either
--module commonjs
(issue an error), or--module commonjs
override the module
setting and emit with ESM syntaxIs there any reason you don't want to just use .js as an input file extension?
Yes. Since the package has "type": "commonjs"
it would be wrong for the ESM source-file to have a .js
extension, which could trip up static analysis tools and linters relying on the semantics of these extensions in relation to the package.json's type
.
@andrewbranch If using .mts
instead of .ts
is mainly a way to control the output file extension, I can see why that would make sense and would prefer an error instead of an implicit setting override.
I assume both suggestions would also apply to .mjs
files and I'd expect that to remove the ability to transpile .mjs
to commonjs? I think that's a powerful feature, which would be great the the compiler would continue to support, although admittedly on the edge of use-cases that are expected from a TypeScript compiler.
If the code is intended to always target CJS, it should just be a cts file.
A naive syntactic transform from ESM to CJS is liable to end up importing semantically different modules from when the code was written, causing unpredictable breaks. This isn't something that you can "just" do in the general case.
Tagging as backlog for either of the proposed behaviors, not the behavior described in the OP
Converting .mts
files to anything other than ESM is bad and breaks things. Specifying an .mts
extension should indicate that this files module system is fixed as ESM without exception and regardless of the --module
setting.
This is the correct approach.
.mts files in --module commonjs override the module setting and emit with ESM syntax
Please fix this.
The javascript.mjs is transformed to CommonJS (as expected)
Why would that be expected? If you specify a file as .mjs
then it should always be an ES module. tsc
converting that to CJS is just wrong.
.mts files cannot be loaded into a program with --module commonjs (issue an error)
Yes, a compiler error, not user error.
Any .mts
, or .cts
(same for the JS variants) should not have its module system changed. Ever.
Also, why was this labelled Possible Improvement and not a bug?
The javascript.mjs is transformed to CommonJS (as expected)
Why would that be expected?
Because the tsconfig has module
set to "commonjs"
in the example I provided, I'd expect all files emitted to be using that, regardless of the module system used by the input file.
This is where you and TypeScript differ with the way the Node.js runtime operates against file extensions. Too bad.
Congrats on your newborn.
Bug Report
π Search Terms
CommonJS, allowJS, ESM, CJS, file extensions
π Version & Regression Information
β― Playground Link
Sandbox link with relevant code (I couldn't use the playground, since this involves using an .mjs file).
π» Code
π Actual behavior
The
javascript.mjs
is transformed to CommonJS (as expected), but the file extension of the emitted file is still.mjs
. This breaks the package, since.mjs
is supposed to be use exclusively for JavaScript using ESM and the emitted file now uses CommonJS.π Expected behavior
I would expect
tsc
to transform thesrc/javascript.mjs
to CommonJS and either:dist/javascript.cjs
or alternatively"type": "commonjs"
in the package.json and emit the file as.js
as it does with thesrc/typescript.ts
file.