First pass at SDL converter
This commit is contained in:
parent
c744568b52
commit
5008603b66
|
@ -8,7 +8,7 @@ sidebar_label: Library Authors
|
|||
|
||||
If you are a library author building tools for GraphQL, we expose the core types It is recommended that you have `nexus` specified as a peer dependency rather than a direct dependency of your wrapping plugin, so duplicate copies of the library are not installed.
|
||||
|
||||
One example of this pattern is in `nexus-contrib` where functions for creating relay-style connections are constructed:
|
||||
One example of this pattern would be for creating relay-style connections:
|
||||
|
||||
```ts
|
||||
export const UserConnectionTypes = connectionType("User");
|
||||
|
@ -17,21 +17,9 @@ export const UserConnectionTypes = connectionType("User");
|
|||
Where `connectionType` is really just a wrapper creating two `objectTypes`:
|
||||
|
||||
```ts
|
||||
import { Types } from './nexus';
|
||||
import { core } from './nexus';
|
||||
|
||||
const PageInfo = objectType({
|
||||
type: 'PageInfo',
|
||||
definition(t) {
|
||||
t.boolean('hasNextPage')
|
||||
t.boolean('hasPreviousPage')
|
||||
}
|
||||
});
|
||||
|
||||
interface ConnectionTypeConfig {
|
||||
name: NexusGen['objectNames']
|
||||
}
|
||||
|
||||
export function connectionType(config: ConnectionTypeConfig) {
|
||||
export function connectionType(type: core.AllOutputTypes) {
|
||||
const Connection = objectType({
|
||||
name: `${name}Connection`,
|
||||
definition(t) {
|
||||
|
@ -48,6 +36,13 @@ export function connectionType(config: ConnectionTypeConfig) {
|
|||
t.field('node', { type: name });
|
||||
}
|
||||
});
|
||||
const PageInfo = objectType({
|
||||
type: `${type}PageInfo`,
|
||||
definition(t) {
|
||||
t.boolean('hasNextPage')
|
||||
t.boolean('hasPreviousPage')
|
||||
}
|
||||
});
|
||||
return { Connection, Edge, PageInfo }
|
||||
}
|
||||
```
|
||||
|
|
|
@ -3,9 +3,6 @@ import {
|
|||
GraphQLCompositeType,
|
||||
GraphQLInputObjectType,
|
||||
} from "graphql";
|
||||
import { GenTypesShape } from "../core";
|
||||
|
||||
export type MaybeThunk<T> = T | (() => T);
|
||||
|
||||
export type BaseScalars = "String" | "Int" | "Float" | "ID" | "Boolean";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { GetGen, HasGen, GetGen2 } from "../typegenTypeHelpers";
|
||||
import { GetGen, GetGen2 } from "../typegenTypeHelpers";
|
||||
import { AllNexusInputTypeDefs } from "./wrapping";
|
||||
import { NexusTypes, withNexusSymbol } from "./_types";
|
||||
|
||||
|
|
|
@ -6,14 +6,30 @@ import {
|
|||
GraphQLInputObjectType,
|
||||
GraphQLScalarType,
|
||||
GraphQLUnionType,
|
||||
GraphQLField,
|
||||
GraphQLSchema,
|
||||
GraphQLNamedType,
|
||||
isSpecifiedScalarType,
|
||||
GraphQLField,
|
||||
isWrappingType,
|
||||
isNonNullType,
|
||||
isListType,
|
||||
GraphQLOutputType,
|
||||
GraphQLWrappingType,
|
||||
isScalarType,
|
||||
isObjectType,
|
||||
} from "graphql";
|
||||
import { groupTypes, GroupedTypes } from "./utils";
|
||||
import { groupTypes, GroupedTypes, isInterfaceField, objValues } from "./utils";
|
||||
|
||||
export function convertSDL(sdl: string, commonjs: boolean = false) {
|
||||
return new SDLConverter(commonjs, sdl).print();
|
||||
export function convertSDL(
|
||||
sdl: string,
|
||||
commonjs: null | boolean = false,
|
||||
json = JSON
|
||||
) {
|
||||
try {
|
||||
return new SDLConverter(sdl, commonjs, json).print();
|
||||
} catch (e) {
|
||||
return `Error Parsing SDL into Schema: ${e.stack}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,11 +37,16 @@ export function convertSDL(sdl: string, commonjs: boolean = false) {
|
|||
*/
|
||||
export class SDLConverter {
|
||||
protected export: string;
|
||||
protected schema: GraphQLSchema;
|
||||
protected schema: GraphQLSchema | null;
|
||||
protected groupedTypes: GroupedTypes;
|
||||
|
||||
constructor(commonjs: boolean = false, sdl: string) {
|
||||
this.export = commonjs ? "exports." : "export const ";
|
||||
constructor(
|
||||
sdl: string,
|
||||
commonjs: null | boolean = false,
|
||||
protected json: JSON
|
||||
) {
|
||||
this.export =
|
||||
commonjs === null ? "const " : commonjs ? "exports." : "export const ";
|
||||
this.schema = buildSchema(sdl);
|
||||
this.groupedTypes = groupTypes(this.schema);
|
||||
}
|
||||
|
@ -38,7 +59,9 @@ export class SDLConverter {
|
|||
this.printUnionTypes(),
|
||||
this.printEnumTypes(),
|
||||
this.printScalarTypes(),
|
||||
].join("\n\n");
|
||||
]
|
||||
.filter((f) => f)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
printObjectTypes() {
|
||||
|
@ -51,27 +74,98 @@ export class SDLConverter {
|
|||
}
|
||||
|
||||
printObjectType(type: GraphQLObjectType): string {
|
||||
const implementing = type.getInterfaces().map((i) => i.name);
|
||||
const implementsInterfaces =
|
||||
implementing.length > 0
|
||||
? ` t.implements(${implementing
|
||||
.map((i) => this.json.stringify(i))
|
||||
.join(", ")})`
|
||||
: "";
|
||||
return this.printBlock([
|
||||
`${this.export}${type.name} = objectType({`,
|
||||
` name: "${type.name}"`,
|
||||
` name: "${type.name}",`,
|
||||
` definition(t) {`,
|
||||
implementsInterfaces,
|
||||
this.printObjectFields(type),
|
||||
` }`,
|
||||
`})`,
|
||||
]);
|
||||
// if (type.getInterfaces().length > 0) {
|
||||
// const interfaceNames = type
|
||||
// .getInterfaces()
|
||||
// .map((i) => JSON.stringify(i.name))
|
||||
// .join(", ");
|
||||
// str.push(` t.implements(${interfaceNames})`);
|
||||
// }
|
||||
// Object.keys(type.getFields()).forEach((fieldName) => {
|
||||
// if (isInterfaceField(type, fieldName)) {
|
||||
// return;
|
||||
// }
|
||||
// eachObj(type.getFields(), (field, key) => {
|
||||
// getFieldType(field);
|
||||
// });
|
||||
// });
|
||||
// return str.join("\n");
|
||||
}
|
||||
|
||||
printObjectFields(type: GraphQLObjectType | GraphQLInterfaceType) {
|
||||
return objValues(type.getFields())
|
||||
.map((field) => {
|
||||
if (isObjectType(type) && isInterfaceField(type, field.name)) {
|
||||
return;
|
||||
}
|
||||
return this.printField("output", field);
|
||||
})
|
||||
.filter((f) => f)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
printField(source: "input" | "output", field: GraphQLField<any, any>) {
|
||||
let fieldType = field.type;
|
||||
let isNonNull = false;
|
||||
const list = [];
|
||||
while (isWrappingType(fieldType)) {
|
||||
while (isListType(fieldType)) {
|
||||
fieldType = fieldType.ofType;
|
||||
if (isNonNullType(fieldType)) {
|
||||
fieldType = fieldType.ofType;
|
||||
list.unshift(true);
|
||||
} else {
|
||||
list.unshift(false);
|
||||
}
|
||||
}
|
||||
if (isNonNullType(fieldType)) {
|
||||
isNonNull = true;
|
||||
fieldType = fieldType.ofType;
|
||||
}
|
||||
}
|
||||
const prefix = list.length === 1 ? `t.list.` : `t.`;
|
||||
return ` ${prefix}${this.printFieldMethod(
|
||||
source,
|
||||
field,
|
||||
fieldType,
|
||||
list,
|
||||
isNonNull
|
||||
)}`;
|
||||
}
|
||||
|
||||
printFieldMethod(
|
||||
source: "input" | "output",
|
||||
field: GraphQLField<any, any>,
|
||||
type: Exclude<GraphQLOutputType, GraphQLWrappingType>,
|
||||
list: boolean[],
|
||||
isNonNull: boolean
|
||||
) {
|
||||
const objectMeta: Record<string, any> = {};
|
||||
if (field.description) {
|
||||
objectMeta.description = field.description;
|
||||
}
|
||||
if (field.deprecationReason) {
|
||||
objectMeta.deprecation = field.deprecationReason;
|
||||
}
|
||||
if (list.length > 1) {
|
||||
objectMeta.list = list;
|
||||
}
|
||||
if (!isNonNull && source === "output") {
|
||||
objectMeta.nullable = true;
|
||||
} else if (isNonNull && source === "input") {
|
||||
objectMeta.required = true;
|
||||
}
|
||||
let str = "";
|
||||
if (isCommonScalar(type)) {
|
||||
str += `${type.name.toLowerCase()}("${field.name}"`;
|
||||
} else {
|
||||
objectMeta.type = type;
|
||||
str += `field("${field.name}"`;
|
||||
}
|
||||
if (Object.keys(objectMeta).length > 0) {
|
||||
str += `, ${this.json.stringify(objectMeta)}`;
|
||||
}
|
||||
return `${str})`;
|
||||
}
|
||||
|
||||
printInterfaceTypes() {
|
||||
|
@ -89,13 +183,10 @@ export class SDLConverter {
|
|||
` name: "${type.name}",`,
|
||||
this.maybeDescription(type),
|
||||
` definition(t) {`,
|
||||
this.printObjectFields(type),
|
||||
` }`,
|
||||
`});`,
|
||||
]);
|
||||
// eachObj(type.getFields(), (field, key) => {
|
||||
// getFieldType(field);
|
||||
// });
|
||||
// return str.join("\n");
|
||||
}
|
||||
|
||||
printEnumTypes() {
|
||||
|
@ -108,19 +199,27 @@ export class SDLConverter {
|
|||
}
|
||||
|
||||
printEnumType(type: GraphQLEnumType): string {
|
||||
const members = type.getValues().map((val) => {
|
||||
const { description, name, deprecationReason, value } = val;
|
||||
if (!description && !deprecationReason && name === value) {
|
||||
return val.name;
|
||||
}
|
||||
return { description, name, deprecated: deprecationReason, value };
|
||||
});
|
||||
return this.printBlock([
|
||||
`${this.export}${type.name} = enumType({`,
|
||||
` name: "${type.name}",`,
|
||||
this.maybeDescription(type),
|
||||
` definition(t) {`,
|
||||
` }`,
|
||||
` members: ${this.json.stringify(members)},`,
|
||||
`});`,
|
||||
]);
|
||||
}
|
||||
|
||||
printInputObjectTypes() {
|
||||
if (this.groupedTypes.input.length) {
|
||||
return this.groupedTypes.input.map((t) => this.printInputObjectType(t));
|
||||
return this.groupedTypes.input
|
||||
.map((t) => this.printInputObjectType(t))
|
||||
.join("\n");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -159,6 +258,7 @@ export class SDLConverter {
|
|||
printScalarTypes() {
|
||||
if (this.groupedTypes.scalar.length) {
|
||||
return this.groupedTypes.scalar
|
||||
.filter((s) => !isSpecifiedScalarType(s))
|
||||
.map((t) => this.printScalarType(t))
|
||||
.join("\n");
|
||||
}
|
||||
|
@ -168,14 +268,23 @@ export class SDLConverter {
|
|||
printScalarType(type: GraphQLScalarType): string {
|
||||
return this.printBlock([
|
||||
`${this.export}${type.name} = scalarType({`,
|
||||
` name: ${type.name}",`,
|
||||
` name: "${type.name}",`,
|
||||
this.maybeDescription(type),
|
||||
` definition(t) {`,
|
||||
` }`,
|
||||
this.maybeAsNexusType(type),
|
||||
` serialize() { /* Todo */ },`,
|
||||
` parseValue() { /* Todo */ },`,
|
||||
` parseLiteral() { /* Todo */ }`,
|
||||
`});`,
|
||||
]);
|
||||
}
|
||||
|
||||
maybeAsNexusType(type: GraphQLScalarType) {
|
||||
if (isCommonScalar(type)) {
|
||||
return ` asNexusMethod: "${type.name.toLowerCase()}",`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
maybeDescription(type: GraphQLNamedType) {
|
||||
if (type.description) {
|
||||
return ` description: ${JSON.stringify(type.description)},`;
|
||||
|
@ -184,14 +293,17 @@ export class SDLConverter {
|
|||
}
|
||||
|
||||
printBlock(block: (string | null)[]) {
|
||||
return block.filter((t) => t !== null).join("\n");
|
||||
return block.filter((t) => t !== null && t !== "").join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
const getFieldType = (type: GraphQLField<any, any>) => {
|
||||
//
|
||||
};
|
||||
|
||||
const getInputFieldType = (type: GraphQLField<any, any>) => {
|
||||
//
|
||||
};
|
||||
function isCommonScalar(field: GraphQLOutputType): boolean {
|
||||
if (isScalarType(field)) {
|
||||
return (
|
||||
isSpecifiedScalarType(field) ||
|
||||
field.name === "UUID" ||
|
||||
field.name === "Date"
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ export type MaybePromiseDeep<T> = MaybePromise<
|
|||
? Array<MaybePromiseDeep<U>>
|
||||
: T[P] extends ReadonlyArray<infer Y>
|
||||
? ReadonlyArray<MaybePromiseDeep<Y>>
|
||||
: Date extends T[P]
|
||||
? MaybePromise<T[P]>
|
||||
: MaybePromiseDeep<T[P]>
|
||||
}
|
||||
>;
|
||||
|
|
|
@ -1,12 +1,161 @@
|
|||
import { convertSDL, core } from "..";
|
||||
import { EXAMPLE_SDL } from "./_sdl";
|
||||
|
||||
const { SDLConverter } = core;
|
||||
|
||||
describe("SDLConverter", () => {
|
||||
test("printObjectTypes", () => {});
|
||||
test("printObjectTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printObjectTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.field(\\"createPost\\", {\\"type\\":\\"Post\\"})
|
||||
t.field(\\"registerClick\\", {\\"type\\":\\"Query\\"})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
definition(t) {
|
||||
t.implements(\\"Node\\")
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", {\\"type\\":\\"User\\"})
|
||||
t.float(\\"geo\\", {\\"list\\":[true,true]})
|
||||
t.float(\\"messyGeo\\", {\\"list\\":[true,false],\\"nullable\\":true})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", {\\"type\\":\\"User\\"})
|
||||
t.list.field(\\"posts\\", {\\"type\\":\\"Post\\"})
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(\\"Node\\")
|
||||
t.string(\\"name\\")
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", {\\"nullable\\":true})
|
||||
t.list.field(\\"posts\\", {\\"type\\":\\"Post\\"})
|
||||
t.field(\\"outEnum\\", {\\"nullable\\":true,\\"type\\":\\"SomeEnum\\"})
|
||||
}
|
||||
})"
|
||||
`);
|
||||
});
|
||||
|
||||
test("printEnumTypes", () => {});
|
||||
|
||||
test("printScalarTypes", () => {});
|
||||
|
||||
test("printInterfaceTypes", () => {});
|
||||
test("printEnumTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printEnumTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",\\"B\\"],
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
test("printScalarTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printScalarTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
test("printInterfaceTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printInterfaceTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
}
|
||||
});"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
test("convertSDL", () => {
|
||||
expect(convertSDL(EXAMPLE_SDL)).toMatchInlineSnapshot(`
|
||||
"export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.field(\\"createPost\\", {\\"type\\":\\"Post\\"})
|
||||
t.field(\\"registerClick\\", {\\"type\\":\\"Query\\"})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
definition(t) {
|
||||
t.implements(\\"Node\\")
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", {\\"type\\":\\"User\\"})
|
||||
t.float(\\"geo\\", {\\"list\\":[true,true]})
|
||||
t.float(\\"messyGeo\\", {\\"list\\":[true,false],\\"nullable\\":true})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", {\\"type\\":\\"User\\"})
|
||||
t.list.field(\\"posts\\", {\\"type\\":\\"Post\\"})
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(\\"Node\\")
|
||||
t.string(\\"name\\")
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", {\\"nullable\\":true})
|
||||
t.list.field(\\"posts\\", {\\"type\\":\\"Post\\"})
|
||||
t.field(\\"outEnum\\", {\\"nullable\\":true,\\"type\\":\\"SomeEnum\\"})
|
||||
}
|
||||
})
|
||||
|
||||
export const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
}
|
||||
});
|
||||
|
||||
export const CreatePostInput = inputObjectType({
|
||||
name: \\"CreatePostInput\\",
|
||||
definition(t) {
|
||||
}
|
||||
});
|
||||
export const PostFilters = inputObjectType({
|
||||
name: \\"PostFilters\\",
|
||||
definition(t) {
|
||||
}
|
||||
});
|
||||
|
||||
export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",\\"B\\"],
|
||||
});
|
||||
|
||||
export const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
"links": {
|
||||
"Getting Started": "Getting Started",
|
||||
"Playground": "Playground",
|
||||
"SDL Converter": "SDL Converter",
|
||||
"Examples": "Examples",
|
||||
"GitHub": "GitHub"
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"@babel/preset-env": "^7.1.5",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/preset-typescript": "^7.1.0",
|
||||
"@types/json5": "^0.0.30",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/fs-extra": "^5.0.4",
|
||||
"@types/get-port": "^4.0.0",
|
||||
|
@ -53,6 +54,7 @@
|
|||
"typescript": "3.1.6",
|
||||
"use-debounce": "^0.0.7",
|
||||
"webpack": "^4.25.1",
|
||||
"webpack-cli": "^3.1.2"
|
||||
"webpack-cli": "^3.1.2",
|
||||
"json5": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||
/>
|
||||
<title>Nexus SDL Converter</title>
|
||||
<meta
|
||||
name="apple-mobile-web-app-status-bar-style"
|
||||
content="black-translucent"
|
||||
/>
|
||||
<meta name="msapplication-starturl" content="/" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<link rel="icon" href="img/favicon.png" />
|
||||
<meta name="twitter:image" content="https://nexus.js.org/img/nexus.png" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.4.4/lz-string.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.development.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.development.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.12.0/graphiql.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
<link rel="stylesheet" href="separate-css/playground.css" />
|
||||
</head>
|
||||
<body class="sideNavVisible separateOnPageNav">
|
||||
<div class="fixedHeaderContainer">
|
||||
<div class="headerWrapper wrapper">
|
||||
<header>
|
||||
<a href="index.html"
|
||||
><img class="logo" src="img/nexus.png" alt="Nexus GraphQL" />
|
||||
<h2 class="headerTitleWithLogo">Nexus GraphQL</h2></a
|
||||
>
|
||||
<div class="navigationWrapper navigationSlider">
|
||||
<nav class="slidingNav">
|
||||
<ul class="nav-site nav-site-internal">
|
||||
<li>
|
||||
<a href="docs/getting-started" target="_self"
|
||||
>Getting Started</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="playground" target="_self">Playground</a>
|
||||
</li>
|
||||
<li class="siteNavGroupActive siteNavItemActive">
|
||||
<a href="converter" target="_self">SDL Converter</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/graphql-nexus/nexus/tree/develop/examples"
|
||||
>Examples</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/graphql-nexus/nexus"
|
||||
target="_self"
|
||||
>GitHub</a
|
||||
>
|
||||
</li>
|
||||
<li class="navSearchWrapper reactNavSearchWrapper">
|
||||
<input
|
||||
type="text"
|
||||
id="search_input_react"
|
||||
placeholder="Search"
|
||||
title="Search"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
<div id="root" class="playground-container"></div>
|
||||
<script src="playground-dist/converter.js"></script>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=UA-39763513-4"
|
||||
></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag("js", new Date());
|
||||
|
||||
gtag("config", "UA-39763513-4");
|
||||
</script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"
|
||||
></script>
|
||||
<script>
|
||||
document.addEventListener("keyup", function(e) {
|
||||
if (e.target !== document.body) {
|
||||
return;
|
||||
}
|
||||
// keyCode for '/' (slash)
|
||||
if (e.keyCode === 191) {
|
||||
const search = document.getElementById("search_input_react");
|
||||
search && search.focus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
var search = docsearch({
|
||||
apiKey: "80f72460aa3d84abf56948140313b079",
|
||||
indexName: "graphql-nexus",
|
||||
inputSelector: "#search_input_react",
|
||||
algoliaOptions: {},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -30,8 +30,8 @@
|
|||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.12.0/graphiql.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="separate-css/playground.css" />
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
<link rel="stylesheet" href="separate-css/playground.css" />
|
||||
</head>
|
||||
<body class="sideNavVisible separateOnPageNav">
|
||||
<div class="fixedHeaderContainer">
|
||||
|
@ -52,6 +52,9 @@
|
|||
<li class="siteNavGroupActive siteNavItemActive">
|
||||
<a href="playground" target="_self">Playground</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="converter" target="_self">SDL Converter</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/graphql-nexus/nexus/tree/develop/examples"
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import ReactDOM from "react-dom";
|
||||
import debounce from "lodash.debounce";
|
||||
import { convertSDL } from "nexus";
|
||||
import json5 from "json5";
|
||||
import { EXAMPLE_SDL } from "../../tests/_sdl";
|
||||
import "./monaco-graphql";
|
||||
|
||||
const COMMON_CONFIG: monaco.editor.IEditorConstructionOptions = {
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
lineNumbersMinChars: 3,
|
||||
};
|
||||
const COMMON_READONLY_CONFIG: monaco.editor.IEditorConstructionOptions = {
|
||||
...COMMON_CONFIG,
|
||||
readOnly: true,
|
||||
contextmenu: false,
|
||||
renderLineHighlight: "none",
|
||||
};
|
||||
|
||||
function monacoRef() {
|
||||
return useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
|
||||
}
|
||||
|
||||
export function Converter() {
|
||||
const [content, setContent] = useState(
|
||||
[`## WIP, not all types supported yet!!`, `# Paste your SDL here:`]
|
||||
.concat(EXAMPLE_SDL)
|
||||
.join("\n")
|
||||
);
|
||||
const [sdlDiv, outputDiv] = [
|
||||
useRef<null | HTMLDivElement>(null),
|
||||
useRef<null | HTMLDivElement>(null),
|
||||
];
|
||||
const [sdlEditorRef, outputEditorRef] = [monacoRef(), monacoRef()];
|
||||
useEffect(() => {
|
||||
if (sdlDiv.current && outputDiv.current) {
|
||||
const sdlEditor = monaco.editor.create(sdlDiv.current, {
|
||||
language: "graphql",
|
||||
model: monaco.editor.createModel(
|
||||
content,
|
||||
"graphql",
|
||||
monaco.Uri.file("sdl.graphql")
|
||||
),
|
||||
...COMMON_CONFIG,
|
||||
});
|
||||
const outputEditor = monaco.editor.create(outputDiv.current, {
|
||||
language: "typescript",
|
||||
...COMMON_READONLY_CONFIG,
|
||||
});
|
||||
sdlEditorRef.current = sdlEditor;
|
||||
outputEditorRef.current = outputEditor;
|
||||
const debouncedChange = debounce(() => {
|
||||
setContent(sdlEditor.getValue());
|
||||
}, 100);
|
||||
sdlEditor.onDidChangeModelContent(debouncedChange as any);
|
||||
return () => sdlEditor.dispose();
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const converted = convertSDL(content, null, json5 as JSON);
|
||||
if (outputEditorRef.current) {
|
||||
outputEditorRef.current.setValue(converted);
|
||||
}
|
||||
}, [content]);
|
||||
return (
|
||||
<div className="editors-container">
|
||||
<div className="editors">
|
||||
<div
|
||||
style={{ flexBasis: "50%", height: "100%", flexDirection: "column" }}
|
||||
>
|
||||
<div ref={sdlDiv} style={{ height: "100%" }} />
|
||||
</div>
|
||||
<div
|
||||
style={{ flexBasis: "50%", height: "100%", flexDirection: "column" }}
|
||||
>
|
||||
<div ref={outputDiv} style={{ height: "100%" }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Converter />, document.getElementById("root"));
|
|
@ -282,6 +282,7 @@ function getCurrentSchema(code: string): SchemaOrError {
|
|||
return val;
|
||||
}
|
||||
const singleton = {
|
||||
core,
|
||||
objectType(obj: any) {
|
||||
return add(objectType(obj));
|
||||
},
|
||||
|
@ -303,6 +304,7 @@ function getCurrentSchema(code: string): SchemaOrError {
|
|||
};
|
||||
try {
|
||||
const fn = new Function(
|
||||
"core",
|
||||
"objectType",
|
||||
"interfaceType",
|
||||
"inputObjectType",
|
||||
|
@ -321,6 +323,7 @@ function getCurrentSchema(code: string): SchemaOrError {
|
|||
`
|
||||
);
|
||||
fn(
|
||||
singleton.core,
|
||||
singleton.objectType,
|
||||
singleton.interfaceType,
|
||||
singleton.inputObjectType,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as monaco from "monaco-editor";
|
||||
import "./monaco-graphql";
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.ES2016,
|
||||
|
@ -93,6 +94,7 @@ import * as nexus from 'nexus'
|
|||
// Re-export these so we can use globally in the sandbox
|
||||
// while still preserving the typegen
|
||||
declare global {
|
||||
declare const core: typeof nexus.core;
|
||||
declare const arg: typeof nexus.arg;
|
||||
declare const intArg: typeof nexus.intArg;
|
||||
declare const stringArg: typeof nexus.stringArg;
|
||||
|
@ -110,47 +112,3 @@ declare global {
|
|||
`,
|
||||
"file:///sandbox-globals.ts"
|
||||
);
|
||||
|
||||
monaco.languages.register({ id: "graphql" });
|
||||
|
||||
// https://code.visualstudio.com/blogs/2017/02/08/syntax-highlighting-optimizations
|
||||
monaco.languages.setMonarchTokensProvider("graphql", {
|
||||
ignoreCase: true,
|
||||
// @ts-ignore
|
||||
keywords: [
|
||||
"type",
|
||||
"input",
|
||||
"scalar",
|
||||
"enum",
|
||||
"union",
|
||||
"implements",
|
||||
"interface",
|
||||
"directive",
|
||||
],
|
||||
tokenizer: {
|
||||
root: [
|
||||
{ include: "@whitespace" },
|
||||
[
|
||||
/[a-z_$][\w$]*/,
|
||||
{
|
||||
cases: {
|
||||
"@keywords": "keyword",
|
||||
"@default": "identifier",
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
whitespace: [
|
||||
[/\s+/, "white"],
|
||||
[/(^#.*$)/, "comment"],
|
||||
[/(""".*""")/, "string"],
|
||||
[/""".*$/, "string", "@endDblDocString"],
|
||||
],
|
||||
endDocString: [[/.*$/, "string"]],
|
||||
endDblDocString: [
|
||||
[/\\"/, "string"],
|
||||
[/.*"""/, "string", "@popall"],
|
||||
[/.*$/, "string"],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import * as monaco from "monaco-editor";
|
||||
|
||||
monaco.languages.register({ id: "graphql" });
|
||||
|
||||
// https://code.visualstudio.com/blogs/2017/02/08/syntax-highlighting-optimizations
|
||||
monaco.languages.setMonarchTokensProvider("graphql", {
|
||||
// Set defaultToken to invalid to see what you do not tokenize yet
|
||||
defaultToken: "invalid",
|
||||
tokenPostfix: ".gql",
|
||||
|
||||
keywords: [
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"query",
|
||||
"mutation",
|
||||
"subscription",
|
||||
"extend",
|
||||
"schema",
|
||||
"directive",
|
||||
"scalar",
|
||||
"type",
|
||||
"interface",
|
||||
"union",
|
||||
"enum",
|
||||
"input",
|
||||
"implements",
|
||||
"fragment",
|
||||
"on",
|
||||
],
|
||||
|
||||
typeKeywords: ["Int", "Float", "String", "Boolean", "ID"],
|
||||
|
||||
directiveLocations: [
|
||||
"SCHEMA",
|
||||
"SCALAR",
|
||||
"OBJECT",
|
||||
"FIELD_DEFINITION",
|
||||
"ARGUMENT_DEFINITION",
|
||||
"INTERFACE",
|
||||
"UNION",
|
||||
"ENUM",
|
||||
"ENUM_VALUE",
|
||||
"INPUT_OBJECT",
|
||||
"INPUT_FIELD_DEFINITION",
|
||||
"QUERY",
|
||||
"MUTATION",
|
||||
"SUBSCRIPTION",
|
||||
"FIELD",
|
||||
"FRAGMENT_DEFINITION",
|
||||
"FRAGMENT_SPREAD",
|
||||
"INLINE_FRAGMENT",
|
||||
"VARIABLE_DEFINITION",
|
||||
],
|
||||
|
||||
operators: ["=", "!", "?", ":", "&", "|"],
|
||||
|
||||
// we include these common regular expressions
|
||||
symbols: /[=!?:&|]+/,
|
||||
|
||||
// https://facebook.github.io/graphql/draft/#sec-String-Value
|
||||
escapes: /\\(?:["\\\/bfnrt]|u[0-9A-Fa-f]{4})/,
|
||||
|
||||
// The main tokenizer for our languages
|
||||
tokenizer: {
|
||||
root: [
|
||||
// identifiers and keywords
|
||||
[
|
||||
/[a-z_$][\w$]*/,
|
||||
{
|
||||
cases: {
|
||||
"@keywords": "keyword",
|
||||
"@default": "identifier",
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
/[A-Z][\w\$]*/,
|
||||
{
|
||||
cases: {
|
||||
"@typeKeywords": "keyword",
|
||||
"@default": "type.identifier",
|
||||
},
|
||||
},
|
||||
], // to show class names nicely
|
||||
|
||||
// whitespace
|
||||
{ include: "@whitespace" },
|
||||
|
||||
// delimiters and operators
|
||||
[/[{}()\[\]]/, "@brackets"],
|
||||
[/@symbols/, { cases: { "@operators": "operator", "@default": "" } }],
|
||||
|
||||
// @ annotations.
|
||||
// As an example, we emit a debugging log message on these tokens.
|
||||
// Note: message are supressed during the first load -- change some lines to see them.
|
||||
[
|
||||
/@\s*[a-zA-Z_\$][\w\$]*/,
|
||||
{ token: "annotation", log: "annotation token: $0" },
|
||||
],
|
||||
|
||||
// numbers
|
||||
[/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"],
|
||||
[/0[xX][0-9a-fA-F]+/, "number.hex"],
|
||||
[/\d+/, "number"],
|
||||
|
||||
// delimiter: after number because of .\d floats
|
||||
[/[;,.]/, "delimiter"],
|
||||
|
||||
[/"""/, { token: "string", next: "@mlstring", nextEmbedded: "markdown" }],
|
||||
|
||||
// strings
|
||||
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
|
||||
[/"/, { token: "string.quote", bracket: "@open", next: "@string" }],
|
||||
],
|
||||
|
||||
mlstring: [
|
||||
[/[^"]+/, "string"],
|
||||
// @ts-ignore
|
||||
['"""', { token: "string", next: "@pop", nextEmbedded: "@pop" }],
|
||||
],
|
||||
|
||||
string: [
|
||||
[/[^\\"]+/, "string"],
|
||||
[/@escapes/, "string.escape"],
|
||||
[/\\./, "string.escape.invalid"],
|
||||
[/"/, { token: "string.quote", bracket: "@close", next: "@pop" }],
|
||||
],
|
||||
|
||||
whitespace: [[/[ \t\r\n]+/, ""], [/#.*$/, "comment"]],
|
||||
},
|
||||
});
|
|
@ -31,6 +31,7 @@ const siteConfig = {
|
|||
headerLinks: [
|
||||
{ doc: "getting-started", label: "Getting Started" },
|
||||
{ page: "playground", label: "Playground" },
|
||||
{ page: "converter", label: "SDL Converter" },
|
||||
{
|
||||
href: "https://github.com/graphql-nexus/nexus/tree/develop/examples",
|
||||
label: "Examples",
|
||||
|
|
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
devtool: "source-map",
|
||||
entry: {
|
||||
playground: "./playground/index.tsx",
|
||||
converter: "./playground/Converter.tsx",
|
||||
},
|
||||
output: {
|
||||
publicPath: "/playground-dist/",
|
||||
|
|
|
@ -793,6 +793,11 @@
|
|||
"@types/orchestrator" "*"
|
||||
"@types/vinyl" "*"
|
||||
|
||||
"@types/json5@^0.0.30":
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818"
|
||||
integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==
|
||||
|
||||
"@types/lodash.debounce@^4.0.4":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.4.tgz#a3d082628ef1bb1964c2b6a2d4f45acae7209e7a"
|
||||
|
@ -4394,6 +4399,13 @@ json5@^0.5.0:
|
|||
version "0.5.1"
|
||||
resolved "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
|
||||
|
||||
json5@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"
|
||||
integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
|
|
Loading…
Reference in New Issue