Skip to content

Commit eb64c62

Browse files
beaussanwebpro
andauthoredOct 23, 2023
Add graphql-codegen support (#305)
* Add graphql-codegen support * Add `PACKAGE_JSON_PATH` to graphql-codegen * cleenup tests * cleenup logs --------- Co-authored-by: Lars Kappert <lars@webpro.nl>
1 parent b96c807 commit eb64c62

File tree

11 files changed

+233
-0
lines changed

11 files changed

+233
-0
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ Knip contains a growing list of plugins:
277277
- [ESLint][plugin-eslint]
278278
- [Gatsby][plugin-gatsby]
279279
- [GitHub Actions][plugin-github-actions]
280+
- [Graphql Codegen][plugin-graphql-codegen]
280281
- [husky][plugin-husky]
281282
- [Jest][plugin-jest]
282283
- [Lefthook][plugin-lefthook]
@@ -928,6 +929,7 @@ Special thanks to the wonderful people who have contributed to this project:
928929
[plugin-eslint]: ./src/plugins/eslint
929930
[plugin-gatsby]: ./src/plugins/gatsby
930931
[plugin-github-actions]: ./src/plugins/github-actions
932+
[plugin-graphql-codegen]: ./src/plugins/graphql-codegen
931933
[plugin-husky]: ./src/plugins/husky
932934
[plugin-jest]: ./src/plugins/jest
933935
[plugin-lefthook]: ./src/plugins/lefthook
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = {
2+
schema: 'schema.graphql',
3+
overwrite: true,
4+
generates: {
5+
'./graphql.schema.json': {
6+
plugins: ['introspection'],
7+
},
8+
'./graphql.schema.graphql': {
9+
'schema-ast': {},
10+
},
11+
'./src/generated/graphql.ts': {
12+
documents: ['./src/**/*.tsx'],
13+
plugins: ['typescript', 'typescript-operations', 'typescript-urql'],
14+
},
15+
'./lib/': {
16+
documents: ['./lib/**/*.tsx'],
17+
preset: 'near-operation-file-preset',
18+
plugins: ['typescript-operations', 'typescript-msw'],
19+
},
20+
},
21+
};

‎fixtures/plugins/graphql-codegen/node_modules/@graphql-codegen/cli/package.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@fixtures/_template",
3+
"version": "*",
4+
"devDependencies": {
5+
"@graphql-codegen/cli": "*"
6+
},
7+
"codegen": {
8+
"schema": "schema.graphql",
9+
"documents": ["src/**/*.tsx", "!src/gql/**/*"],
10+
"generates": {
11+
"./src/gql/": {
12+
"preset": "client"
13+
}
14+
}
15+
}
16+
}

‎schema.json

