Skip to content

Commit

Permalink
feat(codegen): expose referenced type as hidden symbol
Browse files Browse the repository at this point in the history
  • Loading branch information
sgulseth committed Mar 18, 2024
1 parent 0a18f59 commit 53dde4e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 1 deletion.
@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`generateSchemaTypes can generate well known types 1`] = `"export declare const internalGroqTypeReferenceTo: unique symbol;"`;

exports[`generateSchemaTypes generateTypeNodeTypes should be able to generate types for type nodes: boolean 1`] = `"export type test_2 = boolean;"`;

exports[`generateSchemaTypes generateTypeNodeTypes should be able to generate types for type nodes: null 1`] = `"export type test_5 = null;"`;
Expand Down Expand Up @@ -30,9 +32,11 @@ export type Post = {
author?: {
_ref: string;
_weak?: boolean;
[internalGroqTypeReferenceTo]?: \\"author\\";
} | {
_ref: string;
_weak?: boolean;
[internalGroqTypeReferenceTo]?: \\"ghost\\";
};
slug?: Slug;
excerpt?: string;
Expand All @@ -41,6 +45,7 @@ export type Post = {
asset: {
_ref: string;
_weak?: boolean;
[internalGroqTypeReferenceTo]?: \\"sanity.imageAsset\\";
};
caption?: string;
attribution?: string;
Expand Down
Expand Up @@ -24,6 +24,12 @@ describe('generateSchemaTypes', () => {
expect(typeDeclarations).toMatchSnapshot()
})

test('can generate well known types', async () => {
const typeDeclarations = TypeGenerator.generateKnownTypes()

expect(typeDeclarations).toMatchSnapshot()
})

test('should generate correct types for document schema with string fields', () => {
const schema: SchemaType = [
{
Expand Down Expand Up @@ -247,6 +253,7 @@ describe('generateSchemaTypes', () => {
_ref: string;
_type: \\"reference\\";
_weak?: boolean;
[internalGroqTypeReferenceTo]?: \\"author\\";
};
};
Expand Down
23 changes: 23 additions & 0 deletions packages/@sanity/codegen/src/typescript/typeGenerator.ts
Expand Up @@ -11,6 +11,8 @@ import {
type UnionTypeNode,
} from 'groq-js'

const REFERENCE_SYMBOL_NAME = 'internalGroqTypeReferenceTo'

/**
* A class used to generate TypeScript types from a given schema
* @internal
Expand Down Expand Up @@ -71,6 +73,18 @@ export class TypeGenerator {
return new CodeGenerator(t.exportNamedDeclaration(typeAlias)).generate().code
}

static generateKnownTypes(): string {
const typeOperator = t.tsTypeOperator(t.tsSymbolKeyword())
typeOperator.operator = 'unique'

const identifier = t.identifier(REFERENCE_SYMBOL_NAME)
identifier.typeAnnotation = t.tsTypeAnnotation(typeOperator)

const decleration = t.variableDeclaration('const', [t.variableDeclarator(identifier)])
decleration.declare = true
return new CodeGenerator(t.exportNamedDeclaration(decleration)).generate().code
}

/**
* Since we are sanitizing identifiers we migt end up with collisions. Ie there might be a type mux.video and muxVideo, both these
* types would be sanityized into MuxVideo. To avoid this we keep track of the generated type names and add a index to the name.
Expand Down Expand Up @@ -204,6 +218,15 @@ export class TypeGenerator {
}
}
}
if (typeNode.dereferencesTo !== undefined) {
const derefType = t.tsPropertySignature(
t.identifier(REFERENCE_SYMBOL_NAME),
t.tsTypeAnnotation(t.tsLiteralType(t.stringLiteral(typeNode.dereferencesTo))),
)
derefType.computed = true
derefType.optional = true
props.push(derefType)
}
return t.tsTypeLiteral(props)
}

Expand Down
Expand Up @@ -58,7 +58,10 @@ async function main() {
const schema = await readSchema(opts.schemaPath)

const typeGenerator = new TypeGenerator(schema)
const schemaTypes = typeGenerator.generateSchemaTypes()
const schemaTypes = [
typeGenerator.generateSchemaTypes(),
TypeGenerator.generateKnownTypes(),
].join('\n')
const resolver = getResolver()

parentPort?.postMessage({
Expand Down

0 comments on commit 53dde4e

Please sign in to comment.