Skip to content

Commit

Permalink
Merge pull request #6 from liontariai/AIC-26-GraphQL-Fragments-support
Browse files Browse the repository at this point in the history
[AIC] Implement explicit fragments as feature
  • Loading branch information
liontariai authored Jul 1, 2024
2 parents fbf6823 + 6080e1a commit ce14b9e
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 7 deletions.
33 changes: 31 additions & 2 deletions src/lib/codegen/flavors/default/generator-flavor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export class GeneratorSelectionTypeFlavorDefault extends GeneratorSelectionTypeF
isRootType?: "Query" | "Mutation" | "Subscription";
onTypeFragment?: string;
isFragment?: string;
} | undefined;
type CleanupNever<A> = Omit<A, keyof A> & {
Expand All @@ -139,8 +140,23 @@ export class GeneratorSelectionTypeFlavorDefault extends GeneratorSelectionTypeF
: never;
},
> = Prettify<CleanupNever<R>>;
type ReturnTypeFromFragment<T> = T extends (
this: any,
...args: any[]
) => infer R
? R
: never;
type ArgumentsTypeFromFragment<T> = T extends (
this: any,
...args: infer A
) => any
? A
: never;
type SelectionHelpers<S, T> = {
$fragment: <F extends (this: any, ...args: any[]) => any>(
f: F,
) => (...args: ArgumentsTypeFromFragment<F>) => ReturnTypeFromFragment<F>;
$scalars: () => ScalarsFromSelection<S, T>;
};
`;
Expand Down Expand Up @@ -334,7 +350,7 @@ export class GeneratorSelectionTypeFlavorDefault extends GeneratorSelectionTypeF

let selectionType = "";

if (this.typeMeta.isUnion || this.typeMeta.isInterface) {
if (this.typeMeta.isUnion) {
const types = this.typeMeta.possibleTypes
.map(
(t) =>
Expand Down Expand Up @@ -462,6 +478,12 @@ export class GeneratorSelectionTypeFlavorDefault extends GeneratorSelectionTypeF
`;
} else {
helperFunctions = `
$fragment: (f: (this: any, ...args: any) => any) =>
f.bind({
collector: this,
fieldName: "",
isFragment: f.name,
}),
$scalars: () =>
selectScalars(
make${selectionFunctionName}Input.bind(this)(),
Expand Down Expand Up @@ -507,15 +529,22 @@ export class GeneratorSelectionTypeFlavorDefault extends GeneratorSelectionTypeF
}", r, this, parent?.collector, parent?.args, parent?.argsMeta);
_result[SLW_IS_ROOT_TYPE] = parent?.isRootType;
_result[SLW_IS_ON_TYPE_FRAGMENT] = parent?.onTypeFragment;
_result[SLW_IS_FRAGMENT] = parent?.isFragment;
Object.keys(r).forEach((key) => (_result as T)[key as keyof T]);
const result = _result as unknown as T${this.typeMeta.isList ? "[]" : ""};
if (parent?.onTypeFragment) {
return {
[parent.onTypeFragment]: result,
};
} as unknown as typeof result;
}
if (parent?.isFragment) {
return {
[parent.isFragment]: result,
} as unknown as typeof result;
}
return result;
}
return innerFn.bind(new OperationSelectionCollector("${selectionFunctionName}", parent?.collector))();
Expand Down
46 changes: 41 additions & 5 deletions src/lib/codegen/flavors/default/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@ export class RootOperation {
} ${op.selection}
`,
variables: op.variables,
fragments: op.usedFragments,
},
}),
{} as Record<string, { query: string; variables: any }>,
{} as Record<
string,
{
query: string;
variables: any;
fragments: Map<string, string>;
}
>,
);
// const subscription = `{${subscriptions.join("")}}`;

Expand All @@ -65,7 +73,11 @@ export class RootOperation {
}

