@soda-gql/codegen 0.11.18 → 0.11.20
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/index.cjs +97 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +28 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +98 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -13,16 +13,6 @@ let node_crypto = require("node:crypto");
|
|
|
13
13
|
const emitOperation = (operation, options) => {
|
|
14
14
|
const lines = [];
|
|
15
15
|
const schema = options.schemaDocument ? require_generator.createSchemaIndex(options.schemaDocument) : null;
|
|
16
|
-
lines.push(`import { gql } from "${options.graphqlSystemPath}";`);
|
|
17
|
-
if (operation.fragmentDependencies.length > 0 && options.fragmentImports) {
|
|
18
|
-
for (const fragName of operation.fragmentDependencies) {
|
|
19
|
-
const importPath = options.fragmentImports.get(fragName);
|
|
20
|
-
if (importPath) {
|
|
21
|
-
lines.push(`import { ${fragName}Fragment } from "${importPath}";`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
lines.push("");
|
|
26
16
|
const exportName = `${operation.name}Compat`;
|
|
27
17
|
const operationType = operation.kind;
|
|
28
18
|
lines.push(`export const ${exportName} = gql.${options.schemaName}(({ ${operationType}, $var }) =>`);
|
|
@@ -49,16 +39,6 @@ const emitFragment = (fragment, options) => {
|
|
|
49
39
|
const lines = [];
|
|
50
40
|
const schema = options.schemaDocument ? require_generator.createSchemaIndex(options.schemaDocument) : null;
|
|
51
41
|
const hasVariables = fragment.variables.length > 0;
|
|
52
|
-
lines.push(`import { gql } from "${options.graphqlSystemPath}";`);
|
|
53
|
-
if (fragment.fragmentDependencies.length > 0 && options.fragmentImports) {
|
|
54
|
-
for (const fragName of fragment.fragmentDependencies) {
|
|
55
|
-
const importPath = options.fragmentImports.get(fragName);
|
|
56
|
-
if (importPath) {
|
|
57
|
-
lines.push(`import { ${fragName}Fragment } from "${importPath}";`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
lines.push("");
|
|
62
42
|
const exportName = `${fragment.name}Fragment`;
|
|
63
43
|
const destructure = hasVariables ? "fragment, $var" : "fragment";
|
|
64
44
|
lines.push(`export const ${exportName} = gql.${options.schemaName}(({ ${destructure} }) =>`);
|
|
@@ -190,6 +170,9 @@ const emitFieldSelection = (field, indent, variableNames, schema) => {
|
|
|
190
170
|
const selections = field.selections;
|
|
191
171
|
const hasArgs = args && args.length > 0;
|
|
192
172
|
const hasSelections = selections && selections.length > 0;
|
|
173
|
+
if (!hasArgs && !hasSelections) {
|
|
174
|
+
return (0, neverthrow.ok)(`${padding}${field.name}: true,`);
|
|
175
|
+
}
|
|
193
176
|
let line = `${padding}...f.${field.name}(`;
|
|
194
177
|
if (hasArgs) {
|
|
195
178
|
const argsResult = emitArguments(args, variableNames);
|
|
@@ -586,6 +569,52 @@ const buildModifier = (structure) => {
|
|
|
586
569
|
return structure.inner + structure.lists.join("");
|
|
587
570
|
};
|
|
588
571
|
/**
|
|
572
|
+
* Check if source modifier can be assigned to target modifier.
|
|
573
|
+
* Implements GraphQL List Coercion: depth difference of 0 or 1 is allowed.
|
|
574
|
+
*
|
|
575
|
+
* Rules:
|
|
576
|
+
* - A single value can be coerced into a list (depth diff = 1)
|
|
577
|
+
* - At each level, non-null can be assigned to nullable (but not vice versa)
|
|
578
|
+
*
|
|
579
|
+
* @param source - The modifier of the value being assigned (variable's type)
|
|
580
|
+
* @param target - The modifier expected by the position (field argument's type)
|
|
581
|
+
* @returns true if assignment is valid
|
|
582
|
+
*/
|
|
583
|
+
const isModifierAssignable = (source, target) => {
|
|
584
|
+
const srcStruct = parseModifierStructure(source);
|
|
585
|
+
const tgtStruct = parseModifierStructure(target);
|
|
586
|
+
const depthDiff = tgtStruct.lists.length - srcStruct.lists.length;
|
|
587
|
+
if (depthDiff < 0 || depthDiff > 1) return false;
|
|
588
|
+
const tgtListsToCompare = depthDiff === 1 ? tgtStruct.lists.slice(1) : tgtStruct.lists;
|
|
589
|
+
if (depthDiff === 1 && srcStruct.lists.length === 0 && srcStruct.inner === "?" && tgtStruct.lists[0] === "[]!") {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
if (srcStruct.inner === "?" && tgtStruct.inner === "!") return false;
|
|
593
|
+
for (let i = 0; i < srcStruct.lists.length; i++) {
|
|
594
|
+
const srcList = srcStruct.lists[i];
|
|
595
|
+
const tgtList = tgtListsToCompare[i];
|
|
596
|
+
if (srcList === "[]?" && tgtList === "[]!") return false;
|
|
597
|
+
}
|
|
598
|
+
return true;
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* Derive minimum modifier needed to satisfy expected modifier.
|
|
602
|
+
* When List Coercion can apply, returns one level shallower.
|
|
603
|
+
*
|
|
604
|
+
* @param expectedModifier - The modifier expected by the field argument
|
|
605
|
+
* @returns The minimum modifier the variable must have
|
|
606
|
+
*/
|
|
607
|
+
const deriveMinimumModifier = (expectedModifier) => {
|
|
608
|
+
const struct = parseModifierStructure(expectedModifier);
|
|
609
|
+
if (struct.lists.length > 0) {
|
|
610
|
+
return buildModifier({
|
|
611
|
+
inner: struct.inner,
|
|
612
|
+
lists: struct.lists.slice(1)
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return expectedModifier;
|
|
616
|
+
};
|
|
617
|
+
/**
|
|
589
618
|
* Merge two modifiers by taking the stricter constraint at each level.
|
|
590
619
|
* - Non-null (!) is stricter than nullable (?)
|
|
591
620
|
* - List depths must match
|
|
@@ -667,7 +696,8 @@ const collectVariablesFromValue = (value, expectedTypeName, expectedModifier, sc
|
|
|
667
696
|
usages.push({
|
|
668
697
|
name: value.name,
|
|
669
698
|
typeName: expectedTypeName,
|
|
670
|
-
|
|
699
|
+
expectedModifier,
|
|
700
|
+
minimumModifier: deriveMinimumModifier(expectedModifier),
|
|
671
701
|
typeKind
|
|
672
702
|
});
|
|
673
703
|
return null;
|
|
@@ -777,7 +807,12 @@ const getFieldReturnType = (schema, parentTypeName, fieldName) => {
|
|
|
777
807
|
};
|
|
778
808
|
/**
|
|
779
809
|
* Merge multiple variable usages into a single InferredVariable.
|
|
780
|
-
* Validates type compatibility and merges modifiers using
|
|
810
|
+
* Validates type compatibility and merges modifiers using List Coercion rules.
|
|
811
|
+
*
|
|
812
|
+
* The algorithm:
|
|
813
|
+
* 1. Validate all usages have the same type name
|
|
814
|
+
* 2. Merge minimumModifiers to find the candidate (shallowest type that could work)
|
|
815
|
+
* 3. Verify the candidate can satisfy ALL expected modifiers via isModifierAssignable
|
|
781
816
|
*/
|
|
782
817
|
const mergeVariableUsages = (variableName, usages) => {
|
|
783
818
|
if (usages.length === 0) {
|
|
@@ -797,9 +832,9 @@ const mergeVariableUsages = (variableName, usages) => {
|
|
|
797
832
|
});
|
|
798
833
|
}
|
|
799
834
|
}
|
|
800
|
-
let
|
|
835
|
+
let candidateModifier = first.minimumModifier;
|
|
801
836
|
for (let i = 1; i < usages.length; i++) {
|
|
802
|
-
const result = mergeModifiers(
|
|
837
|
+
const result = mergeModifiers(candidateModifier, usages[i].minimumModifier);
|
|
803
838
|
if (!result.ok) {
|
|
804
839
|
return (0, neverthrow.err)({
|
|
805
840
|
code: "GRAPHQL_VARIABLE_MODIFIER_INCOMPATIBLE",
|
|
@@ -807,12 +842,21 @@ const mergeVariableUsages = (variableName, usages) => {
|
|
|
807
842
|
variableName
|
|
808
843
|
});
|
|
809
844
|
}
|
|
810
|
-
|
|
845
|
+
candidateModifier = result.value;
|
|
846
|
+
}
|
|
847
|
+
for (const usage of usages) {
|
|
848
|
+
if (!isModifierAssignable(candidateModifier, usage.expectedModifier)) {
|
|
849
|
+
return (0, neverthrow.err)({
|
|
850
|
+
code: "GRAPHQL_VARIABLE_MODIFIER_INCOMPATIBLE",
|
|
851
|
+
message: `Variable "$${variableName}" with modifier "${candidateModifier}" cannot satisfy expected "${usage.expectedModifier}"`,
|
|
852
|
+
variableName
|
|
853
|
+
});
|
|
854
|
+
}
|
|
811
855
|
}
|
|
812
856
|
return (0, neverthrow.ok)({
|
|
813
857
|
name: variableName,
|
|
814
858
|
typeName: first.typeName,
|
|
815
|
-
modifier:
|
|
859
|
+
modifier: candidateModifier,
|
|
816
860
|
typeKind: first.typeKind
|
|
817
861
|
});
|
|
818
862
|
};
|
|
@@ -1008,7 +1052,8 @@ const transformFragment = (frag, schema, resolvedFragmentVariables) => {
|
|
|
1008
1052
|
const allUsages = [...directUsages, ...spreadVariables.map((v) => ({
|
|
1009
1053
|
name: v.name,
|
|
1010
1054
|
typeName: v.typeName,
|
|
1011
|
-
|
|
1055
|
+
expectedModifier: v.modifier,
|
|
1056
|
+
minimumModifier: v.modifier,
|
|
1012
1057
|
typeKind: v.typeKind
|
|
1013
1058
|
}))];
|
|
1014
1059
|
const variablesResult = inferVariablesFromUsages(allUsages);
|
|
@@ -1296,6 +1341,23 @@ const generateDefsStructure = (schemaName, categoryVars, chunkSize) => {
|
|
|
1296
1341
|
|
|
1297
1342
|
//#endregion
|
|
1298
1343
|
//#region packages/codegen/src/file.ts
|
|
1344
|
+
const removeDirectory = (dirPath) => {
|
|
1345
|
+
const targetPath = (0, node_path.resolve)(dirPath);
|
|
1346
|
+
try {
|
|
1347
|
+
(0, node_fs.rmSync)(targetPath, {
|
|
1348
|
+
recursive: true,
|
|
1349
|
+
force: true
|
|
1350
|
+
});
|
|
1351
|
+
return (0, neverthrow.ok)(undefined);
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1354
|
+
return (0, neverthrow.err)({
|
|
1355
|
+
code: "REMOVE_FAILED",
|
|
1356
|
+
message,
|
|
1357
|
+
outPath: targetPath
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1299
1361
|
const writeModule = (outPath, contents) => {
|
|
1300
1362
|
const targetPath = (0, node_path.resolve)(outPath);
|
|
1301
1363
|
try {
|
|
@@ -1492,6 +1554,13 @@ export * from "./_internal";
|
|
|
1492
1554
|
const defsPaths = [];
|
|
1493
1555
|
if (categoryVars) {
|
|
1494
1556
|
const outDir = (0, node_path.dirname)(outPath);
|
|
1557
|
+
const defsDir = (0, node_path.join)(outDir, "_defs");
|
|
1558
|
+
if ((0, node_fs.existsSync)(defsDir)) {
|
|
1559
|
+
const removeResult = removeDirectory(defsDir);
|
|
1560
|
+
if (removeResult.isErr()) {
|
|
1561
|
+
return (0, neverthrow.err)(removeResult.error);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1495
1564
|
const combinedVars = {
|
|
1496
1565
|
enums: [],
|
|
1497
1566
|
inputs: [],
|
|
@@ -1547,6 +1616,7 @@ exports.emitFragment = emitFragment;
|
|
|
1547
1616
|
exports.emitOperation = emitOperation;
|
|
1548
1617
|
exports.hashSchema = hashSchema;
|
|
1549
1618
|
exports.inferVariablesFromUsages = inferVariablesFromUsages;
|
|
1619
|
+
exports.isModifierAssignable = isModifierAssignable;
|
|
1550
1620
|
exports.loadSchema = loadSchema;
|
|
1551
1621
|
exports.mergeModifiers = mergeModifiers;
|
|
1552
1622
|
exports.mergeVariableUsages = mergeVariableUsages;
|