Closed PabloSzx closed 3 years ago
It's just invalid TS to have require
in a mts file and static import
in a cts file. So, like your last paragraph, there's no reason to configure it imo.
Furthered by the fact that these extensions exist so that your typescript can include
import { foobar } from "./hello.mjs";
// Automatically resolved to hello.mts
It's just invalid TS to have
require
in a mts file and staticimport
in a cts file. So, like your last paragraph, there's no reason to configure it imo.Furthered by the fact that these extensions exist so that your typescript can include
import { foobar } from "./hello.mjs"; // Automatically resolved to hello.mts
nothing you said changes the fact that tsm should have "format": "cjs" for ".cts" and "format": "esm" for ".mts", which is what I mentioned
Actually it does, because the integrity of the file itself is determined by the TS checker. tsm transforms the files based on how it was used, so -r
loads items as CommonJS and --loader
/cli loads it as ESM. So long as the semantics of the files' contents are preserved (they are), then interchanging formats is fine so long as it's consistent.
This is/was a big reason why others like ts-node
and even esm
are still high friction, because they provide a level of interop up until they don't and then you're stuck in a weird spot.
A real-world example of this is the following:
// src/math.mjs
// or src/math.js w/ type: module
export const sum = (a, b) => a + b;
// test/math.ts
import * as assert from 'assert';
import * as math from '../src/math';
assert.equal(math.sum(1, 2), 3);
run via
$ node -r tsm test/math.ts
This would convert the TS file into require
statements, only to load an ESM file that wasn't transformed. Error. Instead, allow the tool to safely translate the contents into their equivalents, as if it were all passing thru a bundler anyway.
The above would work with node --loader tsm test/math.ts
or tsm test/math.ts
but only because tsm is loaded thru ESM usage and converts the (unaffiliated) TS into ESM, which then handles the source ESM natively. However, working 2/3 times is just added friction when there's no reason to be.
Forcing a format here breaks this and really has no benefit.
your example doesn't have anything to do with what I mentioned, what I am suggesting is following the Typescript 4.5 new convention of .mts
is always ESM and .cts
is always CJS, that's it, following this convention allows you to specify what is the expected format of the transpilation, since I will know that a typescript file with .cts will have "require" available, while ".mts" will always be esm, that's it
I understand you, but I don't think you're understanding me.
Forcing a format would break this example. That's because when the src/math.mjs
– or src/math.mts
for that matter – is loaded, your suggestion would always make it result in ESM syntax (because, natively, it should/is). However, if that were to happen, then the node -r tsm test/math.ts
case would have code that looks like this:
// src/math.mjs (converted, forced/remains as ESM)
export const sum = (a, b) => a + b;
// test/math.ts (converted, forced as CJS because of --require hook)
const assert = require('assert');
const math = require('../src/math.mjs');
// ^^ THIS IS STILL ESM -> throws syntax error
Instead, for tsm transpilation, we need to ignore the file's required format and transform it to the usage's desired format. This can only work if the converter itself produces semantically correct format conversion ... and esbuild does.
So instead, when you run node -r tsm test/math.ts
on the above example, you should get this:
// src/math.mjs (converted, forced because of --require hook)
const sum = (a, b) => a + b;
exports.sum = sum;
// test/math.ts (converted, forced because of --require hook)
const assert = require('assert');
const math = require('../src/math.mjs');
// ^^ THIS IS NOW COMMONJS -> success
The semantics are preserved. And the same guarantee happens when you run tsm
directly or use --loader tsm
except everything is coerced into ESM syntax instead.
Shouldn't the config have these defaults to follow the TypeScript logic?
One could also argue that these extensions format shouldn't even be configurable and force these formats, just as .cjs forces commonjs and .mjs forces ecmascript modules