Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dotansimha committed May 13, 2018
0 parents commit 040896f
Show file tree
Hide file tree
Showing 26 changed files with 4,973 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.idea
dist
build
out
temp
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact = true
6 changes: 6 additions & 0 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"lerna": "2.11.0",
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true
}
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"private": true,
"repository": "https://github.com/Urigo/graphql-modules.git",
"author": "dotansimha <dotansimha@gmail.com>",
"license": "MIT",
"workspaces": {
"packages": [
"packages/*/"
]
},
"scripts": {
"test": "lerna run test",
"build": "lerna run build",
"clean": "lerna clean --yes && rm -rf node_modules"
},
"devDependencies": {
"@types/graphql": "0.13.1",
"@types/jest": "22.2.3",
"graphql": "0.13.2",
"jest": "22.4.3",
"lerna": "^2.11.0",
"rimraf": "2.6.2",
"ts-jest": "22.4.6",
"tslint": "5.10.0",
"typescript": "2.8.3"
}
}
6 changes: 6 additions & 0 deletions packages/core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.idea
dist
build
out
temp
1 change: 1 addition & 0 deletions packages/core/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact = true
27 changes: 27 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@graphql-modules/core",
"version": "0.0.1",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
"test": "jest",
"build": "tsc"
},
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"devDependencies": {
"jest": "22.4.3"
}
}
18 changes: 18 additions & 0 deletions packages/core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"module": "commonjs",
"target": "es5",
"lib": ["es6", "esnext", "es2015"],
"noImplicitAny": false,
"suppressImplicitAnyIndexErrors": true,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"sourceMap": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src"
},
"files": ["src/index.ts"],
"exclude": ["node_modules"]
}
6 changes: 6 additions & 0 deletions packages/glue-schema/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.idea
dist
build
out
temp
1 change: 1 addition & 0 deletions packages/glue-schema/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
save-exact = true
27 changes: 27 additions & 0 deletions packages/glue-schema/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@graphql-modules/glue-schema",
"version": "0.0.1",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
"test": "jest",
"build": "tsc"
},
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"devDependencies": {
"jest": "22.4.3"
}
}
Empty file.
34 changes: 34 additions & 0 deletions packages/glue-schema/src/merge-nodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DefinitionNode } from 'graphql';
import {
isGraphQLEnum,
isGraphQLInputType,
isGraphQLScalar,
isGraphQLType,
isGraphQLUnion,
} from './utils';
import { mergeType } from './mergers/type';
import { mergeEnum } from './mergers/enum';
import { mergeUnion } from './mergers/union';
import { mergeInputType } from './mergers/input-type';

export type MergedResultMap = {[name: string]: DefinitionNode};

export function mergeGraphQLNodes(nodes: ReadonlyArray<DefinitionNode>): MergedResultMap {
return nodes.reduce<MergedResultMap>((prev: MergedResultMap, nodeDefinition: DefinitionNode) => {
const name = (nodeDefinition as any).name.value;

if (isGraphQLType(nodeDefinition)) {
prev[name] = mergeType(nodeDefinition, prev[name] as any);
} else if (isGraphQLEnum(nodeDefinition)) {
prev[name] = mergeEnum(nodeDefinition, prev[name] as any);
} else if (isGraphQLUnion(nodeDefinition)) {
prev[name] = mergeUnion(nodeDefinition, prev[name] as any);
} else if (isGraphQLScalar(nodeDefinition)) {
prev[name] = nodeDefinition;
} else if (isGraphQLInputType(nodeDefinition)) {
prev[name] = mergeInputType(nodeDefinition as any, prev[name] as any);
}

return prev;
}, {});
}
21 changes: 21 additions & 0 deletions packages/glue-schema/src/merge-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DefinitionNode, DocumentNode, parse, Source } from 'graphql';
import { isSourceTypes, isStringTypes } from './utils';
import { mergeGraphQLNodes } from './merge-nodes';

