Skip to content

Commit

Permalink
Support customer account API in hydrogen-codegen (#1569)
Browse files Browse the repository at this point in the history
* Add customer account default values to codegen

* Support customer account schema in codegen

* Fix types path

* Update packages/hydrogen-codegen/src/defaults.ts

Co-authored-by: Michelle Chen <michelle.chen@shopify.com>

* Add readme

* Changesets

* Minor refactor

---------

Co-authored-by: Michelle Chen <michelle.chen@shopify.com>
  • Loading branch information
frandiox and michenly authored Dec 14, 2023
1 parent 081b41e commit 3f3b8db
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-poems-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen-codegen': minor
---

Add default values to support the Customer Account API without configuration.
53 changes: 53 additions & 0 deletions packages/hydrogen-codegen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Hydrogen Codegen

A codegen plugin and preset for generating TypeScript types from GraphQL queries in a `d.ts` file. It does not require any function wrapper and adds no runtime overhead (0 bytes to the bundle).

```ts
const {shop} = await client.query(`#graphql
query {
shop {
name
}
}
`);
```

The GraphQL client must use TypeScript interfaces that are extended in the generated `d.ts` file. See an example in [Hydrogen's Storefront client](https://github.com/Shopify/hydrogen/blob/081b41e0d43c9e1090933e908362625b9dfe7166/packages/hydrogen/src/storefront.ts#L58-L143).

## Usage

When using Hydrogen CLI, this package is already included and configured for you to generate types for the Shopify Storefront API. However, if you want to use it standalone with the GraphQL CLI or just want to add other APIs to Hydrogen, you can use the following example configuration:

```ts
// <root>/codegen.ts

import type {CodegenConfig} from '@graphql-codegen/cli';
import {pluckConfig, preset, getSchema} from '@shopify/hydrogen-codegen';

export default {
overwrite: true,
pluckConfig,
generates: {
'storefrontapi.generated.d.ts': {
preset,
schema: getSchema('storefront'),
documents: [
'./*.{ts,tsx,js,jsx}',
'./app/**/*.{ts,tsx,js,jsx}',
'!./app/graphql/customer/*.{ts,tsx,js,jsx}',
'!./app/graphql/my-cms/*.{ts,tsx,js,jsx}',
],
},
'customerapi.generated.d.ts': {
preset,
schema: getSchema('customer'),
documents: ['./app/graphql/customer/*.{ts,tsx,js,jsx}'],
},
'mycms.generated.d.ts': {
preset,
schema: './my-cms.json',
documents: ['./app/graphql/my-cms/*.{ts,tsx,js,jsx}'],
},
},
} as CodegenConfig;
```
40 changes: 40 additions & 0 deletions packages/hydrogen-codegen/src/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
GENERATED_MUTATION_INTERFACE_NAME,
GENERATED_QUERY_INTERFACE_NAME,
} from './plugin.js';

const sfapiDefaultInterfaceExtensionCode = `
declare module '@shopify/hydrogen' {
interface StorefrontQueries extends ${GENERATED_QUERY_INTERFACE_NAME} {}
interface StorefrontMutations extends ${GENERATED_MUTATION_INTERFACE_NAME} {}
}`;

const caapiDefaultInterfaceExtensionCode = `
declare module '@shopify/hydrogen' {
interface CustomerAccountQueries extends ${GENERATED_QUERY_INTERFACE_NAME} {}
interface CustomerAccountMutations extends ${GENERATED_MUTATION_INTERFACE_NAME} {}
}`;

type DefaultValues = {
importTypesFrom: string;
namespacedImportName: string;
interfaceExtensionCode: string;
};

const sfapiDefaultValues: DefaultValues = {
importTypesFrom: '@shopify/hydrogen/storefront-api-types',
namespacedImportName: 'StorefrontAPI',
interfaceExtensionCode: sfapiDefaultInterfaceExtensionCode,
};

const caapiDefaultValues: DefaultValues = {
importTypesFrom: '@shopify/hydrogen/customer-account-api-types',
namespacedImportName: 'CustomerAccountAPI',
interfaceExtensionCode: caapiDefaultInterfaceExtensionCode,
};

export function getDefaultOptions(outputFile = '') {
return /^(customer|caapi\.)/i.test(outputFile)
? caapiDefaultValues
: sfapiDefaultValues;
}
17 changes: 7 additions & 10 deletions packages/hydrogen-codegen/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as addPlugin from '@graphql-codegen/add';
import * as typescriptPlugin from '@graphql-codegen/typescript';
import * as typescriptOperationPlugin from '@graphql-codegen/typescript-operations';
import {processSources} from './sources.js';
import {getDefaultOptions} from './defaults.js';
import {
plugin as dtsPlugin,
GENERATED_MUTATION_INTERFACE_NAME,
Expand Down Expand Up @@ -39,12 +40,6 @@ export type HydrogenPresetConfig = {
}) => string;
};

