Skip to content

Commit

Permalink
Stop importing fragments that are now unused (#9194)
Browse files Browse the repository at this point in the history
* Stop importing fragments that are now unused

* fix: remove unused argument and import

* chore: add changeset

* feat: add tests for inlined fragments

---------

Co-authored-by: Adrien HARNAY <adrien@harnay.me>
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
  • Loading branch information
3 people authored Mar 29, 2023
1 parent e347e36 commit acb647e
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changeset/sharp-clouds-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-codegen/visitor-plugin-common': patch
---

Don't emit import statements for unused fragments
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from 'graphql';
import gqlTag from 'graphql-tag';
import { BaseVisitor, ParsedConfig, RawConfig } from './base-visitor.js';
import { generateFragmentImportStatement } from './imports.js';
import { LoadedFragment, ParsedImport } from './types.js';
import { buildScalarsFromConfig, getConfigValue } from './utils.js';

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

public getImports(options: { excludeFragments?: boolean } = {}): string[] {
public getImports(): string[] {
(this._additionalImports || []).forEach(i => this._imports.add(i));

switch (this.config.documentMode) {
Expand Down Expand Up @@ -620,50 +619,6 @@ export class ClientSideBaseVisitor<
break;
}

if (!options.excludeFragments && !this.config.globalNamespace) {
const { documentMode, fragmentImports } = this.config;
if (
documentMode === DocumentMode.graphQLTag ||
documentMode === DocumentMode.string ||
documentMode === DocumentMode.documentNodeImportFragments
) {
// keep track of what imports we've already generated so we don't try
// to import the same identifier twice
const alreadyImported = new Map<string, Set<string>>();

const deduplicatedImports = fragmentImports
.map(fragmentImport => {
const { path, identifiers } = fragmentImport.importSource;
if (!alreadyImported.has(path)) {
alreadyImported.set(path, new Set<string>());
}

const alreadyImportedForPath = alreadyImported.get(path);
const newIdentifiers = identifiers.filter(identifier => !alreadyImportedForPath.has(identifier.name));
newIdentifiers.forEach(newIdentifier => alreadyImportedForPath.add(newIdentifier.name));

// filter the set of identifiers in this fragment import to only
// the ones we haven't already imported from this path
return {
...fragmentImport,
importSource: {
...fragmentImport.importSource,
identifiers: newIdentifiers,
},
emitLegacyCommonJSImports: this.config.emitLegacyCommonJSImports,
};
})
// remove any imports that now have no identifiers in them
.filter(fragmentImport => fragmentImport.importSource.identifiers.length > 0);

deduplicatedImports.forEach(fragmentImport => {
if (fragmentImport.outputPath !== fragmentImport.importSource.path) {
this._imports.add(generateFragmentImportStatement(fragmentImport, 'document'));
}
});
}
}

return Array.from(this._imports);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { buildSchema, OperationDefinitionNode, parse } from 'graphql';
import { buildSchema, FragmentDefinitionNode, OperationDefinitionNode, parse, Kind } from 'graphql';
import { ClientSideBaseVisitor, DocumentMode } from '../src/client-side-base-visitor.js';

describe('getImports', () => {
Expand Down Expand Up @@ -129,4 +129,77 @@ describe('getImports', () => {
});
});
});

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

it('does not import 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.documentNodeImportFragments,
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();
imports.forEach(i => {
expect(i).not.toContain('FragmentDoc');
});
});
});
});

0 comments on commit acb647e

Please sign in to comment.