Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 56 additions & 38 deletions e2e/esm/index.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,49 +119,67 @@ describe('GraphQL features test', () => {
field4: ({ field: string } | null)[];
}>();
});
it('interface', async () => {
const TypeWithInterfaceField = defineInterfaceTest_TypeWithInterfaceFieldFactory({
defaultFields: {
interface: undefined,
},
});
const ImplementingTypeFactory = defineInterfaceTest_ImplementingTypeFactory({
defaultFields: {
id: 'ImplementingType-0',
field: 'field',
},
});
const typeWithInterfaceField = await TypeWithInterfaceField.build({
interface: await ImplementingTypeFactory.build(),
describe('interface', () => {
it('basic', async () => {
const TypeWithInterfaceField = defineInterfaceTest_TypeWithInterfaceFieldFactory({
defaultFields: {
interface: undefined,
},
});
const ImplementingTypeFactory = defineInterfaceTest_ImplementingTypeFactory({
defaultFields: {
id: 'ImplementingType-0',
field: 'field',
},
});
const typeWithInterfaceField = await TypeWithInterfaceField.build({
interface: await ImplementingTypeFactory.build(),
});
expect(typeWithInterfaceField).toStrictEqual({
interface: {
id: 'ImplementingType-0',
field: 'field',
},
});
expectTypeOf(typeWithInterfaceField).toEqualTypeOf<{ interface: { id: string; field: string } }>();
});
expect(typeWithInterfaceField).toStrictEqual({
interface: {
id: 'ImplementingType-0',
field: 'field',
},
it('the fields of a union field is optional', async () => {
defineInterfaceTest_TypeWithInterfaceFieldFactory({
defaultFields: {
interface: {},
},
});
});
expectTypeOf(typeWithInterfaceField).toEqualTypeOf<{ interface: { id: string; field: string } }>();
});
it('union', async () => {
const TypeFactory = defineUnionTest_TypeFactory({
defaultFields: {
union: undefined,
},
});
const Member1Factory = defineUnionTest_Member1Factory({
defaultFields: {
field1: 'field1',
},
});
const type = await TypeFactory.build({
union: await Member1Factory.build(),
describe('union', () => {
it('basic', async () => {
const TypeFactory = defineUnionTest_TypeFactory({
defaultFields: {
union: undefined,
},
});
const Member1Factory = defineUnionTest_Member1Factory({
defaultFields: {
field1: 'field1',
},
});
const type = await TypeFactory.build({
union: await Member1Factory.build(),
});
expect(type).toStrictEqual({
union: {
field1: 'field1',
},
});
expectTypeOf(type).toEqualTypeOf<{ union: { field1: string } }>();
});
expect(type).toStrictEqual({
union: {
field1: 'field1',
},
it('the fields of a union field is optional', async () => {
defineUnionTest_TypeFactory({
defaultFields: {
union: {},
},
});
});
expectTypeOf(type).toEqualTypeOf<{ union: { field1: string } }>();
});
it('enum', async () => {
const TypeFactory = defineEnumTest_TypeFactory({
Expand Down
4 changes: 3 additions & 1 deletion src/__snapshots__/code-generator.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports[`generateCode > generates code 1`] = `
type FieldsResolver,
defineTypeFactoryInternal,
} from '@mizdra/graphql-codegen-typescript-fabbrica/helper';
import type { Maybe, Book, Author } from './types';
import type { Maybe, Book, Author, Node } from './types';

export * from '@mizdra/graphql-codegen-typescript-fabbrica/helper';
export type OptionalBook = {
Expand Down Expand Up @@ -101,5 +101,7 @@ export function defineAuthorFactory<
return defineAuthorFactoryInternal(options);
}

export type OptionalNode = OptionalBook | OptionalAuthor;

"
`;
24 changes: 21 additions & 3 deletions src/code-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { fakeConfig, oneOf } from './test/util.js';

describe('generateOptionalTypeDefinitionCode', () => {
it('generates description comment', () => {
const typeInfo: TypeInfo = {
const typeInfo1: TypeInfo = {
type: 'object',
name: 'Book',
fields: [
{ name: 'id', typeString: 'string | undefined' },
{ name: 'title', typeString: 'string | undefined', comment: transformComment('The book title') },
],
comment: transformComment('The book'),
};
const actual = generateOptionalTypeDefinitionCode(typeInfo);
expect(actual).toMatchInlineSnapshot(`
expect(generateOptionalTypeDefinitionCode(typeInfo1)).toMatchInlineSnapshot(`
"/** The book */
export type OptionalBook = {
id?: string | undefined;
Expand All @@ -24,6 +24,17 @@ describe('generateOptionalTypeDefinitionCode', () => {
};
"
`);
const typeInfo2: TypeInfo = {
type: 'abstract',
name: 'Node',
possibleTypes: ['Book', 'Author'],
comment: transformComment('The node'),
};
expect(generateOptionalTypeDefinitionCode(typeInfo2)).toMatchInlineSnapshot(`
"/** The node */
export type OptionalNode = OptionalBook | OptionalAuthor;
"
`);
});
});

Expand All @@ -35,6 +46,7 @@ describe('generateCode', () => {
});
const typeInfos: TypeInfo[] = [
{
type: 'object',
name: 'Book',
fields: [
{ name: 'id', typeString: 'string | undefined' },
Expand All @@ -43,13 +55,19 @@ describe('generateCode', () => {
],
},
{
type: 'object',
name: 'Author',
fields: [
{ name: 'id', typeString: 'string | undefined' },
{ name: 'name', typeString: 'string | undefined' },
{ name: 'books', typeString: 'Book[] | undefined' },
],
},
{
type: 'abstract',
name: 'Node',
possibleTypes: ['Book', 'Author'],
},
];
const actual = generateCode(config, typeInfos);
expect(actual).toMatchSnapshot();
Expand Down
43 changes: 27 additions & 16 deletions src/code-generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Config } from './config.js';
import { TypeInfo } from './schema-scanner.js';
import { ObjectTypeInfo, TypeInfo } from './schema-scanner.js';

function generatePreludeCode(config: Config, typeInfos: TypeInfo[]): string {
const joinedTypeNames = typeInfos.map(({ name }) => name).join(', ');
Expand All @@ -19,28 +19,37 @@ export * from '@mizdra/graphql-codegen-typescript-fabbrica/helper';
}

export function generateOptionalTypeDefinitionCode(typeInfo: TypeInfo): string {
const { name, fields } = typeInfo;
const comment = typeInfo.comment ?? '';
const joinedPropDefinitions = fields
.map((field) => {
const comment = field.comment ? ` ${field.comment}` : '';
return `${comment} ${field.name}?: ${field.typeString};`;
})
.join('\n');
return `
if (typeInfo.type === 'object') {
const { name, fields } = typeInfo;
const comment = typeInfo.comment ?? '';
const joinedPropDefinitions = fields
.map((field) => {
const comment = field.comment ? ` ${field.comment}` : '';
return `${comment} ${field.name}?: ${field.typeString};`;
})
.join('\n');
return `
${comment}export type Optional${name} = {
${joinedPropDefinitions}
};
`.trimStart();
} else {
const { name, possibleTypes } = typeInfo;
const comment = typeInfo.comment ?? '';
const joinedPossibleTypes = possibleTypes.map((type) => `Optional${type}`).join(' | ');
return `
${comment}export type Optional${name} = ${joinedPossibleTypes};
`.trimStart();
}
}

function generateFieldNamesDefinitionCode(typeInfo: TypeInfo): string {
function generateFieldNamesDefinitionCode(typeInfo: ObjectTypeInfo): string {
const { name, fields } = typeInfo;
const joinedFieldNames = fields.map((field) => `'${field.name}'`).join(', ');
return `const ${name}FieldNames = [${joinedFieldNames}] as const;\n`;
}

function generateTypeFactoryCode(config: Config, typeInfo: TypeInfo): string {
function generateTypeFactoryCode(config: Config, typeInfo: ObjectTypeInfo): string {
const { name } = typeInfo;
function wrapRequired(str: string) {
return config.nonOptionalDefaultFields ? `Required<${str}>` : str;
Expand Down Expand Up @@ -91,10 +100,12 @@ export function generateCode(config: Config, typeInfos: TypeInfo[]): string {
for (const typeInfo of typeInfos) {
code += generateOptionalTypeDefinitionCode(typeInfo);
code += '\n';
code += generateFieldNamesDefinitionCode(typeInfo);
code += '\n';
code += generateTypeFactoryCode(config, typeInfo);
code += '\n';
if (typeInfo.type === 'object') {
code += generateFieldNamesDefinitionCode(typeInfo);
code += '\n';
code += generateTypeFactoryCode(config, typeInfo);
code += '\n';
}
}
return code;
}
Loading