export const defaultInterfaceExtensionCode = `
declare module '@shopify/hydrogen' {
interface StorefrontQueries extends ${GENERATED_QUERY_INTERFACE_NAME} {}
interface StorefrontMutations extends ${GENERATED_MUTATION_INTERFACE_NAME} {}
}`;

export const preset: Types.OutputPreset<HydrogenPresetConfig> = {
buildGeneratesSection: (options) => {
if (!options.baseOutputDir.endsWith('.d.ts')) {
Expand All @@ -63,18 +58,20 @@ export const preset: Types.OutputPreset<HydrogenPresetConfig> = {
const sourcesWithOperations = processSources(options.documents);
const sources = sourcesWithOperations.map(({source}) => source);

const defaultOptions = getDefaultOptions(options.baseOutputDir);

const importTypes = options.presetConfig.importTypes ?? true;
const namespacedImportName =
options.presetConfig.namespacedImportName ?? 'StorefrontAPI';
options.presetConfig.namespacedImportName ??
defaultOptions.namespacedImportName;
const importTypesFrom =
options.presetConfig.importTypesFrom ??
'@shopify/hydrogen/storefront-api-types';
options.presetConfig.importTypesFrom ?? defaultOptions.importTypesFrom;

const interfaceExtensionCode =
options.presetConfig.interfaceExtension?.({
queryType: GENERATED_QUERY_INTERFACE_NAME,
mutationType: GENERATED_MUTATION_INTERFACE_NAME,
}) ?? defaultInterfaceExtensionCode;
}) ?? defaultOptions.interfaceExtensionCode;

const pluginMap = {
...options.pluginMap,
Expand Down
27 changes: 22 additions & 5 deletions packages/hydrogen-codegen/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
// This comment is used during ESM build:
//! import {createRequire} from 'module'; const require = createRequire(import.meta.url);
export const getSchema = () =>
require.resolve('@shopify/hydrogen-react/storefront.schema.json');

let staticSchema = '';
/**
* Resolves a schema path for the provided API type. Only the API types currently
* bundled in Hydrogen are allowed: "storefront" and "customer".
* @param api
* @returns
*/
export const getSchema = (api = 'storefront' as 'storefront' | 'customer') => {
if (api !== 'storefront' && api !== 'customer') {
throw new Error(
`The provided API type "${api}" is unknown. Please use "storefront" or "customer".`,
);
}

return require.resolve(`@shopify/hydrogen-react/${api}.schema.json`);
};

let staticSFAPISchema = '';

try {
staticSchema = getSchema();
staticSFAPISchema = getSchema('storefront');
} catch (error) {
// This can happen at build time or when '@shopify/hydrogen-react' is not found.
// Generally this shouldn't be an issue in real apps so let's ignore the error.
// Also, this package could be used in non-Hydrogen apps.
}

export const schema = staticSchema;
/**
* The resolved schema path for the Storefront API.
*/
export const schema = staticSFAPISchema;
4 changes: 2 additions & 2 deletions packages/hydrogen-codegen/tests/codegen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe('Hydrogen Codegen', async () => {
// Patch dependency before importing the Codegen CLI
await import('../src/patch.js');
const {preset, schema, pluckConfig} = await import('../src/index.js');
const {defaultInterfaceExtensionCode} = await import('../src/preset.js');
const {getDefaultOptions} = await import('../src/defaults.js');
const {executeCodegen} = await import('@graphql-codegen/cli');

const getCodegenOptions = (fixture: string, output = 'out.d.ts') => ({
Expand Down Expand Up @@ -64,7 +64,7 @@ describe('Hydrogen Codegen', async () => {
);

// Augments query/mutation types
expect(generatedCode).toMatch(defaultInterfaceExtensionCode);
expect(generatedCode).toMatch(getDefaultOptions().interfaceExtensionCode);

expect(generatedCode).toMatchInlineSnapshot(`
"/* eslint-disable eslint-comments/disable-enable-pair */
Expand Down

0 comments on commit 3f3b8db

Please sign in to comment.