fabien0102 / ts-to-zod

Generate zod schemas from typescript types/interfaces
MIT License
1.22k stars 67 forks source link

Generated imports fail when running in node and using `type: "module"` in package.json #259

Open t-animal opened 3 months ago

t-animal commented 3 months ago

Bug description

ts-to-zod does not include the filename in such a scenario, making it incompatible with "module"-packages running in node.

When running in node (i.e. not targeting a bundler/webbrowser), the typescript "module"-property must be set to "node16" or "nodenext". When using type:"module" in package.json, this means that imports have to include the ".js" suffix.

A minimal example would be:

//package.json
{
  "name": "test-modules",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "ts-to-zod": "^3.10.0",
    "typescript": "^5.5.4",
    "zod": "^3.23.8"
  }
}

//tsconfig.json
{
  "compilerOptions": {
    "lib": ["ES2020"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

//ts-to-zod.config.cjs
module.exports = [
  { name: "b", input: "b.ts", output: "b.zod.ts" },
  { name: "a", input: "a.ts", output: "a.zod.ts" },
];

//a.ts
import { B } from "./b"; // This should be `import {B} from "./b.js";`

export type A = B | "a";

//b.ts
export type B = "b";

Running tsc gives the following error:

npx tsc
a.ts:3:19 - error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './b.js'?

1 import { B } from "./b"; // This should be `import {B} from "./b.js";`
                    ~~~~~

Found 1 error in a.ts:3

Our current solution is to fix the imports using sed in our build-script:

"build": "node ts-to-zod.config.cjs; npm run patch-zod-import; tsc",
"patch-zod-import": "sed -i 's/\\(^import [^\"]*\"\\.\\/[^\"]*\\).zod\";/\\1.zod.js\";/' src/schemas/*.zod.ts",

Input

Multiple files containing types, referencing each other. E.g:

//a.ts
import { B } from "./b.js";

export type A = B | "a";

//b.ts
export type B = "b";

Expected output

a.zod.ts should be:

// Generated by ts-to-zod
import { z } from "zod";

import { bSchema } from "./b.zod.js";

export const aSchema = z.union([bSchema, z.literal("a")]);

Actual output

// Generated by ts-to-zod
import { z } from "zod";

import { bSchema } from "./b.zod";

export const aSchema = z.union([bSchema, z.literal("a")]);

Versions

ydennisy commented 2 months ago

Hi @tvillaren do you have an ETA for a fix for this?

tvillaren commented 1 month ago

Hello,

Not really. I you have a solution in mind, please feel free to open a PR! 🙏 Thanks!

t-animal commented 1 month ago

Fwiw, I saw that detection for this setting is already in the codebase, somewhere. I can't work on this atm, though....