diff --git a/.vscode/settings.json b/.vscode/settings.json index f6dd0d06..ca3dda8d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "codegen", "combobox", "Diátaxis", "faustjs", diff --git a/src/pages/docs/explanation/migrating-to-typescript/index.mdx b/src/pages/docs/explanation/migrating-to-typescript/index.mdx new file mode 100644 index 00000000..100236ba --- /dev/null +++ b/src/pages/docs/explanation/migrating-to-typescript/index.mdx @@ -0,0 +1,12 @@ +export const metadata = { + title: "Migrating to TypeScript", +}; + +If you have existing Next.js pages that you want to migrate to TypeScript, you can follow the [official TypeScript Docs](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) for general TypeScript migration tips. + +For Faust, see the [TypeScript Reference Doc](/docs/reference/typescript). + +## Further Reading + +- [TypeScript Reference](/docs/reference/typescript) +- [Typing GraphQL Queries with GraphQL Codegen](/docs/how-to/generate-types-with-graphql-codegen) diff --git a/src/pages/docs/how-to/basic-setup/index.mdx b/src/pages/docs/how-to/basic-setup/index.mdx index d1094cc1..d67a78d3 100644 --- a/src/pages/docs/how-to/basic-setup/index.mdx +++ b/src/pages/docs/how-to/basic-setup/index.mdx @@ -61,7 +61,7 @@ export default setConfig({ ### Create Faust API route -Create an API route for Faust.js to use. You can do this by creating a file in `pages/api/faust/[[...route]].`js, with the following code: +Create an API route for Faust.js to use. You can do this by creating a file in `pages/api/faust/[[...route]].js`, with the following code: ```js title="pages/api/faust/[[...route]].js" import "../../../faust.config"; @@ -71,7 +71,7 @@ export { apiRouter as default } from "@faustwp/core"; ### Update `_app.js` file -Once the API router is set up, head to `pages/_api.js` and paste this code into the file: +Once the API router is set up, head to `pages/_app.js` and add this code to the file: ```js title="pages/_app.js" import { useRouter } from "next/router"; @@ -89,7 +89,7 @@ export default function MyApp({ Component, pageProps }) { } ``` -## Setup the Template Hierarchy +## Set up the Template Hierarchy ### Generate types @@ -109,6 +109,9 @@ Add the following generate script to your Next.js project's `package.json` file, Run `npm run generate` on the command line. Confirm that a possibleTypes.json has been generated in the root of your project. +> [!IMPORTANT] Important +> Be sure to enable WPGraphQL introspection before running the `npm run generate` command since it is [disabled by default](https://www.wpgraphql.com/docs/security#introspection-disabled-by-default). + ### Add a template Create a new `wp-templates` folder in the root of your project. This is where your template files will be stored. We'll start by adding a template for rendering single blog posts. diff --git a/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-one.png b/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-one.png new file mode 100644 index 00000000..fd885cc4 Binary files /dev/null and b/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-one.png differ diff --git a/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-two.png b/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-two.png new file mode 100644 index 00000000..c6cf476c Binary files /dev/null and b/src/pages/docs/how-to/generate-types-with-graphql-codegen/images/ts-image-two.png differ diff --git a/src/pages/docs/how-to/generate-types-with-graphql-codegen/index.mdx b/src/pages/docs/how-to/generate-types-with-graphql-codegen/index.mdx new file mode 100644 index 00000000..d0a25915 --- /dev/null +++ b/src/pages/docs/how-to/generate-types-with-graphql-codegen/index.mdx @@ -0,0 +1,142 @@ +export const metadata = { + title: "Generate types with GraphQL Codegen", +}; + +Faust.js provides built-in TypeScript support, including types for Templates, Blocks, and more. This guide will show you how to generate fully typed definitions for your custom GraphQL queries and fragments using [GraphQL Code Generator](https://the-guild.dev/graphql/codegen). + +> [!TIP] +> If you're looking to generate [Apollo-specific fragment matching](https://www.apollographql.com/docs/react/data/fragments#using-fragments-with-unions-and-interfaces) metadata (i.e. `possibleTypes`), see the `faust generatePossibleTypes` command. However, that command does **not** produce TypeScript definitions for your queries. For typing your queries in Faust, continue reading below. + +## 0. Prerequisites + +If you haven't already, follow the [Basic Setup](/docs/how-to/basic-setup/) steps to get Faust.js configured. Once your project is set up, add `@graphql-codegen/cli` to your project: + +```bash +npm install -D typescript @graphql-codegen/cli +``` + +## 1. Add Codegen Configuration + +In the root of your project, create a configuration file named `codegen.ts` + +```ts title="codegen.ts" +import { CodegenConfig } from "@graphql-codegen/cli"; + +const config: CodegenConfig = { + schema: "https://faustexample.wpengine.com/graphql", + documents: ["src/**/*.{tsx,ts}"], + generates: { + "./src/__generated__/": { + preset: "client", + plugins: [], + presetConfig: { + gqlTagName: "gql", + }, + }, + }, + ignoreNoDocuments: true, +}; + +export default config; +``` + +## 2. Add Codegen Script + +Next, update your package.json to add a script for running the code generator: + +```json title="package.json" +{ + "scripts": { + "generate:types": "graphql-codegen" + } +} +``` + +Now you can run: + +```bash +npm run generate:types +``` + +This command will scan your `src/` folder for any GraphQL queries or fragments, then generate TypeScript types in `src/__generated__/graphql.ts`. + +> [!IMPORTANT] Important +> Be sure to enable WPGraphQL introspection before running the `npm run generate` command since it is [disabled by default](https://www.wpgraphql.com/docs/security#introspection-disabled-by-default). + +## 3. Using Generated Types + +### A. Typing Templates + +After Codegen runs, you'll see auto-generated types in graphql.ts. For example: + +```ts title="src/__generated__/graphql.ts" +export type GetPostQueryVariables = Exact<{ + databaseId: Scalars["ID"]; + asPreview?: InputMaybe; +}>; + +export type GetPostQuery = { + // ... +}; +``` + +You can use these types with the `FaustTemplate` helper in your WordPress templates: + +```tsx title="wp-templates/single.tsx" +import { gql } from "../__generated__"; +import { GetPostQuery } from "../__generated__/graphql"; +import { FaustTemplate } from "@faustwp/core"; + +const Component: FaustTemplate = (props) => { + // `props.data` and other fields are now typed! + return
{props?.data?.post?.title}
; +}; + +export const pageQuery = gql(/* GraphQL */ ` + query GetPost($databaseId: ID!, $asPreview: Boolean) { + ... + } +`); +``` + +Then you can inspect all the types in the `props` parameters as you type: + +![](./images/ts-image-one.png) + +All the data from the query results will be properly typed based on the introspected schema: + +![](./images/ts-image-two.png) + +### B. Typing Block Components + +If you create blocks with `@faustwp/blocks`, you can use the WordPressBlock type to add strong typing to those components: + +```tsx title="wp-blocks/CoreParagraph.tsx" +import { gql } from "../__generated__"; +import { WordPressBlock } from "@faustwp/blocks"; +import { CoreParagraphFragmentFragment } from "../__generated__/graphql"; + +const CoreParagraph: WordPressBlock = ( + props, +) => { + return

{props.attributes?.content}

; +}; + +export const fragments = { + entry: gql(` + fragment CoreParagraphFragment on CoreParagraph { + attributes { + content + } + } + `), + key: "CoreParagraphFragment", +}; +``` + +By passing in `CoreParagraphFragmentFragment` to WordPressBlock, TypeScript enforces that props only contains fields you've declared in the GraphQL fragment. + +## Further Reading + +- [Migrating to TypeScript](/docs/explanation/migrating-to-typescript) +- [TypeScript Reference](/docs/reference/typescript) diff --git a/src/pages/docs/nav.json b/src/pages/docs/nav.json index 6014530a..57bc6dc8 100644 --- a/src/pages/docs/nav.json +++ b/src/pages/docs/nav.json @@ -11,7 +11,14 @@ "title": "Basic Setup", "route": "/docs/how-to/basic-setup/" }, - + { + "title": "Generate types with GraphQL Codegen", + "route": "/docs/how-to/generate-types-with-graphql-codegen/" + }, + { + "title": "Template Hierarchy", + "route": "/docs/how-to/template-hierarchy/" + }, { "title": "Rendering Blocks", "route": "/docs/how-to/rendering-blocks/" diff --git a/src/pages/docs/reference/typescript/images/typescript-fausthooks-type.png b/src/pages/docs/reference/typescript/images/typescript-fausthooks-type.png new file mode 100644 index 00000000..b1efd2d3 Binary files /dev/null and b/src/pages/docs/reference/typescript/images/typescript-fausthooks-type.png differ diff --git a/src/pages/docs/reference/typescript/index.mdx b/src/pages/docs/reference/typescript/index.mdx new file mode 100644 index 00000000..4d5dfd4f --- /dev/null +++ b/src/pages/docs/reference/typescript/index.mdx @@ -0,0 +1,66 @@ +export const metadata = { + title: "TypeScript", +}; + +Faust has built-in TypeScript support, which means you can use TypeScript to type your Faust components, hooks, and plugins. This may not be exhaustive but should show you common patterns for typing your Faust code. + +## Typing Faust Components + +- `FaustTemplate` for WP Template pages. +- `WordPressBlock` for block components. +- `GetStaticProps`, `GetServerSideProps`, and `GetStaticPaths` from Next.js for data-fetching methods. +- `FaustHooks` for hooking into the Faust plugin system. + +Here's a quick example using `[...wordpressNode].tsx`: + +```tsx title="pages/[...wordpressNode].tsx" +import { getWordPressProps, WordPressTemplate } from "@faustwp/core"; +import { GetStaticPaths, GetStaticProps } from "next"; + +export type WordPressTemplateProps = Parameters[0]; + +export default function Page(props: WordPressTemplateProps) { + return ; +} + +export const getStaticProps: GetStaticProps = (ctx) => { + return getWordPressProps({ ctx }); +}; + +export const getStaticPaths: GetStaticPaths = () => { + return { + paths: [], + fallback: "blocking", + }; +}; +``` + +## Typing Faust Plugins + +When creating Faust plugins, you can use the `FaustHooks` type to get autocompletion and type-safety for the hooks system: + +```ts title="plugins/ProjectTemplatePlugin.ts" +import { FaustHooks, FaustPlugin } from "@faustwp/core"; + +export class ProjectTemplatePlugin implements FaustPlugin { + constructor() {} + + apply(hooks: FaustHooks) { + hooks.addFilter("possibleTemplatesList", "faust", (templates, data) => { + if (data?.seedNode?.__typename === "Project") { + return Array.from(new Set(["project", ...templates])); + } + return templates; + }); + } +} +``` + +Because `FaustHooks` knows about available filters and hooks, TypeScript will automatically guide you on what the callback signatures should look like. + +![](./images/typescript-fausthooks-type.png) + +## Further Reading + +- [Migrating to TypeScript](/docs/explanation/migrating-to-typescript) +- [Typing GraphQL Queries with GraphQL Codegen](/docs/how-to/generate-types-with-graphql-codegen)