export function mergeGraphQLTypes(types: Array<string | Source | DocumentNode>) {
const allNodes: ReadonlyArray<DefinitionNode> = types
.map<DocumentNode>(type => {
if (isStringTypes(type) || isSourceTypes(type)) {
return parse(type);
}

return type;
})
.map(ast => ast.definitions)
.reduce((defs, newDef) => [...defs, ...newDef], []);

const mergedNodes = mergeGraphQLNodes(allNodes);

// TODO: Convert to add, and then add SchemaDefinitionNode to the array
// TODO: Print all definitions with astPrinter
}
12 changes: 12 additions & 0 deletions packages/glue-schema/src/mergers/directives.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DirectiveNode } from 'graphql/language/ast';

function directiveAlreadyExists(directivesArr: ReadonlyArray<DirectiveNode>, otherDirective: DirectiveNode): boolean {
return directivesArr.find(directive => directive.name.value === otherDirective.name.value);
}

export function mergeDirectives(d1: ReadonlyArray<DirectiveNode>, d2: ReadonlyArray<DirectiveNode>): DirectiveNode[] {
return [
...d2,
...(d1.filter(d => !directiveAlreadyExists(d2, d))),
];
}
12 changes: 12 additions & 0 deletions packages/glue-schema/src/mergers/enum-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { EnumValueDefinitionNode } from 'graphql/language/ast';

function alreadyExists(arr: ReadonlyArray<EnumValueDefinitionNode>, other: EnumValueDefinitionNode): boolean {
return arr.find(v => v.name.value === other.name.value);
}

export function mergeEnumValues(first: ReadonlyArray<EnumValueDefinitionNode>, second: ReadonlyArray<EnumValueDefinitionNode>): EnumValueDefinitionNode[] {
return [
...second,
...(first.filter(d => !alreadyExists(second, d))),
];
}
18 changes: 18 additions & 0 deletions packages/glue-schema/src/mergers/enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EnumTypeDefinitionNode } from 'graphql';
import { mergeDirectives } from './directives';
import { mergeEnumValues } from './enum-values';

export function mergeEnum(e1: EnumTypeDefinitionNode, e2: EnumTypeDefinitionNode): EnumTypeDefinitionNode {
if (e2) {
return {
name: e1.name,
description: e1.description || e2.description,
kind: e1.kind,
loc: e1.loc,
directives: mergeDirectives(e1.directives, e2.directives),
values: mergeEnumValues(e1.values, e2.values),
};
}

return e1;
}
24 changes: 24 additions & 0 deletions packages/glue-schema/src/mergers/fields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FieldDefinitionNode, InputValueDefinitionNode } from 'graphql/language/ast';
import { extractType } from '../utils';

function fieldAlreadyExists(fieldsArr: ReadonlyArray<any>, otherField: any): boolean {
const result: FieldDefinitionNode | null = fieldsArr.find(field => field.name.value === otherField.name.value);

if (result) {
const t1 = extractType(result.type);
const t2 = extractType(otherField.type);

if (t1.name.value !== t2.name.value) {
throw new Error(`Field "${otherField.name.value}" already defined with a different type. Declared as "${t1.name.value}", but you tried to override with "${t2.name.value}"`);
}
}

return !!result;
}

export function mergeFields<T>(f1: ReadonlyArray<T>, f2: ReadonlyArray<T>): T[] {
return [
...f2,
...(f1.filter(f => !fieldAlreadyExists(f2, f)))
];
}
24 changes: 24 additions & 0 deletions packages/glue-schema/src/mergers/input-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { InputObjectTypeDefinitionNode, ObjectTypeDefinitionNode } from 'graphql';
import { mergeFields } from './fields';
import { mergeDirectives } from './directives';
import { mergeNamedTypeArray } from './merge-named-type-array';
import { InputValueDefinitionNode } from 'graphql/language/ast';

export function mergeInputType(node: InputObjectTypeDefinitionNode, existingNode: InputObjectTypeDefinitionNode): InputObjectTypeDefinitionNode {
if (existingNode) {
try {
return {
name: node.name,
description: node.description || existingNode.description,
kind: node.kind,
loc: node.loc,
fields: mergeFields<InputValueDefinitionNode>(node.fields, existingNode.fields),
directives: mergeDirectives(node.directives, existingNode.directives),
};
} catch (e) {
throw new Error(`Unable to merge GraphQL type "${node.name.value}": ${e.message}`);
}
}

return node;
}
12 changes: 12 additions & 0 deletions packages/glue-schema/src/mergers/merge-named-type-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { NamedTypeNode } from 'graphql/language/ast';

