Skip to content

Commit

Permalink
GraphQL Code Generator v3: @graphql-codegen/cli changes (#8301)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
charlypoly and github-actions[bot] committed Aug 31, 2022
1 parent 491524a commit 2ed21a4
Show file tree
Hide file tree
Showing 26 changed files with 948 additions and 419 deletions.
7 changes: 7 additions & 0 deletions .changeset/@graphql-codegen_cli-8301-dependencies.md
@@ -0,0 +1,7 @@
---
"@graphql-codegen/cli": patch
---

dependencies updates:

- Added dependency [`cosmiconfig-typescript-loader@^4.0.0` ↗︎](https://www.npmjs.com/package/cosmiconfig-typescript-loader/v/null) (to `dependencies`)
7 changes: 7 additions & 0 deletions .changeset/fast-pears-wonder.md
@@ -0,0 +1,7 @@
---
'@graphql-codegen/cli': minor
'@graphql-cli/codegen': minor
'@graphql-codegen/plugin-helpers': minor
---

Introduces support for TypeScript config file and a new preset lifecycle (required for `client-preset`)
1 change: 1 addition & 0 deletions packages/graphql-codegen-cli/package.json
Expand Up @@ -57,6 +57,7 @@
"chalk": "^4.1.0",
"chokidar": "^3.5.2",
"cosmiconfig": "^7.0.0",
"cosmiconfig-typescript-loader": "^4.0.0",
"debounce": "^1.2.0",
"detect-indent": "^6.0.0",
"graphql-config": "^4.3.5",
Expand Down
34 changes: 19 additions & 15 deletions packages/graphql-codegen-cli/src/codegen.ts
Expand Up @@ -202,21 +202,29 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
const outputConfig = generates[filename];
const hasPreset = !!outputConfig.preset;

const title = hasPreset
? `Generate to ${filename} (using EXPERIMENTAL preset "${outputConfig.preset}")`
: `Generate ${filename}`;
const title = `Generate to ${filename}`;

return {
title,
task: (_, subTask) => {
task: async (_, subTask) => {
let outputSchemaAst: GraphQLSchema;
let outputSchema: DocumentNode;
const outputFileTemplateConfig = outputConfig.config || {};
let outputDocuments: Types.DocumentFile[] = [];
const outputSpecificSchemas = normalizeInstanceOrArray<Types.Schema>(outputConfig.schema);
const outputSpecificDocuments = normalizeInstanceOrArray<Types.OperationDocument>(
outputConfig.documents
);
let outputSpecificDocuments = normalizeInstanceOrArray<Types.OperationDocument>(outputConfig.documents);

const preset: Types.OutputPreset | null = hasPreset
? typeof outputConfig.preset === 'string'
? await getPresetByName(outputConfig.preset, makeDefaultLoader(context.cwd))
: outputConfig.preset
: null;

if (preset) {
if (preset.prepareDocuments) {
outputSpecificDocuments = await preset.prepareDocuments(filename, outputSpecificDocuments);
}
}

return subTask.newListr(
[
Expand Down Expand Up @@ -296,13 +304,9 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
normalizedPluginsArray.map(plugin => getPluginByName(Object.keys(plugin)[0], pluginLoader))
);

const preset: Types.OutputPreset = hasPreset
? typeof outputConfig.preset === 'string'
? await getPresetByName(outputConfig.preset, makeDefaultLoader(context.cwd))
: outputConfig.preset
: null;

const pluginMap: { [name: string]: CodegenPlugin } = Object.fromEntries(
const pluginMap: {
[name: string]: CodegenPlugin;
} = Object.fromEntries(
pluginPackages.map((pkg, i) => {
const plugin = normalizedPluginsArray[i];
const name = Object.keys(plugin)[0];
Expand All @@ -318,7 +322,7 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
emitLegacyCommonJSImports: shouldEmitLegacyCommonJSImports(config, filename),
};

const outputs: Types.GenerateOptions[] = hasPreset
const outputs: Types.GenerateOptions[] = preset
? await context.profiler.run(
async () =>
preset.buildGeneratesSection({
Expand Down
12 changes: 10 additions & 2 deletions packages/graphql-codegen-cli/src/config.ts
@@ -1,4 +1,5 @@
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
import { TypeScriptLoader } from 'cosmiconfig-typescript-loader';
import { resolve } from 'path';
import {
DetailedError,
Expand All @@ -21,6 +22,8 @@ import { createHash } from 'crypto';

const { lstat } = promises;

export type CodegenConfig = Types.Config;

export type YamlCliFlags = {
config: string;
watch: boolean | string | string[];
Expand All @@ -38,7 +41,7 @@ export type YamlCliFlags = {
};

export function generateSearchPlaces(moduleName: string) {
const extensions = ['json', 'yaml', 'yml', 'js', 'config.js'];
const extensions = ['json', 'yaml', 'yml', 'js', 'ts', 'config.js'];
// gives codegen.json...
const regular = extensions.map(ext => `${moduleName}.${ext}`);
// gives .codegenrc.json... but no .codegenrc.config.js
Expand All @@ -47,7 +50,7 @@ export function generateSearchPlaces(moduleName: string) {
return [...regular.concat(dot), 'package.json'];
}

function customLoader(ext: 'json' | 'yaml' | 'js') {
function customLoader(ext: 'json' | 'yaml' | 'js' | 'ts') {
function loader(filepath: string, content: string) {
if (typeof process !== 'undefined' && 'env' in process) {
content = env(content);
Expand All @@ -70,6 +73,10 @@ function customLoader(ext: 'json' | 'yaml' | 'js') {
if (ext === 'js') {
return defaultLoaders['.js'](filepath, content);
}

if (ext === 'ts') {
return TypeScriptLoader()(filepath, content);
}
}

return loader;
Expand Down Expand Up @@ -124,6 +131,7 @@ export async function loadCodegenConfig({
'.yaml': customLoader('yaml'),
'.yml': customLoader('yaml'),
'.js': customLoader('js'),
'.ts': customLoader('ts'),
noExt: customLoader('yaml'),
...customLoaders,
},
Expand Down
1 change: 1 addition & 0 deletions packages/graphql-codegen-cli/src/index.ts
Expand Up @@ -5,3 +5,4 @@ export * from './init/index.js';
export * from './utils/cli-error.js';
export * from './cli.js';
export * from './graphql-config.js';
export { CodegenConfig } from './config.js';
14 changes: 7 additions & 7 deletions packages/graphql-codegen-cli/src/presets.ts
@@ -1,4 +1,4 @@
import { DetailedError, Types } from '@graphql-codegen/plugin-helpers';
import { Types } from '@graphql-codegen/plugin-helpers';
import { resolve } from 'path';

export async function getPresetByName(
Expand Down Expand Up @@ -31,9 +31,9 @@ export async function getPresetByName(
/** ESM Error code */
err.code !== 'ERR_MODULE_NOT_FOUND'
) {
throw new DetailedError(
`Unable to load preset matching ${name}`,
`
throw new Error(
`Unable to load preset matching ${name}
Unable to load preset matching '${name}'.
Reason:
${err.message}
Expand All @@ -51,9 +51,9 @@ export async function getPresetByName(
)
.join('');

throw new DetailedError(
`Unable to find preset matching ${name}`,
`
throw new Error(
`Unable to find preset matching ${name}
Unable to find preset matching '${name}'
Install one of the following packages:
Expand Down
2 changes: 1 addition & 1 deletion packages/presets/graphql-modules/tests/integration.spec.ts
Expand Up @@ -11,7 +11,7 @@ const options = {
'./tests/test-files/modules': {
schema: './tests/test-files/modules/*/types/*.graphql',
plugins: ['typescript', 'typescript-resolvers'],
preset: 'graphql-modules',
preset: 'graphql-modules' as const,
presetConfig: {
baseTypesPath: 'global-types.ts',
filename: 'module-types.ts',
Expand Down
1 change: 1 addition & 0 deletions packages/utils/plugins-helpers/src/index.ts
Expand Up @@ -7,3 +7,4 @@ export * from './errors.js';
export * from './getCachedDocumentNodeFromSchema.js';
export * from './oldVisit.js';
export * from './profiler.js';
export { Types } from './types.js';
14 changes: 13 additions & 1 deletion packages/utils/plugins-helpers/src/types.ts
Expand Up @@ -218,6 +218,14 @@ export namespace Types {
export type NamedPreset = string;
export type OutputConfig = NamedPlugin | ConfiguredPlugin;

export type PresetNamesBase =
| 'client'
| 'near-operation-file'
| 'gql-tag-operations'
| 'graphql-modules'
| 'import-types-preset';
export type PresetNames = `${PresetNamesBase}-preset` | PresetNamesBase;

/**
* @additionalProperties false
*/
Expand All @@ -242,7 +250,7 @@ export namespace Types {
*
* List of available presets: https://graphql-code-generator.com/docs/presets/presets-index
*/
preset?: string | OutputPreset;
preset?: PresetNames | OutputPreset;
/**
* @description If your setup uses Preset to have a more dynamic setup and output, set the configuration object of your preset here.
*
Expand Down Expand Up @@ -330,6 +338,10 @@ export namespace Types {

export type OutputPreset<TPresetConfig = any> = {
buildGeneratesSection: (options: PresetFnArgs<TPresetConfig>) => Promisable<GenerateOptions[]>;
prepareDocuments?: (
outputFilePath: string,
outputSpecificDocuments: Types.OperationDocument[]
) => Promisable<Types.OperationDocument[]>;
};

/* Require Extensions */
Expand Down
66 changes: 34 additions & 32 deletions website/src/pages/docs/advanced/generated-files-colocation.mdx
Expand Up @@ -13,19 +13,21 @@ For similar results on a back-end project, please refer to [`@graphql-codegen/gr

Most GraphQL Code Generator configuration examples (in guides or plugins documentation) generate types in a single common file, as follows:

```yaml
# Configuration for a React URQL setup

schema: http://my-graphql-api.com/graphql
documents: './src/**/*.tsx'
generates:
graphql/generated.ts:
plugins:
- typescript
- typescript-operations
- typescript-urql
config:
withHooks: true
```ts
import { CodegenConfig } from '@graphql-codegen/cli';

// Configuration for a React URQL setup
const config: CodegenConfig = {
schema: 'http://my-graphql-api.com/graphql',
documents: './src/**/*.tsx',
generates: {
'graphql/generated.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-urql'],
config: { withHooks: true },
},
},
};
export default config;
```

All code is generated in one single `graphql/generated.ts` file.
Expand All @@ -51,29 +53,29 @@ Just a few configuration steps are required to get this structure of colocated g

### Configure the preset

To use this preset, you need to add 2 outputs to your `codegen.yml` file:
To use this preset, you need to add 2 outputs to your `codegen.ts` file:

- The first is the base types, generated by `typescript` plugin.
- The second is the one in charge of generating types per operation.

```yaml
schema: http://my-graphql-api.com/graphql
# we are looking for operations in .tsx files,
# but not the generated ones.
documents: 'src/**/!(*.generated).{ts,tsx}'
generates:
src/types.ts:
- typescript
src/:
preset: near-operation-file
presetConfig:
extension: .generated.tsx
baseTypesPath: types.ts
plugins:
- typescript-operations
- typescript-urql
config:
withHooks: true

```ts filename="codegen.ts"
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'http://my-graphql-api.com/graphql',
documents: 'src/**/!(*.generated).{ts,tsx}',
generates: {
'src/types.ts': ['typescript'],
'src/': {
preset: 'near-operation-file',
presetConfig: { extension: '.generated.tsx', baseTypesPath: 'types.ts' },
plugins: ['typescript-operations', 'typescript-urql'],
config: { withHooks: true },
},
},
};
export default config;
```

<Callout>
Expand Down
22 changes: 12 additions & 10 deletions website/src/pages/docs/advanced/how-does-it-work.mdx
Expand Up @@ -78,16 +78,18 @@ fragment UserFields on User {
}
```

And with the following `codegen.yml` configuration file:

```yaml filename="codegen.yml"
schema: http://localhost:3333
generates:
types-and-hooks.tsx:
plugins:
- typescript
- typescript-operations
- typescript-react-query
And with the following `codegen.ts` configuration file:

```ts filename="codegen.ts"
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'http://localhost:3333',
generates: {
'types-and-hooks.tsx': { plugins: ['typescript', 'typescript-operations', 'typescript-react-query'] },
},
};
export default config;
```

`@graphql-codegen/typescript` plugin can generate the following TypeScript typings and React hooks files based on defined operations:
Expand Down
2 changes: 1 addition & 1 deletion website/src/pages/docs/advanced/profiler.mdx
Expand Up @@ -5,7 +5,7 @@ import { PackageCmd } from '@theguild/components'
GraphQL Code Generator CLI provides a flag that enables the profiler mode, as follows:

<PackageCmd
packages={[{ name: 'graphql-code-generator --config graphql-codegen.yml --profile', cmd: 'run', isNpx: true }]}
packages={[{ name: 'graphql-code-generator --config graphql-codegen.ts --profile', cmd: 'run', isNpx: true }]}
/>

GraphQL Code Generator operates as usual (generating your files) but also generates a `codegen-[timestamp].json` profile file.
Expand Down

0 comments on commit 2ed21a4

Please sign in to comment.