+4
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@
276276
"title": "github-actions plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/github-actions/README.md)",
277277
"$ref": "#/definitions/plugin"
278278
},
279+
"graphql-codegen": {
280+
"title": "graphql-codegen plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/graphql-codegen/README.md)",
281+
"$ref": "#/definitions/plugin"
282+
},
279283
"husky": {
280284
"title": "husky plugin configuration (https://github.com/webpro/knip/blob/main/src/plugins/husky/README.md)",
281285
"$ref": "#/definitions/plugin"

‎src/ConfigurationValidator.ts

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export const pluginSchema = z.union([
7373
]);
7474

7575
const pluginsSchema = z.object({
76+
'graphql-codegen': pluginSchema,
7677
astro: pluginSchema,
7778
angular: pluginSchema,
7879
ava: pluginSchema,

‎src/plugins/graphql-codegen/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Graphql Codegen
2+
3+
## Enabled
4+
5+
This plugin is enabled when any of the following package names and/or regular expressions has a match in `dependencies`
6+
or `devDependencies`:
7+
8+
- `/^@graphql-codegen\//`
9+
10+
## Default configuration
11+
12+
```json
13+
{
14+
"graphql-codegen": {
15+
"config": ["codegen.{ts,js,json,yml,mjs,cts}", "package.json"]
16+
}
17+
}
18+
```
19+
20+
Also see [Knip plugins][1] for more information about plugins.
21+
22+
[1]: https://github.com/webpro/knip/blob/main/README.md#plugins

‎src/plugins/graphql-codegen/index.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { timerify } from '../../util/Performance.js';
2+
import { hasDependency, load } from '../../util/plugin.js';
3+
import { isConfigurationOutput } from './types.js';
4+
import type { ConfiguredPlugin, GraphqlCodegenTypes, PresetNames } from './types.js';
5+
import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';
6+
7+
export const NAME = 'Graphql Codegen';
8+
9+
/** @public */
10+
export const ENABLERS = [/^@graphql-codegen\//];
11+
12+
export const PACKAGE_JSON_PATH = 'codegen';
13+
14+
export const isEnabled: IsPluginEnabledCallback = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
15+
16+
export const CONFIG_FILE_PATTERNS = ['codegen.{ts,js,json,yml,mjs,cts}', 'package.json'];
17+
18+
const findPluginDependencies: GenericPluginCallback = async (configFilePath, options) => {
19+
const { manifest, isProduction } = options;
20+
21+
if (isProduction) return [];
22+
23+
// load configuration file from `configFilePath` (or grab `manifest` for package.json)
24+
// load(FAKE_PATH) will return `undefined`
25+
const localConfig: GraphqlCodegenTypes | undefined = configFilePath.endsWith('package.json')
26+
? manifest[PACKAGE_JSON_PATH]
27+
: await load(configFilePath);
28+
29+
if (!localConfig) return [];
30+
31+
const generateSet = Object.values(localConfig.generates);
32+
33+
const configurationOutput = generateSet.filter(isConfigurationOutput);
34+
35+
const presets = configurationOutput
36+
.map(configOutput => (configOutput.preset ? configOutput.preset : undefined))
37+
.filter((preset): preset is PresetNames => typeof preset === 'string')
38+
.map(presetName => `@graphql-codegen/${presetName}${presetName.endsWith('-preset') ? '' : '-preset'}`);
39+
40+
const flatPlugins = generateSet
41+
.filter((config): config is ConfiguredPlugin => !isConfigurationOutput(config))
42+
.map(item => Object.keys(item))
43+
.flat()
44+
.map(plugin => `@graphql-codegen/${plugin}`);
45+
46+
const nestedPlugins = configurationOutput
47+
.map(configOutput => (configOutput.plugins ? configOutput.plugins : []))
48+
.flat()
49+
.map(plugin => `@graphql-codegen/${plugin}`);
50+
51+
return [...presets, ...flatPlugins, ...nestedPlugins];
52+
};
53+
54+
export const findDependencies = timerify(findPluginDependencies);

‎src/plugins/graphql-codegen/types.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
type PluginConfig<T = unknown> = { [key: string]: T };
2+
export interface ConfiguredPlugin {
3+
[name: string]: PluginConfig;
4+
}
5+
type NamedPlugin = string;
6+
7+
type OutputConfig = NamedPlugin | ConfiguredPlugin;
8+
type PresetNamesBase = 'client' | 'near-operation-file' | 'gql-tag-operations' | 'graphql-modules' | 'import-types';
9+
export type PresetNames = `${PresetNamesBase}-preset` | PresetNamesBase;
10+
11+
type OutputPreset = {
12+
buildGeneratesSection: (options: unknown) => Promise<unknown>;
13+
prepareDocuments?: (outputFilePath: string, outputSpecificDocuments: unknown) => Promise<unknown>;
14+
};
15+
16+
export function isConfigurationOutput(config: ConfiguredOutput | ConfiguredPlugin[]): config is ConfiguredOutput {
17+
return 'preset' in config || 'plugins' in config;
18+
}
19+
20+
interface ConfiguredOutput {
21+
/**
22+
* @type array
23+
* @items { "$ref": "#/definitions/GeneratedPluginsMap" }
24+
* @description List of plugins to apply to this current output file.
25+
*
26+
* You can either specify plugins from the community using the NPM package name (after you installed it in your project), or you can use a path to a local file for custom plugins.
27+
*
28+
* You can find a list of available plugins here: https://the-guild.dev/graphql/codegen/docs/plugins/index
29+
* Need a custom plugin? read this: https://the-guild.dev/graphql/codegen/docs/custom-codegen/index
30+
*/
31+
plugins?: OutputConfig[];
32+
/**
33+
* @description If your setup uses Preset to have a more dynamic setup and output, set the name of your preset here.
34+
*
35+
* Presets are a way to have more than one file output, for example: https://the-guild.dev/graphql/codegen/docs/presets/near-operation-file
36+
*
37+
* You can either specify a preset from the community using the NPM package name (after you installed it in your project), or you can use a path to a local file for a custom preset.
38+
*
39+
* List of available presets: https://graphql-code-generator.com/docs/presets/presets-index
40+
*/
41+
preset?: PresetNames | OutputPreset;
42+
}
43+
// Extracted from https://github.com/dotansimha/graphql-code-generator/blob/master/packages/utils/plugins-helpers/src/types.ts
44+
export interface GraphqlCodegenTypes {
45+
/**
46+
* @description A map where the key represents an output path for the generated code and the value represents a set of options which are relevant for that specific file.
47+
*
48+
* For more details: https://graphql-code-generator.com/docs/config-reference/codegen-config
49+
*/
50+
generates: {
51+
[outputPath: string]: ConfiguredOutput | ConfiguredPlugin[];
52+
};
53+
}

‎src/plugins/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ export * as typescript from './typescript/index.js';
4343
export * as vite from './vite/index.js';
4444
export * as vitest from './vitest/index.js';
4545
export * as webpack from './webpack/index.js';
46+
export * as graphqlCodegen from './graphql-codegen/index.js';

‎test/plugins/graphql-codegen.test.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import assert from 'node:assert/strict';
2+
import test from 'node:test';
3+
import { main } from '../../src/index.js';
4+
import * as graphqlCodegen from '../../src/plugins/graphql-codegen/index.js';
5+
import { resolve, join } from '../../src/util/path.js';
6+
import baseArguments from '../helpers/baseArguments.js';
7+
import baseCounters from '../helpers/baseCounters.js';
8+
import { getManifest, pluginConfig as config } from '../helpers/index.js';
9+
10+
const cwd = resolve('fixtures/plugins/graphql-codegen');
11+
const manifest = getManifest(cwd);
12+
13+
test('Find dependencies in graphql-codegen configuration (json)', async () => {
14+
const configFilePath = join(cwd, 'package.json');
15+
const dependencies = await graphqlCodegen.findDependencies(configFilePath, { manifest, config });
16+
assert.deepEqual(dependencies, ['@graphql-codegen/client-preset']);
17+
});
18+
19+
test('Find dependencies in graphql-codegen configuration (codegen.ts)', async () => {
20+
const configFilePath = join(cwd, 'codegen.ts');
21+
const dependencies = await graphqlCodegen.findDependencies(configFilePath, { manifest, config });
22+
assert.deepEqual(dependencies, [
23+
'@graphql-codegen/near-operation-file-preset',
24+
'@graphql-codegen/schema-ast',
25+
'@graphql-codegen/introspection',
26+
'@graphql-codegen/typescript',
27+
'@graphql-codegen/typescript-operations',
28+
'@graphql-codegen/typescript-urql',
29+
'@graphql-codegen/typescript-operations',
30+
'@graphql-codegen/typescript-msw',
31+
]);
32+
});
33+
34+
test('Find dependencies in graphql-codegen configuration (codegen.ts function)', async () => {
35+
const { issues, counters } = await main({
36+
...baseArguments,
37+
cwd,
38+
});
39+
40+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/near-operation-file-preset']);
41+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/schema-ast']);
42+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/introspection']);
43+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript']);
44+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-operations']);
45+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-urql']);
46+
assert(issues.unlisted['codegen.ts']['@graphql-codegen/typescript-msw']);
47+
assert(issues.unlisted['package.json']['@graphql-codegen/client-preset']);
48+
49+
assert.deepEqual(counters, {
50+
...baseCounters,
51+
unlisted: 8,
52+
devDependencies: 1,
53+
processed: 1,
54+
total: 1,
55+
});
56+
});

0 commit comments

Comments
 (0)
Please sign in to comment.