This lesson preview is part of the Bundling and Automation in Monorepos course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Bundling and Automation in Monorepos, plus 90+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Bundling and Automation in Monorepos
  • [00:00 - 00:10] In our previous lesson, we showed the limitation of directly exporting TypeScript. Not all bundlers are going to pick up changes in related code. There is something else we can do.

    [00:11 - 00:21] However, if we go to our apps/server-express app and look at our package.json. Currently we are using tsup to run our dev server.

    [00:22 - 00:38] However, since Node version 22.6 and 23.6, it is possible to directly run TypeScript in Node.js. Here I have version 22.15.

    [00:39 - 01:10] I can do "Node -- experimental-strip-types --watch src/index.ts", and this will start a watcher directly in Node that can run TypeScript. Indeed, if I open another tab and go to our logger package and open our logging function, I can change it here. Go back and see that Node will correctly restart, and our change is picked up.

    [01:11 - 01:45] So if you're able to run Node 22.6 or later or 23, this is the preferred way that that I would suggest running any TypeScript Node application. However, as you can see, we have a bunch of warnings and the TypeScript we are currently writing is not safe for running in Node. The code that I have in my example repo was accidentally safe, but there is no guarantee unless we change our TSConfig.

    [01:46 - 01:57] So let's start with making sure we have the minimum required version of TypeScript. I'm going to do "pnpm up -r [email protected]".

    [01:58 - 02:13] This will upgrade TypeScript in all our monorepo workspaces to 5.8.3. Then I'm going to go to our config package and edit our tsconfig.base.json.

    [02:14 - 02:40] I'm going to add an option called "erasableSyntaxOnly: true". What this does is it prevents you from using some features of TypeScript that need a more complex compiler, and instead forces you to only use types that can be directly erased from code. One of the examples of a feature that requires more complex compiler is enums.

    [02:41 - 02:53] Enum doesn't exist in JavaScript, so an enum declaration in TypeScript must be compiled into JavaScript. Similarly for namespaces or legacy decorators.

    [02:54 - 03:15] The next thing I'm going to do is I'm going to open on the right side another file, and I'm going to call it tsconfig.node.json, and I'm going to paste in the code that I have prepared ahead of time. This is the configuration that I would suggest be used when you're writing a Node application in a monorepo.

    [03:16 - 03:34] The differences are that "target" is "ESNext", which means being able to use the latest Node features. "moduleResolution" is "nodenext", which means we're using Node 20's ESM resolution, as we discussed in the first lesson of this module.

    [03:35 - 04:04] And "rewriteRelativeImportExtensions" means that we can import files with the ".ts" extension, rather than having to use the kind of confusing ".js" extension which is otherwise required when we use module "nodenext". With these changes, I'm going to go over to our server-express workspace, and I'm going to edit my tsconfig.json to use the Node configuration that we just created.

    [04:05 - 04:16] So now I can run "pnpm test-types" and I expect to see this failing. And it does. To fix this we need to do a couple of things.

    [04:17 - 04:34] I should edit our package.json to have type: module, which I have ahead of time, and for the other error when we are importing code, now we need to add extensions. So index.ts instead of just the relative index import that we had before.

    [04:35 - 04:44] With that change let me check test-types again. It fails because of a problem in the logger package.

    [04:45 - 04:58] So let me go there. In here I also need to add type: module. Go back to the other tab. Run test-types again. And now it passes.

    [04:59 - 05:35] Then I'm going to edit my package.json, and for dev instead of using tsup I'm directly going to use "node --experimental-strip-types --watch src/index.ts". And if I did everything correctly, I should be able to now run our water and it runs. The server runs correctly and if I change something in our logger, it is going to restart and reflect the change.

    [05:36 - 06:10] I'm going to go back to the root of the monorepo, and then I'm going to, just in case, run "pnpm test-types" from the root, make sure that the types are correct after our TypeScript changes I'm then going to "git add --intent-to-add .", and then "git add -p". We can see that the changes are that in all packages we have upgraded TypeScript to 5.8.

    [06:11 - 06:21] We have changed our server-express dev script to use Node directly. Another update of TypeScript.

    [06:22 - 06:56] We have to use extensions in our imports in server-express because it uses our Node TypeScript configuration, and this is the change to use that Node configuration. We have added erasableSyntaxOnly to our base TypeScript configuration, because that's a good setting to have for all applications, regardless if they are Node-specific or web apps. And we have added our TypeScript Node configuration.

    [06:57 - 07:05] We needed to add type: module to our logger. And I'm going to skip this one change because it's not meaningful.

    [07:06 - 07:13] The last thing is just changes to pnpm-lock.yml. With all that in place.

    [07:14 - 07:24] I'm going to commit this as "Use Node to run TypeScript files directly". And all our checks pass.

    [07:25 - 07:49] Now here I'm using Node version 22. If I was using Node 23, I could go into my server-express/package.json and I could delete this experimental strip types option because in Node 23 this is now enabled by default and not behind an experimental flag.

    [07:50 - 08:00] You will notice that I didn't update our build scripts. This is because when we run code on our production server, we still want to run bundled code.

    [08:01 - 08:24] Bundled code is going to be much faster to execute than running with Node's TypeScript parsing, and it won't depend on the environment, while the Node TypeScript parsing does depend on stuff like tsconfig.json. So we run TypeScript directly Node when we develop, but we still do a full build and run that in our production server.

    [08:25 - 08:30] That said, this is all for this lesson and I will see you in the next one.