Skip to content

Commit

Permalink
Rewrite to not use plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
kazekyo committed Feb 19, 2023
1 parent 6f57c11 commit 64e8376
Show file tree
Hide file tree
Showing 16 changed files with 627 additions and 222 deletions.
122 changes: 79 additions & 43 deletions .changeset/short-toes-relax.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,97 @@
"@graphql-codegen/graphql-modules-preset": minor
---

Add a feature to transform documents.
Introduce a new feature called DocumentTransform.

Plugin will have the following functions:
```js
module.exports = {
plugin: () => {
return 'hello'
},
transformDocuments: (_schema, documents) => {
// Make some changes to the documents
return documents;
DocumentTransform is a functionality that allows you to modify `documents` before they are processed by plugins. You can use functions passed to the `documentTransforms` option to make changes to GraphQL documents or extend the schema, as needed.

To use this feature, you can write `documentTransforms` as follows:

```ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'https://localhost:4000/graphql',
documents: ['src/**/*.tsx'],
generates: {
'./src/gql/': {
preset: 'client',
documentTransforms: [
{
transform: ({ documents }) => {
// Make some changes to the documents
return documents;
},
addToSchema: 'extend type Query { test: String! }',
},
],
},
},
};
export default config;
```

Use it as follows:
For instance, to remove a `@localOnlyDirective` directive from `documents`, you can write the following code:

```ts
import type { CodegenConfig } from '@graphql-codegen/cli'
```js
import type { CodegenConfig } from '@graphql-codegen/cli';
import { visit } from 'graphql';

const config: CodegenConfig = {
schema: 'https://localhost:4000/graphql',
documents: ['src/**/*.tsx'],
generates: {
'./src/gql/': {
preset: 'client',
documentTransformPlugins: ['./my-plugin.js'],
plugins: ['./my-plugin.js']
}
}
}
export default config
schema: 'https://localhost:4000/graphql',
documents: ['src/**/*.tsx'],
generates: {
'./src/gql/': {
preset: 'client',
documentTransforms: [
{
transform: ({ documents }) => {
return documents.map(documentFile => {
documentFile.document = visit(documentFile.document, {
Directive: {
leave(node) {
if (node.name.value === 'localOnlyDirective') return null;
},
},
});
return documentFile;
});
},
},
],
},
},
};
export default config;
```

For example, to remove a `@localOnlyDirective` directive from documents:
DocumentTransform can also be specified by file name. You can create a custom file for a specific transformation and pass it to `documentTransforms`.

```js
const { visit, print } = require('graphql')
Let's create the document transform as a file:

```js
module.exports = {
plugin(schema, documents, config) {
// Output `documents` as an example.
return documents.map(documentFile => `${print(documentFile.document)}`).join('\n')
transform: ({ documents }) => {
// Make some changes to the documents
return documents;
},
transformDocuments(schema, documents, config) {
return documents.map(documentFile => {
documentFile.document = visit(documentFile.document, {
Directive: {
leave(node) {
if (node.name.value === 'localOnlyDirective') return null
}
}
})
return documentFile
})
}
}
};
```

Then, you can specify the file name as follows:

```ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'https://localhost:4000/graphql',
documents: ['src/**/*.tsx'],
generates: {
'./src/gql/': {
preset: 'client',
documentTransforms: ['./my-document-transform.js'],
},
},
};
export default config;
```
19 changes: 12 additions & 7 deletions packages/graphql-codegen-cli/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { CodegenContext, ensureContext, shouldEmitLegacyCommonJSImports } from '
import { getPluginByName } from './plugins.js';
import { getPresetByName } from './presets.js';
import { debugLog, printLogs } from './utils/debugging.js';
import { getDocumentTransform } from './documentTransforms.js';

/**
* Poor mans ESM detection.
Expand Down Expand Up @@ -316,13 +317,17 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
emitLegacyCommonJSImports: shouldEmitLegacyCommonJSImports(config),
};

const documentTransforms = await Promise.all(
normalizeConfig(outputConfig.documentTransforms).map(async pluginConfig => {
const name = Object.keys(pluginConfig)[0];
const plugin = await getPluginByName(name, pluginLoader);
return { [name]: { plugin, config: Object.values(pluginConfig)[0] } };
})
);
const documentTransforms = Array.isArray(outputConfig.documentTransforms)
? await Promise.all(
outputConfig.documentTransforms.map(async (config, index) => {
return await getDocumentTransform(
config,
makeDefaultLoader(context.cwd),
`the element at index ${index} of the documentTransforms`
);
})
)
: [];

const outputs: Types.GenerateOptions[] = preset
? await context.profiler.run(
Expand Down
82 changes: 82 additions & 0 deletions packages/graphql-codegen-cli/src/documentTransforms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { resolve } from 'path';
import { Types } from '@graphql-codegen/plugin-helpers';

export async function getDocumentTransform(
documentTransform: Types.OutputDocumentTransform,
loader: Types.PackageLoaderFn<Types.DocumentTransformObject>,
defaultName: string
): Promise<Types.ConfiguredDocumentTransform> {
if (typeof documentTransform === 'string') {
const transformObject = await getDocumentTransformByName(documentTransform, loader);
return { name: documentTransform, transformObject };
}
if (isTransformObject(documentTransform)) {
return { name: defaultName, transformObject: documentTransform };
}
if (isTransformFileConfig(documentTransform)) {
const name = Object.keys(documentTransform)[0];
const transformObject = await getDocumentTransformByName(name, loader);
return { name, transformObject, config: Object.values(documentTransform)[0] };
}
throw new Error(
`
An unknown format document transform: '${defaultName}'.
`
);
}

function isTransformObject(config: Types.OutputDocumentTransform): config is Types.DocumentTransformObject {
return typeof config === 'object' && config.transform && typeof config.transform === 'function';
}

function isTransformFileConfig(config: Types.OutputDocumentTransform): config is Types.DocumentTransformFileConfig {
const keys = Object.keys(config);
return keys.length === 1 && typeof keys[0] === 'string';
}

export async function getDocumentTransformByName(
name: string,
loader: Types.PackageLoaderFn<Types.DocumentTransformObject>
): Promise<Types.DocumentTransformObject> {
const possibleNames = [
`@graphql-codegen/${name}`,
`@graphql-codegen/${name}-document-transform`,
name,
resolve(process.cwd(), name),
];

const possibleModules = possibleNames.concat(resolve(process.cwd(), name));

for (const moduleName of possibleModules) {
try {
return await loader(moduleName);
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND' && err.code !== 'ERR_MODULE_NOT_FOUND') {
throw new Error(
`
Unable to load document transform matching '${name}'.
Reason:
${err.message}
`
);
}
}
}

const possibleNamesMsg = possibleNames
.map(name =>
`
- ${name}
`.trimEnd()
)
.join('');

throw new Error(
`
Unable to find document transform matching '${name}'
Install one of the following packages:
${possibleNamesMsg}
`
);
}
Loading

0 comments on commit 64e8376

Please sign in to comment.