Skip to content

Commit dd55408

Browse files
committed
feat: add astMerge(ast1, ast2, ...) method for merging several ast obtained from directoryToAst
1 parent 15cf3e6 commit dd55408

File tree

12 files changed

+169
-7
lines changed

12 files changed

+169
-7
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'A.mutation.createTask',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'A.query.me',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'A.query.tasks.byId',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'A.query.tasks.list',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'B.query.me',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'B.query.tasks.byId',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'B.query.tasks.byIds',
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
type: 'String',
3+
description: 'B.subscription.events',
4+
};

src/__tests__/astMerge-test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { astMerge } from '../astMerge';
2+
import { directoryToAst, AstRootNode } from '../directoryToAst';
3+
import { astToSchema } from '../astToSchema';
4+
5+
describe('astMerge', () => {
6+
let ast1: AstRootNode;
7+
let ast2: AstRootNode;
8+
9+
beforeEach(() => {
10+
ast1 = directoryToAst(module, { relativePath: './__fixtures__/merge/schema1' });
11+
ast2 = directoryToAst(module, { relativePath: './__fixtures__/merge/schema2' });
12+
});
13+
14+
it('should merge two schemas', () => {
15+
const mergedAst = astMerge(ast1, ast2);
16+
const schema = astToSchema(mergedAst);
17+
18+
expect(schema.toSDL()).toMatchInlineSnapshot(`
19+
"type Mutation {
20+
\\"\\"\\"A.mutation.createTask\\"\\"\\"
21+
createTask: String
22+
}
23+
24+
type Query {
25+
\\"\\"\\"B.query.me\\"\\"\\"
26+
me: String
27+
tasks: QueryTasks
28+
}
29+
30+
type QueryTasks {
31+
\\"\\"\\"B.query.tasks.byId\\"\\"\\"
32+
byId: String
33+
34+
\\"\\"\\"A.query.tasks.list\\"\\"\\"
35+
list: String
36+
37+
\\"\\"\\"B.query.tasks.byIds\\"\\"\\"
38+
byIds: String
39+
}
40+
41+
\\"\\"\\"
42+
The \`String\` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
43+
\\"\\"\\"
44+
scalar String
45+
46+
type Subscription {
47+
\\"\\"\\"B.subscription.events\\"\\"\\"
48+
events: String
49+
}"
50+
`);
51+
});
52+
});

src/astMerge.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { AstRootNode, AstRootTypeNode, RootTypeNames, AstDirChildren } from './directoryToAst';
2+
3+
export function astMerge(...asts: Array<AstRootNode>): AstRootNode {
4+
const mergedAST = {
5+
kind: 'root',
6+
name: 'merged',
7+
absPath: 'merged',
8+
children: {},
9+
} as AstRootNode;
10+
11+
asts.forEach((ast) => {
12+
mergedAST.name += `; ${ast.name}`;
13+
mergedAST.absPath += `; ${ast.absPath}`;
14+
15+
// merge rootTypes
16+
Object.keys(ast.children).forEach((key) => {
17+
const rootName = key as RootTypeNames;
18+
const rootTypeNode = ast.children[rootName] as AstRootTypeNode;
19+
20+
let mergedRootTypeAST = mergedAST.children[rootName];
21+
if (!mergedRootTypeAST) {
22+
mergedRootTypeAST = {
23+
kind: 'rootType',
24+
name: rootTypeNode.name,
25+
absPath: 'merged',
26+
children: {},
27+
} as AstRootTypeNode;
28+
mergedAST.children[rootName] = mergedRootTypeAST;
29+
}
30+
mergedRootTypeAST.absPath += `; ${rootTypeNode.absPath}`;
31+
32+
// maybe in future namespaceConfig will be refactored
33+
// but now it just take last one
34+
if (rootTypeNode.namespaceConfig) {
35+
mergedRootTypeAST.namespaceConfig = rootTypeNode.namespaceConfig;
36+
}
37+
38+
mergedRootTypeAST.children = mergeChildren(mergedRootTypeAST.children, rootTypeNode.children);
39+
});
40+
});
41+
42+
return mergedAST;
43+
}
44+
45+
function mergeChildren(target: AstDirChildren, source: AstDirChildren): AstDirChildren {
46+
const result = { ...target };
47+
Object.keys(source).forEach((key) => {
48+
const targetChild = target[key];
49+
const sourceChild = source[key];
50+
if (!targetChild) {
51+
// add new key from source
52+
result[key] = sourceChild;
53+
} else if (targetChild.kind === 'dir') {
54+
if (sourceChild.kind === 'dir') {
55+
// merge dirs
56+
const mergedDirNode = {
57+
...targetChild,
58+
absPath: `merged; ${targetChild.absPath}; ${sourceChild.absPath}`,
59+
children: mergeChildren(targetChild.children, sourceChild.children),
60+
};
61+
if (sourceChild.namespaceConfig) {
62+
mergedDirNode.namespaceConfig = sourceChild.namespaceConfig;
63+
}
64+
result[key] = mergedDirNode;
65+
} else if (sourceChild.kind === 'file') {
66+
// replace dir by file from source
67+
result[key] = sourceChild;
68+
}
69+
} else if (targetChild.kind === 'file') {
70+
// replace file by any source type
71+
result[key] = sourceChild;
72+
}
73+
});
74+
75+
return result;
76+
}

0 commit comments

Comments
 (0)