function alreadyExists(arr: ReadonlyArray<NamedTypeNode>, other: NamedTypeNode): boolean {
return arr.find(i => i.name.value === other.name.value);
}

export function mergeNamedTypeArray(first: ReadonlyArray<NamedTypeNode>, second: ReadonlyArray<NamedTypeNode>): NamedTypeNode[] {
return [
...second,
...(first.filter(d => !alreadyExists(second, d))),
];
}
24 changes: 24 additions & 0 deletions packages/glue-schema/src/mergers/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ObjectTypeDefinitionNode } from 'graphql';
import { mergeFields } from './fields';
import { mergeDirectives } from './directives';
import { mergeNamedTypeArray } from './merge-named-type-array';

export function mergeType(node: ObjectTypeDefinitionNode, existingNode: ObjectTypeDefinitionNode): ObjectTypeDefinitionNode {
if (existingNode) {
try {
return {
name: node.name,
description: node.description || existingNode.description,
kind: node.kind,
loc: node.loc,
fields: mergeFields(node.fields, existingNode.fields),
directives: mergeDirectives(node.directives, existingNode.directives),
interfaces: mergeNamedTypeArray(node.interfaces, existingNode.interfaces),
};
} catch (e) {
throw new Error(`Unable to merge GraphQL type "${node.name.value}": ${e.message}`);
}
}

return node;
}
18 changes: 18 additions & 0 deletions packages/glue-schema/src/mergers/union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UnionTypeDefinitionNode } from 'graphql';
import { mergeDirectives } from './directives';
import { mergeNamedTypeArray } from './merge-named-type-array';

export function mergeUnion(first: UnionTypeDefinitionNode, second: UnionTypeDefinitionNode): UnionTypeDefinitionNode {
if (second) {
return {
name: first.name,
description: first.description || second.description,
directives: mergeDirectives(first.directives, second.directives),
kind: first.kind,
loc: first.loc,
types: mergeNamedTypeArray(first.types, second.types),
};
}

return first;
}
49 changes: 49 additions & 0 deletions packages/glue-schema/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
TypeNode,
DefinitionNode,
EnumTypeDefinitionNode,
NamedTypeNode,
ObjectTypeDefinitionNode,
Source,
UnionTypeDefinitionNode, SchemaDefinitionNode, ScalarTypeDefinitionNode, InputObjectTypeDefinitionNode
} from 'graphql';

export function isStringTypes(types: any): types is string {
return typeof types === 'string';
}

export function isSourceTypes(types: any): types is Source {
return types instanceof Source;
}

export function isGraphQLType(definition: DefinitionNode): definition is ObjectTypeDefinitionNode {
return definition.kind === 'ObjectTypeDefinition';
}

export function isGraphQLEnum(definition: DefinitionNode): definition is EnumTypeDefinitionNode {
return definition.kind === 'EnumTypeDefinition';
}

export function isGraphQLUnion(definition: DefinitionNode): definition is UnionTypeDefinitionNode {
return definition.kind === 'UnionTypeDefinition';
}

export function isGraphQLScalar(definition: DefinitionNode): definition is ScalarTypeDefinitionNode {
return definition.kind === 'ScalarTypeDefinition';
}

export function isGraphQLInputType(definition: DefinitionNode): definition is InputObjectTypeDefinitionNode {
return definition.kind === 'InputObjectTypeDefinition';
}

export function isSchemaDefinitionNode(definition: DefinitionNode): definition is SchemaDefinitionNode {
return definition.kind === 'SchemaDefinition';
}

export function extractType(type: TypeNode): NamedTypeNode {
if (type.kind === 'ListType' || type.kind === 'NonNullType') {
return type.type as any;
}

return type as any;
}
Loading

0 comments on commit 040896f

Please sign in to comment.