Skip to content

Commit

Permalink
Graphql as function (#776)
Browse files Browse the repository at this point in the history
* find_graphql looks for graphql function

* dry up js and svelte processing

* extract load functions

* reactive transform respect graphql functions

* cleanup

* add graphql_tag_export to plugins

* compute overloaded return values

* generate graphql overloads

* remove unused import and types

* fix extraction

* tests pass

* support root graphql functions too

* unused import

* changeset

* generate typedefs for fragment functions

* linter

* linter and update snapshots

* update snapshots

* update all docs to use graphql function

* remove unnecessary type import

* add graphql function to release notes

* doc tweak

* missed one

* clean up comments

* can -> should

* docs housekeeping

* remove file

* include paginated fragments as valid args to fragment
  • Loading branch information
AlecAivazis authored Dec 19, 2022
1 parent f0ac816 commit 8f70291
Show file tree
Hide file tree
Showing 50 changed files with 1,257 additions and 315 deletions.
6 changes: 6 additions & 0 deletions .changeset/dull-steaks-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'houdini': patch
'houdini-svelte': patch
---

graphql template tag can now be used as a function for automatic typing
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
// src/routes/items/+page.svelte
import { graphql } from '$houdini'
const AllItems = graphql`
const AllItems = graphql(`
query AllItems {
items {
text
}
}
`
`)
</script>
{#each $AllItems.data.items as item}
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/lib/QueryComponent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
// svelte-ignore unused-export-let
export let id = '';
const result: FragmentQueryVarsStore = graphql`
const result = graphql(`
query FragmentQueryVars($id: ID!) {
user(id: $id, snapshot: "preprocess-query-variable") {
name
}
}
`;
`);
</script>

{$result.data?.user.name}
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/lib/QueryExtNoAutoFetch.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { graphql } from '$houdini';
const store = graphql`
const store = graphql(`
query QueryExtNoAutoFetch @manual_load {
usersList(limit: 3, snapshot: "QueryExtNoAutoFetch") {
id
name
}
}
`;
`);
</script>

<p id="QueryExt-result">
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
return { key, value: (routes as Record<string, string>)[key] };
});
const info = graphql`
const info = graphql(`
query LayoutSession {
session
}
`;
`);
</script>

<slot />
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { graphql } from '$houdini';
const store = graphql`
const store = graphql(`
query isFetching_route_1 {
user(id: 1, snapshot: "isFetching_route_1", delay: 200) {
id
name
}
}
`;
`);
</script>

<a href="./route_2">route_2</a>
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { graphql } from '$houdini';
const store = graphql`
const store = graphql(`
query isFetching_w {
user(id: 1, snapshot: "isFetching_w", delay: 200) {
id
name
}
}
`;
`);
$: console.info(`with_load - isFetching: ${$store.isFetching}`);
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
<script lang="ts">
import {
graphql,
paginatedFragment,
type BackwardsCursorFragment,
type UserFragmentBackwardsCursorQueryStore
} from '$houdini';
import { graphql, paginatedFragment } from '$houdini';
const queryResult: UserFragmentBackwardsCursorQueryStore = graphql`
const queryResult = graphql(`
query UserFragmentBackwardsCursorQuery {
user(id: "1", snapshot: "pagination-fragment-backwards-cursor") {
...BackwardsCursorFragment
}
}
`;
`);
const fragmentResult = paginatedFragment<BackwardsCursorFragment>(
const fragmentResult = paginatedFragment(
$queryResult.data?.user ?? null,
graphql`
graphql(`
fragment BackwardsCursorFragment on User {
friendsConnection(last: 2) @paginate {
edges {
Expand All @@ -26,16 +21,16 @@
}
}
}
`
`)
);
</script>

