Skip to content

Commit 296dd09

Browse files
committed
feat(graphql-codegen): configurable document/fragment variable naming
The plugin previously hardcoded "Document" and "FragmentDoc" suffixes when generating import identifiers. This meant setting e.g. `documentVariableSuffix: ''` in codegen config would cause a mismatch. Adds six new config options matching @graphql-codegen/client-preset: - documentVariablePrefix (default: "") - documentVariableSuffix (default: "Document") - fragmentVariablePrefix (default: "") - fragmentVariableSuffix (default: "FragmentDoc") - dedupeOperationSuffix (default: false) - omitOperationSuffix (default: false) dedupeOperationSuffix and omitOperationSuffix only affect fragment variable names, matching upstream codegen's getFragmentVariableName. All defaults match previous hardcoded values — fully backwards compatible.
1 parent d14102d commit 296dd09

13 files changed

Lines changed: 462 additions & 20 deletions

contrib/graphql-codegen-client-preset/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ You will need to provide the `artifactDirectory` path that should be the same as
1616

1717
The plugin also supports a `namingConvention` option to match the naming convention configured in your `codegen.ts`. The default is `"change-case-all#pascalCase"` which matches the default for `@graphql-codegen/client-preset`. If you have set `namingConvention: "change-case-all#upperCaseFirst"` in your `codegen.ts`, you must also set it in the plugin options.
1818

19+
#### Variable prefix/suffix options
20+
21+
By default the plugin appends `"Document"` to operation names and `"FragmentDoc"` to fragment names when generating import identifiers — matching the `@graphql-codegen/client-preset` defaults. If you've changed these in your codegen config, set the same values here so the plugin generates matching imports.
22+
23+
| Option | Default | Description |
24+
|--------|---------|-------------|
25+
| `documentVariablePrefix` | `""` | Prefix for operation variable names |
26+
| `documentVariableSuffix` | `"Document"` | Suffix for operation variable names |
27+
| `fragmentVariablePrefix` | `""` | Prefix for fragment variable names |
28+
| `fragmentVariableSuffix` | `"FragmentDoc"` | Suffix for fragment variable names |
29+
| `dedupeOperationSuffix` | `false` | When `true`, deduplicates overlapping suffix on fragments (e.g. `MyFragment` + `FragmentDoc``MyFragmentDoc` instead of `MyFragmentFragmentDoc`) |
30+
| `omitOperationSuffix` | `false` | When `true`, omits the fragment variable suffix entirely |
31+
1932
#### Vite
2033

