@devticon-os/graphql-codegen-axios 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devticon-os/graphql-codegen-axios",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "repository": "https://github.com/devticon/graphql-codegen-axios",
package/src/enums.js ADDED
@@ -0,0 +1,24 @@
1
+ const { GraphQLEnumType } = require('graphql/type');
2
+ const findUsageEnums = (types, schema) => {
3
+ const enums = [];
4
+ for (let type of types) {
5
+ enums.push(...findEnumInType(type, schema, enums));
6
+ }
7
+
8
+ return enums.map(e => ({
9
+ name: e.name,
10
+ values: e._values.map(({ name, value }) => ({ name, value })),
11
+ }));
12
+ };
13
+
14
+ const findEnumInType = (type, schema, ignore) => {
15
+ const enums = [];
16
+ for (let field of type.fields) {
17
+ const type = schema._typeMap[field.typeName];
18
+ if (type instanceof GraphQLEnumType && !ignore.includes(type)) {
19
+ enums.push(type);
20
+ }
21
+ }
22
+ return enums;
23
+ };
24
+ module.exports = { findUsageEnums };
@@ -0,0 +1,40 @@
1
+ const { GraphQLObjectType } = require('graphql/type');
2
+ const { getGraphqlTypeInfo } = require('./types');
3
+ const findUsageFragments = (documents, schema) => {
4
+ const fragments = [];
5
+ for (let { document } of documents) {
6
+ for (const definition of document.definitions) {
7
+ if (definition.kind === 'FragmentDefinition') {
8
+ const parentName = definition.typeCondition.name.value;
9
+ const parent = findObjectTypeInSchema(schema, parentName);
10
+ fragments.push({
11
+ name: definition.name.value,
12
+ type: definition,
13
+ fields: definition.selectionSet.selections.map(f => {
14
+ const selections = f.selectionSet?.selections || [];
15
+ const fragments = selections.filter(s => s.kind === 'FragmentSpread');
16
+ const union = fragments.map(f => f.name.value);
17
+ const parentField = parent._fields[f.name.value];
18
+ const typeInfo = getGraphqlTypeInfo(parentField.type);
19
+ return {
20
+ name: f.name.value,
21
+ ...typeInfo,
22
+ fields: [],
23
+ typeName: union.length ? '{}' : typeInfo.typeName,
24
+ union,
25
+ };
26
+ }),
27
+ });
28
+ }
29
+ }
30
+ }
31
+ return fragments;
32
+ };
33
+
34
+ const findObjectTypeInSchema = (schema, name) => {
35
+ const type = schema._typeMap[name];
36
+ if (type instanceof GraphQLObjectType) {
37
+ return type;
38
+ }
39
+ };
40
+ module.exports = { findUsageFragments };
package/src/index.js CHANGED
@@ -1,111 +1,96 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const { print } = require("graphql");
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { print } = require('graphql');
4
+ const { GraphQLList, GraphQLNonNull, GraphQLInputObjectType } = require('graphql/type');
5
+ const { findUsageInputs } = require('./input');
6
+ const { getVariablesFields } = require('./variables');
7
+ const { getResultsFields } = require('./results');
8
+ const { findScalars } = require('./scalar');
9
+ const { renderType, renderQuery, renderSdk, renderScalars, renderEnum } = require('./render');
10
+ const { findUsageEnums } = require('./enums');
11
+ const { findUsageFragments } = require('./fragments');
4
12
 
