ts-rest / ts-rest

RPC-like client, contract, and server implementation for a pure REST API
https://ts-rest.com
MIT License
2.11k stars 91 forks source link

Build error with tsup #606

Closed zbeyens closed 1 week ago

zbeyens commented 1 month ago

Describe the bug

When building with tsup, I get this error:

src/plugins/api-contract.ts(5,14): error TS4023: Exported variable 'contract' has or is using name 'tag' from external module "/Users/zbeyens/GitHub/youflix/node_modules/.pnpm/@ts-rest+core@3.45.2_zod@3.23.7/node_modules/@ts-rest/core/src/lib/type-utils" but cannot be named.

How to reproduce

Build with tsup

Expected behavior

No response

Code reproduction

import { initContract } from '@ts-rest/core';

const c = initContract();

export const apiContract = c.router({
  createPost: {
    body: c.type<{ title: string }>(),
    method: 'POST',
    path: '/posts',
    responses: {
      201: c.type<{}>(),
    },
    summary: 'Create a post',
  },
  getPost: {
    method: 'GET',
    path: `/posts/:id`,
    responses: {
      200: c.type<{} | null>(),
    },
    summary: 'Get a post by id',
  },
});

ts-rest version

3.45.2

firxworx commented 1 week ago

It would be helpful to provide your tsup.config.ts and/or the cli tsup command you are using with options rather than writing "build with tsup".

FWIW I am building using esbuild (which tsup uses under the hood) and have not experienced this problem with v3.45.2

zbeyens commented 1 week ago

It happened with this config, exporting the contract in that core package.

firxworx commented 1 week ago

Hi @zbeyens I posted up the code that I am building with esbuild.

Working example with esbuild

The fastify API is what is getting built: https://github.com/firxworx/ts-rest-workspace/blob/main/apps/fastify-api

The API is importing contracts from packages/contracts but that's a TS-only internal packages so the actual compilation of the contract is getting handled by the Fastify API build.

Refer to build.ts: https://github.com/firxworx/ts-rest-workspace/blob/main/apps/fastify-api/build.ts And to the fastify-api's package.json for how it gets called.

At least that can serve as a working reference for you. tsup uses esbuild under-the-hood and you can just use esbuild on its own.

I was able to build of my contracts package with tsup

Incidentally my repo has a tsup configuration in @workspace/contracts (packages/contracts) but it is not used because I opted for the TS-only internal package route. You can go this route too if it works for your project.

In any case I commented out my packages/contracts/tsup.config.ts and replaced it with yours to try it out and see if I could get my contracts package to build with tsup.

I only had a problem building when I didn't set clean: true (this fully deletes the dist folder contents before building).

I realize changing this might impact your development workflow but at least that's something to go on.

Maybe it helps you to know that I could build using tsup with your configuration (minus the clean option)?

We are both running similar versions of everything.

Observations & other options for you

Right now it looks like you're building your package (which comes from orbitkit which you forked) and your package.json exports from the dist/ dir so then anything internal would be consuming the build js version.

Since you're modifying from orbitkit note your tsconfig references a tsbuildinfo file but then its not mentioned anywhere else (maybe you want to drop this entirely)? Orbitkit looks like they actually reference it throughout. I'm not sure if this is related to your error or not.

Another option to consider is maybe you don't want to pre-build your "core" as a standalone package (i.e. export typescript with no build step for internal packages). You can look at my repo as an example for how to modify your package.json and how to import in downstream pacakges/apps since your project is also a pnpm workspace.

In development the watch mode on your project as it stands would trigger tsup every time you changed something in a contract. Overall I think this is a more complicated process as working in your monorepo you'd be triggering watch builds all over the place when changing things. Perhaps this is leading to some gremlins?

Its hard to say because your error relates to a package called "api-contract" and that's presumably in a repo that you do not have public.

I have no reference of any export named "contract" that your error message is referring to so I can't help further.

Conclusion for ts-rest maintainers

I was able to successfully build a package exporting only ts-rest contracts on my end using tsup. I do not think this is a ts-rest issue.

zbeyens commented 1 week ago

I appreciate your detailed answer. I've ended up:

instead of building in watch mode, and that works better as you've mentioned. Thanks!