When exporting TypeScript breaks (and how to fix it)
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.
Get unlimited access to Bundling and Automation in Monorepos, plus 90+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:18] In the previous lesson, we had luck that our consumers, which were Next.js and Vite, can handle our code without any changes to the configuration. However, that's not always the case. I have prepared another shared package under packages/logger.
[00:19 - 00:52] It again contains only three files package.json, TSConfig.json and an index.ts. Let's look at them. So we have a TSConfig that just extends from the base without any changes. Our dependencies are just on TypeScript and on our config, and in our src/index.ts file, we have a logger function that writes "Logging from Monorepo logger" and then writes the message.
[00:53 - 01:02] We're going to use this in our server application. So let's go there apps/server-express.
[01:03 - 01:25] And to remember how this application is set up it actually does building and development with "tsup" up as its bundler. This means that unlike Next or Vite, we're completely in control of what the bundler is doing. And as we're going to find out, it's not going to work out of the box.
[01:26 - 01:40] I'm going to add our @monorepo/logger package, and I'm going to open src/index.ts. And I'm going to change this console.log() to be using the logger.
[01:41 - 01:59] So "import { logger } from '@monorepo/logger'" and then use it here. Close this. Then we're going to try to run "pnpm dev" and it fails.
[02:00 - 02:29] It fails because this bundler does not automatically try to parse external modules. It finds the TypeScript notation, in the case in our logger function, we have the colon string argument type, and it breaks because it cannot parse the colon. To make this work, we need to tell our bundler that it needs to treat the logger package as an internal package to be bundled.
[02:30 - 02:53] For tsup, to do that, I will open tsup.config.ts and import defineConfig from tsup. And then export default defineConfig that takes an object.
[02:54 - 03:23] And in here I'm going to say "noExternal" takes an array, and we tell it that our "@monorepo/logger" package is not an external package, it should be bundled. With this changing configuration I can go back and do pnpm dev. And now we see that it is actually using our logger and it is running the server.
[03:24 - 03:43] If I open another tab and edit my index.ts file. To have this edit in here, it will correctly restart and show the change. However, let me remove this if I go to.
[03:44 - 03:57] If I go to my logger itself and change this. The server will not restart.
[03:58 - 04:20] This is because tsup will only watch the files in the current package, and will not cross a module boundary to add watchers. Unfortunately, this means that when we make changes to our shared code with this bundler, we must then manually restart our dev process for those changes to be picked up.
[04:21 - 04:40] Rerunning again, we see that it does pick up the changed logger that we changed in the shared package. This is the big trade off with exporting TypeScript directly. You're then beholden to what your bundler can do or cannot do.
[04:41 - 05:15] Depending on the bundler of choice, this might not work at all, although most bundlers can have extra packages added to their compile list just the way that we did here. Whether they're able to watch the files in those packages is a separate question. Still, sometimes this is acceptable behavior. For now, I'm going to add all my changes, which are adding the logger package to our server, using it in our server, and then a package lock change.
[05:16 - 05:44] Actually I missed our tsup.config.ts file, so let me just add this "git add --intend-to -add ." "git add -p ." This is the change to our bundler config that we needed to make in order to have this code work, without bundling it on the side of the logger itself. I'm going to commit this as "Add shared @onorepo/logger package". That's everything. And see you in the next lesson.