<div id="result">
{$fragmentResult.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
{$fragmentResult?.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
</div>

<div id="pageInfo">
{JSON.stringify($fragmentResult.pageInfo)}
{JSON.stringify($fragmentResult?.pageInfo)}
</div>

<button id="previous" on:click={() => fragmentResult.loadPreviousPage()}>previous</button>
<button id="previous" on:click={() => fragmentResult?.loadPreviousPage()}>previous</button>
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
<script lang="ts">
import {
paginatedFragment,
graphql,
type UserFragmentForwardsCursorQueryStore,
type ForwardsCursorFragment
} from '$houdini';
import { paginatedFragment, graphql } from '$houdini';
const queryResult: UserFragmentForwardsCursorQueryStore = graphql`
const queryResult = graphql(`
query UserFragmentForwardsCursorQuery {
user(id: "1", snapshot: "pagination-fragment-forwards-cursor") {
...ForwardsCursorFragment
}
}
`;
`);
const fragmentResult = paginatedFragment<ForwardsCursorFragment>(
const fragmentResult = paginatedFragment(
$queryResult.data?.user ?? null,
graphql`
graphql(`
fragment ForwardsCursorFragment on User {
friendsConnection(first: 2) @paginate {
edges {
Expand All @@ -26,16 +21,16 @@
}
}
}
`
`)
);
</script>

<div id="result">
{$fragmentResult.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
{$fragmentResult?.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
</div>

<div id="pageInfo">
{JSON.stringify($fragmentResult.pageInfo)}
{JSON.stringify($fragmentResult?.pageInfo)}
</div>

<button id="next" on:click={() => fragmentResult.loadNextPage()}>next</button>
<button id="next" on:click={() => fragmentResult?.loadNextPage()}>next</button>
21 changes: 8 additions & 13 deletions e2e/sveltekit/src/routes/pagination/fragment/offset/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
<script lang="ts">
import {
paginatedFragment,
graphql,
type OffsetFragment,
type UserFragmentOffsetQueryStore
} from '$houdini';
import { paginatedFragment, graphql } from '$houdini';
const queryResult: UserFragmentOffsetQueryStore = graphql`
const queryResult = graphql(`
query UserFragmentOffsetQuery {
user(id: "1", snapshot: "pagination-fragment-offset") {
...OffsetFragment
}
}
`;
`);
const fragmentResult = paginatedFragment<OffsetFragment>(
const fragmentResult = paginatedFragment(
$queryResult.data?.user ?? null,
graphql`
graphql(`
fragment OffsetFragment on User {
friendsList(limit: 2) @paginate {
name
}
}
`
`)
);
</script>

<div id="result">
{$fragmentResult.data?.friendsList.map((node) => node?.name).join(', ')}
{$fragmentResult?.data?.friendsList.map((node) => node?.name).join(', ')}
</div>

<button id="next" on:click={() => fragmentResult.loadNextPage()}>next</button>
<button id="next" on:click={() => fragmentResult?.loadNextPage()}>next</button>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { CachePolicy, graphql, type BackwardsCursorPaginationQueryStore } from '$houdini';
import { CachePolicy, graphql } from '$houdini';
const result: BackwardsCursorPaginationQueryStore = graphql`
const result = graphql(`
query BackwardsCursorPaginationQuery {
usersConnection(last: 2, snapshot: "pagination-query-backwards-cursor") @paginate {
edges {
Expand All @@ -11,7 +11,7 @@
}
}
}
`;
`);
</script>

<div id="result">
Expand Down
4 changes: 2 additions & 2 deletions e2e/sveltekit/src/routes/query-param/UserName.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
user &&
fragment(
user,
graphql`
graphql(/* GraphQL */ `
fragment UserName on User {
name
}
`
`)
);
</script>

Expand Down
3 changes: 2 additions & 1 deletion packages/houdini-react/src/plugin/extract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { parse } from '@babel/parser'
import { Config } from 'houdini'
import * as recast from 'recast'

