diff --git a/.changeset/dull-steaks-grab.md b/.changeset/dull-steaks-grab.md
new file mode 100644
index 000000000..ce7a85fdf
--- /dev/null
+++ b/.changeset/dull-steaks-grab.md
@@ -0,0 +1,6 @@
+---
+'houdini': patch
+'houdini-svelte': patch
+---
+
+graphql template tag can now be used as a function for automatic typing
diff --git a/README.md b/README.md
index 80e1c80a9..6b13336d3 100644
--- a/README.md
+++ b/README.md
@@ -33,13 +33,13 @@
// src/routes/items/+page.svelte
import { graphql } from '$houdini'
- const AllItems = graphql`
+ const AllItems = graphql(`
query AllItems {
items {
text
}
}
- `
+ `)
{#each $AllItems.data.items as item}
diff --git a/e2e/sveltekit/src/lib/QueryComponent.svelte b/e2e/sveltekit/src/lib/QueryComponent.svelte
index e7fd2f886..69ffd407f 100644
--- a/e2e/sveltekit/src/lib/QueryComponent.svelte
+++ b/e2e/sveltekit/src/lib/QueryComponent.svelte
@@ -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
}
}
- `;
+ `);
{$result.data?.user.name}
diff --git a/e2e/sveltekit/src/lib/QueryExtNoAutoFetch.svelte b/e2e/sveltekit/src/lib/QueryExtNoAutoFetch.svelte
index 7965f98f2..81f48875e 100644
--- a/e2e/sveltekit/src/lib/QueryExtNoAutoFetch.svelte
+++ b/e2e/sveltekit/src/lib/QueryExtNoAutoFetch.svelte
@@ -1,14 +1,14 @@
diff --git a/e2e/sveltekit/src/routes/+layout.svelte b/e2e/sveltekit/src/routes/+layout.svelte
index 415d0ea2f..636488d60 100644
--- a/e2e/sveltekit/src/routes/+layout.svelte
+++ b/e2e/sveltekit/src/routes/+layout.svelte
@@ -17,11 +17,11 @@
return { key, value: (routes as Record)[key] };
});
- const info = graphql`
+ const info = graphql(`
query LayoutSession {
session
}
- `;
+ `);
diff --git a/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte b/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte
index 70b883696..55e3e6a4d 100644
--- a/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte
+++ b/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte
@@ -1,14 +1,14 @@
route_2
diff --git a/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte b/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
index 825966a7a..5f5dabd32 100644
--- a/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
+++ b/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
@@ -1,14 +1,14 @@
diff --git a/e2e/sveltekit/src/routes/pagination/fragment/backwards-cursor/+page.svelte b/e2e/sveltekit/src/routes/pagination/fragment/backwards-cursor/+page.svelte
index fc73bdf9f..d79bb5ccf 100644
--- a/e2e/sveltekit/src/routes/pagination/fragment/backwards-cursor/+page.svelte
+++ b/e2e/sveltekit/src/routes/pagination/fragment/backwards-cursor/+page.svelte
@@ -1,22 +1,17 @@
- {$fragmentResult.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
+ {$fragmentResult?.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
- {JSON.stringify($fragmentResult.pageInfo)}
+ {JSON.stringify($fragmentResult?.pageInfo)}
-
+
diff --git a/e2e/sveltekit/src/routes/pagination/fragment/forward-cursor/+page.svelte b/e2e/sveltekit/src/routes/pagination/fragment/forward-cursor/+page.svelte
index 37a7e95e2..02da5641c 100644
--- a/e2e/sveltekit/src/routes/pagination/fragment/forward-cursor/+page.svelte
+++ b/e2e/sveltekit/src/routes/pagination/fragment/forward-cursor/+page.svelte
@@ -1,22 +1,17 @@
- {$fragmentResult.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
+ {$fragmentResult?.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
- {JSON.stringify($fragmentResult.pageInfo)}
+ {JSON.stringify($fragmentResult?.pageInfo)}
-
+
diff --git a/e2e/sveltekit/src/routes/pagination/fragment/offset/+page.svelte b/e2e/sveltekit/src/routes/pagination/fragment/offset/+page.svelte
index fc99d0f35..f9aa76b71 100644
--- a/e2e/sveltekit/src/routes/pagination/fragment/offset/+page.svelte
+++ b/e2e/sveltekit/src/routes/pagination/fragment/offset/+page.svelte
@@ -1,33 +1,28 @@
- {$fragmentResult.data?.friendsList.map((node) => node?.name).join(', ')}
+ {$fragmentResult?.data?.friendsList.map((node) => node?.name).join(', ')}
-
+
diff --git a/e2e/sveltekit/src/routes/pagination/query/backwards-cursor/+page.svelte b/e2e/sveltekit/src/routes/pagination/query/backwards-cursor/+page.svelte
index 89f85e292..a3c1c229d 100644
--- a/e2e/sveltekit/src/routes/pagination/query/backwards-cursor/+page.svelte
+++ b/e2e/sveltekit/src/routes/pagination/query/backwards-cursor/+page.svelte
@@ -1,7 +1,7 @@
diff --git a/e2e/sveltekit/src/routes/query-param/UserName.svelte b/e2e/sveltekit/src/routes/query-param/UserName.svelte
index c88a6429c..bccf79c6f 100644
--- a/e2e/sveltekit/src/routes/query-param/UserName.svelte
+++ b/e2e/sveltekit/src/routes/query-param/UserName.svelte
@@ -10,11 +10,11 @@
user &&
fragment(
user,
- graphql`
+ graphql(/* GraphQL */ `
fragment UserName on User {
name
}
- `
+ `)
);
diff --git a/packages/houdini-react/src/plugin/extract.ts b/packages/houdini-react/src/plugin/extract.ts
index 932a5970a..35ee902ee 100644
--- a/packages/houdini-react/src/plugin/extract.ts
+++ b/packages/houdini-react/src/plugin/extract.ts
@@ -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[] = []
diff --git a/packages/houdini-react/src/plugin/transform.ts b/packages/houdini-react/src/plugin/transform.ts
index 70e993aa9..0532600b6 100644
--- a/packages/houdini-react/src/plugin/transform.ts
+++ b/packages/houdini-react/src/plugin/transform.ts
@@ -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,
diff --git a/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/fragmentTypedefs.test.ts b/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/fragmentTypedefs.test.ts
new file mode 100644
index 000000000..be6fedcb1
--- /dev/null
+++ b/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/fragmentTypedefs.test.ts
@@ -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
>(ref: _Fragment, fragment: FragmentStore<_Fragment['shape']>): Readable> & {
+ data: Readable<_Fragment>;
+ };
+ export declare function fragment<_Fragment extends Fragment>(ref: _Fragment | null, fragment: FragmentStore<_Fragment['shape']>): Readable | null> & {
+ data: Readable<_Fragment | null>;
+ };
+ export declare function paginatedFragment<_Fragment extends Fragment>(initialValue: _Fragment | null, document: FragmentStore<_Fragment['shape']>): FragmentStorePaginated<_Fragment['shape'], {}>;
+ export declare function paginatedFragment<_Fragment extends Fragment>(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;
+
+ export function fragment(
+ initialValue: {
+ $fragments: {
+ TestFragment: true;
+ };
+ } | null,
+ document: TestFragmentStore
+ ): ReturnType | null;
+
+ export declare function fragment<_Fragment extends Fragment>(ref: _Fragment, fragment: FragmentStore<_Fragment["shape"]>): Readable> & {
+ data: Readable<_Fragment>;
+ };
+
+ export declare function fragment<_Fragment extends Fragment>(ref: _Fragment | null, fragment: FragmentStore<_Fragment["shape"]>): Readable | null> & {
+ data: Readable<_Fragment | null>;
+ };
+
+ export declare function paginatedFragment<_Fragment extends Fragment>(
+ initialValue: _Fragment | null,
+ document: FragmentStore<_Fragment["shape"]>
+ ): FragmentStorePaginated<_Fragment["shape"], {}>;
+
+ export declare function paginatedFragment<_Fragment extends Fragment>(initialValue: _Fragment, document: FragmentStore<_Fragment["shape"]>): FragmentStorePaginated<_Fragment["shape"], {}>;
+ `)
+})
diff --git a/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/index.ts b/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/index.ts
new file mode 100644
index 000000000..70cd22b6e
--- /dev/null
+++ b/packages/houdini-svelte/src/plugin/codegen/fragmentTypedefs/index.ts
@@ -0,0 +1,153 @@
+import { StatementKind } from 'ast-types/lib/gen/kinds'
+import { parseJS, path, fs, ArtifactKind, ensureImports, CollectedGraphQLDocument } from 'houdini'
+import * as recast from 'recast'
+
+import { PluginGenerateInput } from '..'
+import { stores_directory_name, store_name } from '../../kit'
+
+const AST = recast.types.builders
+
+export default async function fragmentTypedefs(input: PluginGenerateInput) {
+ // before we update the typedefs lets find all of the fragments so we can overload the correct function
+ let fragments: Record> = {}
+
+ for (const doc of input.documents) {
+ if (doc.kind === ArtifactKind.Fragment) {
+ // if the fragment is paginated, add it to the paginated one
+ if (doc.refetch?.paginated) {
+ fragments = {
+ ...fragments,
+ ['paginatedFragment']: {
+ ...fragments['paginatedFragment'],
+ [doc.originalString]: doc,
+ },
+ }
+ }
+
+ // always add the fragment
+ fragments = {
+ ...fragments,
+ ['fragment']: {
+ ...fragments['fragment'],
+ [doc.originalString]: doc,
+ },
+ }
+ }
+ }
+
+ // find the path for the fragment typedefs
+ const target_path = path.join(
+ input.config.pluginRuntimeDirectory('houdini-svelte'),
+ 'fragments.d.ts'
+ )
+
+ const contents = await parseJS((await fs.readFile(target_path)) || '')!
+ if (!contents) {
+ return
+ }
+
+ function insert_exports(which: string, statements: StatementKind[]) {
+ for (const [i, expression] of [...(contents!.script.body ?? [])].entries()) {
+ if (
+ expression.type !== 'ExportNamedDeclaration' ||
+ expression.declaration?.type !== 'TSDeclareFunction' ||
+ expression.declaration.id?.name !== which
+ ) {
+ continue
+ }
+
+ // it should return the right thing
+ contents!.script.body.splice(i, 0, ...statements)
+
+ // we're done
+ break
+ }
+ }
+
+ for (const [which, docs] of Object.entries(fragments)) {
+ // insert a definition for every fragment
+ insert_exports(
+ which,
+ Object.entries(docs).flatMap(([queryString, doc]) => {
+ if (!doc.generateStore) {
+ return []
+ }
+
+ // make sure we are importing the store
+ const store = store_name({ config: input.config, name: doc.name })
+ const import_path = path.join('..', stores_directory_name(), doc.name)
+ // build up the documentInput with the query string as a hard coded value
+ const fragment_map = AST.tsTypeLiteral([
+ AST.tsPropertySignature(
+ AST.identifier('$fragments'),
+ AST.tsTypeAnnotation(
+ AST.tsTypeLiteral([
+ AST.tsPropertySignature(
+ AST.identifier(doc.name),
+ AST.tsTypeAnnotation(
+ AST.tsLiteralType(AST.booleanLiteral(true))
+ )
+ ),
+ ])
+ )
+ ),
+ ])
+ // build up the 2 input options
+ const initial_value_input = AST.identifier('initialValue')
+ initial_value_input.typeAnnotation = AST.tsTypeAnnotation(fragment_map)
+ const initial_value_or_null_input = AST.identifier('initialValue')
+ initial_value_or_null_input.typeAnnotation = AST.tsTypeAnnotation(
+ AST.tsUnionType([fragment_map, AST.tsNullKeyword()])
+ )
+
+ // regardless of the input value, we need to pass the document store
+ const document_input = AST.identifier('document')
+ document_input.typeAnnotation = AST.tsTypeAnnotation(
+ AST.tsTypeReference(AST.identifier(store))
+ )
+
+ // the return value
+ const return_value = AST.tsTypeReference(
+ AST.identifier('ReturnType'),
+ AST.tsTypeParameterInstantiation([
+ AST.tsIndexedAccessType(
+ AST.tsTypeReference(AST.identifier(store)),
+ AST.tsLiteralType(AST.stringLiteral('get'))
+ ),
+ ])
+ )
+
+ // make sure the store is imported
+ ensureImports({
+ config: input.config,
+ body: contents!.script.body!,
+ sourceModule: import_path,
+ import: [store],
+ })
+
+ // if the user passes the string, return the correct store
+ return [
+ AST.exportNamedDeclaration(
+ AST.tsDeclareFunction(
+ AST.identifier(which),
+ [initial_value_input, document_input],
+ AST.tsTypeAnnotation(return_value)
+ )
+ ),
+ AST.exportNamedDeclaration(
+ AST.tsDeclareFunction(
+ AST.identifier(which),
+ [initial_value_or_null_input, document_input],
+ AST.tsTypeAnnotation(
+ AST.tsUnionType([return_value, AST.tsNullKeyword()])
+ )
+ )
+ ),
+ ]
+ })
+ )
+ }
+
+ // write the updated file
+ await fs.writeFile(target_path, recast.prettyPrint(contents.script).code)
+}
diff --git a/packages/houdini-svelte/src/plugin/codegen/index.ts b/packages/houdini-svelte/src/plugin/codegen/index.ts
index ddb8efff9..a46bb1d8a 100644
--- a/packages/houdini-svelte/src/plugin/codegen/index.ts
+++ b/packages/houdini-svelte/src/plugin/codegen/index.ts
@@ -2,6 +2,7 @@ import { GenerateHookInput, fs, Config } from 'houdini'
import { stores_directory, type_route_dir } from '../kit'
import components from './components'
+import fragmentTypedefs from './fragmentTypedefs'
import kit from './routes'
import stores from './stores'
@@ -17,6 +18,7 @@ export default async function (input: PluginGenerateInput) {
kit(input.framework, input),
stores(input),
components(input.framework, input),
+ fragmentTypedefs(input),
])
}
diff --git a/packages/houdini-svelte/src/plugin/extract.ts b/packages/houdini-svelte/src/plugin/extract.ts
index 5b76f717e..f7bc2744e 100644
--- a/packages/houdini-svelte/src/plugin/extract.ts
+++ b/packages/houdini-svelte/src/plugin/extract.ts
@@ -1,29 +1,21 @@
-import { parseJS, type Maybe, type Script } from 'houdini'
+import { parseJS, type Maybe, type Script, find_graphql, Config } from 'houdini'
import * as svelte from 'svelte/compiler'
-export default async function (filepath: string, contents: string): Promise {
+export default async function (
+ config: Config,
+ filepath: string,
+ contents: string
+): Promise {
const documents: string[] = []
-
let parsedFile = await parseSvelte(contents)
if (!parsedFile) {
return documents
}
- // look for any template tag literals in the script body
- svelte.walk(parsedFile.script, {
- enter(node) {
- // if we are looking at the graphql template tag
- if (
- node.type === 'TaggedTemplateExpression' &&
- // @ts-ignore
- node.tag.name === 'graphql'
- ) {
- // @ts-ignore
- // parse the tag contents to get the info we need
- const printedDoc = node.quasi.quasis[0].value.raw
-
- documents.push(printedDoc)
- }
+ // look for graphql documents like normal
+ await find_graphql(config, parsedFile.script, {
+ tag({ tagContent }) {
+ documents.push(tagContent)
},
})
diff --git a/packages/houdini-svelte/src/plugin/extractLoadFunction.test.ts b/packages/houdini-svelte/src/plugin/extractLoadFunction.test.ts
index d73c0ba71..5370fac02 100644
--- a/packages/houdini-svelte/src/plugin/extractLoadFunction.test.ts
+++ b/packages/houdini-svelte/src/plugin/extractLoadFunction.test.ts
@@ -24,6 +24,24 @@ describe('extract_load_function', function () {
}
}
\`
+ `,
+ expected: {
+ exports: [houdini_load_fn],
+ houdini_load: ['Foo'],
+ },
+ },
+ {
+ title: 'handle functions',
+ source: `
+ import { graphql } from '$houdini'
+
+ export const _houdini_load = graphql(\`
+ query Foo {
+ viewer {
+ id
+ }
+ }
+ \`)
`,
expected: {
exports: [houdini_load_fn],
@@ -43,6 +61,26 @@ describe('extract_load_function', function () {
}
\`
+ export const _houdini_load = store
+ `,
+ expected: {
+ exports: [houdini_load_fn],
+ houdini_load: ['Foo'],
+ },
+ },
+ {
+ title: 'load single inline identifier as function',
+ source: `
+ import { graphql } from '$houdini'
+
+ const store = graphql(\`
+ query Foo {
+ viewer {
+ id
+ }
+ }
+ \`)
+
export const _houdini_load = store
`,
expected: {
@@ -231,6 +269,20 @@ describe('extract_load_function', function () {
exports: [houdini_after_load_fn, houdini_before_load_fn],
},
},
+ {
+ title: 'ignores call expressions inside of functions',
+ source: `
+ fragment(foo, graphql(\`
+ query MyQuery {
+ foo
+ }
+ \`))
+ `,
+ expected: {
+ exports: [],
+ houdini_load: [],
+ },
+ },
]
for (const row of table) {
diff --git a/packages/houdini-svelte/src/plugin/extractLoadFunction.ts b/packages/houdini-svelte/src/plugin/extractLoadFunction.ts
index 46c461409..f6ac3641a 100644
--- a/packages/houdini-svelte/src/plugin/extractLoadFunction.ts
+++ b/packages/houdini-svelte/src/plugin/extractLoadFunction.ts
@@ -187,6 +187,28 @@ async function processScript(
)
}
load.push(element.quasi.quasis[0].value.raw)
+ } else if (element.type === 'CallExpression') {
+ // if the function is not called graphql, ignore it
+ if (
+ element.callee.type !== 'Identifier' ||
+ element.callee.name !== 'graphql' ||
+ element.arguments.length !== 1
+ ) {
+ throw new Error(`only graphql function can be passed to ${houdini_load_fn}`)
+ }
+ let documentString: string
+ const argument = element.arguments[0]
+
+ // if we have a template or string literal, use its value
+ if (argument.type === 'TemplateLiteral') {
+ documentString = argument.quasis[0].value.raw
+ } else if (argument.type === 'StringLiteral') {
+ documentString = argument.value
+ } else {
+ throw new Error('only strings can be passed to the graphql function')
+ }
+
+ load.push(documentString)
} else if (element.type === 'NewExpression') {
const suffix = store_suffix(config)
if (
@@ -240,6 +262,25 @@ function identifyQueryReference(
if (value.type === 'Identifier' && value.name in imports) {
return { local, query: imports[value.name] }
}
+ if (
+ value.type === 'CallExpression' &&
+ value.callee.type === 'Identifier' &&
+ value.callee.name in imports
+ ) {
+ return { local, query: imports[value.callee.name] }
+ }
+ if (
+ value.type === 'CallExpression' &&
+ value.callee.type === 'Identifier' &&
+ value.callee.name === 'graphql' &&
+ value.arguments.length === 1
+ ) {
+ if (value.arguments[0].type === 'StringLiteral') {
+ return { local, query: value.arguments[0].value }
+ } else if (value.arguments[0].type === 'TemplateLiteral') {
+ return { local, query: value.arguments[0].quasis[0].value.raw }
+ }
+ }
if (value.type === 'NewExpression' && value.callee.type == 'Identifier') {
return { local, query: imports[value.callee.name] }
diff --git a/packages/houdini-svelte/src/plugin/index.ts b/packages/houdini-svelte/src/plugin/index.ts
index db2f768a0..4cf35cdca 100644
--- a/packages/houdini-svelte/src/plugin/index.ts
+++ b/packages/houdini-svelte/src/plugin/index.ts
@@ -3,7 +3,14 @@ import { HoudiniError, PluginFactory, path, fs } from 'houdini'
import generate from './codegen'
import extract from './extract'
import fs_patch from './fsPatch'
-import { plugin_config, resolve_relative, stores_directory, type Framework } from './kit'
+import {
+ plugin_config,
+ resolve_relative,
+ stores_directory,
+ store_name,
+ store_import_path,
+ type Framework,
+} from './kit'
import apply_transforms from './transforms'
import validate from './validate'
@@ -70,6 +77,24 @@ export const error = svelteKitError
})
},
+ graphql_tag_return({ config, doc, ensure_import }) {
+ // if we're supposed to generate a store then add an overloaded declaration
+ if (doc.generateStore) {
+ // make sure we are importing the store
+ const store = store_name({ config, name: doc.name })
+ ensure_import({
+ identifier: store,
+ module: store_import_path({
+ config,
+ name: doc.name,
+ }).replaceAll('$houdini', '..'),
+ })
+
+ // and use the store as the return value
+ return store
+ }
+ },
+
// we need to add the exports to the index files (this one file processes index.js and index.d.ts)
index_file({ config, content, export_star_from, plugin_root }) {
const storesDir =
diff --git a/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts b/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts
index b393b745e..262ee0ddd 100644
--- a/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts
+++ b/packages/houdini-svelte/src/plugin/transforms/kit/load.test.ts
@@ -3,22 +3,55 @@ import { test, expect, describe } from 'vitest'
import { route_test } from '../../../test'
describe('kit route processor', function () {
- test('inline store', async function () {
+ test('inline function', async function () {
const route = await route_test({
component: `
`,
})
+ expect(route.script).toMatchInlineSnapshot(`
+ import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery";
+ import { getCurrentConfig } from "$houdini/runtime/lib/config";
+ import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session";
+ import _TestQueryArtifact from "$houdini/artifacts/TestQuery";
+
+ export async function load(context) {
+ const houdini_context = new RequestContext(context);
+ const houdiniConfig = await getCurrentConfig();
+ const promises = [];
+ const inputs = {};
+ inputs["TestQuery"] = {};
+
+ promises.push(load_TestQuery({
+ "variables": inputs["TestQuery"],
+ "event": context,
+ "blocking": false
+ }));
+
+ let result = {};
+
+ try {
+ result = Object.assign({}, ...(await Promise.all(promises)));
+ } catch (err) {
+ throw err;
+ }
+
+ return {
+ ...houdini_context.returnValue,
+ ...result
+ };
+ }
+ `)
expect(route.component).toMatchInlineSnapshot(`
export let data;
@@ -27,7 +60,7 @@ describe('kit route processor', function () {
`)
})
- test('inline query', async function () {
+ test('inline template', async function () {
const route = await route_test({
component: `
+ `,
+ })
+
+ expect(route.component).toMatchInlineSnapshot(`
+ export let data;
+
+ $:
+ result = data.TestQuery;
+ `)
+
+ expect(route.script).toMatchInlineSnapshot(`
+ import { load_TestQuery } from "$houdini/plugins/houdini-svelte/stores/TestQuery";
+ import { getCurrentConfig } from "$houdini/runtime/lib/config";
+ import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session";
+ import _TestQueryArtifact from "$houdini/artifacts/TestQuery";
+
+ export async function load(context) {
+ const houdini_context = new RequestContext(context);
+ const houdiniConfig = await getCurrentConfig();
+ const promises = [];
+ const inputs = {};
+ inputs["TestQuery"] = {};
+
+ promises.push(load_TestQuery({
+ "variables": inputs["TestQuery"],
+ "event": context,
+ "blocking": false
+ }));
+
+ let result = {};
+
+ try {
+ result = Object.assign({}, ...(await Promise.all(promises)));
+ } catch (err) {
+ throw err;
+ }
+
+ return {
+ ...houdini_context.returnValue,
+ ...result
+ };
+ }
+ `)
+})
+
test('onError hook', async function () {
const route = await route_test({
script: `
diff --git a/packages/houdini-svelte/src/plugin/transforms/query.test.ts b/packages/houdini-svelte/src/plugin/transforms/query.test.ts
index 55ab09385..a37d1980c 100644
--- a/packages/houdini-svelte/src/plugin/transforms/query.test.ts
+++ b/packages/houdini-svelte/src/plugin/transforms/query.test.ts
@@ -98,6 +98,68 @@ test('with variables', async function () {
`)
})
+test('graphql function', async function () {
+ const route = await component_test(
+ `
+ export function _TestQueryVariables() {
+ return {
+ hello: 'world'
+ }
+ }
+
+ export let prop1 = 'hello'
+ export const prop2 = 'goodbye'
+ export let prop3, prop4
+
+ const result = graphql(\`
+ query TestQuery($test: String!) {
+ users(stringValue: $test) {
+ id
+ }
+ }
+ \`)
+ `
+ )
+
+ // make sure we added the right stuff
+ expect(route).toMatchInlineSnapshot(`
+ import { TestQueryStore } from "$houdini/plugins/houdini-svelte/stores/TestQuery";
+ import { isBrowser } from "$houdini/plugins/houdini-svelte/runtime/adapter";
+ import { RequestContext } from "$houdini/plugins/houdini-svelte/runtime/session";
+ import { marshalInputs } from "$houdini/runtime/lib/scalars";
+ const _houdini_TestQuery = new TestQueryStore();
+
+ export function _TestQueryVariables() {
+ return {
+ hello: "world"
+ };
+ }
+
+ export let prop1 = "hello";
+ export const prop2 = "goodbye";
+ export let prop3, prop4;
+
+ $:
+ result = _houdini_TestQuery;
+
+ $:
+ marshalInputs({
+ artifact: _houdini_TestQuery.artifact,
+
+ input: _TestQueryVariables.call(new RequestContext(), {
+ props: {
+ prop1: prop1,
+ prop2: prop2,
+ prop3: prop3,
+ prop4: prop4
+ }
+ })
+ }).then(_TestQuery_Input => isBrowser && _houdini_TestQuery.fetch({
+ variables: _TestQuery_Input
+ }));
+ `)
+})
+
test('missing variables', async function () {
vi.spyOn(console, 'error')
diff --git a/packages/houdini-svelte/src/plugin/transforms/reactive.test.ts b/packages/houdini-svelte/src/plugin/transforms/reactive.test.ts
new file mode 100644
index 000000000..01a9e4bb9
--- /dev/null
+++ b/packages/houdini-svelte/src/plugin/transforms/reactive.test.ts
@@ -0,0 +1,69 @@
+import { test, expect } from 'vitest'
+
+import { component_test } from '../../test'
+
+test('graphql template tag in a function', async function () {
+ const route = await component_test(
+ `
+ const result = fragment(user, graphql\`
+ fragment Foo on Bar {
+ users(stringValue: $test) {
+ id
+ }
+ }
+ \`)
+ `
+ )
+
+ // make sure we added the right stuff
+ expect(route).toMatchInlineSnapshot(`
+ import { FooStore } from "$houdini/plugins/houdini-svelte/stores/Foo";
+
+ $:
+ result = fragment(user, new FooStore());
+ `)
+})
+
+test('graphql function in a function', async function () {
+ const route = await component_test(
+ `
+ const result = fragment(user, graphql\`
+ fragment Foo on Bar {
+ users(stringValue: $test) {
+ id
+ }
+ }
+ \`)
+ `
+ )
+
+ // make sure we added the right stuff
+ expect(route).toMatchInlineSnapshot(`
+ import { FooStore } from "$houdini/plugins/houdini-svelte/stores/Foo";
+
+ $:
+ result = fragment(user, new FooStore());
+ `)
+})
+
+test('graphql function in a function', async function () {
+ const route = await component_test(
+ `
+ const result = fragment(user, graphql(\`
+ fragment Foo on Bar {
+ users(stringValue: $test) {
+ id
+ }
+ }
+ \`))
+ `
+ )
+
+ // make sure we added the right stuff
+ expect(route).toMatchInlineSnapshot(`
+ import { FooStore } from "$houdini/plugins/houdini-svelte/stores/Foo";
+
+ $:
+ result = fragment(user, new FooStore());
+ `)
+})
diff --git a/packages/houdini-svelte/src/plugin/transforms/reactive.ts b/packages/houdini-svelte/src/plugin/transforms/reactive.ts
index d8a1d9ef3..fa6c0a16a 100644
--- a/packages/houdini-svelte/src/plugin/transforms/reactive.ts
+++ b/packages/houdini-svelte/src/plugin/transforms/reactive.ts
@@ -15,15 +15,9 @@ type TaggedTemplateExpression = recast.types.namedTypes.TaggedTemplateExpression
export default async function ReactiveProcessor(config: Config, page: SvelteTransformPage) {
// if a file imports graphql from $houdini then they might have an inline document
// that needs to be transformed into a reactive statement.
- // in order to avoid situations where graphql`` is passed around to functions we are going to
- // look for graphql`` being passed specifically to a function that matches some list
- // being used as an assignemtn
- //
- // ie:
- //
- // const value = graphql`` -> $: value = query(graphql``)
- // const { value } = graphql`` -> $: { value } = query(graphql``)
- //
+ // in order to avoid situations where graphql is passed around to functions we are going to
+ // look for graphql being passed specifically to a function that matches some list
+ // being used as an assignment
if (
!is_component(config, page.framework, page.filepath) &&
!is_route(config, page.framework, page.filepath)
@@ -32,7 +26,7 @@ export default async function ReactiveProcessor(config: Config, page: SvelteTran
}
// look for the list of magic functions the user has imported
- const magicFunctions = ['query', 'graphql', 'fragment', 'paginatedFragment', 'paginatedQuery']
+ const magicFunctions = ['graphql', 'fragment', 'paginatedFragment']
// if they didn't import graphql and at least something else, there's nothing to do
if (!magicFunctions.includes('graphql') || magicFunctions.length === 1) {
@@ -98,15 +92,34 @@ function filterCallExpr(expr: CallExpression) {
// that matches a magic function that was imported from the runtime
return
}
- const callExpr = expr as CallExpression
+
+ // if the name of the function is 'graphql' then we should look for a string or
+ // template literal
+ if (
+ expr.callee.type === 'Identifier' &&
+ expr.callee.name === 'graphql' &&
+ expr.arguments.length === 1 &&
+ (expr.arguments[0].type === 'StringLiteral' || expr.arguments[0].type === 'TemplateLiteral')
+ ) {
+ return true
+ }
// one of the arguments to the function must be a tagged template literal
- // with the graphql tag
- const tag = callExpr.arguments.find(
+ // with the graphql tag or a function named graphql with a string or template
+ // literal
+ const tag = expr.arguments.find(
(arg) =>
- arg.type === 'TaggedTemplateExpression' &&
- arg.tag.type === 'Identifier' &&
- arg.tag.name === 'graphql'
+ // if one of the arguments is a graphql template tag
+ (arg.type === 'TaggedTemplateExpression' &&
+ arg.tag.type === 'Identifier' &&
+ arg.tag.name === 'graphql') ||
+ // or an graphql function with a string or template literal
+ (arg.type === 'CallExpression' &&
+ arg.callee.type === 'Identifier' &&
+ arg.callee.name === 'graphql' &&
+ arg.arguments.length === 1 &&
+ (arg.arguments[0].type === 'StringLiteral' ||
+ arg.arguments[0].type === 'TemplateLiteral'))
)
if (!tag) {
return
diff --git a/packages/houdini-svelte/src/plugin/transforms/tags.ts b/packages/houdini-svelte/src/plugin/transforms/tags.ts
index 81fd3af08..3bcd699c0 100644
--- a/packages/houdini-svelte/src/plugin/transforms/tags.ts
+++ b/packages/houdini-svelte/src/plugin/transforms/tags.ts
@@ -7,7 +7,7 @@ import { SvelteTransformPage } from './types'
const AST = recast.types.builders
export default async function GraphQLTagProcessor(config: Config, page: SvelteTransformPage) {
- // all graphql template tags need to be turned into a reference to the appropriate store
+ // all graphql documents need to be turned into a reference to the appropriate store
await find_graphql(config, page.script, {
dependency: page.watch_file,
tag(tag) {
diff --git a/packages/houdini/src/codegen/generators/indexFile/indexFile.test.ts b/packages/houdini/src/codegen/generators/indexFile/indexFile.test.ts
index 364e86b1a..24890e4a7 100644
--- a/packages/houdini/src/codegen/generators/indexFile/indexFile.test.ts
+++ b/packages/houdini/src/codegen/generators/indexFile/indexFile.test.ts
@@ -24,6 +24,7 @@ test('index file - esm', async function () {
// open up the index file
const queryContents = await fs.readFile(path.join(config.artifactDirectory, 'index.js'))
+ console.log({ queryContents })
expect(queryContents).toBeTruthy()
// parse the contents
const parsedQuery: ProgramKind = recast.parse(queryContents!, {
diff --git a/packages/houdini/src/codegen/generators/runtime/index.ts b/packages/houdini/src/codegen/generators/runtime/index.ts
index a8b15c349..cff6f02e5 100644
--- a/packages/houdini/src/codegen/generators/runtime/index.ts
+++ b/packages/houdini/src/codegen/generators/runtime/index.ts
@@ -1,6 +1,20 @@
-import { Config, siteURL as SITE_URL, fs, HoudiniError, path, houdini_mode } from '../../../lib'
+import * as recast from 'recast'
-export default async function runtimeGenerator(config: Config) {
+import {
+ Config,
+ siteURL as SITE_URL,
+ fs,
+ HoudiniError,
+ path,
+ houdini_mode,
+ CollectedGraphQLDocument,
+ parseJS,
+ ensureImports,
+} from '../../../lib'
+
+const AST = recast.types.builders
+
+export default async function runtimeGenerator(config: Config, docs: CollectedGraphQLDocument[]) {
// generate the adapter to normalize interactions with the framework
// update the generated runtime to point to the client
await Promise.all([
@@ -22,6 +36,80 @@ export default async function runtimeGenerator(config: Config) {
.filter((plugin) => plugin.include_runtime)
.map((plugin) => generatePluginRuntime(config, plugin)),
])
+
+ // we need to find the index of the `export default function graphql` in the index.d.ts of the runtime
+ const indexPath = path.join(config.runtimeDirectory, 'index.d.ts')
+ const contents = await parseJS((await fs.readFile(indexPath)) || '')
+
+ // figure out if any of the plugins provide a graphql tag export
+ const graphql_tag_return = config.plugins.find(
+ (plugin) => plugin.graphql_tag_return
+ )?.graphql_tag_return
+ if (graphql_tag_return && contents) {
+ // build up the mapping of hard coded strings to exports
+ const overloaded_returns: Record = {}
+ for (const doc of docs) {
+ const return_value = graphql_tag_return!({
+ config,
+ doc,
+ ensure_import({ identifier, module }) {
+ ensureImports({
+ config,
+ body: contents.script.body,
+ sourceModule: module,
+ import: [identifier],
+ })
+ },
+ })
+ if (return_value) {
+ overloaded_returns[doc.originalString] = return_value
+ }
+ }
+
+ // if we have any overloaded return values then we need to update the index.d.ts of the
+ // runtime to return those values
+ if (Object.keys(overloaded_returns).length > 0) {
+ for (const [i, expression] of (contents?.script.body ?? []).entries()) {
+ if (
+ expression.type !== 'ExportNamedDeclaration' ||
+ expression.declaration?.type !== 'TSDeclareFunction' ||
+ expression.declaration.id?.name !== 'graphql'
+ ) {
+ continue
+ }
+
+ // we need to insert an overloaded definition for every entry we found
+ for (const [queryString, returnValue] of Object.entries(overloaded_returns)) {
+ // build up the input with the query string as a hard coded value
+ const input = AST.identifier('str')
+ input.typeAnnotation = AST.tsTypeAnnotation(
+ AST.tsLiteralType(AST.stringLiteral(queryString))
+ )
+
+ // it should return the right thing
+ contents?.script.body.splice(
+ i,
+ 0,
+ AST.exportNamedDeclaration(
+ AST.tsDeclareFunction(
+ AST.identifier('graphql'),
+ [input],
+ AST.tsTypeAnnotation(
+ AST.tsTypeReference(AST.identifier(returnValue))
+ )
+ )
+ )
+ )
+ }
+
+ // we're done here
+ break
+ }
+
+ // write the result back to the file
+ await fs.writeFile(indexPath, recast.prettyPrint(contents!.script).code)
+ }
+ }
}
async function generatePluginRuntime(config: Config, plugin: Config['plugins'][number]) {
diff --git a/packages/houdini/src/codegen/index.ts b/packages/houdini/src/codegen/index.ts
index da1bb1168..d24e67bea 100755
--- a/packages/houdini/src/codegen/index.ts
+++ b/packages/houdini/src/codegen/index.ts
@@ -100,7 +100,6 @@ export async function runPipeline(config: Config, docs: CollectedGraphQLDocument
docs
)
} catch (e) {
- console.log(e)
error = e as Error
}
@@ -189,8 +188,8 @@ async function collectDocuments(config: Config): Promise [content]
- const javascript_extractor = (filepath: string, content: string) =>
+ const graphql_extractor = (config: Config, filepath: string, content: string) => [content]
+ const javascript_extractor = (fconfig: Config, ilepath: string, content: string) =>
processJSFile(config, content)
extractors['.ts'].push(javascript_extractor)
extractors['.js'].push(javascript_extractor)
@@ -222,13 +221,13 @@ async function collectDocuments(config: Config): Promise 0) {
documents.push(...found.map((document) => ({ filepath, document })))
}
diff --git a/packages/houdini/src/lib/config.ts b/packages/houdini/src/lib/config.ts
index eff2d0261..14e248b43 100644
--- a/packages/houdini/src/lib/config.ts
+++ b/packages/houdini/src/lib/config.ts
@@ -939,10 +939,19 @@ export type Plugin = {
extensions?: string[]
transform_runtime?: Record string>
after_load?: (config: Config) => Promise | void
- extract_documents?: (filepath: string, content: string) => Promise | string[]
+ extract_documents?: (
+ config: Config,
+ filepath: string,
+ content: string
+ ) => Promise | string[]
generate?: GenerateHook
transform_file?: (page: TransformPage) => Promise<{ code: string }> | { code: string }
index_file?: ModuleIndexTransform
+ graphql_tag_return?: (args: {
+ config: Config
+ doc: CollectedGraphQLDocument
+ ensure_import: (import_args: { identifier: string; module: string }) => void
+ }) => string | undefined
validate?: (args: {
config: Config
documents: CollectedGraphQLDocument[]
diff --git a/packages/houdini/src/lib/fs.ts b/packages/houdini/src/lib/fs.ts
index 8fdf435e3..8325ad7dc 100644
--- a/packages/houdini/src/lib/fs.ts
+++ b/packages/houdini/src/lib/fs.ts
@@ -99,8 +99,9 @@ export async function writeFile(filepath: string, data: string) {
return
}
- // no mock in tests
+ // write the file when testing
if (houdini_mode.is_testing) {
+ memfs.mkdirpSync(path.dirname(filepath))
return memfs.writeFileSync(filepath, data)
}
diff --git a/packages/houdini/src/lib/walk.ts b/packages/houdini/src/lib/walk.ts
index 10a5abf96..ce37dbd01 100644
--- a/packages/houdini/src/lib/walk.ts
+++ b/packages/houdini/src/lib/walk.ts
@@ -1,4 +1,4 @@
-import type { TaggedTemplateExpressionKind, IdentifierKind } from 'ast-types/lib/gen/kinds'
+import type { TaggedTemplateExpressionKind, CallExpressionKind } from 'ast-types/lib/gen/kinds'
import { asyncWalk, BaseNode } from 'estree-walker'
import * as graphql from 'graphql'
@@ -40,65 +40,101 @@ export async function find_graphql(
): Promise {
await asyncWalk(parsedScript!, {
async enter(node, parent) {
- // if we are looking at the graphql template tag
- if (
- node.type === 'TaggedTemplateExpression' &&
- ((node as TaggedTemplateExpressionKind).tag as IdentifierKind).name === 'graphql'
- ) {
- const expr = node as TaggedTemplateExpressionKind
- // we're going to replace the tag with something the runtime can use
+ // graphql documents come in a few forms:
+ // - graphql template tags
+ // - strings passed to graphql function
+ if (node.type !== 'TaggedTemplateExpression' && node.type !== 'CallExpression') {
+ return
+ }
- // first, lets parse the tag contents to get the info we need
- const tagContent = expr.quasi.quasis[0].value.raw
- const parsedTag = graphql.parse(tagContent)
+ let documentString: string
+
+ // process template tags
+ if (node.type === 'TaggedTemplateExpression') {
+ // grab the string passed to the template tag as the document
+ const expr = node as TaggedTemplateExpressionKind
- // if there is a predicate and the graphql tag does not satisfy it
- if (walker.where && !walker.where(parsedTag)) {
- // ignore the tag
+ // we only care about graphql template tags
+ if (expr.tag.type !== 'Identifier' || expr.tag.name !== 'graphql') {
return
}
- // pull out the name of the thing
- const definition = config.extractDefinition(parsedTag) as
- | graphql.OperationDefinitionNode
- | graphql.FragmentDefinitionNode
- const name = definition.name?.value
- if (!name) {
- throw new Error('Could not find definition name')
+ documentString = expr.quasi.quasis[0].value.raw
+ }
+ // process function calls
+ else if (node.type === 'CallExpression') {
+ const expr = node as CallExpressionKind
+ // if the function is not called graphql, ignore it
+ if (
+ expr.callee.type !== 'Identifier' ||
+ expr.callee.name !== 'graphql' ||
+ expr.arguments.length !== 1
+ ) {
+ return
}
- let kind: CompiledDocumentKind
- if (definition.kind === 'FragmentDefinition') {
- kind = CompiledFragmentKind
+ const argument = expr.arguments[0]
+
+ // if we have a template or string literal, use its value
+ if (argument.type === 'TemplateLiteral') {
+ documentString = argument.quasis[0].value.raw
+ } else if (argument.type === 'StringLiteral') {
+ documentString = argument.value
} else {
- if (definition.operation === 'query') {
- kind = CompiledQueryKind
- } else if (definition.operation === 'mutation') {
- kind = CompiledMutationKind
- } else {
- kind = CompiledSubscriptionKind
- }
+ return
}
+ } else {
+ return
+ }
+
+ // if we got this far, {documentString} holds the query
+ const parsedTag = graphql.parse(documentString)
- // tell the walk there was a dependency
- walker.dependency?.(config.artifactPath(parsedTag))
+ // if there is a predicate and the graphql tag does not satisfy it
+ if (walker.where && !walker.where(parsedTag)) {
+ // ignore the tag
+ return
+ }
- // invoker the walker's callback with the right context
- await walker.tag({
- parsedDocument: parsedTag,
- node: {
- ...node,
- ...this,
- remove: this.remove,
- replaceWith: this.replace,
- },
- artifact: {
- name,
- kind,
- },
- parent,
- tagContent,
- })
+ // pull out the name of the thing
+ const definition = config.extractDefinition(parsedTag) as
+ | graphql.OperationDefinitionNode
+ | graphql.FragmentDefinitionNode
+ const name = definition.name?.value
+ if (!name) {
+ throw new Error('Could not find definition name')
+ }
+ let kind: CompiledDocumentKind
+ if (definition.kind === 'FragmentDefinition') {
+ kind = CompiledFragmentKind
+ } else {
+ if (definition.operation === 'query') {
+ kind = CompiledQueryKind
+ } else if (definition.operation === 'mutation') {
+ kind = CompiledMutationKind
+ } else {
+ kind = CompiledSubscriptionKind
+ }
}
+
+ // tell the walk there was a dependency
+ walker.dependency?.(config.artifactPath(parsedTag))
+
+ // invoker the walker's callback with the right context
+ await walker.tag({
+ parsedDocument: parsedTag,
+ node: {
+ ...node,
+ ...this,
+ remove: this.remove,
+ replaceWith: this.replace,
+ },
+ artifact: {
+ name,
+ kind,
+ },
+ parent,
+ tagContent: documentString,
+ })
},
})
}
diff --git a/packages/houdini/src/runtime/index.ts b/packages/houdini/src/runtime/index.ts
index 9dd64410f..5b5667199 100644
--- a/packages/houdini/src/runtime/index.ts
+++ b/packages/houdini/src/runtime/index.ts
@@ -1,5 +1,3 @@
-// this template tag gets removed by the preprocessor so it should never be invoked.
-// this function must return any so that we can assign it a type in a variable declaration (ie an inline store)
import _cache from './cache'
import { Cache as InternalCache } from './cache/cache'
import type { CacheTypeDef } from './generated'
@@ -7,8 +5,10 @@ import { Cache } from './public'
export * from './lib'
+// this template tag gets removed by the preprocessor so it should never be invoked.
+// this function must return any so that we can assign it a type in a variable declaration (ie an inline store)
// ideally we would be able to parse the input for values but typescript does not yet support that kind of matches in template args
-export function graphql(str: TemplateStringsArray): any {
+export function graphql(str: string | TemplateStringsArray): any {
// if we are executing this function as part of the plugin, we need to return
// the query instead of throwing an error. We don't want to bundle the graphql
// module into the runtime so all we can do is return the query string
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5183779d0..70bfe2de6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -49,11 +49,11 @@ importers:
graphql-ws: ^5.8.2
ws: ^8.8.1
dependencies:
- '@graphql-yoga/node': 2.13.13_graphql@15.8.0
+ '@graphql-yoga/node': 2.13.13_graphql@16.6.0
'@kitql/helper': 0.5.0
- graphql: 15.8.0
- graphql-relay: 0.10.0_graphql@15.8.0
- graphql-ws: 5.11.2_graphql@15.8.0
+ graphql: 16.6.0
+ graphql-relay: 0.10.0_graphql@16.6.0
+ graphql-ws: 5.11.2_graphql@16.6.0
ws: 8.11.0
e2e/next:
@@ -1294,6 +1294,35 @@ packages:
'@envelop/types': 2.4.0_graphql@15.8.0
graphql: 15.8.0
tslib: 2.4.0
+ dev: true
+
+ /@envelop/core/2.6.0_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-yTptKinJN//i6m1kXUbnLBl/FobzddI4ehURAMS08eRUOQwAuXqJU9r8VdTav8nIZLb4t6cuDWFb3n331LiwLw==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
+ dependencies:
+ '@envelop/types': 2.4.0_graphql@16.6.0
+ graphql: 16.6.0
+ tslib: 2.4.0
+ dev: false
+
+ /@envelop/parser-cache/4.7.0_4hr55tbjlvoppd2sokdhrbpreq:
+ resolution:
+ {
+ integrity: sha512-63NfXDcW/vGn4U6NFxaZ0JbYWAcJb9A6jhTvghsSz1ZS+Dny/ci8bVSgVmM1q+N56hPyGsVPuyI+rIc71mPU5g==,
+ }
+ peerDependencies:
+ '@envelop/core': ^2.6.0
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
+ dependencies:
+ '@envelop/core': 2.6.0_graphql@16.6.0
+ graphql: 16.6.0
+ lru-cache: 6.0.0
+ tslib: 2.4.1
+ dev: false
/@envelop/parser-cache/4.7.0_nhznfxrlclsvs4aen6pcdf2xd4:
resolution:
@@ -1308,6 +1337,7 @@ packages:
graphql: 15.8.0
lru-cache: 6.0.0
tslib: 2.4.1
+ dev: true
/@envelop/types/2.4.0_graphql@15.8.0:
resolution:
@@ -1319,6 +1349,34 @@ packages:
dependencies:
graphql: 15.8.0
tslib: 2.4.1
+ dev: true
+
+ /@envelop/types/2.4.0_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-pjxS98cDQBS84X29VcwzH3aJ/KiLCGwyMxuj7/5FkdiaCXAD1JEvKEj9LARWlFYj1bY43uII4+UptFebrhiIaw==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
+ dependencies:
+ graphql: 16.6.0
+ tslib: 2.4.1
+ dev: false
+
+ /@envelop/validation-cache/4.7.0_4hr55tbjlvoppd2sokdhrbpreq:
+ resolution:
+ {
+ integrity: sha512-PzL+GfWJRT+JjsJqZAIxHKEkvkM3hxkeytS5O0QLXT8kURNBV28r+Kdnn2RCF5+6ILhyGpiDb60vaquBi7g4lw==,
+ }
+ peerDependencies:
+ '@envelop/core': ^2.6.0
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
+ dependencies:
+ '@envelop/core': 2.6.0_graphql@16.6.0
+ graphql: 16.6.0
+ lru-cache: 6.0.0
+ tslib: 2.4.1
+ dev: false
/@envelop/validation-cache/4.7.0_nhznfxrlclsvs4aen6pcdf2xd4:
resolution:
@@ -1333,6 +1391,7 @@ packages:
graphql: 15.8.0
lru-cache: 6.0.0
tslib: 2.4.1
+ dev: true
/@esbuild/android-arm/0.15.18:
resolution:
@@ -1629,6 +1688,19 @@ packages:
graphql: 15.8.0
tslib: 2.4.1
+ /@graphql-tools/merge/8.3.14_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-zV0MU1DnxJLIB0wpL4N3u21agEiYFsjm6DI130jqHpwF0pR9HkF+Ni65BNfts4zQelP0GjkHltG+opaozAJ1NA==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+ dependencies:
+ '@graphql-tools/utils': 9.1.3_graphql@16.6.0
+ graphql: 16.6.0
+ tslib: 2.4.1
+ dev: false
+
/@graphql-tools/schema/9.0.12_graphql@15.8.0:
resolution:
{
@@ -1643,6 +1715,21 @@ packages:
tslib: 2.4.1
value-or-promise: 1.0.11
+ /@graphql-tools/schema/9.0.12_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-DmezcEltQai0V1y96nwm0Kg11FDS/INEFekD4nnVgzBqawvznWqK6D6bujn+cw6kivoIr3Uq//QmU/hBlBzUlQ==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+ dependencies:
+ '@graphql-tools/merge': 8.3.14_graphql@16.6.0
+ '@graphql-tools/utils': 9.1.3_graphql@16.6.0
+ graphql: 16.6.0
+ tslib: 2.4.1
+ value-or-promise: 1.0.11
+ dev: false
+
/@graphql-tools/utils/8.13.1_graphql@15.8.0:
resolution:
{
@@ -1653,6 +1740,19 @@ packages:
dependencies:
graphql: 15.8.0
tslib: 2.4.1
+ dev: true
+
+ /@graphql-tools/utils/8.13.1_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+ dependencies:
+ graphql: 16.6.0
+ tslib: 2.4.1
+ dev: false
/@graphql-tools/utils/9.1.3_graphql@15.8.0:
resolution:
@@ -1665,6 +1765,18 @@ packages:
graphql: 15.8.0
tslib: 2.4.1
+ /@graphql-tools/utils/9.1.3_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-bbJyKhs6awp1/OmP+WKA1GOyu9UbgZGkhIj5srmiMGLHohEOKMjW784Sk0BZil1w2x95UPu0WHw6/d/HVCACCg==,
+ }
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+ dependencies:
+ graphql: 16.6.0
+ tslib: 2.4.1
+ dev: false
+
/@graphql-typed-document-node/core/3.1.1_graphql@15.8.0:
resolution:
{
@@ -1674,6 +1786,18 @@ packages:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
dependencies:
graphql: 15.8.0
+ dev: true
+
+ /@graphql-typed-document-node/core/3.1.1_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==,
+ }
+ peerDependencies:
+ graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
+ dependencies:
+ graphql: 16.6.0
+ dev: false
/@graphql-yoga/common/2.12.12_graphql@15.8.0:
resolution:
@@ -1696,6 +1820,30 @@ packages:
tslib: 2.4.1
transitivePeerDependencies:
- encoding
+ dev: true
+
+ /@graphql-yoga/common/2.12.12_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-La2ygIw2qlIJZrRGT4nW70Nam7gQ2xZkOn0FDCnKWSJhQ4nHw4aFAkeHIJdZGK0u2TqtXRrNSAj5cb/TZoqUiQ==,
+ }
+ peerDependencies:
+ graphql: ^15.2.0 || ^16.0.0
+ dependencies:
+ '@envelop/core': 2.6.0_graphql@16.6.0
+ '@envelop/parser-cache': 4.7.0_4hr55tbjlvoppd2sokdhrbpreq
+ '@envelop/validation-cache': 4.7.0_4hr55tbjlvoppd2sokdhrbpreq
+ '@graphql-tools/schema': 9.0.12_graphql@16.6.0
+ '@graphql-tools/utils': 8.13.1_graphql@16.6.0
+ '@graphql-typed-document-node/core': 3.1.1_graphql@16.6.0
+ '@graphql-yoga/subscription': 2.2.3
+ '@whatwg-node/fetch': 0.3.2
+ dset: 3.1.2
+ graphql: 16.6.0
+ tslib: 2.4.1
+ transitivePeerDependencies:
+ - encoding
+ dev: false
/@graphql-yoga/node/2.13.13_graphql@15.8.0:
resolution:
@@ -1714,6 +1862,26 @@ packages:
tslib: 2.4.1
transitivePeerDependencies:
- encoding
+ dev: true
+
+ /@graphql-yoga/node/2.13.13_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-3NmdEq3BkuVLRbo5yUi401sBiwowSKgY8O1DN1RwYdHRr0nu2dXzlYEETf4XLymyP6mKsVfQgsy7HQjwsc1oNw==,
+ }
+ peerDependencies:
+ graphql: ^15.2.0 || ^16.0.0
+ dependencies:
+ '@envelop/core': 2.6.0_graphql@16.6.0
+ '@graphql-tools/utils': 8.13.1_graphql@16.6.0
+ '@graphql-yoga/common': 2.12.12_graphql@16.6.0
+ '@graphql-yoga/subscription': 2.2.3
+ '@whatwg-node/fetch': 0.3.2
+ graphql: 16.6.0
+ tslib: 2.4.1
+ transitivePeerDependencies:
+ - encoding
+ dev: false
/@graphql-yoga/subscription/2.2.3:
resolution:
@@ -5957,7 +6125,7 @@ packages:
integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==,
}
- /graphql-relay/0.10.0_graphql@15.8.0:
+ /graphql-relay/0.10.0_graphql@16.6.0:
resolution:
{
integrity: sha512-44yBuw2/DLNEiMypbNZBt1yMDbBmyVPVesPywnteGGALiBmdyy1JP8jSg8ClLePg8ZZxk0O4BLhd1a6U/1jDOQ==,
@@ -5966,7 +6134,7 @@ packages:
peerDependencies:
graphql: ^16.2.0
dependencies:
- graphql: 15.8.0
+ graphql: 16.6.0
dev: false
/graphql-tag/2.12.6_graphql@15.8.0:
@@ -5994,6 +6162,18 @@ packages:
graphql: 15.8.0
dev: false
+ /graphql-ws/5.11.2_graphql@16.6.0:
+ resolution:
+ {
+ integrity: sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w==,
+ }
+ engines: { node: '>=10' }
+ peerDependencies:
+ graphql: '>=0.11 <=16'
+ dependencies:
+ graphql: 16.6.0
+ dev: false
+
/graphql/15.8.0:
resolution:
{
@@ -6001,6 +6181,14 @@ packages:
}
engines: { node: '>= 10.x' }
+ /graphql/16.6.0:
+ resolution:
+ {
+ integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==,
+ }
+ engines: { node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0 }
+ dev: false
+
/hard-rejection/2.1.0:
resolution:
{
diff --git a/site/src/routes/+page.svelte b/site/src/routes/+page.svelte
index 02c35ceb2..a56d8c77f 100644
--- a/site/src/routes/+page.svelte
+++ b/site/src/routes/+page.svelte
@@ -6,13 +6,13 @@
@@ -104,13 +104,13 @@ Fragments may also contain a paginated field, similar to queries. A paginated fr
// the reference will get passed as a prop
export let user
- $: friendList = paginatedFragment(user, graphql`
+ $: friendList = paginatedFragment(user, graphql(`
fragment UserWithFriends on User {
friends(first: 10) @paginate {
name
}
}
- `)
+ `))