New Library: rescript-derive-builder - Automatic Builder Pattern Generation

I’ve been working on a new library that automatically generates fluent builder patterns for ReScript types, and I’d love to get some feedback from the community before continuing development.

:rocket: What it does

rescript-derive-builder scans your ReScript codebase and generates type-safe builder patterns for types annotated with @@deriving(builder). Here’s a quick example:

/**
 * @@deriving(builder)
 * User profile with optional fields
 */
type t = {
  name: string,
  age: int,
  email?: string,  // Optional field support!
}

Generates:

let user = UserBuilder.empty()
  ->UserBuilder.name("Alice")
  ->UserBuilder.age(25)
  ->UserBuilder.email(Some("alice@example.com"))
  ->UserBuilder.build()
  
// Result: result<User.t, string> with specific error messages

GitHub: GitHub - nathan-tranquilla/rescript-derive-builder
NPM: https://www.npmjs.com/package/rescript-derive-builder

:dart: Design Decisions I’d Love Feedback On

1. Using rescript-tools doc for Type Extraction

Instead of using PPX, I chose to use rescript-tools doc to extract type information:

Pros:

  • :white_check_mark: Leverages the official ReScript toolchain
  • :white_check_mark: Gets accurate type information (handles all ReScript syntax)
  • :white_check_mark: JSON output is stable and well-structured
  • :white_check_mark: Future-proof against ReScript syntax changes
  • :white_check_mark: Allows me to write codegen in ReScript!

Cons:

  • :x: Requires an extra compilation step

Question: Does this approach feel right to the community? Are there concerns about the approach?

2. Docstring Annotations + JSON Config

I went with a hybrid approach:

  • Docstring annotations (@@deriving(builder)) mark which types to process
  • JSON configuration within the rescript.json file specify file patterns and output directories
{
  "derive-builder": {
    "include": ["src/**/*.res"],
    "output": "src/generated" 
  }
}

Rationale:

  • Docstring annotations keep the intent close to the type definition
  • JSON config handles project-level concerns (file patterns, output paths)

Question: How does this feel compared to any alternative approaches like a dedicated config file for the builder?

3. Strategy Pattern Architecture

The library uses a chain-of-responsibility pattern for code generation:

module type HandlerInterface = {
  let canHandle: JSON.t => bool
  let handle: JSON.t => string
}

This makes it easy to add support for different type patterns by implementing new handlers.

:construction: Current Limitations (Intentional)

Right now, the library only supports the main type pattern (single type t within a file):

// ✅ Supported
type t = { name: string, age: int }

// ❌ Multiple non-t types not yet supported  
type user = { name: string, age: int }
...

Why start here?

  • Most common ReScript pattern (module-scoped .t types)
  • Validates the architecture before expanding
  • Keeps initial scope manageable

:bulb: Key Features

  • Optional field support with ReScript’s field?: type syntax
  • Comprehensive error messages for missing required fields
  • Type-safe builders with proper ReScript typing
  • Automatic discovery of configuration files (looks for the first rescript.json file in parent having the “derive-builder” configuration key)
  • Extensible architecture for future type patterns

:thinking: Questions for the Community

  1. Architecture: Does the rescript-tools doc + docstring approach feel idiomatic?
  2. API Design: How does the generated builder API feel? Any ergonomic issues?
  3. Limitations: What type patterns should I prioritize next?
  4. Performance: Any concerns about the build-time code generation step?
  5. Integration: How would this fit into your existing ReScript workflows?

:chart_with_upwards_trend: Next Steps

Next steps would involve expanding type generation for other type patterns


Try it out:

npm install rescript-derive-builder
npx rescript-derive-builder

I’d really appreciate any thoughts, concerns, or suggestions! This is my first major ReScript library, so community input is invaluable for making sure I’m heading in the right direction.

5 Likes

Side note, but something supporting this type of workflow more first class (generate code from ReScript source) is in the cards for v12+ and something we care about getting right.

We’ll talk more about this in a little while, but in a nutshell we want to make it easy to hook into the build system and generate ReScript code from ReScript sources, in a first class way. No need to orchestrate a watcher etc yourself. Kind of like a PPX but without rewriting the source. More about this in a while!

2 Likes

This is great to hear, keep me informed, and if I can participate, let me know.