export function extract_documents(filepath: string, content: string) {
export function extract_documents(config: Config, filepath: string, content: string) {
// the documents we've found
const documents: string[] = []

Expand Down
3 changes: 0 additions & 3 deletions packages/houdini-react/src/plugin/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,10 @@ export function transform_file(page: TransformPage): { code: string } {
query = argument.quasis[0].value.raw
} else if (argument.type === 'StringLiteral') {
query = argument.value
} else {
console.log(value.callee.name)
}

// we want to replace the template tag with an import to the appropriate
// artifact

let name = page.config.documentName(graphql.parse(query))
let artifact_name = ensureArtifactImport({
config: page.config,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { parseJS, fs, path } from 'houdini'
import { mockCollectedDoc, testConfig } from 'houdini/test'
import { test, expect } from 'vitest'

import generate from '..'

const config = testConfig()
const plugin_root = config.pluginDirectory('hodini-svelte')

test('generates types for fragments', async function () {
// create the mock filesystem
await fs.mock({
[path.join(config.pluginDirectory('houdini-svelte'), 'runtime', 'fragments.d.ts')]: `
import { Fragment } from '$houdini/runtime/lib/types';
import { Readable } from 'svelte/store';
import { FragmentStore } from './stores';
import type { FragmentStorePaginated } from './stores/pagination/fragment';
export declare function fragment<_Fragment extends Fragment<any>>(ref: _Fragment, fragment: FragmentStore<_Fragment['shape']>): Readable<NonNullable<_Fragment['shape']>> & {
data: Readable<_Fragment>;
};
export declare function fragment<_Fragment extends Fragment<any>>(ref: _Fragment | null, fragment: FragmentStore<_Fragment['shape']>): Readable<NonNullable<_Fragment['shape']> | null> & {
data: Readable<_Fragment | null>;
};
export declare function paginatedFragment<_Fragment extends Fragment<any>>(initialValue: _Fragment | null, document: FragmentStore<_Fragment['shape']>): FragmentStorePaginated<_Fragment['shape'], {}>;
export declare function paginatedFragment<_Fragment extends Fragment<any>>(initialValue: _Fragment, document: FragmentStore<_Fragment['shape']>): FragmentStorePaginated<_Fragment['shape'], {}>;
`,
})

// execute the generator
await generate({
config,
documents: [mockCollectedDoc(`fragment TestFragment on Query { viewer { id } } `)],
framework: 'kit',
plugin_root,
})

// load the contents of the file
const queryContents = await fs.readFile(
path.join(config.pluginRuntimeDirectory('houdini-svelte'), 'fragments.d.ts')
)

expect(queryContents).toBeTruthy()

//the parser doesn't work right but the type imports are correct.
const parsedQuery = (await parseJS(queryContents!))?.script

// verify contents
expect(parsedQuery).toMatchInlineSnapshot(`
import { TestFragmentStore } from "../stores/TestFragment";
import { Fragment } from "$houdini/runtime/lib/types";
import { Readable } from "svelte/store";
import { FragmentStore } from "./stores";
import type { FragmentStorePaginated } from "./stores/pagination/fragment";
export function fragment(
initialValue: {
$fragments: {
TestFragment: true;
};
},
document: TestFragmentStore
): ReturnType<TestFragmentStore["get"]>;
export function fragment(
initialValue: {
$fragments: {
TestFragment: true;
};
} | null,
document: TestFragmentStore
): ReturnType<TestFragmentStore["get"]> | null;
export declare function fragment<_Fragment extends Fragment<any>>(ref: _Fragment, fragment: FragmentStore<_Fragment["shape"]>): Readable<NonNullable<_Fragment["shape"]>> & {
data: Readable<_Fragment>;
};
export declare function fragment<_Fragment extends Fragment<any>>(ref: _Fragment | null, fragment: FragmentStore<_Fragment["shape"]>): Readable<NonNullable<_Fragment["shape"]> | null> & {
data: Readable<_Fragment | null>;
};
export declare function paginatedFragment<_Fragment extends Fragment<any>>(
initialValue: _Fragment | null,
document: FragmentStore<_Fragment["shape"]>
): FragmentStorePaginated<_Fragment["shape"], {}>;
export declare function paginatedFragment<_Fragment extends Fragment<any>>(initialValue: _Fragment, document: FragmentStore<_Fragment["shape"]>): FragmentStorePaginated<_Fragment["shape"], {}>;
`)
})
Loading

0 comments on commit 8f70291

Please sign in to comment.