From 848ce1cbd00bd9f86c65032f9a3cf3951409ed3d Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Sun, 29 Sep 2024 12:55:36 +0200 Subject: [PATCH] docs(TYPEGEN): lessons learned for Sanity TypeGen --- README.md | 4 ++ TYPEGEN.md | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 TYPEGEN.md diff --git a/README.md b/README.md index a00191d92..0c3b16e1e 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,10 @@ Simply use the CSS background property on the root element with a base64-encoded background: url("data:image/png;base64,..."); ``` +### Sanity TypeGen + +[TypeGen for Sanity](https://www.sanity.io/docs/sanity-typegen) was considered for this project in september 2024. At that time, it was not considered a good fit. This was mainly because of the tool being in beta, and concerns regarding ownership and flexibility of type definitions. See [TYPEGEN.md](TYPEGEN.md) for more details on the lessons learned. + ### Troubleshooting - Sanity Preview: While the Sanity preview functionality is not fully optimized, it currently meets the essential requirements. diff --git a/TYPEGEN.md b/TYPEGEN.md new file mode 100644 index 000000000..738bf2084 --- /dev/null +++ b/TYPEGEN.md @@ -0,0 +1,136 @@ +# TypeGen - Lessons Learned + +Collection of findings and "note-to-future-self"s related +to [Sanity TypeGen](https://www.sanity.io/docs/sanity-typegen). + +See [#610](https://github.com/varianter/variant.no/issues/610) for the chronological uncut version. + +> [!NOTE] +> In the examples below, we assume a project with two workspaces: `studio` and `sharedStudio`. This might have changed +> since this document was written, so adjust accordingly. + +## Setup + +### Multiple Workspaces + +The TypeGen setup is slightly complicated by the use of multiple Sanity workspaces. The proposed solution when testing +out TypeGen was to run type generation for each workspace separately, with their own config file and type definition +file. The file structure looked something like this: + +``` +... +studio/ + ... + sanity.types.ts + sanity-typegen.json +studioShared/ + ... + sanity.types.ts + sanity-typegen.json +``` + +### Configuration File (`sanity-typegen.json`) + +The TypeGen configuration file is used to specify the location of source code and schema definition files to generate +types, in addition to defining output paths for schema and type definition files. Each workspace defines its own +configuration, as shown in the below examples. + +`studio/sanity-typegen.json` + +```json +{ + "path": [ + "./src/**/*.{ts,tsx,js,jsx}", + "studio/**/*.{ts,tsx,js,jsx}" + ], + "schema": "studio/schema.json", + "generates": "studio/sanity.types.ts", + "overloadClientMethods": true +} +``` + +`studioShared/sanity-typegen.json` + +```json +{ + "path": [ + "./src/**/*.{ts,tsx,js,jsx}", + "studioShared/**/*.{ts,tsx,js,jsx}" + ], + "schema": "studioShared/schema.json", + "generates": "studioShared/sanity.types.ts", + "overloadClientMethods": true +} +``` + +### Convenience scripts + +The following `package.json` scripts can be defined to perform schema extraction and type generation for multiple +workspaces, all in one go. + +```json +{ + "scripts": { + "typegen-template": "rimraf \"$SCHEMA_PATH/schema.json\" && sanity schema extract --enforce-required-fields --workspace \"$WORKSPACE\" --path \"$SCHEMA_PATH/schema.json\" && sanity typegen generate --config-path \"$SCHEMA_PATH/sanity-typegen.json\" && rimraf \"$SCHEMA_PATH/schema.json\"", + "typegen:studio": "cross-env SCHEMA_PATH=studio WORKSPACE=studio npm run typegen-template", + "typegen:shared": "cross-env SCHEMA_PATH=studioShared WORKSPACE=sharedStudio npm run typegen-template", + "typegen": "npm-run-all --parallel typegen:*" + } +} +``` + +> requires dev dependencies `cross-env`, `rimraf` and `npm-run-all` for cross-platform support + +## Usage + +Use the [official documentation](https://www.sanity.io/docs/sanity-typegen) for general usage of the generated types. + +### Custom types + +One downside of the generated types was that they only defined types for whole documents, with a high degree of nesting. +Un-nesting these type definitions for cases where only a portion of a document was needed, required some highly verbose +TypeScript definitions. To hide this from the rest of the codebase, an approach for defining custom types in addition to +the generated ones was needed. One proposal is shown below. + +`studio/types.ts` + +```typescript +import * as types from './sanity.types'; + +export { types }; + +// "not too bad"-case +export type YearBonuses = NonNullable< + NonNullable[number]['yearlyBonuses'] +>; + +// "way too nested"-case +export type NavigationFooterRichTextLinkChildMark = NonNullable< + NonNullable< + Extract< + NonNullable< + Extract< + NonNullable< + NonNullable< + NonNullable['footer'] + >[number]['linksAndContent'] + >[number], + { _type: 'richTextObject' } + >['richText'] + >[number], + { _type: 'block' } + >['children'] + >[number]['marks'] +>[number]; +``` + +resulting in the following import statement + +```typescript +import { types, NavigationFooterRichTextLinkChildMark } from "studio/types"; + +interface SomeProps { + compensations: types.Compensations; + marks: NavigationFooterRichTextLinkChildMark; +} +``` \ No newline at end of file