Skip to content

Commit ed2a5c0

Browse files
authored
config: modulePathPrefix, relativeToCwd, prefix (rubengrill#13)
1 parent 7a431fd commit ed2a5c0

File tree

4 files changed

+172
-10
lines changed

4 files changed

+172
-10
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ generates:
5454
```
5555
<!-- AUTO-GENERATED-CONTENT:END -->
5656
57+
The `apollo-typed-documents` plugin also accepts the same `modulePathPrefix`, `relativeToCwd` and `prefix` config settings as [typescript-graphql-files-modules](https://graphql-code-generator.com/docs/plugins/typescript-graphql-files-modules).
58+
5759
`tsconfig.json`:
5860

5961
Add `node_modules/apollo-typed-documents/lib/reactHooks.d.ts` in `include` to override the typings for `@apollo/react-hooks`, so that types can be inferred from typed documents.

src/__tests__/codegenTypedDocuments.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,119 @@ describe("codegenTypedDocuments", () => {
138138
}"
139139
`);
140140
});
141+
142+
describe("module path customization", () => {
143+
const queryDocument = parse(`
144+
query authors {
145+
authors {
146+
idField
147+
}
148+
}
149+
`);
150+
151+
const mutationDocument = parse(`
152+
mutation createAuthor {
153+
createAuthor {
154+
idField
155+
}
156+
}
157+
`);
158+
159+
const documents = [
160+
{ document: queryDocument, location: "literary/types/authors.gql" },
161+
{ document: mutationDocument, location: "mutations/createAuthor.gql" },
162+
];
163+
164+
const getPathConfig = (pluginConfig: Record<string, unknown>) => {
165+
return getConfig({
166+
documents,
167+
plugins: [
168+
{
169+
codegenTypedDocuments: {
170+
typesModule: "@codegen-types",
171+
...pluginConfig,
172+
},
173+
},
174+
],
175+
});
176+
};
177+
178+
it("wildcards the basename by default", async () => {
179+
const config = getPathConfig({});
180+
const output = await codegen(config);
181+
182+
expect(output).toMatchInlineSnapshot(`
183+
"declare module \\"*/authors.gql\\" {
184+
import { TypedDocumentNode } from \\"apollo-typed-documents\\";
185+
import { AuthorsQuery, AuthorsQueryVariables } from \\"@codegen-types\\";
186+
export const authors: TypedDocumentNode<AuthorsQueryVariables, AuthorsQuery>;
187+
export default authors;
188+
}
189+
190+
declare module \\"*/createAuthor.gql\\" {
191+
import { TypedDocumentNode } from \\"apollo-typed-documents\\";
192+
import { CreateAuthorMutation, CreateAuthorMutationVariables } from \\"@codegen-types\\";
193+
export const createAuthor: TypedDocumentNode<CreateAuthorMutationVariables, CreateAuthorMutation>;
194+
export default createAuthor;
195+
}"
196+
`);
197+
});
198+
199+
it("respects the relativeToCwd setting", async () => {
200+
const config = getPathConfig({ relativeToCwd: true });
201+
const output = await codegen(config);
202+
203+
expect(output).toEqual(
204+
expect.stringContaining(`declare module "*/literary/types/authors.gql"`)
205+
);
206+
expect(output).toEqual(
207+
expect.stringContaining(`declare module "*/mutations/createAuthor.gql"`)
208+
);
209+
});
210+
211+
it("respects the prefix setting", async () => {
212+
const config = getPathConfig({ prefix: "gql/" });
213+
const output = await codegen(config);
214+
215+
expect(output).toEqual(
216+
expect.stringContaining(`declare module "gql/authors.gql"`)
217+
);
218+
expect(output).toEqual(
219+
expect.stringContaining(`declare module "gql/createAuthor.gql"`)
220+
);
221+
});
222+
223+
it("even respects the superfluous modulePathPrefix setting", async () => {
224+
const config = getPathConfig({ modulePathPrefix: "stuff/" });
225+
const output = await codegen(config);
226+
227+
expect(output).toEqual(
228+
expect.stringContaining(`declare module "*/stuff/authors.gql"`)
229+
);
230+
expect(output).toEqual(
231+
expect.stringContaining(`declare module "*/stuff/createAuthor.gql"`)
232+
);
233+
});
234+
235+
it("allows combining path settings", async () => {
236+
const config = getPathConfig({
237+
prefix: "",
238+
modulePathPrefix: "defs/",
239+
relativeToCwd: true,
240+
});
241+
242+
const output = await codegen(config);
243+
244+
expect(output).toEqual(
245+
expect.stringContaining(
246+
`declare module "defs/literary/types/authors.gql"`
247+
)
248+
);
249+
expect(output).toEqual(
250+
expect.stringContaining(
251+
`declare module "defs/mutations/createAuthor.gql"`
252+
)
253+
);
254+
});
255+
});
141256
});

