ricokahler / sanity-codegen

Generate TypeScript types from your Sanity.io schemas
sanity-codegen-dev.vercel.app
MIT License
270 stars 19 forks source link

feat(v1): custom declarations #303

Open ochicf opened 1 year ago

ochicf commented 1 year ago

I've started this branch from https://github.com/ricokahler/sanity-codegen/pull/299, so that should be merged first.


The intent of this PR is to allow to configure declarations in the configuration file so they are included in the generated types. This only covers the possibility of ADDING declarations, not alter the generated ones.

Possible use cases:

The declarations configuration can be:

/**
 * Custom declarations to be added to the generated types
 */
declarations?:
  | (string | t.TSModuleDeclaration)[]
  | ((context: {
      t: typeof t;
      normalizedSchemas: Sanity.SchemaDef.Schema[];
      generateTypeName: GenerateTypesOptions['generateTypeName'];
      generateWorkspaceName: GenerateTypesOptions['generateWorkspaceName'];
      defaultGenerateTypeName: typeof defaultGenerateTypeName;
      getWorkspaceName: (normalizedSchema: Sanity.SchemaDef.Schema) => string;
    }) =>
      | (string | t.TSModuleDeclaration)[]
      | Promise<(string | t.TSModuleDeclaration)[]>);

The above allows to directly pass declarations (as strings or with babel's t helpers for typesafety), or a function that returns the very same. This can be used to add more complex logic to generate types derived from the schemas (see exmaples below).


Example 1:

Add a static type (this would help us solve the https://github.com/ricokahler/sanity-codegen/issues/298 issue):

const config: SanityCodegenConfig = {
  // ... other
  declarations: [
    /* ts */ `
      namespace Sanity {
        type Reference<T> =
          | T
          | {
            _type: "reference";
            _ref: string;
        };
      }
    `,
  ],
};

Example 2:

Generate a new Documents type for each schema, which is a union of all types in that schema:

const config: SanityCodegenConfig = {
  // ... other
  declarations: ({
    t,
    normalizedSchemas,
    getWorkspaceName,
    getTypeName,
  }) =>
    normalizedSchemas.flatMap((normalizedSchema) => [
      t.tsModuleDeclaration(
        t.identifier('Sanity'),
        t.tsModuleDeclaration(
          t.identifier(getWorkspaceName(normalizedSchema)),
          t.tsModuleDeclaration(
            t.identifier('Schema'),
            t.tsModuleBlock([
              t.tsTypeAliasDeclaration(
                t.identifier('Documents'),
                undefined,
                t.tsUnionType(
                  normalizedSchema.documents.map((node) =>
                    t.tsTypeReference(t.identifier(getTypeName(node))),
                  ),
                ),
              ),
            ]),
          ),
        ),
      ),
    ]),
};

That yields something like:

namespace Sanity.Default.Schema {
  type Documents = Book | Editorial;
}

@ricokahler please let me know your thoughts on this, I'm finding it pretty useful!

vercel[bot] commented 1 year ago

@ochicf is attempting to deploy a commit to the Rico Kahler Team on Vercel.

A member of the Team first needs to authorize it.