For running a Node backend I'm looking for recommendations or comparisons about the relative strengths of these two approaches. It seems like tsx might work better for ESM with Node, but I haven't made up my mind in terms of CJS vs ESM for the server (see https://discordapp.com/channels/508357248330760243/1230261217256280185/1230261217256280185)
#tsx vs ts-node
1 messages · Page 1 of 1 (latest)
i don't use either, but from what ive seen it seems like tsx is generally recommended over ts-node
i would personally recommend using esm for new, modern projects
What do you use?
if you don't like using extensions with imports, you can absolutely setup tooling and workflow to support that
i just use tsc+node
Like, two stages?
tsc compiles .ts -> .js
node consumes the .js?
this is the set of scripts i used in a past project
"scripts": {
"start": "node .",
"debug": "node . --debug",
"build": "npm run lint && npm run compile",
"start:build": "npm run build && npm start",
"debug:build": "npm run compile && npm run debug",
"compile": "npm run clean; tsc && tsc-alias",
"clean": "rm -rf build/*",
"check": "npm run lint && npm run typecheck",
"typecheck": "tsc --noEmit",
"lint": "eslint src",
"lint:all": "eslint src --no-inline-config"
},
yes
though, with scripts, so never really separate stages when i do both
I would worry that the errors are more confusing since they're referencing the tsc output rather than the original .ts sources. Is that not an issue for you?
with hindsight, i probably shouldve done tsc before eslint.
my build isn't bundled or minified, so not really
I'm curious about how that would work. Presumably some sort of pre-processing to add the extensions back in since my understanding is that (at least with ESM) node requires extensions on relative imports
Nice. TIL
`!t6:paths-are-not-magic`:
The paths and baseUrl compiler options don't cause any remapping of imports paths, they only inform TS of existing mappings, which you'll have to setup with some other tool.
baseUrl is a pretty well-supported option (e.g. using the NODE_PATH environment variable with node or resolve.modules with webpack).
paths can be trickier to setup, (especially with node see this for node), and you may find it to not be worth the effort.
and there are a few packages that do the remapping of imports thing, like tsc-alias
and they read the paths option so you don't need to configure them
i wouldn't explicitly recommend this workflow, but it is a thing you can do
You don't really get runtime errors if you use TypeScript with strict mode. That's the idea of it
Stopping runtimes errors that could crash your app when deployed
Totally; I'm not lookng to stop the errors. I'm more in a place where errors get surfaced through hot reloads during dev cycles
Does that mean you are not using strict mode, so you still have errors?
I see what you're saying: the errors are going to come from the tsc complication step, which will be referring to the .ts sources, yes?
Yep
Well actually you see the errors in your IDE, when you write them
Because your IDE is running tsc on your code live, and showing the errors
We'll still get runtime errors b/c of bugs in our code though (unchecked null and the like), and those stacktraces will reference the compiled .js sources. That's the complication I was looking to avoid
I've actually never used ts-node or tsx, but if that maps errors to the .ts source I can see the benefit of that, for when runtime errors do occur
Unchecked null is a TS error, so this will appear in your IDE
if you have strict settings on, as I say
Yes, assuming perfect typing coverage. But even then, there can be runtime errors because of exceptions in the external environment of course (HTTP requests fail, etc)
strict mode requires everything to be typed : )
But yeah if you are converting a JS app it can be difficult to do that all in one go
any still exists though, right?
Sure
I mean it shouldn't be used, but as I say its difficult to bring a whole code base up to standard at once
Just so
`You will need a "source map" to map the JavaScript line numbers to TypeScript line numbers.
You can do this with the source-map-support node module. First install it in your project:
npm install source-map-support
Then add this to your TypeScript file (index.ts):
require('source-map-support').install()`
No idea how he is running his TS/JS though
Thanks! I'll check that out
Any thoughts on ESM vs CJS for running TS in Node, @exotic sonnet ?
ESM
CJS files can't import ESM without rigmarole
ESM packages are async, so you have to create an async function and handle importing inside that, in CJS
and then you can't re-export because CJS files are sync, and the ESM import won't be done until later
People have been publishing packages in ESM for a couple of years, with CJS you don't really want to try to load ESM packages, so you would like end up having to avoid using those
In summary, because ESM is the future, so getting on it now is future-proofing against the pain of ESM-only libraries down the road?
But none we depend on b/c our app is currently CJS
Hmm
Any other practical benefits today? I know on the frontend there's benefits to tree shaking and the like
then perhaps you should keep it CJS
going ESM now introduces a big change in the code output
Why would that be a problem?
It wouldn't necessarily
There could be problems if you have more than 1 package, and imports between them, or you publish packages for others
As publishing ESM requires consumers to move to ESM
Nah, we don't publish anything. We're not OSS
I think you said this is server side right?
Yeah. The app has TS (React) for frontend, mobile (React Native) and backend (Node)
You don't have __dirname in ESM, so if you rely on that you need to change it
that's pretty common to see in apps
Really depends on what code you have in your app, and whether you are doing anything that relies on the CJS system
some apps use a lot of require.resolve
which is not available in ESM, unless you use createRequire
We don't use __dirname too much, so converting to import.meta should be ok
Well I think you have to weigh up the gains and costs
If you're not actively developing, I would tend to leave it as is
Definitely. All this is helpful though. I think the argument that we'd be in a better place to use more modern libraries is pretty compelling
If you're going to be spending the next year adding features, or you just have a prototype, then I'd look to make the move
No, it's a mature app in production, but we're very low usage this part of the year, so it's a good time for upgrades
The only awkward thing in ESM is reloading modules. Once you load a file its there forever, and can't be refreshed
It's not really an issue in most apps
with CJS you can clear the module cache
Then how does something like Vite make hot reloading work? 🤔
Node just restarts the server process it seems
I'm not sure
I have a workaround that involves transforming files into files that don't use import statements, and instead load each other with function calls
There is an approach based on adding a query string, like import('./foo.js?123'), but that seems that the mem usage would rise as it kept everything in memory
Well, I think this gives me enough info to talk to the rest of the team about the decision. Off the top of your head, do you know of any particularly compelling libraries that are ESM-only?
I think chalk is now ESM
Widely used to colorise console output
But I suppose chalk v4 can still be used
I don't really know, as I've been using ESM for about 2 years, and don't have to think about it
Thanks for all the input!
BTW, if your team are new to TS, tell everyone to use unknown instead of any. unknown is the correct way to accept any type or type something that you don't know the type of. It's treat as being any type, and you can then safely narrow the type down with checks.
any simply turns off the typechecker, so its very dangerous, those any types can cascade through your app and hide things that should be errors.
Agreed
not really. errors aren't just due to type mismatches.
I guess throws are a thing too. I tend to avoid using them due to the lack of typing.
sometimes can't avoid them in the stdlib or external libs
yep
typed throws are on the bucket list of things to create at some point in my life
have you seen the ts issue for them?
I have probably read it. No plans to add, or am I wrong?
yeah
I did some research and came upon people complaining about checked errors in Java
but it seems that Java mainly suffered from failing to auto infer types, making developers repetitively annotate or rethrow