Skip to content

Commit a228f1b

Browse files
committed
Add eslint
1 parent 215aec7 commit a228f1b

11 files changed

+3715
-103
lines changed

.eslintrc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"sourceType": "module"
5+
},
6+
"extends": [
7+
"eslint:recommended",
8+
"plugin:@typescript-eslint/eslint-recommended",
9+
"plugin:@typescript-eslint/recommended",
10+
"plugin:prettier/recommended"
11+
],
12+
"rules": {
13+
"@typescript-eslint/explicit-function-return-type": "off"
14+
}
15+
}

package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
"lib"
2121
],
2222
"scripts": {
23-
"build": "tsc",
24-
"prepare": "yarn run build",
23+
"build": "rimraf lib && tsc",
24+
"preversion": "yarn test",
25+
"version": "yarn run build",
2526
"postversion": "git push && git push --tags",
26-
"test": "jest"
27+
"test": "jest",
28+
"lint": "eslint --ext .ts src/"
2729
},
2830
"peerDependencies": {
2931
"graphql": "^14.6.0",
@@ -38,13 +40,19 @@
3840
"@types/node": "^13.11.1",
3941
"@types/react": "^16.9.34",
4042
"@types/tmp": "^0.1.0",
43+
"@typescript-eslint/eslint-plugin": "^2.28.0",
44+
"@typescript-eslint/parser": "^2.28.0",
4145
"apollo-boost": "^0.4.7",
46+
"eslint": "^6.8.0",
47+
"eslint-config-prettier": "^6.10.1",
48+
"eslint-plugin-prettier": "^3.1.3",
4249
"graphql": "^14.6.0",
4350
"import-sort-style-module": "^6.0.0",
4451
"jest": "^25.3.0",
4552
"pascal-case": "^3.1.1",
4653
"prettier": "^2.0.4",
4754
"prettier-plugin-import-sort": "^0.0.4",
55+
"rimraf": "^3.0.2",
4856
"tmp": "^0.1.0",
4957
"ts-jest": "^25.3.1",
5058
"typescript": "^3.8.3"

src/codegenApolloMock.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ expect.addSnapshotSerializer({
1212
test(value) {
1313
return !!(value?.request && value?.result);
1414
},
15+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1516
print(value: any) {
1617
const {
1718
request: { variables },
@@ -88,6 +89,7 @@ const getApolloMock = (output: string) => {
8889

8990
fs.writeFileSync(tempFile.name, output);
9091

92+
// eslint-disable-next-line @typescript-eslint/no-var-requires
9193
const module = require(tempFile.name);
9294

9395
return module.default;

src/codegenApolloMock.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import MockVisitor from "./visitors/MockVisitor";
55
import TypedVisitor from "./visitors/TypedVisitor";
66

77
export const plugin: PluginFunction = (schema, documents) => {
8-
const documentNodes = documents.map((document) => document.document!);
8+
const documentNodes = documents.map((document) => {
9+
if (!document.document) {
10+
throw new Error("Missing document node");
11+
}
12+
return document.document;
13+
});
14+
915
const output: string[] = [];
1016
const inputObjectTypeOutput: string[] = [];
1117
const mockVisitor = new MockVisitor(output, inputObjectTypeOutput);

src/codegenTypedDocuments.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,21 @@ export const plugin: PluginFunction<Config> = (schema, documents, config) => {
1515
const output: string[] = [];
1616

1717
documents.forEach((document) => {
18-
const basename = path.basename(document.location!);
18+
if (!document.location) {
19+
throw new Error("Missing document location");
20+
}
21+
if (!document.document) {
22+
throw new Error("Missing document node");
23+
}
24+
25+
const basename = path.basename(document.location);
1926
const typedVisitor = new TypedVisitor(schema);
2027
const typedDocumentVisitor = new TypedDocumentVisitor(
2128
output,
2229
basename,
2330
config
2431
);
25-
const typedDocumentNode = visit(document.document!, typedVisitor);
32+
const typedDocumentNode = visit(document.document, typedVisitor);
2633

2734
visit(typedDocumentNode, typedDocumentVisitor);
2835
});

src/createApolloMock.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type ApolloMockFn = <TVariables extends OperationVariables, TData>(
2727
options?: ApolloMockOptions
2828
) => ApolloMock<TVariables, TData>;
2929

30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3031
export default (operations: any): ApolloMockFn => <
3132
TVariables extends OperationVariables,
3233
TData
@@ -38,7 +39,12 @@ export default (operations: any): ApolloMockFn => <
3839
): ApolloMock<TVariables, TData> => {
3940
const definitionNode = documentNode.definitions[0];
4041
const operationNode = definitionNode as OperationDefinitionNode;
41-
const operationName = operationNode.name!.value;
42+
43+
if (!operationNode.name) {
44+
throw new Error("Missing operation name");
45+
}
46+
47+
const operationName = operationNode.name.value;
4248
const operation = operations[operationName];
4349

4450
if (!operation) {

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { DocumentNode } from "graphql";
22

3-
export type OperationVariables = Record<string, any>;
3+
export type OperationVariables = Record<string, unknown>;
44

5+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
56
export interface TypedDocumentNode<
67
TVariables extends OperationVariables,
78
TData

src/visitors/MockVisitor.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ export default class MockVisitor {
1919
get OperationDefinition() {
2020
return {
2121
leave: (node: OperationDefinitionNode) => {
22-
const name = node.name!.value;
22+
if (!node.name) {
23+
throw new Error("Operation doesn't have a name");
24+
}
25+
26+
const name = node.name.value;
2327
const output: string[] = [];
2428

2529
output.push(`operations.${name} = {};\n`);
@@ -53,8 +57,8 @@ export default class MockVisitor {
5357
}
5458

5559
getTypedFieldSetOutput(
56-
fields: TypedField<any>[],
57-
parent?: TypedField<any>,
60+
fields: TypedField[],
61+
parent?: TypedField,
5862
{ indent = 0, input = false } = {}
5963
) {
6064
const output: string[] = [];
@@ -84,8 +88,8 @@ export default class MockVisitor {
8488
.join(",\n")
8589
);
8690

87-
if (parent && !input) {
88-
const typename = parent.objectType!.name;
91+
if (parent && parent.objectType && !input) {
92+
const typename = parent.objectType.name;
8993

9094
output.push(",\n");
9195
output.push(" ".repeat(indent + 2));
@@ -106,8 +110,12 @@ export default class MockVisitor {
106110
return output.join("");
107111
}
108112

109-
getTypedInputFieldSetOutput(field: TypedField<any>) {
110-
const inputObjectTypeName = field.objectType!.name;
113+
getTypedInputFieldSetOutput(field: TypedField) {
114+
if (!field.objectType) {
115+
throw new Error("Method must only be used for object type fields");
116+
}
117+
118+
const inputObjectTypeName = field.objectType.name;
111119

112120
if (!this.inputObjectTypeOutputState[inputObjectTypeName]) {
113121
const output: string[] = [];
@@ -125,10 +133,7 @@ export default class MockVisitor {
125133
return inputObjectTypeName;
126134
}
127135

128-
getTypedFieldOutput(
129-
field: TypedField<any>,
130-
{ indent = 0, input = false } = {}
131-
) {
136+
getTypedFieldOutput(field: TypedField, { indent = 0, input = false } = {}) {
132137
const name = field.name;
133138
const output: string[] = [];
134139

src/visitors/TypedDocumentVisitor.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import { DocumentNode, OperationDefinitionNode } from "graphql";
1+
import { OperationDefinitionNode } from "graphql";
22
import { pascalCase } from "pascal-case";
33

4-
export type Config = { typesModule: string };
5-
6-
interface OutputNode {
7-
output: string;
4+
export interface Config {
5+
typesModule: string;
86
}
97

108
export default class TypedDocumentVisitor {
119
output: string[];
1210
basename: string;
1311
config: Config;
12+
operationOutput: { [operationName: string]: string } = {};
1413

1514
constructor(output: string[], basename: string, config: Config) {
1615
this.output = output;
@@ -20,52 +19,53 @@ export default class TypedDocumentVisitor {
2019

2120
get Document() {
2221
return {
23-
leave: (node: DocumentNode) => {
22+
leave: () => {
2423
const output: string[] = [];
24+
const operationOutputKeys = Object.keys(this.operationOutput);
25+
const operationOutputValues = Object.values(this.operationOutput);
2526

26-
output.push(`declare module "*/${this.basename}" {`);
27-
output.push(
28-
node.definitions
29-
.map(
30-
(definitionNode) => ((definitionNode as any) as OutputNode).output
31-
)
32-
.join("\n")
33-
);
27+
output.push(`declare module "*/${this.basename}" {\n`);
28+
29+
operationOutputValues.forEach((operationOutput) => {
30+
output.push(operationOutput);
31+
output.push("\n");
32+
});
3433

35-
if (node.definitions.length === 1) {
36-
const definitionNode = node.definitions[0];
37-
const operationNode = definitionNode as OperationDefinitionNode;
38-
output.push(` export default ${operationNode.name!.value}`);
34+
if (operationOutputKeys.length === 1) {
35+
const [operationName] = operationOutputKeys;
36+
output.push(` export default ${operationName}\n`);
3937
}
4038

4139
output.push(`}`);
4240

43-
this.output.push(output.join("\n"));
41+
this.output.push(output.join(""));
4442
},
4543
};
4644
}
4745

4846
get OperationDefinition() {
4947
return {
50-
enter: (_node: OperationDefinitionNode) => {
51-
const node = _node as OperationDefinitionNode & OutputNode;
52-
const output: string[] = [];
53-
const typeName = pascalCase(node.name!.value);
48+
enter: (node: OperationDefinitionNode) => {
49+
if (!node.name) {
50+
throw new Error("Operation must have a name");
51+
}
52+
53+
const operationName = node.name.value;
54+
const typeName = pascalCase(operationName);
5455
const typeNameSuffix = pascalCase(node.operation);
56+
const output: string[] = [];
5557

5658
output.push(
57-
' import { TypedDocumentNode } from "apollo-typed-documents"'
59+
' import { TypedDocumentNode } from "apollo-typed-documents"\n'
5860
);
5961
output.push(
60-
` import {${typeName}${typeNameSuffix}, ${typeName}${typeNameSuffix}Variables} from "${this.config.typesModule}"`
62+
` import { ${typeName}${typeNameSuffix}, ${typeName}${typeNameSuffix}Variables } from "${this.config.typesModule}"\n`
6163
);
6264
output.push(
63-
` export const ${
64-
node.name!.value
65-
}: TypedDocumentNode<${typeName}${typeNameSuffix}Variables, ${typeName}${typeNameSuffix}>`
65+
` export const ${operationName}: TypedDocumentNode<${typeName}${typeNameSuffix}Variables, ${typeName}${typeNameSuffix}>`
6666
);
6767

68-
node.output = output.join("\n");
68+
this.operationOutput[operationName] = output.join("");
6969

7070
return false; // No need to traverse deeper
7171
},

0 commit comments

Comments
 (0)