Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typed document node builder sdk #7383

Closed
wants to merge 17 commits into from
Closed
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"postinstall": "patch-package && husky install",
"clean": "rimraf node_modules packages/{*,plugins/*/*,presets/*,utils/*}/node_modules",
"prebuild": "rimraf packages/{*,plugins/*/*,presets/*,utils/*}/dist",
"build": "tsc --project tsconfig.json && bob build",
"build": "yarn workspace @graphql-codegen/typed-document-sdk run build && tsc --project tsconfig.json && bob build",
"watch-build": "npx tsc-watch --project tsconfig.json --onSuccess \"bob build\"",
"test": "jest --forceExit --no-watchman",
"lint": "eslint --ext .ts .",
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/typescript/typed-document-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/sdk-static.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @graphql-codegen/typed-document-sdk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../../../../jest.project')({ dirname: __dirname });
48 changes: 48 additions & 0 deletions packages/plugins/typescript/typed-document-sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@graphql-codegen/typed-document-sdk",
"version": "0.0.0",
"description": "GraphQL Code Generator plugin for generating a TypedDocumentNode builder SDK.",
"repository": {
"type": "git",
"url": "https://github.com/dotansimha/graphql-code-generator.git",
"directory": "packages/plugins/typescript/typed-document-sdk"
},
"license": "MIT",
"scripts": {
"lint": "eslint **/*.ts",
"test": "jest --no-watchman --config ../../../../jest.config.js",
"prepack": "bob prepack",
"build": "node scripts/write-sdk.js"
},
"peerDependencies": {
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
},
"dependencies": {
"@graphql-codegen/plugin-helpers": "^2.3.2",
"common-tags": "^1.8.0"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"exports": {
"./package.json": "./package.json",
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./*": {
"require": "./dist/*.js",
"import": "./dist/*.mjs"
}
},
"typings": "dist/index.d.ts",
"typescript": {
"definition": "dist/index.d.ts"
},
"buildOptions": {
"input": "./src/index.ts"
},
"publishConfig": {
"directory": "dist",
"access": "public"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const fs = require('fs');
const path = require('path');

const sdkBasePath = path.resolve(__dirname, '..', 'src', 'sdk-base.ts');
const sdkOutPath = path.resolve(__dirname, '..', 'src', 'sdk-static.ts');

const sdkBaseContents = fs.readFileSync(sdkBasePath, 'utf-8');

const escape = str => str.replace(/`/g, `\``);

const [imports, body] = sdkBaseContents.split('// IMPORTS END');

if (body === undefined) {
throw new Error(`Missing '// IMPORTS END' that splits the body from the head within '${sdkBasePath}'.`);
}

fs.writeFileSync(
sdkOutPath,
`
export const importsString = ${JSON.stringify(imports)};
export const contentsString = ${JSON.stringify(body)};
`
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { GraphQLBoolean, GraphQLInterfaceType, GraphQLObjectType, GraphQLSchema } from 'graphql';
import { buildInterfaceArgumentString } from './buildInterfaceArgumentString';

describe('buildInterfaceArgumentString', () => {
it('single interface member', () => {
const interfaceType = new GraphQLInterfaceType({
name: 'Foo',
fields: {
a: {
type: GraphQLBoolean,
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
a: {
type: interfaceType,
},
},
}),
types: [
new GraphQLObjectType({ name: 'Hee', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
],
});

expect(buildInterfaceArgumentString(schema, interfaceType)).toMatchInlineSnapshot(`
"type GeneratedSDKArgumentsFoo = SDKSelectionSet<{
\\"...Hee\\": GeneratedSDKArgumentsHee;
}>;"
`);
});
it('multiple interface member', () => {
const interfaceType = new GraphQLInterfaceType({
name: 'Foo',
fields: {
a: {
type: GraphQLBoolean,
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
a: {
type: interfaceType,
},
},
}),
types: [
new GraphQLObjectType({ name: 'Hee', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
new GraphQLObjectType({ name: 'Hoo', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
],
});

expect(buildInterfaceArgumentString(schema, interfaceType)).toMatchInlineSnapshot(`
"type GeneratedSDKArgumentsFoo = SDKSelectionSet<{
\\"...Hee\\": GeneratedSDKArgumentsHee;
\\"...Hoo\\": GeneratedSDKArgumentsHoo;
}>;"
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { stripIndent } from 'common-tags';
import type { GraphQLInterfaceType, GraphQLSchema } from 'graphql';
import { buildObjectArgumentsName } from './buildObjectTypeArgumentString';

export const buildInterfaceArgumentString = (schema: GraphQLSchema, ttype: GraphQLInterfaceType) => {
const implementedTypes = schema
.getImplementations(ttype)
.objects.map(ttype => `"...${ttype.name}": ${buildObjectArgumentsName(ttype.name)};`);

return stripIndent`
type ${buildObjectArgumentsName(ttype.name)} = SDKSelectionSet<{
${implementedTypes.join(`\n `)}
}>;
`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { GraphQLBoolean, GraphQLInterfaceType, GraphQLObjectType, GraphQLSchema } from 'graphql';
import { buildInterfaceSelectionString } from './buildInterfaceSelectionString';

describe('buildInterfaceSelectionString', () => {
it('correct selection set for single interface member', () => {
const interfaceType = new GraphQLInterfaceType({
name: 'Foo',
fields: {
a: {
type: GraphQLBoolean,
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
a: {
type: interfaceType,
},
},
}),
types: [
new GraphQLObjectType({ name: 'Hee', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
],
});

expect(buildInterfaceSelectionString(schema, interfaceType)).toMatchInlineSnapshot(`
"type GeneratedSDKSelectionSetFoo = SDKSelectionSet<{
\\"...Hee\\": GeneratedSDKSelectionSetHee;
}>;"
`);
});
it('correct selection set for multi interface member', () => {
const interfaceType = new GraphQLInterfaceType({
name: 'Foo',
fields: {
a: {
type: GraphQLBoolean,
},
},
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
a: {
type: interfaceType,
},
},
}),
types: [
new GraphQLObjectType({ name: 'Hee', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
new GraphQLObjectType({ name: 'Hoo', fields: { a: { type: GraphQLBoolean } }, interfaces: [interfaceType] }),
],
});

expect(buildInterfaceSelectionString(schema, interfaceType)).toMatchInlineSnapshot(`
"type GeneratedSDKSelectionSetFoo = SDKSelectionSet<{
\\"...Hee\\": GeneratedSDKSelectionSetHee;
\\"...Hoo\\": GeneratedSDKSelectionSetHoo;
}>;"
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { stripIndent } from 'common-tags';
import type { GraphQLInterfaceType, GraphQLSchema } from 'graphql';
import { buildSelectionSetName } from './buildObjectTypeSelectionString';

export const buildInterfaceSelectionString = (schema: GraphQLSchema, ttype: GraphQLInterfaceType) => {
const implementedTypes = schema
.getImplementations(ttype)
.objects.map(ttype => `"...${ttype.name}": ${buildSelectionSetName(ttype.name)};`);

return stripIndent`
type ${buildSelectionSetName(ttype.name)} = SDKSelectionSet<{
${implementedTypes.join(`\n `)}
}>;
`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { GraphQLInt, GraphQLObjectType } from 'graphql';
import { buildObjectTypeArgumentString } from './buildObjectTypeArgumentString';

describe('buildObjectTypeArgumentString', () => {
it('primitive field', () => {
const graphQLObjectType = new GraphQLObjectType({
name: 'Hello',
fields: {
a: {
type: GraphQLInt,
},
},
});

expect(buildObjectTypeArgumentString(graphQLObjectType)).toMatchInlineSnapshot(`
"type GeneratedSDKArgumentsHello = {
a: {};
};"
`);
});
it('object field', () => {
const graphQLObjectType = new GraphQLObjectType({
name: 'Hello',
fields: () => ({
a: {
type: graphQLObjectType,
},
}),
});

expect(buildObjectTypeArgumentString(graphQLObjectType)).toMatchInlineSnapshot(`
"type GeneratedSDKArgumentsHello = {
a: GeneratedSDKArgumentsHello;
};"
`);
});
it('primitive field with args.', () => {
const graphQLObjectType = new GraphQLObjectType({
name: 'Hello',
fields: () => ({
a: {
type: GraphQLInt,
args: {
arg: {
type: GraphQLInt,
},
},
},
}),
});

expect(buildObjectTypeArgumentString(graphQLObjectType)).toMatchInlineSnapshot(`
"type GeneratedSDKArgumentsHello = {
a: {} & {
[SDKFieldArgumentSymbol]: {
arg: \\"Int\\";
}
};
};"
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { stripIndent } from 'common-tags';
import { GraphQLObjectType, GraphQLField, getNamedType, isScalarType, isEnumType } from 'graphql';

export const buildObjectArgumentsName = (name: string) => `GeneratedSDKArguments${name}`;

const buildFieldArgumentsString = (field: GraphQLField<any, any>): string => {
const resultType = getNamedType(field.type);

let fieldPartial =
isScalarType(resultType) || isEnumType(resultType) ? '{}' : buildObjectArgumentsName(resultType.name);

if (field.args.length) {
const argumentPartials: Array<string> = [];

for (const arg of field.args) {
argumentPartials.push(`${arg.name}: "${arg.type.toString()}"`);
}

fieldPartial =
fieldPartial +
' & ' +
stripIndent`
{
[SDKFieldArgumentSymbol]: {
${argumentPartials.join(`;\n `)};
}
}
`
.split(`\n`)
.map((line, i) => (i === 0 ? line : ` ${line}`))
.join(`\n`);
}

return `${field.name}: ${fieldPartial};`;
};

export const buildObjectTypeArgumentString = (objectType: GraphQLObjectType) => {
const fields: Array<string> = [];
for (const field of Object.values(objectType.getFields())) {
fields.push(buildFieldArgumentsString(field));
}
return stripIndent`
type ${buildObjectArgumentsName(objectType.name)} = {
${fields.join(`\n `)}
};
`;
};
Loading