src/codegenTypedDocuments.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import path from "path";
2-
31
import {
42
PluginFunction,
53
PluginValidateFn,
@@ -8,9 +6,44 @@ import { visit } from "graphql";
86

97
import TypedDocumentVisitor from "./visitors/TypedDocumentVisitor";
108

11-
export type Config = { typesModule: string };
9+
export type UserConfig = {
10+
typesModule: string;
11+
12+
/**
13+
* @default ""
14+
* @description Allows specifying a module definition path prefix to provide
15+
* distinction between generated types.
16+
*/
17+
modulePathPrefix?: string;
18+
19+
/**
20+
* @default false
21+
* @description By default, only the filename is being used to generate TS
22+
* module declarations. Setting this to `true` will generate it with a full
23+
* path based on the CWD.
24+
*/
25+
relativeToCwd?: boolean;
26+
27+
/**
28+
* @default *\/
29+
* @description By default, a wildcard is being added as prefix, you can
30+
* change that to a custom prefix.
31+
*/
32+
prefix?: string;
33+
};
34+
35+
export const plugin: PluginFunction<UserConfig> = (
36+
_schema,
37+
documents,
38+
{ typesModule, modulePathPrefix = "", relativeToCwd, prefix = "*/" }
39+
) => {
40+
const config = {
41+
typesModule,
42+
modulePathPrefix,
43+
useRelative: relativeToCwd === true,
44+
prefix,
45+
};
1246

13-
export const plugin: PluginFunction<Config> = (_schema, documents, config) => {
1447
const output: string[] = [];
1548

1649
documents.forEach((document) => {
@@ -21,16 +54,15 @@ export const plugin: PluginFunction<Config> = (_schema, documents, config) => {
2154
throw new Error("Missing document node");
2255
}
2356

24-
const basename = path.basename(document.location);
25-
const visitor = new TypedDocumentVisitor(output, basename, config);
57+
const visitor = new TypedDocumentVisitor(output, document.location, config);
2658

2759
visit(document.document, visitor);
2860
});
2961

3062
return output.join("\n\n");
3163
};
3264

33-
export const validate: PluginValidateFn<Config> = (
65+
export const validate: PluginValidateFn<UserConfig> = (
3466
_schema,
3567
_documents,
3668
config

src/visitors/TypedDocumentVisitor.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import { basename, relative } from "path";
2+
13
import { DefinitionNode, DocumentNode, OperationDefinitionNode } from "graphql";
24
import { pascalCase } from "pascal-case";
35

4-
export type Config = { typesModule: string };
6+
export type Config = {
7+
typesModule: string;
8+
modulePathPrefix: string;
9+
useRelative: boolean;
10+
prefix: string;
11+
};
512

613
const isOperationDefinitionNode = (
714
node: DefinitionNode
@@ -10,15 +17,21 @@ const isOperationDefinitionNode = (
1017
export default class TypedDocumentVisitor {
1118
constructor(
1219
readonly output: string[],
13-
readonly basename: string,
20+
readonly location: string,
1421
readonly config: Config
1522
) {}
1623

1724
Document = (node: DocumentNode) => {
1825
const operationNodes = node.definitions.filter(isOperationDefinitionNode);
1926
const output: string[] = [];
2027

21-
output.push(`declare module "*/${this.basename}" {\n`);
28+
const filepath = this.config.useRelative
29+
? relative(process.cwd(), this.location)
30+
: basename(this.location);
31+
32+
const modulePath = `${this.config.prefix}${this.config.modulePathPrefix}${filepath}`;
33+
34+
output.push(`declare module "${modulePath}" {\n`);
2235
output.push(
2336
' import { TypedDocumentNode } from "apollo-typed-documents";\n'
2437
);

0 commit comments

Comments
 (0)