5
- const getType = (name, operationType, type) => {
6
- name = name.charAt(0).toUpperCase() + name.slice(1);
7
- operationType =
8
- operationType.charAt(0).toUpperCase() + operationType.slice(1);
9
- switch (type) {
10
- case "variables":
11
- return `${name}${operationType}Variables`;
12
- case "results":
13
- return `${name}${operationType}`;
14
- }
15
- };
16
-
17
- const functionsToString = (functions) => {
18
- let str = "{";
19
- for (let [name, body] of Object.entries(functions)) {
20
- str += `${name}: ${body},\n`;
21
- }
22
- str += "}";
23
- return str;
24
- };
25
-
26
- const getUsedFragments = (raw, allFragments, results = []) => {
27
- const fragments = [...raw.matchAll(/\.\.\.(\w*)$/gm)]
28
- .map(([_, name]) => name)
29
- .map((name) => allFragments.find((d) => d.name.value === name));
30
-
31
- for (let fragment of fragments) {
32
- getUsedFragments(print(fragment), allFragments, results);
33
- }
34
- results.push(...fragments);
35
- return results;
36
- };
37
- const printOperation = (ast, allFragments) => {
38
- ast.directives = ast.directives.filter(
39
- (d) => !["first", "firstOrFail"].includes(d.name.value)
40
- );
41
- const raw = print(ast)
42
- .replace("@firstOrFail", "")
43
- .replace("@first", "")
44
- .replace("@nonNullable", "");
13
+ const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);
45
14
 