2134
```ts

contrib/graphql-codegen-client-preset/src/lib.rs

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ pub struct GraphQLCodegenOptions {
122122
pub artifact_directory: String,
123123
pub gql_tag_name: String,
124124
pub naming_convention: String,
125+
pub document_variable_prefix: String,
126+
pub document_variable_suffix: String,
127+
pub fragment_variable_prefix: String,
128+
pub fragment_variable_suffix: String,
129+
pub dedupe_operation_suffix: bool,
130+
pub omit_operation_suffix: bool,
125131
}
126132

127133
pub struct GraphQLVisitor {
@@ -137,6 +143,23 @@ impl GraphQLVisitor {
137143
}
138144
}
139145

146+
fn resolve_fragment_suffix(&self, fragment_name: &str) -> &str {
147+
if self.options.omit_operation_suffix {
148+
""
149+
} else if self.options.dedupe_operation_suffix
150+
&& fragment_name.to_lowercase().ends_with("fragment")
151+
&& self
152+
.options
153+
.fragment_variable_suffix
154+
.to_lowercase()
155+
.starts_with("fragment")
156+
{
157+
&self.options.fragment_variable_suffix["fragment".len()..]
158+
} else {
159+
&self.options.fragment_variable_suffix
160+
}
161+
}
162+
140163
fn handle_error(&self, details: &str, span: Span) {
141164
let message = format!("@graphql-codegen/client-preset-swc-plugin details: {details}");
142165
HANDLER.with(|handler| handler.struct_span_err(span, &message).emit());
@@ -246,29 +269,31 @@ impl VisitMut for GraphQLVisitor {
246269

247270
let operation_name = match first_definition {
248271
graphql_parser::query::Definition::Fragment(fragment) => {
249-
fragment.name.to_string() + "FragmentDoc"
272+
let suffix = self.resolve_fragment_suffix(fragment.name);
273+
format!(
274+
"{}{}{}",
275+
self.options.fragment_variable_prefix, fragment.name, suffix
276+
)
250277
}
251-
graphql_parser::query::Definition::Operation(op) => match op {
252-
graphql_parser::query::OperationDefinition::Query(query) => {
253-
match query.name {
254-
Some(name) => name.to_string() + "Document",
255-
None => return,
278+
graphql_parser::query::Definition::Operation(op) => {
279+
let name = match op {
280+
graphql_parser::query::OperationDefinition::Query(q) => q.name,
281+
graphql_parser::query::OperationDefinition::Mutation(m) => m.name,
282+
graphql_parser::query::OperationDefinition::Subscription(s) => {
283+
s.name
256284
}
257-
}
258-
graphql_parser::query::OperationDefinition::Mutation(mutation) => {
259-
match mutation.name {
260-
Some(name) => name.to_string() + "Document",
261-
None => return,
262-
}
263-
}
264-
graphql_parser::query::OperationDefinition::Subscription(
265-
subscription,
266-
) => match subscription.name {
267-
Some(name) => name.to_string() + "Document",
285+
_ => return,
286+
};
287+
match name {
288+
Some(name) => format!(
289+
"{}{}{}",
290+
self.options.document_variable_prefix,
291+
name,
292+
self.options.document_variable_suffix
293+
),
268294
None => return,
269-
},
270-
_ => return,
271-
},
295+
}
296+
}
272297
};
273298

274299
let import_name =
@@ -349,6 +374,14 @@ fn naming_convention_default() -> String {
349374
"change-case-all#pascalCase".to_string()
350375
}
351376

377+
fn document_variable_suffix_default() -> String {
378+
"Document".to_string()
379+
}
380+
381+
fn fragment_variable_suffix_default() -> String {
382+
"FragmentDoc".to_string()
383+
}
384+
352385
#[derive(Deserialize)]
353386
#[serde(untagged)]
354387
enum NamingConventionOption {
@@ -383,6 +416,24 @@ struct PluginOptions {
383416

384417
#[serde(default = "naming_convention_option_default")]
385418
namingConvention: NamingConventionOption,
419+
420+
#[serde(default)]
421+
documentVariablePrefix: String,
422+
423+
#[serde(default = "document_variable_suffix_default")]
424+
documentVariableSuffix: String,
425+
426+
#[serde(default)]
427+
fragmentVariablePrefix: String,
428+
429+
#[serde(default = "fragment_variable_suffix_default")]
430+
fragmentVariableSuffix: String,
431+
432+
#[serde(default)]
433+
dedupeOperationSuffix: bool,
434+
435+
#[serde(default)]
436+
omitOperationSuffix: bool,
386437
}
387438

388439
#[plugin_transform]
@@ -414,6 +465,12 @@ pub fn process_transform(program: Program, metadata: TransformPluginProgramMetad
414465
artifact_directory,
415466
gql_tag_name: plugin_config.gqlTagName,
416467
naming_convention: plugin_config.namingConvention.as_type_name_convention(),
468+
document_variable_prefix: plugin_config.documentVariablePrefix,
469+
document_variable_suffix: plugin_config.documentVariableSuffix,
470+
fragment_variable_prefix: plugin_config.fragmentVariablePrefix,
471+
fragment_variable_suffix: plugin_config.fragmentVariableSuffix,
472+
dedupe_operation_suffix: plugin_config.dedupeOperationSuffix,
473+
omit_operation_suffix: plugin_config.omitOperationSuffix,
417474
});
418475

419476
program.apply(&mut visit_mut_pass(visitor))

0 commit comments

Comments
 (0)