Published: August 24, 2020
2
0
3

just spent a day trying and failing to share an enum between my client and server (both TypeScript) using Yarn Workspaces 1/n

the process involved fiddly modifications to 6 separate config files: a root package.json, a root tsconfig, and a package.json + tsconfig for both client and server "peak complexity" is right https://x.com/dan_abramov/stat...

for illustration purposes lets say you're trying to share code in your `server` directory with your `client`

Image in tweet by colinhacks/zod

the biggest gotcha (that no one seems to mention...) is that Yarn Workspaces basically doesn't work out of the box for TypeScript

for the uninitiated (read: lucky ones) Yarn Workspaces lets you split up your codebase into local "packages" and trick Yarn into thinking these packages are NPM modules. so you can import/export from them.

this lets you get around things like the create-react-app import restriction https://stackoverflow.com/ques...

because Yarn is treating your local "packages" like NPM modules, its imports from your build folder. (this is how TypeScript npm modules work; even pure TypeScript projects don't send *.ts files to npm. They build their code to *.js and *.d.ts files first

a consequence of this: if you haven't run `yarn build` on your package yet (e.g. if you just set up the project) you'll get the dreated "module not found" error. you will have a hell of a time debugging this because it's a TS-specific problem

another serious consequence of this: you need to re-build your packages every time you update them in order for their exports to be up-to-date (assuming your updates changes the implementation/typings of your exports)

this is extremely obnoxious if you're importing a significant fraction of your server code into your client. I'm importing the type signature of my entire API, because I'm using trpc: #creating-a-client-sdk class="text-blue-500 hover:underline" target="_blank" rel="noopener noreferrer">https://github.com/vriad/trpc#... so I'd have to rebuild every time I make _any_ change.

to get around this, you have to set up Project References, a TypeScript feature that lets you tell the TypeScript engine about dependencies between separate projects (each of which has its own tsconfig.json). https://www.typescriptlang.org...

this is pretty easy, just create a tsconfig.json in the root of your project with this

Image in tweet by colinhacks/zod

then wire up the dependencies between your packages, by also using the "references" property in the tsconfig. in your example, you'd just add this to the tsconfig for `client`:

Image in tweet by colinhacks/zod

with this set up, your import statements in `client` will reflect the current source code of `server`, so you don't have to rebuild every time 🚀

you might still be getting obscure `Module not found` errors, in which case you're probably using one of the many projects/libraries that don't play nice with Yarn Workspaces' practice of "hoisting" all your dependencies to your project root

Prisma is one of these, as I learned the hard way

Image in tweet by colinhacks/zod

So unless you have major space constraints I recommend turning off hoisting by adding this to your root project.json:

Image in tweet by colinhacks/zod

"nohoist" is literally not documented anywhere except in the blog post where it was introduced https://classic.yarnpkg.com/bl...

to really twist the knife, the valid values of the "workspaces" config option is documented with a **flow type** 😑

Image in tweet by colinhacks/zod

i now believe Next.js will dominate the next 5 years of webdev mostly because they cracked client-server codesharing without any of this bullsh*t

@vriad And it's silently not supported in yarn 2. And that's not documented :/

Share this thread

Read on Twitter

View original thread

Navigate thread

1/21