46
- let fragments = getUsedFragments(raw, allFragments);
47
- fragments = [...new Set(fragments)].map((f) => print(f));
48
-
49
- return fragments.join("\n") + "\n" + raw;
50
- };
51
-
52
- const helpers = fs.readFileSync(path.join(__dirname, "helpers.ts"), "utf-8");
15
+ const helpers = fs.readFileSync(path.join(__dirname, 'helpers.ts'), 'utf-8');
53
16
  module.exports = {
54
17
  plugin(schema, documents, config) {
55
- const functions = {};
56
- const queries = [];
57
- const fragments = [];
18
+ try {
19
+ const functions = [];
20
+ const queries = [];
21
+ const fragments = findUsageFragments(documents, schema);
22
+ const types = [...fragments];
23
+ const scalars = findScalars(schema);
58
24
 
59
- for (let { document } of documents) {
60
- for (const definition of document.definitions) {
61
- if (definition.kind === "FragmentDefinition") {
62
- fragments.push(definition);
63
- }
64
- }
65
- }
25
+ for (let { document } of documents) {
26
+ const inputs = findUsageInputs(document, schema);
27
+ types.push(...inputs);
66
28
 
67
- for (let { document } of documents) {
68
- for (const definition of document.definitions) {
69
- const name = definition.name.value;
29
+ for (const definition of document.definitions) {
30
+ if (definition.kind !== 'OperationDefinition') {
31
+ continue;
32
+ }
33
+ const name = definition.name.value;
70
34
 
71
- if (definition.kind !== "OperationDefinition") {
72
- continue;
73
- }
74
- const variablesType = getType(name, definition.operation, "variables");
75
- const resultsType = getType(name, definition.operation, "results");
35
+ const results = {
36
+ name: capitalize(`${name}Results`),
37
+ fields: getResultsFields(definition, schema, document),
38
+ };
39
+ types.push(results);
76
40
 
77
- queries.push(
78
- `const ${name}RawQuery = \`${printOperation(
79
- definition,
80
- fragments
81
- )}\`;`
82
- );
83
- let func = `(variables: ${variablesType}, config?: AxiosRequestConfig) => client.post<GraphqlResponse<${resultsType}>>("", {variables, query: ${name}RawQuery}, config).then(handleResponse)`;
41
+ const variables = {
42
+ name: capitalize(`${name}Variables`),
43
+ fields: getVariablesFields(definition, schema),
44
+ };
45
+ types.push(variables);
84
46
 
85
- if (definition.selectionSet.selections.length === 1) {
86
- if (!["query", "mutation"].includes(definition.operation)) {
87
- continue;
88
- }
89
- const selection = definition.selectionSet.selections[0];
90
- const directives = selection.directives.map((d) => d.name.value);
91
- const propertyName = selection.name.value;
92
- func += `.then(unpackSingleResults("${propertyName}"))\n`;
93
- for (let directive of directives) {
94
- if (directive === "nonNullable" || directive === "firstOrFail") {
95
- func += `.then(${directive}({variables, query: ${name}RawQuery}))\n`;
96
- } else {
97
- func += `.then(${directive})\n`;
98
- }
99
- }
47
+ queries.push({
48
+ name,
49
+ ast: definition,
50
+ allFragments: fragments,
51
+ });
52
+
53
+ functions.push({
54
+ name,
55
+ results,
56
+ variables,
57
+ chain: [],
58
+ });
100
59
  }
101
- functions[name] = func;
60
+
61
+ // if (definition.selectionSet.selections.length === 1) {
62
+ // if (!['query', 'mutation'].includes(definition.operation)) {
63
+ // continue;
64
+ // }
65
+ // const selection = definition.selectionSet.selections[0];
66
+ // const directives = selection.directives.map(d => d.name.value);
67
+ // const propertyName = selection.name.value;
68
+ // func += `.then(unpackSingleResults("${propertyName}"))\n`;
69
+ // for (let directive of directives) {
70
+ // if (directive === 'nonNullable' || directive === 'firstOrFail') {
71
+ // func += `.then(${directive}({variables, query: ${name}RawQuery}))\n`;
72
+ // } else {
73
+ // func += `.then(${directive})\n`;
74
+ // }
75
+ // }
76
+ // }
77
+ // functions[name] = func;
78
+ // }
102
79
  }
103
- }
104
80
 
105
- const sdk = `export const getSdk = (client: AxiosInstance) => (${functionsToString(
106
- functions
107
- )})`;
108
- return [...queries, helpers, sdk].join("\n");
81
+ const enums = findUsageEnums(types, schema);
82
+ return [
83
+ helpers,
84
+ renderScalars(scalars),
85
+ ...enums.map(e => renderEnum(e)),
86
+ ...types.map(t => renderType(t)),
87
+ ...queries.map(q => renderQuery(q)),
88
+ renderSdk(functions),
89
+ ].join('\n');
90
+ } catch (e) {
91
+ console.log(e);
92
+ process.exit(1);
93
+ }
109
94
  },
110
95
  addToSchema: /* GraphQL */ `
111
96
  directive @first on OBJECT
package/src/input.js ADDED
@@ -0,0 +1,62 @@
1
+ const { GraphQLInputObjectType, GraphQLNonNull, GraphQLList } = require('graphql/type');
2
+ const { getGraphqlTypeInfo } = require('./types');
3
+ const findUsageInputs = (document, schema) => {
4
+ const inputs = [];
5
+ for (let definition of document.definitions) {
6
+ if (definition.kind !== 'OperationDefinition') {
7
+ continue;
8
+ }
9
+ for (const variableDefinition of definition.variableDefinitions) {
10
+ const name = variableDefinition.type.name.value;
11
+ const input = findInputInSchema(name, schema);
12
+ if (input) {
13
+ inputs.push(input);
14
+ inputs.push(...findInputDependencies(input, schema, inputs));
15
+ }
16
+ }
17
+ }
18
+ return inputs.map(input => ({
19
+ name: input.name,
20
+ fields: getInputFields(input),
21
+ }));
22
+ };
23
+ const findInputInSchema = (name, schema) => {
24
+ const type = schema._typeMap[name];
25
+ if (type instanceof GraphQLInputObjectType) {
26
+ return type;
27
+ }
28
+ };
29
+ const findInputDependencies = (input, schema, ignore) => {
30
+ const dependencies = [];
31
+ for (let field of Object.values(input._fields)) {
32
+ const type = unpackInputType(field.type);
33
+ if (type instanceof GraphQLInputObjectType && !ignore.includes(type)) {
34
+ dependencies.push(type);
35
+ dependencies.push(...findInputDependencies(type, schema, [...ignore, ...dependencies]));
36
+ }
37
+ }
38
+ return dependencies;
39
+ };
40
+
41
+ const getInputFields = input => {
42
+ return Object.values(input._fields).map(field => {
43
+ const typeInfo = getGraphqlTypeInfo(field.type);
44
+ return {
45
+ name: field.name,
46
+ fields: [],
47
+ ...typeInfo,
48
+ };
49
+ });
50
+ };
51
+
52
+ const unpackInputType = type => {
53
+ if (type instanceof GraphQLNonNull) {
54
+ return unpackInputType(type.ofType);
55
+ }
56
+ if (type instanceof GraphQLList) {
57
+ return unpackInputType(type.ofType);
58
+ }
59
+ return type;
60
+ };
61
+
62
+ module.exports = { findUsageInputs, findInputInSchema };
package/src/query.js ADDED
@@ -0,0 +1,14 @@
1
+ const { print } = require('graphql/index');
2
+ const getUsedFragments = (raw, allFragments, results = []) => {
3
+ const fragments = [...raw.matchAll(/\.\.\.(\w*)$/gm)]
4
+ .map(([_, name]) => name)
5
+ .map(name => allFragments.find(d => d.name === name));
6
+
7
+ for (let fragment of fragments) {
8
+ getUsedFragments(print(fragment.type), allFragments, results);
9
+ }
10
+ results.push(...fragments);
11
+ return results;
12
+ };
13
+
14
+ module.exports = { getUsedFragments };
package/src/render.js ADDED
@@ -0,0 +1,74 @@
1
+ const { print } = require('graphql/index');
2
+ const { getUsedFragments } = require('./query');
3
+ const renderType = ({ name, fields }) => {
4
+ return `export type ${name} = { ${renderTypeField(fields)} };`;
5
+ };
6
+ const renderTypeField = fields => {
7
+ return fields
8
+ .map(({ isList, isNullable, typeName, name, fields, isScalar, union }) => {
9
+ let tsType = '';
10
+ if (union && union.length) {
11
+ tsType += [...union, ''].join(' & ');
12
+ }
13
+ if (isScalar) {
14
+ tsType += getScalarTsType(typeName);
15
+ } else {
16
+ if (fields.length) {
17
+ tsType += `{${renderTypeField(fields)}}`;
18
+ } else {
19
+ tsType += typeName;
20
+ }
21
+ }
22
+ return `${name}${isNullable ? '?' : ''}: ${tsType}${isList ? '[]' : ''}`;
23
+ })
24
+ .join(',\n');
25
+ };
26
+
27
+ const renderSdk = functions => {
28
+ let str = '{';
29
+ for (let func of functions) {
30
+ str += `${func.name}: ${renderFunction(func)},\n`;
31
+ }
32
+ str += '}';
33
+ return `export const getSdk = (client: AxiosInstance) => (${str})`;
34
+ };
35
+ const renderFunction = ({ name, variables, results }) => {
36
+ return `(variables: ${variables.name}, config?: AxiosRequestConfig) => client.post<GraphqlResponse<${results.name}>>("", {variables, query: ${name}RawQuery}, config).then(handleResponse)`;
37
+ };
38
+
39
+ const renderQuery = ({ name, ast, allFragments }) => {
40
+ const raw = print(ast).replace('@firstOrFail', '').replace('@first', '').replace('@nonNullable', '');
41
+
42
+ let fragments = getUsedFragments(raw, allFragments);
43
+ fragments = [...new Set(fragments)].map(f => print(f.type));
44
+
45
+ const gql = fragments.join('\n') + '\n' + raw;
46
+ return `const ${name}RawQuery = \`${gql}\`;`;
47
+ };
48
+
49
+ const getScalarTsType = name => `Scalar["${name}"]`;
50
+
51
+ const renderEnum = e => {
52
+ let str = `export enum ${e.name} {`;
53
+ for (let { name, value } of e.values) {
54
+ str += `${name} = "${value}",`;
55
+ }
56
+ str += `};`;
57
+ return str;
58
+ };
59
+ const renderScalars = (scalars, map = {}) => {
60
+ map = {
61
+ String: 'string',
62
+ Boolean: 'boolean',
63
+ Int: 'number',
64
+ Float: 'number',
65
+ ...map,
66
+ };
67
+ return `export type Scalar = {${scalars
68
+ .map(({ name }) => {
69
+ const type = map[name] || 'string';
70
+ return `${name}: ${type}`;
71
+ })
72
+ .join(',')}};`;
73
+ };
74
+ module.exports = { renderType, renderQuery, renderSdk, renderScalars, renderEnum };
package/src/results.js ADDED
@@ -0,0 +1,34 @@
1
+ const { getGraphqlTypeInfo } = require('./types');
2
+ const getResultsFields = (definition, schema, document) => {
3
+ const operationType = definition.operation.charAt(0).toUpperCase() + definition.operation.slice(1);
4
+ const parent = schema._typeMap[operationType];
5
+ return definition.selectionSet.selections.map(field => getField(parent, field, schema, document));
6
+ };
7
+
8
+ const getField = (parent, field, schema, document) => {
9
+ const name = field.name.value;
10
+ const selections = field.selectionSet?.selections || [];
11
+ const fragments = selections.filter(s => s.kind === 'FragmentSpread');
12
+ let type = getGraphqlTypeInfo(parent._fields[name].type);
13
+ const union = fragments.map(f => f.name.value);
14
+ const fields = selections.filter(s => s.kind !== 'FragmentSpread').map(f => getField(type.type, f, schema, document));
15
+
16
+ //
17
+ // const fields = [];
18
+ // for (let selection of selections) {
19
+ // const isFragment = selection.kind === 'FragmentSpread';
20
+ // if (isFragment) {
21
+ // fields.push({ typeName: selection.name.value, isScalar: false, isList: false, isNullable: false, fields: [] });
22
+ // } else {
23
+ // fields.push(getField(type.type, selection, schema, document));
24
+ // }
25
+ // }
26
+ return {
27
+ name,
28
+ ...type,
29
+ fields,
30
+ union,
31
+ };
32
+ };
33
+
34
+ module.exports = { getResultsFields };
package/src/scalar.js ADDED
@@ -0,0 +1,6 @@
1
+ const { GraphQLScalarType } = require('graphql/type');
2
+ const findScalars = schema => {
3
+ return Object.values(schema._typeMap).filter(t => t instanceof GraphQLScalarType);
4
+ };
5
+
6
+ module.exports = { findScalars };
package/src/types.js ADDED
@@ -0,0 +1,15 @@
1
+ const { GraphQLList, GraphQLNonNull, GraphQLScalarType } = require('graphql/type');
2
+ const getGraphqlTypeInfo = (type, isList = false, isNullable = true) => {
3
+ if (type instanceof GraphQLList) {
4
+ isList = true;
5
+ return getGraphqlTypeInfo(type.ofType, isList, isNullable);
6
+ }
7
+ if (type instanceof GraphQLNonNull) {
8
+ isNullable = false;
9
+ return getGraphqlTypeInfo(type.ofType, isList, isNullable);
10
+ }
11
+ const isScalar = type instanceof GraphQLScalarType;
12
+ return { type, isList, isNullable, typeName: type.name, isScalar };
13
+ };
14
+
15
+ module.exports = { getGraphqlTypeInfo };
@@ -0,0 +1,24 @@
1
+ const { findInputInSchema } = require('./input');
2
+ const getVariablesFields = (definition, schema) => {
3
+ return definition.variableDefinitions.map(variable => ({
4
+ name: variable.variable.name.value,
5
+ ...getVariableType(variable.type, schema),
6
+ fields: [],
7
+ }));
8
+ };
9
+
10
+ const getVariableType = (type, schema, isList = false, isNullable = true) => {
11
+ if (type.kind === 'ListType') {
12
+ isList = true;
13
+ return getVariableType(type.type, schema, isList, isNullable);
14
+ }
15
+ if (type.kind === 'NonNullType') {
16
+ isNullable = false;
17
+ return getVariableType(type.type, schema, isList, isNullable);
18
+ }
19
+ const typeName = type.name.value;
20
+ const isScalar = !findInputInSchema(typeName, schema);
21
+ return { type: type.type, isList, isNullable, typeName, isScalar };
22
+ };
23
+
24
+ module.exports = { getVariablesFields };