graphql-jerky 1.0.1 → 1.0.3
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/build/bin/jerky.mjs +1 -0
- package/build/lib.cjs +120 -54
- package/build/lib.d.ts +5 -3
- package/build/lib.mjs +120 -54
- package/package.json +3 -1
package/build/bin/jerky.mjs
CHANGED
package/build/lib.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var url = require('url');
|
|
|
10
10
|
var fs = require('fs/promises');
|
|
11
11
|
var ts = require('typescript');
|
|
12
12
|
var glob = require('glob');
|
|
13
|
+
var pm = require('picomatch');
|
|
13
14
|
var zip = require('just-zip-it');
|
|
14
15
|
|
|
15
16
|
async function parseSource(filepath) {
|
|
@@ -38,23 +39,26 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
38
39
|
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
39
40
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
40
41
|
if (node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
41
|
-
exports.default = { declaration: node, source, name: "default" };
|
|
42
|
+
(exports.default || (exports.default = [])).push({ declaration: node, source, name: "default" });
|
|
42
43
|
}
|
|
43
44
|
else if (node.name) {
|
|
44
|
-
|
|
45
|
+
const name = node.name.getText(source);
|
|
46
|
+
(exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
else if (ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
|
|
49
51
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
50
|
-
|
|
52
|
+
const name = node.name.getText(source);
|
|
53
|
+
(exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
else if (ts.isVariableStatement(node)) {
|
|
54
57
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
55
58
|
for (const decl of node.declarationList.declarations) {
|
|
56
59
|
if (decl.name) {
|
|
57
|
-
|
|
60
|
+
const name = decl.name.getText(source);
|
|
61
|
+
(exports[name] || (exports[name] = [])).push({ declaration: decl, source, name: name });
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
@@ -72,7 +76,7 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
72
76
|
if (!(name in moduleExports)) {
|
|
73
77
|
throw new Error(`Exported name ${name} not found in ${JSON.stringify(specifier)}`);
|
|
74
78
|
}
|
|
75
|
-
exports[name] = moduleExports[name];
|
|
79
|
+
(exports[name] || (exports[name] = [])).push(...moduleExports[name]);
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
else {
|
|
@@ -81,7 +85,6 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
81
85
|
name: element.name.text,
|
|
82
86
|
property: element.propertyName?.text,
|
|
83
87
|
});
|
|
84
|
-
// exports[element.name.getText(source)] = { declaration: element, source };
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
}
|
|
@@ -97,57 +100,63 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
97
100
|
if (ts.isVariableStatement(statement)) {
|
|
98
101
|
for (const decl of statement.declarationList.declarations) {
|
|
99
102
|
if (ts.isIdentifier(decl.name) && decl.name.getText(source) in names) {
|
|
100
|
-
|
|
103
|
+
const name = names[decl.name.getText(source)];
|
|
104
|
+
(exports[name] || (exports[name] = [])).push({
|
|
101
105
|
declaration: decl,
|
|
102
106
|
source,
|
|
103
107
|
name: names[decl.name.getText(source)],
|
|
104
|
-
};
|
|
108
|
+
});
|
|
105
109
|
}
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
if (ts.isFunctionDeclaration(statement)) {
|
|
109
113
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
110
|
-
|
|
114
|
+
const name = names[statement.name.getText(source)];
|
|
115
|
+
(exports[name] || (exports[name] = [])).push({
|
|
111
116
|
declaration: statement,
|
|
112
117
|
source,
|
|
113
118
|
name: names[statement.name.getText(source)],
|
|
114
|
-
};
|
|
119
|
+
});
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
122
|
if (ts.isClassDeclaration(statement)) {
|
|
118
123
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
119
|
-
|
|
124
|
+
const name = names[statement.name.getText(source)];
|
|
125
|
+
(exports[name] || (exports[name] = [])).push({
|
|
120
126
|
declaration: statement,
|
|
121
127
|
source,
|
|
122
128
|
name: names[statement.name.getText(source)],
|
|
123
|
-
};
|
|
129
|
+
});
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
if (ts.isTypeAliasDeclaration(statement)) {
|
|
127
133
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
128
|
-
|
|
134
|
+
const name = names[statement.name.getText(source)];
|
|
135
|
+
(exports[name] || (exports[name] = [])).push({
|
|
129
136
|
declaration: statement,
|
|
130
137
|
source,
|
|
131
138
|
name: names[statement.name.getText(source)],
|
|
132
|
-
};
|
|
139
|
+
});
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
if (ts.isInterfaceDeclaration(statement)) {
|
|
136
143
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
137
|
-
|
|
144
|
+
const name = names[statement.name.getText(source)];
|
|
145
|
+
(exports[name] || (exports[name] = [])).push({
|
|
138
146
|
declaration: statement,
|
|
139
147
|
source,
|
|
140
148
|
name: names[statement.name.getText(source)],
|
|
141
|
-
};
|
|
149
|
+
});
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
if (ts.isEnumDeclaration(statement)) {
|
|
145
153
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
146
|
-
|
|
154
|
+
const name = names[statement.name.getText(source)];
|
|
155
|
+
(exports[name] || (exports[name] = [])).push({
|
|
147
156
|
declaration: statement,
|
|
148
157
|
source,
|
|
149
158
|
name: names[statement.name.getText(source)],
|
|
150
|
-
};
|
|
159
|
+
});
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
if (ts.isImportDeclaration(statement)) {
|
|
@@ -167,11 +176,12 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
167
176
|
if (!importExports[element.name.getText(source)]) {
|
|
168
177
|
throw new Error(`Exported name ${element.name.getText(source)} not found in ${JSON.stringify(statement.moduleSpecifier.getText(source))}`);
|
|
169
178
|
}
|
|
170
|
-
|
|
179
|
+
const name = names[element.propertyName.getText(source)];
|
|
180
|
+
(exports[name] || (exports[name] = [])).push({
|
|
171
181
|
declaration: element,
|
|
172
182
|
source,
|
|
173
183
|
name: names[element.propertyName.getText(source)],
|
|
174
|
-
};
|
|
184
|
+
});
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
187
|
}
|
|
@@ -238,16 +248,9 @@ function compareEntryKey([a], [b]) {
|
|
|
238
248
|
function compareName({ name: a }, { name: b }) {
|
|
239
249
|
return a.localeCompare(b);
|
|
240
250
|
}
|
|
251
|
+
const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
|
|
241
252
|
function isPrimitiveType(type) {
|
|
242
|
-
|
|
243
|
-
case "ID":
|
|
244
|
-
case "Int":
|
|
245
|
-
case "Float":
|
|
246
|
-
case "String":
|
|
247
|
-
case "Boolean":
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
return false;
|
|
253
|
+
return PRIMITIVE_TYPES.has(type.name);
|
|
251
254
|
}
|
|
252
255
|
function importAlias(line) {
|
|
253
256
|
const pos = line.indexOf(" as ");
|
|
@@ -419,7 +422,7 @@ class GeneratorContext {
|
|
|
419
422
|
// NOTE: Resolver functions on interface definitions are not used by the default graphql-js
|
|
420
423
|
// executor implementation, but nonetheless accessible by a potential alternate implementation.
|
|
421
424
|
resolveFn && jen.prop("resolve", jen.parens(resolveFn).as.any),
|
|
422
|
-
subscriber && jen.prop("subscribe", renderSymbolReference(this._pkgs, subscriber)),
|
|
425
|
+
subscriber && jen.prop("subscribe", jen.parens(renderSymbolReference(this._pkgs, subscriber)).as.any),
|
|
423
426
|
]));
|
|
424
427
|
}
|
|
425
428
|
_renderFieldsThunk(type, inputFieldDirectives, noresolver) {
|
|
@@ -501,7 +504,7 @@ class GeneratorContext {
|
|
|
501
504
|
}
|
|
502
505
|
_renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives }) {
|
|
503
506
|
const typeInterfaces = type.getInterfaces();
|
|
504
|
-
return jen.new.add(this._gql("GraphQLObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives)), ...truthify([
|
|
507
|
+
return jen.new.add(this._gql("GraphQLObjectType")).call(jen.obj(jen.prop("name", jen.lit(type.name)), jen.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, type.name === "Subscription")), ...truthify([
|
|
505
508
|
type.description && jen.prop("description", jen.lit(type.description)),
|
|
506
509
|
typeInterfaces.length && jen.prop("interfaces", jen.arrow().arr(
|
|
507
510
|
// do not sort interfaces, the declaration order may be important
|
|
@@ -741,6 +744,20 @@ class GeneratorContext {
|
|
|
741
744
|
.map((directive) => [directive.name, this._renderGraphQLDirectiveDefinition(directive)])
|
|
742
745
|
.sort(compareEntryKey),
|
|
743
746
|
].map(([, expr]) => expr),
|
|
747
|
+
...Array.from(PRIMITIVE_TYPES)
|
|
748
|
+
.flatMap((type) => {
|
|
749
|
+
const info = this._scalarsInfo[type];
|
|
750
|
+
if (!info) {
|
|
751
|
+
return [];
|
|
752
|
+
}
|
|
753
|
+
return [
|
|
754
|
+
jen.statements(...[
|
|
755
|
+
info.serializeFunc && this._gql("GraphQL" + type).dot("serialize").op("=").parens(renderSymbolReference(this._pkgs, info.serializeFunc).as.any),
|
|
756
|
+
info.parseValueFunc && this._gql("GraphQL" + type).dot("parseValue").op("=").parens(renderSymbolReference(this._pkgs, info.parseValueFunc).as.any),
|
|
757
|
+
info.parseLiteralFunc && this._gql("GraphQL" + type).dot("parseLiteral").op("=").parens(renderSymbolReference(this._pkgs, info.parseLiteralFunc).as.any),
|
|
758
|
+
].filter(Boolean))
|
|
759
|
+
];
|
|
760
|
+
}),
|
|
744
761
|
// render schema
|
|
745
762
|
jen.const.id("schema").op("=").new.add(this._gql("GraphQLSchema")).call(jen.obj(...(schema.getQueryType()
|
|
746
763
|
? [jen.prop("query", this._renderGraphQLTypeIdentifier(schema.getQueryType()))]
|
|
@@ -880,9 +897,44 @@ function generate(schema, outfile, options = {}) {
|
|
|
880
897
|
return createGenerator(schema, options).generate(outfile);
|
|
881
898
|
}
|
|
882
899
|
|
|
900
|
+
function createMatcher(pattern) {
|
|
901
|
+
if (!pattern) {
|
|
902
|
+
return () => false;
|
|
903
|
+
}
|
|
904
|
+
if (typeof pattern === "function") {
|
|
905
|
+
return pattern;
|
|
906
|
+
}
|
|
907
|
+
if (pattern instanceof RegExp) {
|
|
908
|
+
return (s) => pattern.test(s);
|
|
909
|
+
}
|
|
910
|
+
if (typeof pattern === "string") {
|
|
911
|
+
return pm(pattern);
|
|
912
|
+
}
|
|
913
|
+
let matchers = [];
|
|
914
|
+
let globs = [];
|
|
915
|
+
for (const p of Array.isArray(pattern) ? pattern : [pattern]) {
|
|
916
|
+
if (typeof p === "string") {
|
|
917
|
+
globs.push(p);
|
|
918
|
+
}
|
|
919
|
+
else if (typeof p === "function") {
|
|
920
|
+
matchers.push(p);
|
|
921
|
+
}
|
|
922
|
+
else if (p instanceof RegExp) {
|
|
923
|
+
matchers.push(RegExp.prototype.test.bind(p));
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
throw new TypeError(`Invalid pattern: ${p}`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (globs.length) {
|
|
930
|
+
matchers.push(pm(globs));
|
|
931
|
+
}
|
|
932
|
+
return (s) => matchers.some((m) => m(s));
|
|
933
|
+
}
|
|
934
|
+
|
|
883
935
|
async function parseResolverFile(path) {
|
|
884
936
|
return Object.fromEntries(Object.entries((await parseModule(path)).exports)
|
|
885
|
-
.flatMap(([name, { declaration }
|
|
937
|
+
.flatMap(([name, decls]) => decls.flatMap(({ declaration }) => {
|
|
886
938
|
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
887
939
|
if (ts.isFunctionExpression(declaration.initializer)) {
|
|
888
940
|
return [[name, { module: path, symbol: name }]];
|
|
@@ -895,13 +947,14 @@ async function parseResolverFile(path) {
|
|
|
895
947
|
return [[name, { module: path, symbol: name }]];
|
|
896
948
|
}
|
|
897
949
|
return [];
|
|
898
|
-
}));
|
|
950
|
+
})));
|
|
899
951
|
}
|
|
900
|
-
async function parseTypeResolvers(typeName, glob$1) {
|
|
952
|
+
async function parseTypeResolvers(typeName, glob$1, ignore) {
|
|
953
|
+
const shouldIgnore = createMatcher(ignore);
|
|
901
954
|
const result = {};
|
|
902
955
|
for await (const entry of glob.globIterate(glob$1, { withFileTypes: true })) {
|
|
903
956
|
const fullpath = entry.fullpath();
|
|
904
|
-
if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
957
|
+
if (!shouldIgnore(fullpath) && entry.isFile() && entry.name.endsWith(".ts")) {
|
|
905
958
|
for (const [name, reference] of Object.entries(await parseResolverFile(fullpath))) {
|
|
906
959
|
let realName = name;
|
|
907
960
|
if (realName === "default") {
|
|
@@ -916,7 +969,7 @@ async function parseTypeResolvers(typeName, glob$1) {
|
|
|
916
969
|
}
|
|
917
970
|
return result;
|
|
918
971
|
}
|
|
919
|
-
async function parse$2(dirpath) {
|
|
972
|
+
async function parse$2(dirpath, ignore) {
|
|
920
973
|
const dir = await fs.stat(dirpath);
|
|
921
974
|
if (!dir.isDirectory()) {
|
|
922
975
|
throw new Error(`Expected ${dirpath} to be a directory`);
|
|
@@ -925,14 +978,15 @@ async function parse$2(dirpath) {
|
|
|
925
978
|
dirpath += "/";
|
|
926
979
|
}
|
|
927
980
|
const out = {};
|
|
981
|
+
const shouldIgnore = createMatcher(ignore);
|
|
928
982
|
for await (const name of await fs.readdir(dirpath)) {
|
|
929
983
|
const file = path.join(dirpath, name);
|
|
930
984
|
const entry = await fs.stat(file);
|
|
931
985
|
if (entry.isDirectory()) {
|
|
932
|
-
out[name] = await parseTypeResolvers(name, file + "/*.ts");
|
|
986
|
+
out[name] = await parseTypeResolvers(name, file + "/*.ts", shouldIgnore);
|
|
933
987
|
}
|
|
934
988
|
else if (entry.isFile() && name.endsWith(".ts")) {
|
|
935
|
-
out[name.slice(0, -3)] = await parseTypeResolvers(name.slice(0, -3), file);
|
|
989
|
+
out[name.slice(0, -3)] = await parseTypeResolvers(name.slice(0, -3), file, shouldIgnore);
|
|
936
990
|
}
|
|
937
991
|
}
|
|
938
992
|
return out;
|
|
@@ -968,6 +1022,12 @@ function refFromTypeReferenceNode(typeNode, source) {
|
|
|
968
1022
|
return;
|
|
969
1023
|
}
|
|
970
1024
|
const typeName = typeNode.typeName.text;
|
|
1025
|
+
if (typeNode.typeArguments?.length) {
|
|
1026
|
+
if (typeName !== 'Promise') {
|
|
1027
|
+
throw new Error('TODO: handle type arguments');
|
|
1028
|
+
}
|
|
1029
|
+
return refFromTypeNode(typeNode.typeArguments[0], source);
|
|
1030
|
+
}
|
|
971
1031
|
for (const statement of source.statements) {
|
|
972
1032
|
if (ts.isImportDeclaration(statement)) {
|
|
973
1033
|
// search imports
|
|
@@ -1107,21 +1167,23 @@ async function parse$1(path) {
|
|
|
1107
1167
|
const scalarFnsInfos = {};
|
|
1108
1168
|
const scalarTypeNodes = {};
|
|
1109
1169
|
const sources = {};
|
|
1110
|
-
for (const [name,
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
else if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1118
|
-
if (declaration.typeParameters?.length) {
|
|
1119
|
-
console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
|
|
1120
|
-
continue;
|
|
1170
|
+
for (const [name, decls] of Object.entries((await parseModule(path)).exports)) {
|
|
1171
|
+
for (const { declaration, source } of decls) {
|
|
1172
|
+
sources[name] = source;
|
|
1173
|
+
if (ts.isVariableDeclaration(declaration)) {
|
|
1174
|
+
if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
|
|
1175
|
+
scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
|
|
1176
|
+
}
|
|
1121
1177
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1178
|
+
else if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1179
|
+
if (declaration.typeParameters?.length) {
|
|
1180
|
+
console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
const ref = refFromTypeNode(declaration.type, source);
|
|
1184
|
+
if (ref) {
|
|
1185
|
+
scalarTypeNodes[name] = declaration.type;
|
|
1186
|
+
}
|
|
1125
1187
|
}
|
|
1126
1188
|
}
|
|
1127
1189
|
}
|
|
@@ -1134,7 +1196,7 @@ async function parse$1(path) {
|
|
|
1134
1196
|
module: source.fileName,
|
|
1135
1197
|
symbol: name,
|
|
1136
1198
|
} : reconcileScalarType(name, [
|
|
1137
|
-
|
|
1199
|
+
// NOTE: omit serialize input type because it is supposed to be `unknown`
|
|
1138
1200
|
fnsInfo?.parseValueFunc?.declaration?.type,
|
|
1139
1201
|
fnsInfo?.parseLiteralFunc?.declaration?.type,
|
|
1140
1202
|
], source);
|
|
@@ -1517,9 +1579,13 @@ async function* findSchemaFiles(glob$1) {
|
|
|
1517
1579
|
}
|
|
1518
1580
|
}
|
|
1519
1581
|
}
|
|
1520
|
-
async function parse(glob) {
|
|
1582
|
+
async function parse(glob, ignore) {
|
|
1583
|
+
const shouldIgnore = createMatcher(ignore);
|
|
1521
1584
|
const documentNodes = [];
|
|
1522
1585
|
for await (const entry of findSchemaFiles(glob)) {
|
|
1586
|
+
if (shouldIgnore(entry)) {
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1523
1589
|
documentNodes.push(graphql.parse(new graphql.Source(await fs.readFile(entry, "utf-8"), entry)));
|
|
1524
1590
|
}
|
|
1525
1591
|
if (!documentNodes.length) {
|
package/build/lib.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { GraphQLSchema } from 'graphql';
|
|
3
3
|
|
|
4
|
+
type MatchPattern = string | RegExp | ((s: string) => boolean) | Array<string | RegExp | ((s: string) => boolean)>;
|
|
5
|
+
|
|
4
6
|
interface SymbolReference {
|
|
5
7
|
module?: string;
|
|
6
8
|
symbol: string;
|
|
@@ -8,8 +10,8 @@ interface SymbolReference {
|
|
|
8
10
|
property?: string;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
declare function parseTypeResolvers(typeName: string, glob: string | string[]): Promise<Record<string, SymbolReference>>;
|
|
12
|
-
declare function parse$2(dirpath: string): Promise<Record<string, Record<string, SymbolReference>>>;
|
|
13
|
+
declare function parseTypeResolvers(typeName: string, glob: string | string[], ignore?: MatchPattern): Promise<Record<string, SymbolReference>>;
|
|
14
|
+
declare function parse$2(dirpath: string, ignore?: MatchPattern): Promise<Record<string, Record<string, SymbolReference>>>;
|
|
13
15
|
|
|
14
16
|
type FunctionDeclaration = ts.MethodDeclaration | ts.FunctionExpression | ts.ArrowFunction;
|
|
15
17
|
interface ScalarFnsInfo {
|
|
@@ -46,6 +48,6 @@ declare function generateSchema(schema: GraphQLSchema, outfile: string, options?
|
|
|
46
48
|
declare function generateTypes(schema: GraphQLSchema, outfile: string, options?: GenerateOptions): string;
|
|
47
49
|
declare function generate(schema: GraphQLSchema, outfile: string, options?: GenerateOptions): string;
|
|
48
50
|
|
|
49
|
-
declare function parse(glob: string | string[]): Promise<GraphQLSchema>;
|
|
51
|
+
declare function parse(glob: string | string[], ignore?: MatchPattern): Promise<GraphQLSchema>;
|
|
50
52
|
|
|
51
53
|
export { createGenerator, generate, generateSchema, generateTypes, parse$2 as parseResolvers, parse$1 as parseScalars, parse as parseSchema, parseTypeResolvers };
|
package/build/lib.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { pathToFileURL, fileURLToPath } from 'url';
|
|
|
8
8
|
import fs from 'fs/promises';
|
|
9
9
|
import ts, { getLineAndCharacterOfPosition } from 'typescript';
|
|
10
10
|
import { globIterate } from 'glob';
|
|
11
|
+
import pm from 'picomatch';
|
|
11
12
|
import zip from 'just-zip-it';
|
|
12
13
|
|
|
13
14
|
async function parseSource(filepath) {
|
|
@@ -36,23 +37,26 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
36
37
|
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
37
38
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
38
39
|
if (node.modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
39
|
-
exports.default = { declaration: node, source, name: "default" };
|
|
40
|
+
(exports.default || (exports.default = [])).push({ declaration: node, source, name: "default" });
|
|
40
41
|
}
|
|
41
42
|
else if (node.name) {
|
|
42
|
-
|
|
43
|
+
const name = node.name.getText(source);
|
|
44
|
+
(exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
else if (ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
|
|
47
49
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
48
|
-
|
|
50
|
+
const name = node.name.getText(source);
|
|
51
|
+
(exports[name] || (exports[name] = [])).push({ declaration: node, source, name: name });
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
else if (ts.isVariableStatement(node)) {
|
|
52
55
|
if (node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
53
56
|
for (const decl of node.declarationList.declarations) {
|
|
54
57
|
if (decl.name) {
|
|
55
|
-
|
|
58
|
+
const name = decl.name.getText(source);
|
|
59
|
+
(exports[name] || (exports[name] = [])).push({ declaration: decl, source, name: name });
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
}
|
|
@@ -70,7 +74,7 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
70
74
|
if (!(name in moduleExports)) {
|
|
71
75
|
throw new Error(`Exported name ${name} not found in ${JSON.stringify(specifier)}`);
|
|
72
76
|
}
|
|
73
|
-
exports[name] = moduleExports[name];
|
|
77
|
+
(exports[name] || (exports[name] = [])).push(...moduleExports[name]);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
else {
|
|
@@ -79,7 +83,6 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
79
83
|
name: element.name.text,
|
|
80
84
|
property: element.propertyName?.text,
|
|
81
85
|
});
|
|
82
|
-
// exports[element.name.getText(source)] = { declaration: element, source };
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
}
|
|
@@ -95,57 +98,63 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
95
98
|
if (ts.isVariableStatement(statement)) {
|
|
96
99
|
for (const decl of statement.declarationList.declarations) {
|
|
97
100
|
if (ts.isIdentifier(decl.name) && decl.name.getText(source) in names) {
|
|
98
|
-
|
|
101
|
+
const name = names[decl.name.getText(source)];
|
|
102
|
+
(exports[name] || (exports[name] = [])).push({
|
|
99
103
|
declaration: decl,
|
|
100
104
|
source,
|
|
101
105
|
name: names[decl.name.getText(source)],
|
|
102
|
-
};
|
|
106
|
+
});
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
if (ts.isFunctionDeclaration(statement)) {
|
|
107
111
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
108
|
-
|
|
112
|
+
const name = names[statement.name.getText(source)];
|
|
113
|
+
(exports[name] || (exports[name] = [])).push({
|
|
109
114
|
declaration: statement,
|
|
110
115
|
source,
|
|
111
116
|
name: names[statement.name.getText(source)],
|
|
112
|
-
};
|
|
117
|
+
});
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
if (ts.isClassDeclaration(statement)) {
|
|
116
121
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
117
|
-
|
|
122
|
+
const name = names[statement.name.getText(source)];
|
|
123
|
+
(exports[name] || (exports[name] = [])).push({
|
|
118
124
|
declaration: statement,
|
|
119
125
|
source,
|
|
120
126
|
name: names[statement.name.getText(source)],
|
|
121
|
-
};
|
|
127
|
+
});
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
if (ts.isTypeAliasDeclaration(statement)) {
|
|
125
131
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
126
|
-
|
|
132
|
+
const name = names[statement.name.getText(source)];
|
|
133
|
+
(exports[name] || (exports[name] = [])).push({
|
|
127
134
|
declaration: statement,
|
|
128
135
|
source,
|
|
129
136
|
name: names[statement.name.getText(source)],
|
|
130
|
-
};
|
|
137
|
+
});
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
if (ts.isInterfaceDeclaration(statement)) {
|
|
134
141
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
135
|
-
|
|
142
|
+
const name = names[statement.name.getText(source)];
|
|
143
|
+
(exports[name] || (exports[name] = [])).push({
|
|
136
144
|
declaration: statement,
|
|
137
145
|
source,
|
|
138
146
|
name: names[statement.name.getText(source)],
|
|
139
|
-
};
|
|
147
|
+
});
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
if (ts.isEnumDeclaration(statement)) {
|
|
143
151
|
if (statement.name && ts.isIdentifier(statement.name) && statement.name.getText(source) in names) {
|
|
144
|
-
|
|
152
|
+
const name = names[statement.name.getText(source)];
|
|
153
|
+
(exports[name] || (exports[name] = [])).push({
|
|
145
154
|
declaration: statement,
|
|
146
155
|
source,
|
|
147
156
|
name: names[statement.name.getText(source)],
|
|
148
|
-
};
|
|
157
|
+
});
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
160
|
if (ts.isImportDeclaration(statement)) {
|
|
@@ -165,11 +174,12 @@ async function findExports(source, resolveModuleFn = resolveModule) {
|
|
|
165
174
|
if (!importExports[element.name.getText(source)]) {
|
|
166
175
|
throw new Error(`Exported name ${element.name.getText(source)} not found in ${JSON.stringify(statement.moduleSpecifier.getText(source))}`);
|
|
167
176
|
}
|
|
168
|
-
|
|
177
|
+
const name = names[element.propertyName.getText(source)];
|
|
178
|
+
(exports[name] || (exports[name] = [])).push({
|
|
169
179
|
declaration: element,
|
|
170
180
|
source,
|
|
171
181
|
name: names[element.propertyName.getText(source)],
|
|
172
|
-
};
|
|
182
|
+
});
|
|
173
183
|
}
|
|
174
184
|
}
|
|
175
185
|
}
|
|
@@ -236,16 +246,9 @@ function compareEntryKey([a], [b]) {
|
|
|
236
246
|
function compareName({ name: a }, { name: b }) {
|
|
237
247
|
return a.localeCompare(b);
|
|
238
248
|
}
|
|
249
|
+
const PRIMITIVE_TYPES = new Set(["ID", "Int", "Float", "String", "Boolean"]);
|
|
239
250
|
function isPrimitiveType(type) {
|
|
240
|
-
|
|
241
|
-
case "ID":
|
|
242
|
-
case "Int":
|
|
243
|
-
case "Float":
|
|
244
|
-
case "String":
|
|
245
|
-
case "Boolean":
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
|
-
return false;
|
|
251
|
+
return PRIMITIVE_TYPES.has(type.name);
|
|
249
252
|
}
|
|
250
253
|
function importAlias(line) {
|
|
251
254
|
const pos = line.indexOf(" as ");
|
|
@@ -417,7 +420,7 @@ class GeneratorContext {
|
|
|
417
420
|
// NOTE: Resolver functions on interface definitions are not used by the default graphql-js
|
|
418
421
|
// executor implementation, but nonetheless accessible by a potential alternate implementation.
|
|
419
422
|
resolveFn && jen$1.prop("resolve", jen$1.parens(resolveFn).as.any),
|
|
420
|
-
subscriber && jen$1.prop("subscribe", renderSymbolReference(this._pkgs, subscriber)),
|
|
423
|
+
subscriber && jen$1.prop("subscribe", jen$1.parens(renderSymbolReference(this._pkgs, subscriber)).as.any),
|
|
421
424
|
]));
|
|
422
425
|
}
|
|
423
426
|
_renderFieldsThunk(type, inputFieldDirectives, noresolver) {
|
|
@@ -499,7 +502,7 @@ class GeneratorContext {
|
|
|
499
502
|
}
|
|
500
503
|
_renderGraphQLObjectTypeDefinition(type, { inputFieldDirectives }) {
|
|
501
504
|
const typeInterfaces = type.getInterfaces();
|
|
502
|
-
return jen$1.new.add(this._gql("GraphQLObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives)), ...truthify([
|
|
505
|
+
return jen$1.new.add(this._gql("GraphQLObjectType")).call(jen$1.obj(jen$1.prop("name", jen$1.lit(type.name)), jen$1.prop("fields", this._renderFieldsThunk(type, inputFieldDirectives, type.name === "Subscription")), ...truthify([
|
|
503
506
|
type.description && jen$1.prop("description", jen$1.lit(type.description)),
|
|
504
507
|
typeInterfaces.length && jen$1.prop("interfaces", jen$1.arrow().arr(
|
|
505
508
|
// do not sort interfaces, the declaration order may be important
|
|
@@ -739,6 +742,20 @@ class GeneratorContext {
|
|
|
739
742
|
.map((directive) => [directive.name, this._renderGraphQLDirectiveDefinition(directive)])
|
|
740
743
|
.sort(compareEntryKey),
|
|
741
744
|
].map(([, expr]) => expr),
|
|
745
|
+
...Array.from(PRIMITIVE_TYPES)
|
|
746
|
+
.flatMap((type) => {
|
|
747
|
+
const info = this._scalarsInfo[type];
|
|
748
|
+
if (!info) {
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
return [
|
|
752
|
+
jen$1.statements(...[
|
|
753
|
+
info.serializeFunc && this._gql("GraphQL" + type).dot("serialize").op("=").parens(renderSymbolReference(this._pkgs, info.serializeFunc).as.any),
|
|
754
|
+
info.parseValueFunc && this._gql("GraphQL" + type).dot("parseValue").op("=").parens(renderSymbolReference(this._pkgs, info.parseValueFunc).as.any),
|
|
755
|
+
info.parseLiteralFunc && this._gql("GraphQL" + type).dot("parseLiteral").op("=").parens(renderSymbolReference(this._pkgs, info.parseLiteralFunc).as.any),
|
|
756
|
+
].filter(Boolean))
|
|
757
|
+
];
|
|
758
|
+
}),
|
|
742
759
|
// render schema
|
|
743
760
|
jen$1.const.id("schema").op("=").new.add(this._gql("GraphQLSchema")).call(jen$1.obj(...(schema.getQueryType()
|
|
744
761
|
? [jen$1.prop("query", this._renderGraphQLTypeIdentifier(schema.getQueryType()))]
|
|
@@ -878,9 +895,44 @@ function generate(schema, outfile, options = {}) {
|
|
|
878
895
|
return createGenerator(schema, options).generate(outfile);
|
|
879
896
|
}
|
|
880
897
|
|
|
898
|
+
function createMatcher(pattern) {
|
|
899
|
+
if (!pattern) {
|
|
900
|
+
return () => false;
|
|
901
|
+
}
|
|
902
|
+
if (typeof pattern === "function") {
|
|
903
|
+
return pattern;
|
|
904
|
+
}
|
|
905
|
+
if (pattern instanceof RegExp) {
|
|
906
|
+
return (s) => pattern.test(s);
|
|
907
|
+
}
|
|
908
|
+
if (typeof pattern === "string") {
|
|
909
|
+
return pm(pattern);
|
|
910
|
+
}
|
|
911
|
+
let matchers = [];
|
|
912
|
+
let globs = [];
|
|
913
|
+
for (const p of Array.isArray(pattern) ? pattern : [pattern]) {
|
|
914
|
+
if (typeof p === "string") {
|
|
915
|
+
globs.push(p);
|
|
916
|
+
}
|
|
917
|
+
else if (typeof p === "function") {
|
|
918
|
+
matchers.push(p);
|
|
919
|
+
}
|
|
920
|
+
else if (p instanceof RegExp) {
|
|
921
|
+
matchers.push(RegExp.prototype.test.bind(p));
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
throw new TypeError(`Invalid pattern: ${p}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (globs.length) {
|
|
928
|
+
matchers.push(pm(globs));
|
|
929
|
+
}
|
|
930
|
+
return (s) => matchers.some((m) => m(s));
|
|
931
|
+
}
|
|
932
|
+
|
|
881
933
|
async function parseResolverFile(path) {
|
|
882
934
|
return Object.fromEntries(Object.entries((await parseModule(path)).exports)
|
|
883
|
-
.flatMap(([name, { declaration }
|
|
935
|
+
.flatMap(([name, decls]) => decls.flatMap(({ declaration }) => {
|
|
884
936
|
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
885
937
|
if (ts.isFunctionExpression(declaration.initializer)) {
|
|
886
938
|
return [[name, { module: path, symbol: name }]];
|
|
@@ -893,13 +945,14 @@ async function parseResolverFile(path) {
|
|
|
893
945
|
return [[name, { module: path, symbol: name }]];
|
|
894
946
|
}
|
|
895
947
|
return [];
|
|
896
|
-
}));
|
|
948
|
+
})));
|
|
897
949
|
}
|
|
898
|
-
async function parseTypeResolvers(typeName, glob) {
|
|
950
|
+
async function parseTypeResolvers(typeName, glob, ignore) {
|
|
951
|
+
const shouldIgnore = createMatcher(ignore);
|
|
899
952
|
const result = {};
|
|
900
953
|
for await (const entry of globIterate(glob, { withFileTypes: true })) {
|
|
901
954
|
const fullpath = entry.fullpath();
|
|
902
|
-
if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
955
|
+
if (!shouldIgnore(fullpath) && entry.isFile() && entry.name.endsWith(".ts")) {
|
|
903
956
|
for (const [name, reference] of Object.entries(await parseResolverFile(fullpath))) {
|
|
904
957
|
let realName = name;
|
|
905
958
|
if (realName === "default") {
|
|
@@ -914,7 +967,7 @@ async function parseTypeResolvers(typeName, glob) {
|
|
|
914
967
|
}
|
|
915
968
|
return result;
|
|
916
969
|
}
|
|
917
|
-
async function parse$2(dirpath) {
|
|
970
|
+
async function parse$2(dirpath, ignore) {
|
|
918
971
|
const dir = await fs.stat(dirpath);
|
|
919
972
|
if (!dir.isDirectory()) {
|
|
920
973
|
throw new Error(`Expected ${dirpath} to be a directory`);
|
|
@@ -923,14 +976,15 @@ async function parse$2(dirpath) {
|
|
|
923
976
|
dirpath += "/";
|
|
924
977
|
}
|
|
925
978
|
const out = {};
|
|
979
|
+
const shouldIgnore = createMatcher(ignore);
|
|
926
980
|
for await (const name of await fs.readdir(dirpath)) {
|
|
927
981
|
const file = join(dirpath, name);
|
|
928
982
|
const entry = await fs.stat(file);
|
|
929
983
|
if (entry.isDirectory()) {
|
|
930
|
-
out[name] = await parseTypeResolvers(name, file + "/*.ts");
|
|
984
|
+
out[name] = await parseTypeResolvers(name, file + "/*.ts", shouldIgnore);
|
|
931
985
|
}
|
|
932
986
|
else if (entry.isFile() && name.endsWith(".ts")) {
|
|
933
|
-
out[name.slice(0, -3)] = await parseTypeResolvers(name.slice(0, -3), file);
|
|
987
|
+
out[name.slice(0, -3)] = await parseTypeResolvers(name.slice(0, -3), file, shouldIgnore);
|
|
934
988
|
}
|
|
935
989
|
}
|
|
936
990
|
return out;
|
|
@@ -966,6 +1020,12 @@ function refFromTypeReferenceNode(typeNode, source) {
|
|
|
966
1020
|
return;
|
|
967
1021
|
}
|
|
968
1022
|
const typeName = typeNode.typeName.text;
|
|
1023
|
+
if (typeNode.typeArguments?.length) {
|
|
1024
|
+
if (typeName !== 'Promise') {
|
|
1025
|
+
throw new Error('TODO: handle type arguments');
|
|
1026
|
+
}
|
|
1027
|
+
return refFromTypeNode(typeNode.typeArguments[0], source);
|
|
1028
|
+
}
|
|
969
1029
|
for (const statement of source.statements) {
|
|
970
1030
|
if (ts.isImportDeclaration(statement)) {
|
|
971
1031
|
// search imports
|
|
@@ -1105,21 +1165,23 @@ async function parse$1(path) {
|
|
|
1105
1165
|
const scalarFnsInfos = {};
|
|
1106
1166
|
const scalarTypeNodes = {};
|
|
1107
1167
|
const sources = {};
|
|
1108
|
-
for (const [name,
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
else if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1116
|
-
if (declaration.typeParameters?.length) {
|
|
1117
|
-
console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
|
|
1118
|
-
continue;
|
|
1168
|
+
for (const [name, decls] of Object.entries((await parseModule(path)).exports)) {
|
|
1169
|
+
for (const { declaration, source } of decls) {
|
|
1170
|
+
sources[name] = source;
|
|
1171
|
+
if (ts.isVariableDeclaration(declaration)) {
|
|
1172
|
+
if (declaration.initializer && ts.isObjectLiteralExpression(declaration.initializer)) {
|
|
1173
|
+
scalarFnsInfos[name] = scalarInfoFromObjectLiteralExpression(name, new URL(source.fileName).toString(), declaration.initializer.properties);
|
|
1174
|
+
}
|
|
1119
1175
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1176
|
+
else if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1177
|
+
if (declaration.typeParameters?.length) {
|
|
1178
|
+
console.log(`WARNING: skipping scalar type ${name}: type parameters are not supported`);
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
const ref = refFromTypeNode(declaration.type, source);
|
|
1182
|
+
if (ref) {
|
|
1183
|
+
scalarTypeNodes[name] = declaration.type;
|
|
1184
|
+
}
|
|
1123
1185
|
}
|
|
1124
1186
|
}
|
|
1125
1187
|
}
|
|
@@ -1132,7 +1194,7 @@ async function parse$1(path) {
|
|
|
1132
1194
|
module: source.fileName,
|
|
1133
1195
|
symbol: name,
|
|
1134
1196
|
} : reconcileScalarType(name, [
|
|
1135
|
-
|
|
1197
|
+
// NOTE: omit serialize input type because it is supposed to be `unknown`
|
|
1136
1198
|
fnsInfo?.parseValueFunc?.declaration?.type,
|
|
1137
1199
|
fnsInfo?.parseLiteralFunc?.declaration?.type,
|
|
1138
1200
|
], source);
|
|
@@ -1515,9 +1577,13 @@ async function* findSchemaFiles(glob) {
|
|
|
1515
1577
|
}
|
|
1516
1578
|
}
|
|
1517
1579
|
}
|
|
1518
|
-
async function parse(glob) {
|
|
1580
|
+
async function parse(glob, ignore) {
|
|
1581
|
+
const shouldIgnore = createMatcher(ignore);
|
|
1519
1582
|
const documentNodes = [];
|
|
1520
1583
|
for await (const entry of findSchemaFiles(glob)) {
|
|
1584
|
+
if (shouldIgnore(entry)) {
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1521
1587
|
documentNodes.push(parse$3(new Source(await fs.readFile(entry, "utf-8"), entry)));
|
|
1522
1588
|
}
|
|
1523
1589
|
if (!documentNodes.length) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-jerky",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Jerky is a schema-first static graphql schema lightweight code generator.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./build/bin/jerky.cjs",
|
|
@@ -19,11 +19,13 @@
|
|
|
19
19
|
"just-partition": "^2.2.0",
|
|
20
20
|
"just-pascal-case": "^3.2.0",
|
|
21
21
|
"just-zip-it": "^3.2.0",
|
|
22
|
+
"picomatch": "^2.3.1",
|
|
22
23
|
"typescript": "^5.1.3"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@rollup/plugin-typescript": "^11.1.1",
|
|
26
27
|
"@types/node": "^20.3.1",
|
|
28
|
+
"@types/picomatch": "^2.3.0",
|
|
27
29
|
"rollup": "^3.25.1",
|
|
28
30
|
"rollup-plugin-dts": "^5.3.0",
|
|
29
31
|
"tslib": "^2.5.3"
|