private async executeOperation(
query: { query: string; variables: any },
query: {
query: string;
variables: any;
fragments: Map<string, string>;
},
headers: Record<string, string> = {},
) {
const res = await fetch("[ENDPOINT]", {
Expand All @@ -75,7 +87,10 @@ export class RootOperation {
...headers,
},
body: JSON.stringify({
query: query.query,
query: `
${[...query.fragments.values()].join("\n")}
${query.query}
`,
variables: query.variables,
}),
});
Expand Down Expand Up @@ -145,6 +160,7 @@ export class OperationSelectionCollector {
public renderSelections(
path: string[] = [],
opVars: Record<string, any> = {},
usedFragments: Map<string, string> = new Map(),
) {
const result: Record<string, any> = {};
const varDefs: string[] = [];
Expand Down Expand Up @@ -172,11 +188,26 @@ export class OperationSelectionCollector {
selection: subSelection,
variableDefinitions: subVarDefs,
variables: subVars,
} = value[SLW_COLLECTOR].renderSelections(subPath, opVars);
} = value[SLW_COLLECTOR].renderSelections(
subPath,
opVars,
usedFragments,
);

if (value[SLW_IS_ON_TYPE_FRAGMENT]) {
result[key] =
`... on ${value[SLW_IS_ON_TYPE_FRAGMENT]} ${subSelection}`;
} else if (value[SLW_IS_FRAGMENT]) {
const fragmentName = `${key}_${subVarDefs.map((v) => v.split(":")[0].slice(1)).join("_")}`;
result[key] = `...${fragmentName}`;
const fragment = `fragment ${fragmentName} on ${value[SLW_FIELD_TYPE]} ${subSelection}`;
if (!usedFragments.has(fragmentName)) {
usedFragments.set(fragmentName, fragment);
} else if (usedFragments.get(fragmentName) !== fragment) {
console.warn(
`Fragment ${fragmentName} is already defined with a different selection`,
);
}
} else {
result[key] = `${fieldSelection} ${subSelection}`;
}
Expand All @@ -190,9 +221,10 @@ export class OperationSelectionCollector {
for (const [key, value] of Object.entries(result)) {
const isSubSelection = value.toString().startsWith("{");
const isOnType = value.toString().startsWith("... on");
const isFragment = value.toString().startsWith("...");
if (key === value) {
rendered += `${key} `;
} else if (isOnType) {
} else if (isOnType || isFragment) {
rendered += `${value} `;
} else {
rendered += `${key}${!isSubSelection ? ":" : ""} ${value} `;
Expand All @@ -203,6 +235,7 @@ export class OperationSelectionCollector {
selection: rendered,
variableDefinitions: varDefs,
variables,
usedFragments,
};
}

Expand Down Expand Up @@ -272,6 +305,7 @@ export const SLW_FIELD_NAME = Symbol("SLW_FIELD_NAME");
export const SLW_FIELD_TYPE = Symbol("SLW_FIELD_TYPE");
export const SLW_IS_ROOT_TYPE = Symbol("SLW_IS_ROOT_TYPE");
export const SLW_IS_ON_TYPE_FRAGMENT = Symbol("SLW_IS_ON_TYPE_FRAGMENT");
export const SLW_IS_FRAGMENT = Symbol("SLW_IS_FRAGMENT");
export const SLW_VALUE = Symbol("SLW_VALUE");
export const SLW_ARGS = Symbol("SLW_ARGS");
export const SLW_ARGS_META = Symbol("SLW_ARGS_META");
Expand Down Expand Up @@ -307,6 +341,7 @@ export class SelectionWrapperImpl<

[SLW_IS_ROOT_TYPE]?: "Query" | "Mutation" | "Subscription";
[SLW_IS_ON_TYPE_FRAGMENT]?: string;
[SLW_IS_FRAGMENT]?: string;

[SLW_ARGS]?: argsT;
[SLW_ARGS_META]?: Record<string, string>;
Expand Down Expand Up @@ -429,6 +464,7 @@ export class SelectionWrapper<
prop === SLW_FIELD_TYPE ||
prop === SLW_IS_ROOT_TYPE ||
prop === SLW_IS_ON_TYPE_FRAGMENT ||
prop === SLW_IS_FRAGMENT ||
prop === SLW_VALUE ||
prop === SLW_ARGS ||
prop === SLW_ARGS_META ||
Expand Down

0 comments on commit ce14b9e

Please sign in to comment.