@constructive-io/graphql-codegen 2.17.11
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/LICENSE +23 -0
- package/README.md +161 -0
- package/codegen.d.ts +13 -0
- package/codegen.js +267 -0
- package/esm/codegen.js +227 -0
- package/esm/gql.js +721 -0
- package/esm/index.js +3 -0
- package/esm/options.js +21 -0
- package/gql.d.ts +163 -0
- package/gql.js +774 -0
- package/index.d.ts +3 -0
- package/index.js +19 -0
- package/options.d.ts +37 -0
- package/options.js +25 -0
- package/package.json +62 -0
package/esm/codegen.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { join, dirname, isAbsolute, resolve } from 'path';
|
|
3
|
+
import { buildSchema, buildClientSchema, graphql, getIntrospectionQuery, print } from 'graphql';
|
|
4
|
+
const inflection = require('inflection');
|
|
5
|
+
import { generate as generateGql } from './gql';
|
|
6
|
+
import { parseGraphQuery } from 'introspectron';
|
|
7
|
+
import { defaultGraphQLCodegenOptions, mergeGraphQLCodegenOptions } from './options';
|
|
8
|
+
import { codegen as runCoreCodegen } from '@graphql-codegen/core';
|
|
9
|
+
import * as typescriptPlugin from '@graphql-codegen/typescript';
|
|
10
|
+
import * as typescriptOperationsPlugin from '@graphql-codegen/typescript-operations';
|
|
11
|
+
import * as typescriptGraphqlRequestPlugin from '@graphql-codegen/typescript-graphql-request';
|
|
12
|
+
import * as typescriptReactQueryPlugin from '@graphql-codegen/typescript-react-query';
|
|
13
|
+
import { GraphQLClient } from 'graphql-request';
|
|
14
|
+
import { parse } from '@babel/parser';
|
|
15
|
+
import generate from '@babel/generator';
|
|
16
|
+
import * as t from '@babel/types';
|
|
17
|
+
function addDocumentNodeImport(code) {
|
|
18
|
+
const ast = parse(code, {
|
|
19
|
+
sourceType: 'module',
|
|
20
|
+
plugins: ['typescript']
|
|
21
|
+
});
|
|
22
|
+
const importDecl = t.importDeclaration([t.importSpecifier(t.identifier('DocumentNode'), t.identifier('DocumentNode'))], t.stringLiteral('graphql'));
|
|
23
|
+
importDecl.importKind = 'type';
|
|
24
|
+
ast.program.body.unshift(importDecl);
|
|
25
|
+
const output = generate(ast, {}, code);
|
|
26
|
+
return output.code;
|
|
27
|
+
}
|
|
28
|
+
function getFilename(key, convention) {
|
|
29
|
+
if (convention === 'underscore')
|
|
30
|
+
return inflection.underscore(key);
|
|
31
|
+
if (convention === 'dashed')
|
|
32
|
+
return inflection.underscore(key).replace(/_/g, '-');
|
|
33
|
+
if (convention === 'camelUpper')
|
|
34
|
+
return inflection.camelize(key, false);
|
|
35
|
+
return key;
|
|
36
|
+
}
|
|
37
|
+
async function ensureDir(p) {
|
|
38
|
+
await fs.mkdir(p, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
async function readFileUTF8(p) {
|
|
41
|
+
return fs.readFile(p, 'utf8');
|
|
42
|
+
}
|
|
43
|
+
async function writeFileUTF8(p, content) {
|
|
44
|
+
await ensureDir(dirname(p));
|
|
45
|
+
await fs.writeFile(p, content, 'utf8');
|
|
46
|
+
}
|
|
47
|
+
async function getIntrospectionFromSDL(schemaPath) {
|
|
48
|
+
const sdl = await readFileUTF8(schemaPath);
|
|
49
|
+
const schema = buildSchema(sdl);
|
|
50
|
+
const q = getIntrospectionQuery();
|
|
51
|
+
const res = await graphql({ schema, source: q });
|
|
52
|
+
return res.data;
|
|
53
|
+
}
|
|
54
|
+
async function getIntrospectionFromEndpoint(endpoint, headers) {
|
|
55
|
+
const client = new GraphQLClient(endpoint, { headers });
|
|
56
|
+
const q = getIntrospectionQuery();
|
|
57
|
+
const res = await client.request(q);
|
|
58
|
+
return res;
|
|
59
|
+
}
|
|
60
|
+
function generateKeyedObjFromGqlMap(gqlMap) {
|
|
61
|
+
const gen = generateGql(gqlMap);
|
|
62
|
+
return Object.entries(gen).reduce((acc, [key, val]) => {
|
|
63
|
+
if (val?.ast)
|
|
64
|
+
acc[key] = print(val.ast);
|
|
65
|
+
return acc;
|
|
66
|
+
}, {});
|
|
67
|
+
}
|
|
68
|
+
function applyQueryFilters(map, docs) {
|
|
69
|
+
const allow = (docs.allowQueries || []).filter(Boolean);
|
|
70
|
+
const exclude = (docs.excludeQueries || []).filter(Boolean);
|
|
71
|
+
const patterns = (docs.excludePatterns || []).filter(Boolean);
|
|
72
|
+
let keys = Object.keys(map);
|
|
73
|
+
if (allow.length > 0)
|
|
74
|
+
keys = keys.filter((k) => allow.includes(k));
|
|
75
|
+
if (exclude.length > 0)
|
|
76
|
+
keys = keys.filter((k) => !exclude.includes(k));
|
|
77
|
+
if (patterns.length > 0) {
|
|
78
|
+
const regs = patterns.map((p) => {
|
|
79
|
+
try {
|
|
80
|
+
return new RegExp(p);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}).filter((r) => !!r);
|
|
86
|
+
keys = keys.filter((k) => !regs.some((r) => r.test(k)));
|
|
87
|
+
}
|
|
88
|
+
return keys.reduce((acc, k) => {
|
|
89
|
+
acc[k] = map[k];
|
|
90
|
+
return acc;
|
|
91
|
+
}, {});
|
|
92
|
+
}
|
|
93
|
+
async function writeOperationsDocuments(docs, dir, format, convention) {
|
|
94
|
+
await ensureDir(dir);
|
|
95
|
+
const index = [];
|
|
96
|
+
for (const key of Object.keys(docs)) {
|
|
97
|
+
const filename = getFilename(key, convention) + (format === 'ts' ? '.ts' : '.gql');
|
|
98
|
+
if (format === 'ts') {
|
|
99
|
+
const code = `import gql from 'graphql-tag'\nexport const ${key} = gql\`\n${docs[key]}\n\``;
|
|
100
|
+
await writeFileUTF8(join(dir, filename), code);
|
|
101
|
+
index.push(`export * from './${filename}'`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
await writeFileUTF8(join(dir, filename), docs[key]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (format === 'ts')
|
|
108
|
+
await writeFileUTF8(join(dir, 'index.ts'), index.sort().join('\n'));
|
|
109
|
+
}
|
|
110
|
+
export async function runCodegen(opts, cwd) {
|
|
111
|
+
const options = {
|
|
112
|
+
input: { ...(defaultGraphQLCodegenOptions.input), ...(opts.input || {}) },
|
|
113
|
+
output: { ...(defaultGraphQLCodegenOptions.output), ...(opts.output || {}) },
|
|
114
|
+
documents: { ...(defaultGraphQLCodegenOptions.documents), ...(opts.documents || {}) },
|
|
115
|
+
features: { ...(defaultGraphQLCodegenOptions.features), ...(opts.features || {}) }
|
|
116
|
+
};
|
|
117
|
+
const root = join(cwd, options.output.root);
|
|
118
|
+
const typesFile = join(root, options.output.typesFile);
|
|
119
|
+
const operationsDir = join(root, options.output.operationsDir);
|
|
120
|
+
const sdkFile = join(root, options.output.sdkFile);
|
|
121
|
+
const reactQueryFile = join(root, options.output.reactQueryFile || 'react-query.ts');
|
|
122
|
+
const hasSchemaPath = !!options.input.schema && options.input.schema.trim() !== '';
|
|
123
|
+
const hasEndpoint = !!options.input.endpoint && options.input.endpoint.trim() !== '';
|
|
124
|
+
const schemaPath = hasSchemaPath ? (isAbsolute(options.input.schema) ? options.input.schema : resolve(cwd, options.input.schema)) : '';
|
|
125
|
+
const introspection = hasEndpoint
|
|
126
|
+
? await getIntrospectionFromEndpoint(options.input.endpoint, options.input.headers || {})
|
|
127
|
+
: await getIntrospectionFromSDL(schemaPath);
|
|
128
|
+
const { queries, mutations } = parseGraphQuery(introspection);
|
|
129
|
+
const gqlMap = applyQueryFilters({ ...queries, ...mutations }, options.documents);
|
|
130
|
+
let docs = {};
|
|
131
|
+
const schema = hasEndpoint
|
|
132
|
+
? buildClientSchema(introspection)
|
|
133
|
+
: buildSchema(await readFileUTF8(schemaPath));
|
|
134
|
+
if (options.features.emitOperations || options.features.emitSdk || options.features.emitReactQuery) {
|
|
135
|
+
docs = generateKeyedObjFromGqlMap(gqlMap);
|
|
136
|
+
}
|
|
137
|
+
if (options.features.emitOperations) {
|
|
138
|
+
await writeOperationsDocuments(docs, operationsDir, options.documents.format, options.documents.convention);
|
|
139
|
+
}
|
|
140
|
+
if (options.features.emitTypes) {
|
|
141
|
+
const typesContent = await runCoreCodegen({
|
|
142
|
+
filename: typesFile,
|
|
143
|
+
schema: schema,
|
|
144
|
+
documents: [],
|
|
145
|
+
config: {},
|
|
146
|
+
plugins: [{ typescript: {} }],
|
|
147
|
+
pluginMap: { typescript: typescriptPlugin }
|
|
148
|
+
});
|
|
149
|
+
await writeFileUTF8(typesFile, typesContent);
|
|
150
|
+
}
|
|
151
|
+
if (options.features.emitSdk) {
|
|
152
|
+
const documents = [];
|
|
153
|
+
for (const [name, content] of Object.entries(docs)) {
|
|
154
|
+
try {
|
|
155
|
+
const doc = require('graphql').parse(content);
|
|
156
|
+
documents.push({ location: name, document: doc });
|
|
157
|
+
}
|
|
158
|
+
catch (e) { }
|
|
159
|
+
}
|
|
160
|
+
const sdkContent = await runCoreCodegen({
|
|
161
|
+
filename: sdkFile,
|
|
162
|
+
schema: schema,
|
|
163
|
+
documents,
|
|
164
|
+
config: {},
|
|
165
|
+
plugins: [
|
|
166
|
+
{ typescript: {} },
|
|
167
|
+
{ 'typescript-operations': {} },
|
|
168
|
+
{ 'typescript-graphql-request': {} }
|
|
169
|
+
],
|
|
170
|
+
pluginMap: {
|
|
171
|
+
typescript: typescriptPlugin,
|
|
172
|
+
'typescript-operations': typescriptOperationsPlugin,
|
|
173
|
+
'typescript-graphql-request': typescriptGraphqlRequestPlugin
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
// Fix TS2742: Add missing DocumentNode import using Babel AST
|
|
177
|
+
const sdkContentWithImport = addDocumentNodeImport(sdkContent);
|
|
178
|
+
await writeFileUTF8(sdkFile, sdkContentWithImport);
|
|
179
|
+
}
|
|
180
|
+
if (options.features.emitReactQuery) {
|
|
181
|
+
const documents = [];
|
|
182
|
+
for (const [name, content] of Object.entries(docs)) {
|
|
183
|
+
try {
|
|
184
|
+
const doc = require('graphql').parse(content);
|
|
185
|
+
documents.push({ location: name, document: doc });
|
|
186
|
+
}
|
|
187
|
+
catch (e) { }
|
|
188
|
+
}
|
|
189
|
+
const rqConfig = {
|
|
190
|
+
fetcher: options.reactQuery?.fetcher || 'graphql-request',
|
|
191
|
+
legacyMode: options.reactQuery?.legacyMode || false,
|
|
192
|
+
exposeDocument: options.reactQuery?.exposeDocument || false,
|
|
193
|
+
addInfiniteQuery: options.reactQuery?.addInfiniteQuery || false
|
|
194
|
+
};
|
|
195
|
+
const rqContent = await runCoreCodegen({
|
|
196
|
+
filename: reactQueryFile,
|
|
197
|
+
schema: schema,
|
|
198
|
+
documents,
|
|
199
|
+
config: rqConfig,
|
|
200
|
+
plugins: [
|
|
201
|
+
{ typescript: {} },
|
|
202
|
+
{ 'typescript-operations': {} },
|
|
203
|
+
{ 'typescript-react-query': rqConfig }
|
|
204
|
+
],
|
|
205
|
+
pluginMap: {
|
|
206
|
+
typescript: typescriptPlugin,
|
|
207
|
+
'typescript-operations': typescriptOperationsPlugin,
|
|
208
|
+
'typescript-react-query': typescriptReactQueryPlugin
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
await writeFileUTF8(reactQueryFile, rqContent);
|
|
212
|
+
}
|
|
213
|
+
return { root, typesFile, operationsDir, sdkFile };
|
|
214
|
+
}
|
|
215
|
+
export async function runCodegenFromJSONConfig(configPath, cwd) {
|
|
216
|
+
const path = isAbsolute(configPath) ? configPath : resolve(cwd, configPath);
|
|
217
|
+
const content = await readFileUTF8(path);
|
|
218
|
+
let overrides = {};
|
|
219
|
+
try {
|
|
220
|
+
overrides = JSON.parse(content);
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
throw new Error('Invalid JSON config: ' + e);
|
|
224
|
+
}
|
|
225
|
+
const merged = mergeGraphQLCodegenOptions(defaultGraphQLCodegenOptions, overrides);
|
|
226
|
+
return runCodegen(merged, cwd);
|
|
227
|
+
}
|