@soda-gql/codegen 0.11.26 → 0.12.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/dist/{generator-CsQAp9Xj.mjs → generator-76iJu4D4.mjs} +138 -41
- package/dist/generator-76iJu4D4.mjs.map +1 -0
- package/dist/{generator-BJX_PNQq.cjs → generator-BEFJKQs_.cjs} +155 -40
- package/dist/generator-BEFJKQs_.cjs.map +1 -0
- package/dist/generator-C_BtU_dR.cjs +6 -0
- package/dist/generator-DOfX1Run.mjs +3 -0
- package/dist/index.cjs +280 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +51 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +279 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/dist/generator-BJX_PNQq.cjs.map +0 -1
- package/dist/generator-C2g1EDwR.cjs +0 -4
- package/dist/generator-CsQAp9Xj.mjs.map +0 -1
- package/dist/generator-DSYhdRB2.mjs +0 -3
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_generator = require('./generator-
|
|
1
|
+
const require_generator = require('./generator-BEFJKQs_.cjs');
|
|
2
2
|
let neverthrow = require("neverthrow");
|
|
3
3
|
let graphql = require("graphql");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
@@ -328,6 +328,7 @@ const isModifierAssignable = (source, target) => {
|
|
|
328
328
|
for (let i = 0; i < srcStruct.lists.length; i++) {
|
|
329
329
|
const srcList = srcStruct.lists[i];
|
|
330
330
|
const tgtList = tgtListsToCompare[i];
|
|
331
|
+
if (srcList === undefined || tgtList === undefined) break;
|
|
331
332
|
if (srcList === "[]?" && tgtList === "[]!") return false;
|
|
332
333
|
}
|
|
333
334
|
return true;
|
|
@@ -372,6 +373,7 @@ const mergeModifiers = (a, b) => {
|
|
|
372
373
|
for (let i = 0; i < structA.lists.length; i++) {
|
|
373
374
|
const listA = structA.lists[i];
|
|
374
375
|
const listB = structB.lists[i];
|
|
376
|
+
if (listA === undefined || listB === undefined) break;
|
|
375
377
|
mergedLists.push(listA === "[]!" || listB === "[]!" ? "[]!" : "[]?");
|
|
376
378
|
}
|
|
377
379
|
return {
|
|
@@ -550,14 +552,14 @@ const getFieldReturnType = (schema, parentTypeName, fieldName) => {
|
|
|
550
552
|
* 3. Verify the candidate can satisfy ALL expected modifiers via isModifierAssignable
|
|
551
553
|
*/
|
|
552
554
|
const mergeVariableUsages = (variableName, usages) => {
|
|
553
|
-
|
|
555
|
+
const first = usages[0];
|
|
556
|
+
if (!first) {
|
|
554
557
|
return (0, neverthrow.err)({
|
|
555
558
|
code: "GRAPHQL_UNDECLARED_VARIABLE",
|
|
556
559
|
message: `No usages found for variable "${variableName}"`,
|
|
557
560
|
variableName
|
|
558
561
|
});
|
|
559
562
|
}
|
|
560
|
-
const first = usages[0];
|
|
561
563
|
for (const usage of usages) {
|
|
562
564
|
if (usage.typeName !== first.typeName) {
|
|
563
565
|
return (0, neverthrow.err)({
|
|
@@ -569,7 +571,9 @@ const mergeVariableUsages = (variableName, usages) => {
|
|
|
569
571
|
}
|
|
570
572
|
let candidateModifier = first.minimumModifier;
|
|
571
573
|
for (let i = 1; i < usages.length; i++) {
|
|
572
|
-
const
|
|
574
|
+
const usage = usages[i];
|
|
575
|
+
if (!usage) break;
|
|
576
|
+
const result = mergeModifiers(candidateModifier, usage.minimumModifier);
|
|
573
577
|
if (!result.ok) {
|
|
574
578
|
return (0, neverthrow.err)({
|
|
575
579
|
code: "GRAPHQL_VARIABLE_MODIFIER_INCOMPATIBLE",
|
|
@@ -1271,6 +1275,221 @@ const writeInjectTemplate = (outPath) => {
|
|
|
1271
1275
|
};
|
|
1272
1276
|
const getInjectTemplate = () => `${templateContents}\n`;
|
|
1273
1277
|
|
|
1278
|
+
//#endregion
|
|
1279
|
+
//#region packages/codegen/src/reachability.ts
|
|
1280
|
+
/**
|
|
1281
|
+
* Schema type reachability analysis.
|
|
1282
|
+
*
|
|
1283
|
+
* Determines which types are reachable from root types (Query/Mutation/Subscription)
|
|
1284
|
+
* to specified target types (e.g., fragment onType values from .graphql files).
|
|
1285
|
+
* Produces a CompiledFilter for use with existing buildExclusionSet.
|
|
1286
|
+
*
|
|
1287
|
+
* @module
|
|
1288
|
+
*/
|
|
1289
|
+
const extractNamedType = (typeNode) => {
|
|
1290
|
+
switch (typeNode.kind) {
|
|
1291
|
+
case graphql.Kind.NAMED_TYPE: return typeNode.name.value;
|
|
1292
|
+
case graphql.Kind.LIST_TYPE: return extractNamedType(typeNode.type);
|
|
1293
|
+
case graphql.Kind.NON_NULL_TYPE: return extractNamedType(typeNode.type);
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
const addEdge = (graph, from, to) => {
|
|
1297
|
+
let edges = graph.get(from);
|
|
1298
|
+
if (!edges) {
|
|
1299
|
+
edges = new Set();
|
|
1300
|
+
graph.set(from, edges);
|
|
1301
|
+
}
|
|
1302
|
+
edges.add(to);
|
|
1303
|
+
};
|
|
1304
|
+
const buildTypeGraph = (document) => {
|
|
1305
|
+
const schema = require_generator.createSchemaIndex(document);
|
|
1306
|
+
const forward = new Map();
|
|
1307
|
+
const reverse = new Map();
|
|
1308
|
+
const addBidirectional = (from, to) => {
|
|
1309
|
+
addEdge(forward, from, to);
|
|
1310
|
+
addEdge(reverse, to, from);
|
|
1311
|
+
};
|
|
1312
|
+
for (const [typeName, record] of schema.objects) {
|
|
1313
|
+
for (const field of record.fields.values()) {
|
|
1314
|
+
const returnType = extractNamedType(field.type);
|
|
1315
|
+
addBidirectional(typeName, returnType);
|
|
1316
|
+
if (field.arguments) {
|
|
1317
|
+
for (const arg of field.arguments) {
|
|
1318
|
+
const argType = extractNamedType(arg.type);
|
|
1319
|
+
addBidirectional(typeName, argType);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
for (const [typeName, record] of schema.inputs) {
|
|
1325
|
+
for (const field of record.fields.values()) {
|
|
1326
|
+
const fieldType = extractNamedType(field.type);
|
|
1327
|
+
addBidirectional(typeName, fieldType);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
for (const [typeName, record] of schema.unions) {
|
|
1331
|
+
for (const memberName of record.members.keys()) {
|
|
1332
|
+
addBidirectional(typeName, memberName);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
graph: {
|
|
1337
|
+
forward,
|
|
1338
|
+
reverse
|
|
1339
|
+
},
|
|
1340
|
+
schema
|
|
1341
|
+
};
|
|
1342
|
+
};
|
|
1343
|
+
/**
|
|
1344
|
+
* BFS traversal collecting all reachable nodes from seeds.
|
|
1345
|
+
*/
|
|
1346
|
+
const bfs = (adjacency, seeds, constraint) => {
|
|
1347
|
+
const visited = new Set();
|
|
1348
|
+
const queue = [];
|
|
1349
|
+
for (const seed of seeds) {
|
|
1350
|
+
if (!visited.has(seed)) {
|
|
1351
|
+
visited.add(seed);
|
|
1352
|
+
queue.push(seed);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
let head = 0;
|
|
1356
|
+
while (head < queue.length) {
|
|
1357
|
+
const current = queue[head++];
|
|
1358
|
+
if (current === undefined) break;
|
|
1359
|
+
const neighbors = adjacency.get(current);
|
|
1360
|
+
if (!neighbors) continue;
|
|
1361
|
+
for (const neighbor of neighbors) {
|
|
1362
|
+
if (visited.has(neighbor)) continue;
|
|
1363
|
+
if (constraint && !constraint.has(neighbor)) continue;
|
|
1364
|
+
visited.add(neighbor);
|
|
1365
|
+
queue.push(neighbor);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return visited;
|
|
1369
|
+
};
|
|
1370
|
+
/**
|
|
1371
|
+
* Compute the set of type names reachable on paths from root types to target types.
|
|
1372
|
+
*
|
|
1373
|
+
* Algorithm:
|
|
1374
|
+
* 1. Backward BFS from target types to find all upstream types
|
|
1375
|
+
* 2. Forward BFS from root types, constrained to upstream set, to find actual paths
|
|
1376
|
+
* 3. Collect input/enum/scalar types used as field arguments on reachable object types
|
|
1377
|
+
*/
|
|
1378
|
+
const computeReachableTypes = (graph, schema, targetTypes, usedArgumentTypes) => {
|
|
1379
|
+
const upstream = bfs(graph.reverse, targetTypes);
|
|
1380
|
+
const rootTypes = [];
|
|
1381
|
+
if (schema.operationTypes.query) rootTypes.push(schema.operationTypes.query);
|
|
1382
|
+
if (schema.operationTypes.mutation) rootTypes.push(schema.operationTypes.mutation);
|
|
1383
|
+
if (schema.operationTypes.subscription) rootTypes.push(schema.operationTypes.subscription);
|
|
1384
|
+
const validRoots = rootTypes.filter((r) => upstream.has(r));
|
|
1385
|
+
const pathTypes = bfs(graph.forward, validRoots, upstream);
|
|
1386
|
+
const reachable = new Set(pathTypes);
|
|
1387
|
+
const inputQueue = [];
|
|
1388
|
+
for (const typeName of pathTypes) {
|
|
1389
|
+
const objectRecord = schema.objects.get(typeName);
|
|
1390
|
+
if (!objectRecord) continue;
|
|
1391
|
+
for (const field of objectRecord.fields.values()) {
|
|
1392
|
+
const returnType = extractNamedType(field.type);
|
|
1393
|
+
if (!reachable.has(returnType)) {
|
|
1394
|
+
const isKnownComposite = schema.objects.has(returnType) || schema.inputs.has(returnType) || schema.unions.has(returnType);
|
|
1395
|
+
if (!isKnownComposite) {
|
|
1396
|
+
reachable.add(returnType);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
if (!usedArgumentTypes && field.arguments) {
|
|
1400
|
+
for (const arg of field.arguments) {
|
|
1401
|
+
const argType = extractNamedType(arg.type);
|
|
1402
|
+
if (!reachable.has(argType)) {
|
|
1403
|
+
reachable.add(argType);
|
|
1404
|
+
if (schema.inputs.has(argType)) {
|
|
1405
|
+
inputQueue.push(argType);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (usedArgumentTypes) {
|
|
1413
|
+
for (const inputName of usedArgumentTypes) {
|
|
1414
|
+
if (!reachable.has(inputName)) {
|
|
1415
|
+
reachable.add(inputName);
|
|
1416
|
+
inputQueue.push(inputName);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
let inputHead = 0;
|
|
1421
|
+
while (inputHead < inputQueue.length) {
|
|
1422
|
+
const inputName = inputQueue[inputHead++];
|
|
1423
|
+
if (inputName === undefined) break;
|
|
1424
|
+
const inputRecord = schema.inputs.get(inputName);
|
|
1425
|
+
if (!inputRecord) continue;
|
|
1426
|
+
for (const field of inputRecord.fields.values()) {
|
|
1427
|
+
const fieldType = extractNamedType(field.type);
|
|
1428
|
+
if (!reachable.has(fieldType)) {
|
|
1429
|
+
reachable.add(fieldType);
|
|
1430
|
+
if (schema.inputs.has(fieldType)) {
|
|
1431
|
+
inputQueue.push(fieldType);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return reachable;
|
|
1437
|
+
};
|
|
1438
|
+
/**
|
|
1439
|
+
* Compute a filter function that includes only types reachable from root types
|
|
1440
|
+
* to the specified target types.
|
|
1441
|
+
*
|
|
1442
|
+
* When targetTypes is empty, returns a pass-all filter (no filtering).
|
|
1443
|
+
* Warns when target types are not found in the schema.
|
|
1444
|
+
*
|
|
1445
|
+
* @param document - The parsed GraphQL schema document
|
|
1446
|
+
* @param targetTypes - Set of type names that fragments target (e.g., from ParsedFragment.onType)
|
|
1447
|
+
* @returns Filter function and any warnings
|
|
1448
|
+
*/
|
|
1449
|
+
const computeReachabilityFilter = (document, targetTypes, usedArgumentTypes) => {
|
|
1450
|
+
if (targetTypes.size === 0) {
|
|
1451
|
+
return {
|
|
1452
|
+
filter: () => true,
|
|
1453
|
+
warnings: []
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
const { graph, schema } = buildTypeGraph(document);
|
|
1457
|
+
const allTypeNames = new Set([
|
|
1458
|
+
...schema.objects.keys(),
|
|
1459
|
+
...schema.inputs.keys(),
|
|
1460
|
+
...schema.enums.keys(),
|
|
1461
|
+
...schema.unions.keys(),
|
|
1462
|
+
...schema.scalars.keys()
|
|
1463
|
+
]);
|
|
1464
|
+
const warnings = [];
|
|
1465
|
+
const validTargets = new Set();
|
|
1466
|
+
for (const target of targetTypes) {
|
|
1467
|
+
if (allTypeNames.has(target)) {
|
|
1468
|
+
validTargets.add(target);
|
|
1469
|
+
} else {
|
|
1470
|
+
warnings.push(`Target type "${target}" not found in schema`);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (validTargets.size === 0) {
|
|
1474
|
+
return {
|
|
1475
|
+
filter: () => true,
|
|
1476
|
+
warnings
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
const reachable = computeReachableTypes(graph, schema, validTargets, usedArgumentTypes);
|
|
1480
|
+
if (reachable.size === 0) {
|
|
1481
|
+
warnings.push(`No types reachable from root operations to target types: ${[...validTargets].join(", ")}; skipping reachability filter`);
|
|
1482
|
+
return {
|
|
1483
|
+
filter: () => true,
|
|
1484
|
+
warnings
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
return {
|
|
1488
|
+
filter: (context) => reachable.has(context.name),
|
|
1489
|
+
warnings
|
|
1490
|
+
};
|
|
1491
|
+
};
|
|
1492
|
+
|
|
1274
1493
|
//#endregion
|
|
1275
1494
|
//#region packages/codegen/src/bundler/esbuild.ts
|
|
1276
1495
|
const esbuildBundler = {
|
|
@@ -1485,6 +1704,20 @@ const removeDirectory = (dirPath) => {
|
|
|
1485
1704
|
});
|
|
1486
1705
|
}
|
|
1487
1706
|
};
|
|
1707
|
+
const readModule = (filePath) => {
|
|
1708
|
+
const targetPath = (0, node_path.resolve)(filePath);
|
|
1709
|
+
try {
|
|
1710
|
+
const content = (0, node_fs.readFileSync)(targetPath, "utf-8");
|
|
1711
|
+
return (0, neverthrow.ok)(content);
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1714
|
+
return (0, neverthrow.err)({
|
|
1715
|
+
code: "READ_FAILED",
|
|
1716
|
+
message,
|
|
1717
|
+
outPath: targetPath
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1488
1721
|
const writeModule = (outPath, contents) => {
|
|
1489
1722
|
const targetPath = (0, node_path.resolve)(outPath);
|
|
1490
1723
|
try {
|
|
@@ -1612,11 +1845,16 @@ const runCodegen = async (options) => {
|
|
|
1612
1845
|
const schemas = new Map();
|
|
1613
1846
|
const schemaHashes = {};
|
|
1614
1847
|
for (const [name, schemaConfig] of Object.entries(options.schemas)) {
|
|
1615
|
-
const
|
|
1616
|
-
if (
|
|
1617
|
-
|
|
1848
|
+
const preloaded = options.preloadedSchemas?.get(name);
|
|
1849
|
+
if (preloaded) {
|
|
1850
|
+
schemas.set(name, preloaded);
|
|
1851
|
+
} else {
|
|
1852
|
+
const result = await loadSchema(schemaConfig.schema).match((doc) => Promise.resolve((0, neverthrow.ok)(doc)), (error) => Promise.resolve((0, neverthrow.err)(error)));
|
|
1853
|
+
if (result.isErr()) {
|
|
1854
|
+
return (0, neverthrow.err)(result.error);
|
|
1855
|
+
}
|
|
1856
|
+
schemas.set(name, result.value);
|
|
1618
1857
|
}
|
|
1619
|
-
schemas.set(name, result.value);
|
|
1620
1858
|
}
|
|
1621
1859
|
const injectionConfig = new Map();
|
|
1622
1860
|
for (const [schemaName, schemaConfig] of Object.entries(options.schemas)) {
|
|
@@ -1650,15 +1888,10 @@ const runCodegen = async (options) => {
|
|
|
1650
1888
|
chunkSize,
|
|
1651
1889
|
typeFilters: typeFiltersConfig.size > 0 ? typeFiltersConfig : undefined
|
|
1652
1890
|
});
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
* @module
|
|
1656
|
-
* @generated
|
|
1657
|
-
*/
|
|
1658
|
-
export * from "./_internal";
|
|
1659
|
-
`;
|
|
1891
|
+
const schemaNames = Object.keys(options.schemas);
|
|
1892
|
+
const indexCode = require_generator.generateIndexModule(schemaNames);
|
|
1660
1893
|
for (const [name, document] of schemas.entries()) {
|
|
1661
|
-
const schemaIndex = (await Promise.resolve().then(() => require("./generator-
|
|
1894
|
+
const schemaIndex = (await Promise.resolve().then(() => require("./generator-C_BtU_dR.cjs"))).createSchemaIndex(document);
|
|
1662
1895
|
const objects = Array.from(schemaIndex.objects.keys()).filter((n) => !n.startsWith("__")).length;
|
|
1663
1896
|
const enums = Array.from(schemaIndex.enums.keys()).filter((n) => !n.startsWith("__")).length;
|
|
1664
1897
|
const inputs = Array.from(schemaIndex.inputs.keys()).filter((n) => !n.startsWith("__")).length;
|
|
@@ -1719,6 +1952,35 @@ export * from "./_internal";
|
|
|
1719
1952
|
if (indexWriteResult.isErr()) {
|
|
1720
1953
|
return (0, neverthrow.err)(indexWriteResult.error);
|
|
1721
1954
|
}
|
|
1955
|
+
const prebuiltStubPath = (0, node_path.join)((0, node_path.dirname)(outPath), "types.prebuilt.ts");
|
|
1956
|
+
if (!(0, node_fs.existsSync)(prebuiltStubPath)) {
|
|
1957
|
+
const prebuiltStubCode = require_generator.generatePrebuiltStub(schemaNames);
|
|
1958
|
+
const prebuiltWriteResult = await writeModule(prebuiltStubPath, prebuiltStubCode).match(() => Promise.resolve((0, neverthrow.ok)(undefined)), (error) => Promise.resolve((0, neverthrow.err)(error)));
|
|
1959
|
+
if (prebuiltWriteResult.isErr()) {
|
|
1960
|
+
return (0, neverthrow.err)(prebuiltWriteResult.error);
|
|
1961
|
+
}
|
|
1962
|
+
} else {
|
|
1963
|
+
const readResult = readModule(prebuiltStubPath);
|
|
1964
|
+
if (readResult.isErr()) {
|
|
1965
|
+
return (0, neverthrow.err)(readResult.error);
|
|
1966
|
+
}
|
|
1967
|
+
const existingContent = readResult.value;
|
|
1968
|
+
const existingNames = new Set();
|
|
1969
|
+
for (const match of existingContent.matchAll(/export type PrebuiltTypes_(\w+)/g)) {
|
|
1970
|
+
const name = match[1];
|
|
1971
|
+
if (name) existingNames.add(name);
|
|
1972
|
+
}
|
|
1973
|
+
const missingNames = schemaNames.filter((name) => !existingNames.has(name));
|
|
1974
|
+
if (missingNames.length > 0) {
|
|
1975
|
+
const missingStubs = require_generator.generatePrebuiltStub(missingNames);
|
|
1976
|
+
const stubDeclarations = missingStubs.replace(/^\/\*\*[\s\S]*?\*\/\n\n/, "");
|
|
1977
|
+
const updatedContent = `${existingContent.trimEnd()}\n\n${stubDeclarations}`;
|
|
1978
|
+
const patchResult = writeModule(prebuiltStubPath, updatedContent);
|
|
1979
|
+
if (patchResult.isErr()) {
|
|
1980
|
+
return (0, neverthrow.err)(patchResult.error);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1722
1984
|
const bundleOutcome = await esbuildBundler.bundle({
|
|
1723
1985
|
sourcePath: outPath,
|
|
1724
1986
|
external: ["@soda-gql/core", "@soda-gql/runtime"]
|
|
@@ -1739,6 +2001,8 @@ export * from "./_internal";
|
|
|
1739
2001
|
|
|
1740
2002
|
//#endregion
|
|
1741
2003
|
exports.collectVariableUsages = collectVariableUsages;
|
|
2004
|
+
exports.compileTypeFilter = require_generator.compileTypeFilter;
|
|
2005
|
+
exports.computeReachabilityFilter = computeReachabilityFilter;
|
|
1742
2006
|
exports.emitFragment = emitFragment;
|
|
1743
2007
|
exports.emitOperation = emitOperation;
|
|
1744
2008
|
exports.getArgumentType = getArgumentType;
|