Skip to content

Commit 263570e

Browse files
authored
Fix double imports (#6548)
* Deduplicate generated imports * Generate changeset * Add comments; remove unnecessary imports
1 parent 35199de commit 263570e

File tree

5 files changed

+112
-1
lines changed

5 files changed

+112
-1
lines changed

.changeset/shiny-eels-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
---
4+
5+
Don't generate duplicate imports for the same identifier

packages/plugins/other/visitor-plugin-common/src/client-side-base-visitor.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,35 @@ export class ClientSideBaseVisitor<
502502
documentMode === DocumentMode.string ||
503503
documentMode === DocumentMode.documentNodeImportFragments
504504
) {
505-
fragmentImports.forEach(fragmentImport => {
505+
// keep track of what imports we've already generated so we don't try
506+
// to import the same identifier twice
507+
const alreadyImported = new Map<string, Set<string>>();
508+
509+
const deduplicatedImports = fragmentImports
510+
.map(fragmentImport => {
511+
const { path, identifiers } = fragmentImport.importSource;
512+
if (!alreadyImported.has(path)) {
513+
alreadyImported.set(path, new Set<string>());
514+
}
515+
516+
const alreadyImportedForPath = alreadyImported.get(path);
517+
const newIdentifiers = identifiers.filter(identifier => !alreadyImportedForPath.has(identifier.name));
518+
newIdentifiers.forEach(newIdentifier => alreadyImportedForPath.add(newIdentifier.name));
519+
520+
// filter the set of identifiers in this fragment import to only
521+
// the ones we haven't already imported from this path
522+
return {
523+
...fragmentImport,
524+
importSource: {
525+
...fragmentImport.importSource,
526+
identifiers: newIdentifiers,
527+
},
528+
};
529+
})
530+
// remove any imports that now have no identifiers in them
531+
.filter(fragmentImport => fragmentImport.importSource.identifiers.length > 0);
532+
533+
deduplicatedImports.forEach(fragmentImport => {
506534
if (fragmentImport.outputPath !== fragmentImport.importSource.path) {
507535
this._imports.add(generateFragmentImportStatement(fragmentImport, 'document'));
508536
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import gql from 'graphql-tag';
2+
3+
export const usernameFragment = gql`
4+
fragment usernameFragment on User {
5+
username
6+
}
7+
`;
8+
9+
export const userFields = gql`
10+
fragment userFields on User {
11+
...usernameFragment
12+
email
13+
}
14+
15+
${usernameFragment}
16+
`;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import gql from 'graphql-tag';
2+
import { userFields, usernameFragment } from './issue-6546-fragments';
3+
4+
export const limitedUserFieldsQuery = gql`
5+
query user {
6+
user(id: "1") {
7+
...usernameFragment
8+
}
9+
}
10+
11+
${usernameFragment}
12+
`;
13+
14+
export const userAndFriendQuery = gql`
15+
query allUserFields {
16+
user(id: "1") {
17+
...userFields
18+
}
19+
20+
friend: user(id: "2") {
21+
...usernameFragment
22+
}
23+
}
24+
25+
${userFields}
26+
${usernameFragment}
27+
`;

packages/presets/near-operation-file/tests/near-operation-file.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,41 @@ export type CQuery = { __typename?: 'Query', a?: Types.Maybe<string> };
447447

448448
expect(result[0].content).not.toMatch(`import { UserFieldsFragmentDoc }`);
449449
});
450+
451+
it('#6546 - duplicate fragment imports', async () => {
452+
const result = await executeCodegen({
453+
schema: [
454+
/* GraphQL */ `
455+
type Query {
456+
user(id: String!): User!
457+
}
458+
459+
type User {
460+
id: String!
461+
email: String
462+
username: String
463+
}
464+
`,
465+
],
466+
documents: [
467+
path.join(__dirname, 'fixtures/issue-6546-queries.ts'),
468+
path.join(__dirname, 'fixtures/issue-6546-fragments.ts'),
469+
],
470+
generates: {
471+
'out1.ts': {
472+
preset: preset,
473+
presetConfig: {
474+
baseTypesPath: 'types.ts',
475+
},
476+
plugins: ['typescript-operations', 'typescript-react-apollo'],
477+
},
478+
},
479+
});
480+
481+
const queriesContent = result.find(generatedDoc => generatedDoc.filename.match(/issue-6546-queries/)).content;
482+
const imports = queriesContent.match(/import.*UsernameFragmentFragmentDoc/g);
483+
expect(imports).toHaveLength(1);
484+
});
450485
});
451486

452487
it('Should build the correct operation files paths', async () => {

0 commit comments

Comments
 (0)