fix: handle z.brand() in zod-to-valibot codemod#1516
Conversation
The codemod did not recognize .brand(), so z.string().brand("X") was
emitted as v.string()("X"), which does not compile. Handle brand as a
validator that maps z.<schema>().brand(name) to
v.pipe(v.<schema>(), v.brand(name)), and add a brand test fixture.
Fixes open-circle#1501
WalkthroughThe pull request adds support for transforming Zod's 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR fixes the @valibot/zod-to-valibot codemod’s handling of Zod’s .brand() so it no longer emits a schema as a callable function (e.g. v.string()("StationNumber")), and instead converts it into a Valibot pipeline action (v.pipe(..., v.brand(...))), matching Valibot’s API.
Changes:
- Added
brandas a recognized Zod validator and mapped it tov.brand(...)in the validator transform switch. - Introduced a new
transformBrandvalidator transform and exported it via the validators barrel files. - Added a
brandtest fixture and included it in the codemod’s test suite.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| codemod/zod-to-valibot/src/transform/schemas-and-links/validators/index.ts | Re-exports the new brand validator transform. |
| codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/index.ts | Adds barrel export for the new brand validator module. |
| codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/brand.ts | Implements transformBrand emitting v.brand(...). |
| codemod/zod-to-valibot/src/transform/schemas-and-links/schemas-and-links.ts | Wires brand into toValibotActionExp via transformBrand. |
| codemod/zod-to-valibot/src/transform/schemas-and-links/constants.ts | Adds brand to ZOD_VALIDATORS so it’s recognized in chains. |
| codemod/zod-to-valibot/src/test-setup.test.ts | Includes the new brand fixture in the codemod tests. |
| codemod/zod-to-valibot/testfixtures/brand/input.ts | Fixture input demonstrating .brand() usage (including chaining with .min()). |
| codemod/zod-to-valibot/testfixtures/brand/output.ts | Fixture output expecting v.pipe(..., v.brand(...)). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function transformBrand( | ||
| valibotIdentifier: string, | ||
| args: j.CallExpression['arguments'] | ||
| ) { | ||
| return j.callExpression( | ||
| j.memberExpression(j.identifier(valibotIdentifier), j.identifier('brand')), | ||
| args | ||
| ); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f6f154fb83
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ) { | ||
| return j.callExpression( | ||
| j.memberExpression(j.identifier(valibotIdentifier), j.identifier('brand')), | ||
| args |
There was a problem hiding this comment.
Preserve generic-only brand names
When the input uses the supported Zod v3/v4 form where the brand is only in the generic, such as .brand<'UserId'>(), args is empty, so passing args through here emits v.brand(). Valibot's brand API requires a name argument, which leaves the migrated code type-invalid and fails to preserve the nominal brand for this common Zod style; derive the literal from the call's type parameters or avoid transforming zero-argument brands.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/brand.ts (1)
3-11: ⚡ Quick winAdd required JSDoc and pure-factory annotation.
transformBrandis exported incodemod/**and appears pure, so it should include both the required JSDoc and//@NO_SIDE_EFFECTS`` marker.Suggested patch
import j from 'jscodeshift'; +/** + * Transforms Zod `.brand(...)` to a Valibot `v.brand(...)` action call. + */ +// `@__NO_SIDE_EFFECTS__` export function transformBrand( valibotIdentifier: string, args: j.CallExpression['arguments'] ) {As per coding guidelines,
{library,packages,codemod}/**/*.{ts,tsx,js,jsx}: JSDoc required on exported functions (first overload only for overload sets)andAdd //@NO_SIDE_EFFECTScomment before pure factory functions for tree-shaking optimization.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/brand.ts` around lines 3 - 11, The exported function transformBrand is missing required documentation and tree-shaking optimization markers. Add a JSDoc comment block above the transformBrand function that describes what it does, and add a // `@__NO_SIDE_EFFECTS__` comment immediately before the function declaration to mark it as a pure factory function for tree-shaking optimization, as required by the coding guidelines for exported functions in the codemod directory.Source: Coding guidelines
codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/index.ts (1)
1-1: ⚡ Quick winUse
.tsextension in the local re-export path.Update the re-export path to satisfy the ESM import rule enforced by ESLint.
Suggested patch
-export * from './brand'; +export * from './brand.ts';As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Use ESM with .ts extensions in imports (enforced by ESLint).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/index.ts` at line 1, In the export statement, update the re-export path from './brand' to './brand.ts' to include the .ts extension. This satisfies the ESM import rule enforced by ESLint which requires explicit file extensions in import paths for better compatibility with ES modules.Source: Coding guidelines
codemod/zod-to-valibot/src/transform/schemas-and-links/validators/index.ts (1)
2-2: ⚡ Quick winAlign the new validator re-export with
.tsextension imports.This new local re-export should use a
.tsextension to comply with the project’s ESM import rule.Suggested patch
-export * from './brand'; +export * from './brand.ts';As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Use ESM with .ts extensions in imports (enforced by ESLint).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/index.ts` at line 2, The re-export statement for the brand validator is missing the `.ts` file extension, which violates the project's ESM import rule that requires explicit `.ts` extensions. Update the export statement to include the `.ts` extension when re-exporting from the './brand' module to comply with the ESLint coding guidelines.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/brand.ts`:
- Around line 3-11: The exported function transformBrand is missing required
documentation and tree-shaking optimization markers. Add a JSDoc comment block
above the transformBrand function that describes what it does, and add a //
`@__NO_SIDE_EFFECTS__` comment immediately before the function declaration to mark
it as a pure factory function for tree-shaking optimization, as required by the
coding guidelines for exported functions in the codemod directory.
In
`@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/index.ts`:
- Line 1: In the export statement, update the re-export path from './brand' to
'./brand.ts' to include the .ts extension. This satisfies the ESM import rule
enforced by ESLint which requires explicit file extensions in import paths for
better compatibility with ES modules.
In `@codemod/zod-to-valibot/src/transform/schemas-and-links/validators/index.ts`:
- Line 2: The re-export statement for the brand validator is missing the `.ts`
file extension, which violates the project's ESM import rule that requires
explicit `.ts` extensions. Update the export statement to include the `.ts`
extension when re-exporting from the './brand' module to comply with the ESLint
coding guidelines.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7d1668ce-8ea1-4aec-afe9-adabca03ca05
📒 Files selected for processing (8)
codemod/zod-to-valibot/__testfixtures__/brand/input.tscodemod/zod-to-valibot/__testfixtures__/brand/output.tscodemod/zod-to-valibot/src/test-setup.test.tscodemod/zod-to-valibot/src/transform/schemas-and-links/constants.tscodemod/zod-to-valibot/src/transform/schemas-and-links/schemas-and-links.tscodemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/brand.tscodemod/zod-to-valibot/src/transform/schemas-and-links/validators/brand/index.tscodemod/zod-to-valibot/src/transform/schemas-and-links/validators/index.ts
Fixes #1501
The zod-to-valibot codemod didn't recognize
.brand(), soz.string().brand("StationNumber")was emitted asv.string()("StationNumber")— a schema called as a function, which doesn't compile.brandis now handled as a validator, mappingz.<schema>().brand(name)tov.pipe(v.<schema>(), v.brand(name)):brandtoZOD_VALIDATORSand atransformBrandthat emitsv.brand(...)brandtest fixture (string and number brands, plus one chained with.min())This covers the runtime-argument form
.brand("X")from the issue. The type-only form.brand<"X">()carries no runtime value and the validator transform only receives runtime arguments, so it's left out of scope here.Tested with
vitest(72 passing, including the newbrandfixture).Summary by CodeRabbit