Skip to content

Commit

Permalink
Fix fragment imports for near-operation-file with graphQLTag (#9301)
Browse files Browse the repository at this point in the history
  • Loading branch information
wassim-k authored Apr 13, 2023
1 parent 5890ccc commit 386cf90
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-bottles-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-codegen/visitor-plugin-common': patch
---

Fix fragment imports for near-operation-file with graphQLTag
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
import gqlTag from 'graphql-tag';
import { BaseVisitor, ParsedConfig, RawConfig } from './base-visitor.js';
import { LoadedFragment, ParsedImport } from './types.js';
import { buildScalarsFromConfig, getConfigValue } from './utils.js';
import { buildScalarsFromConfig, unique, flatten, getConfigValue, groupBy } from './utils.js';
import { FragmentImport, ImportDeclaration, generateFragmentImportStatement } from './imports.js';

gqlTag.enableExperimentalFragmentVariables();

Expand Down Expand Up @@ -568,7 +569,7 @@ export class ClientSideBaseVisitor<
return path;
}

public getImports(): string[] {
public getImports(options: { excludeFragments?: boolean } = {}): string[] {
for (const i of this._additionalImports || []) {
this._imports.add(i);
}
Expand Down Expand Up @@ -621,6 +622,31 @@ export class ClientSideBaseVisitor<
break;
}

const excludeFragments =
options.excludeFragments || this.config.globalNamespace || this.config.documentMode !== DocumentMode.graphQLTag;

if (!excludeFragments) {
const deduplicatedImports = Object.values(groupBy(this.config.fragmentImports, fi => fi.importSource.path))
.map(
(fragmentImports): ImportDeclaration<FragmentImport> => ({
...fragmentImports[0],
importSource: {
...fragmentImports[0].importSource,
identifiers: unique(
flatten(fragmentImports.map(fi => fi.importSource.identifiers)),
identifier => identifier.name
),
},
emitLegacyCommonJSImports: this.config.emitLegacyCommonJSImports,
})
)
.filter(fragmentImport => fragmentImport.outputPath !== fragmentImport.importSource.path);

for (const fragmentImport of deduplicatedImports) {
this._imports.add(generateFragmentImportStatement(fragmentImport, 'document'));
}
}

return Array.from(this._imports);
}

Expand Down
16 changes: 16 additions & 0 deletions packages/plugins/other/visitor-plugin-common/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,3 +524,19 @@ export function isOneOfInputObjectType(

return isOneOfType;
}

export function groupBy<T>(array: Array<T>, key: (item: T) => string | number): { [key: string]: Array<T> } {
return array.reduce<{ [key: string]: Array<T> }>((acc, item) => {
const group = (acc[key(item)] ??= []);
group.push(item);
return acc;
}, {});
}

export function flatten<T>(array: Array<Array<T>>): Array<T> {
return ([] as Array<T>).concat(...array);
}

export function unique<T>(array: Array<T>, key: (item: T) => string | number = item => item.toString()): Array<T> {
return Object.values(array.reduce((acc, item) => ({ [key(item)]: item, ...acc }), {}));
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,75 @@ describe('getImports', () => {
}
});
});

describe('when documentMode "graphQLTag"', () => {
const schema = buildSchema(/* GraphQL */ `
type Query {
a: A
}
type A {
foo: String
bar: String
}
`);

it('imports FragmentDocs', () => {
const fileName = 'fooBarQuery';
const importPath = `src/queries/${fileName}`;

const document = parse(
`query fooBarQuery {
a {
...fields
}
}
fragment fields on A {
foo
bar
}
`
);

const visitor = new ClientSideBaseVisitor(
schema,
(document.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION) as FragmentDefinitionNode[]).map(
fragmentDef => ({
node: fragmentDef,
name: fragmentDef.name.value,
onType: fragmentDef.typeCondition.name.value,
isExternal: false,
})
),
{
emitLegacyCommonJSImports: true,
importDocumentNodeExternallyFrom: 'near-operation-file',
documentMode: DocumentMode.graphQLTag,
fragmentImports: [
{
baseDir: '/',
baseOutputDir: '',
outputPath: '',
importSource: {
path: '~types',
identifiers: [
{ name: 'FieldsFragmentDoc', kind: 'document' },
{ name: 'FieldsFragment', kind: 'type' },
],
},
emitLegacyCommonJSImports: true,
typesImport: false,
},
],
},
{},
[{ document, location: importPath }]
);

visitor.OperationDefinition(document.definitions[0] as OperationDefinitionNode);

const imports = visitor.getImports();
expect(imports.some(i => i.includes('FragmentDoc'))).toBeTruthy();
});
});
});
53 changes: 53 additions & 0 deletions packages/plugins/other/visitor-plugin-common/tests/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, it } from '@jest/globals';
import { flatten, groupBy, unique } from '../src/utils';

describe('utils', () => {
describe('flatten', () => {
it('should flatten a nested array', () => {
const array = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const actual = flatten(array);
const expected = [1, 2, 3, 4, 5, 6, 7, 8, 9];
expect(actual).toEqual(expected);
});
});

describe('groupBy', () => {
it('should group by a property', () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const actual = groupBy(array, i => i % 2);
const expected = { 0: [2, 4, 6, 8, 10], 1: [1, 3, 5, 7, 9] };
expect(actual).toEqual(expected);
});
});

describe('unique', () => {
it('should return unique items when no key selector is passed', () => {
const array = [1, 2, 3, 1, 2, 4];
const actual = unique(array);
const expected = [1, 2, 3, 4];
expect(actual).toEqual(expected);
});

it('should return unique items based on key selector', () => {
const array = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice #2' },
{ id: 3, name: 'Charlie' },
];

const actual = unique(array, item => item.id);
const expected = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];

expect(actual).toEqual(expected);
});
});
});

0 comments on commit 386cf90

Please sign in to comment.