v4.0.0
New Features
- Added first-class support for
Js.Json.tparsing and serializing
Now it's the default mode for better compatibility with existing tools:
| Input type | Output type | V4 | V3 |
|---|---|---|---|
| Js.Json.t | 'value | parseWith | - |
| 'any | 'value | parseAnyWith | parseWith |
| string | 'value | parseJsonWith | - |
| Js.Json.t | 'value | parseOrRaiseWith | - |
| 'any | 'value | parseAnyOrRaiseWith | parseOrRaiseWith |
| Js.Json.t | 'value | parseAsyncWith | - |
| 'any | 'value | parseAnyAsyncWith | parseAsyncWith |
| Js.Json.t | 'value | parseAsyncInStepsWith | - |
| 'any | 'value | parseAnyAsyncInStepsWith | parseAsyncInStepsWith |
| 'value | Js.Json.t | serializeWith | - |
| 'value | unknown | serializeToUnknownWith | serializeWith |
| 'value | string | serializeToJsonWith | - |
| 'value | Js.Json.t | serializeOrRaiseWith | - |
| 'value | unknown | serializeToUnknownOrRaiseWith | serializeOrRaiseWith |
-
Added new error code
InvalidJsonStruct
It is returned when callingS.serializeWithorS.serializeOrRaiseWithwith a struct incompatible with JSON. -
Added
S.describeandS.description
This can be useful for documenting a field, for example, in a JSON Schema using a library like rescript-json-schema. -
Added
S.unit, which is an alias forS.literal(EmptyOption) -
Added experimental
S.inline
It returns a string with ReScript code featuring how to create provided struct. The idea is to use it for code generation. -
Started embedding meta-information about built-in refinements
You can get it usingS.String.refinements/S.Int.refinements/S.Float.refinements/S.Array.refinements. It will be useful for tooling that convertsrescript-structto something else like rescript-json-schema. -
Added ability to pass
asyncParserargument to theS.refine/S.transform/S.custom
Improvements
-
Made the
S.objectimplementation more reliable
The order ofS.fieldcalls doesn't matter anymore. -
The
S.discriminantbecame redundant and was removed
UseS.fieldwithignoreinstead. -
Made the error's path an opaque type and moved it to
S.Path.t
You can find many utility functions to work with a path in theS.Pathmodule. -
Updated namespace name from
ReScriptStructtoRescriptStruct
Discussion: https://forum.rescript-lang.org/t/rescript-vs-rescript-for-the-lib-module-name/4317. -
Renamed
S.deprecatedtoS.deprecate
For a more consistent Zod-like API. -
Renamed
S.defaultedtoS.default
For a more consistent Zod-like API. -
Renamed
S.String.trimmedtoS.String.trim
For a more consistent Zod-like API. -
Renamed
S.Error.raisetoS.failand added the ability to pass a custom path -
Renamed
S.Error.raiseCustomtoS.advancedFail -
Removed ability to call
S.deprecatewithout a deprecation message -
Updated
S.defaultto accept a function for getting a default value
The function runs every time a default value is needed. Before, you had to pass a value that was allocated even before the struct was created. -
Updated
S.custom's argument types to better align withS.refineandS.transformfunctions
No more labeled arguments for theparserandserializer. -
Removed
S.asyncRefinein favor ofS.refinewithasyncParser -
Changed
S.Defaulted.classifytoS.Default.classifywith updated return value -
Changed
S.Deprecated.classifytoS.deprecationwith updated return value -
Removed unused
fieldtype.
Bug fixes
- Fixed
S.tuple5-S.tuple10types.
Semi-automated migration
The release contains a lot of clean up with API breaking change, so I've prepared a script you can run with comby.dev that will do parts of the migration for you automatically.
-
Create
migration.tomlin your project root -
Copy the following content to the
migration.toml:
[parse-with-to-parse-any-with]
match="S.parseWith"
rewrite="S.parseAnyWith"
[parse-or-raise-with-to-parse-any-or-raise-with]
match="S.parseOrRaiseWith"
rewrite="S.parseAnyOrRaiseWith"
[parse-async-with-to-parse-any-async-with]
match="S.parseAsyncWith"
rewrite="S.parseAnyAsyncWith"
[parse-async-in-steps-with-to-parse-any-async-in-steps-with]
match="S.parseAsyncInStepsWith"
rewrite="S.parseAnyAsyncInStepsWith"
[serialize-with-to-serialize-to-unknown-with]
match="S.serializeWith"
rewrite="S.serializeToUnknownWith"
[serialize-or-raise-with-to-serialize-to-unknown-or-raise-with]
match="S.serializeOrRaiseWith"
rewrite="S.serializeToUnknownOrRaiseWith"
[use-unit]
match="S.literal(EmptyOption)"
rewrite="S.unit()"
[discriminant-to-field]
match="S.discriminant(:[fieldName], :[struct])"
rewrite="S.field(:[fieldName], :[struct])->ignore"
[namespace]
match="ReScriptStruct"
rewrite="RescriptStruct"
[deprecated-with-message-a]
match=":->S.deprecated(~message=:[message], ())"
rewrite="->S.deprecate(:[message])"
[deprecated-with-message-b]
match="S.deprecated(:[struct], ~message=:[message], ())"
rewrite=":[struct]->S.deprecate(:[message])"
[deprecated-without-message]
match="->S.deprecated()"
rewrite="->S.deprecate(\"Deprecated\")"
[trimmed]
match="S.String.trimmed"
rewrite="S.String.trim"
[defaulted-a]
match="->S.defaulted(:[value])"
rewrite="->S.default(() => :[value])"
[defaulted-b]
match="S.defaulted(:[struct], :[value])"
rewrite=":[struct]->S.default(() => :[value])"
[error-raise]
match="S.Error.raise"
rewrite="S.fail"
[error-raise-custom]
match="S.Error.raiseCustom"
rewrite="S.advancedFail"
[async-refine]
match="S.asyncRefine(~parser=:[parser], ())"
rewrite="S.refine(~asyncParser=:[parser], ())"- Run the script in your project root. Assumes
migration.tomlhas been copied in place to your project root.
comby -config migration.toml -f .res -matcher .re -exclude-dir node_modules,__generated__ -iThe migration script is a set of instructions that Comby runs in sequence. You're encouraged to take migration.toml and tweak it so it fits your needs. Comby is powerful. It can do interactive rewriting and numerous other useful stuff. Check it out, but please note it's not intended to cover all of the migration necessary. You'll still likely need to do a few manual fixes after running the migration scripts.