Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AIC] Implement explicit fragments as feature #6

Merged
merged 3 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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