#tsx vs ts-node

1 messages · Page 1 of 1 (latest)

icy cipher
grave viper
#

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

icy cipher
#

What do you use?

grave viper
#

if you don't like using extensions with imports, you can absolutely setup tooling and workflow to support that

grave viper
icy cipher
#

Like, two stages?
tsc compiles .ts -> .js
node consumes the .js?

grave viper
#

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"
  },
grave viper
#

though, with scripts, so never really separate stages when i do both

icy cipher
#

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?

grave viper
grave viper
icy cipher
grave viper
#

yep

#

that's what the tsc-alias in compile there is doing

icy cipher
#

Nice. TIL

grave viper
#

tsconfig provides a paths option, which:

#

!:paths-%

vital vectorBOT
#
tjjfvi#0
`!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.

grave viper
#

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

exotic sonnet
#

Stopping runtimes errors that could crash your app when deployed

icy cipher
#

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

exotic sonnet
#

Does that mean you are not using strict mode, so you still have errors?

icy cipher
#

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?

exotic sonnet
#

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

icy cipher
#

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

exotic sonnet
#

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

exotic sonnet
#

if you have strict settings on, as I say

icy cipher
#

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)

exotic sonnet
#

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

icy cipher
exotic sonnet
#

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

icy cipher
#

Just so

exotic sonnet
#

`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

icy cipher
#

Thanks! I'll check that out

#

Any thoughts on ESM vs CJS for running TS in Node, @exotic sonnet ?

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

icy cipher
#

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?

exotic sonnet
#

ESM libraries exist now

#

ESM is the present

icy cipher
#

But none we depend on b/c our app is currently CJS

exotic sonnet
#

Hmm

icy cipher
#

Any other practical benefits today? I know on the frontend there's benefits to tree shaking and the like

exotic sonnet
#

then perhaps you should keep it CJS

#

going ESM now introduces a big change in the code output

icy cipher
#

Why would that be a problem?

exotic sonnet
#

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

icy cipher
#

Nah, we don't publish anything. We're not OSS

exotic sonnet
#

I think you said this is server side right?

icy cipher
#

Yeah. The app has TS (React) for frontend, mobile (React Native) and backend (Node)

exotic sonnet
#

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

icy cipher
#

We don't use __dirname too much, so converting to import.meta should be ok

exotic sonnet
#

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

icy cipher
#

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

exotic sonnet
#

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

icy cipher
#

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

exotic sonnet
#

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

icy cipher
#

Then how does something like Vite make hot reloading work? 🤔

#

Node just restarts the server process it seems

exotic sonnet
#

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

icy cipher
#

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?

exotic sonnet
#

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

icy cipher
#

Thanks for all the input!

exotic sonnet
#

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.

icy cipher
#

Agreed

grave viper
exotic sonnet
#

I guess throws are a thing too. I tend to avoid using them due to the lack of typing.

grave viper
#

sometimes can't avoid them in the stdlib or external libs

exotic sonnet
#

yep

#

typed throws are on the bucket list of things to create at some point in my life

grave viper
#

have you seen the ts issue for them?

exotic sonnet
#

I have probably read it. No plans to add, or am I wrong?

grave viper
#

yeah

exotic sonnet
#

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

icy cipher
#

I personally am not a fan of exceptions for error handling ever since I got exposed to the monadic approach in rust

#

But I would still consider typed exception to be a significant upgrade

exotic sonnet
#

I'm happy with either. The thing is, many libs in JS and Node throw, and many of those are externally typed by the definitely-typed project

#

So we really need typed exceptions if we want to type those

#

I guess we can make wrappers that convert the output into Monads