Skip to content

Commit

Permalink
Merge pull request #1233 from Quramy/transform_with_global_fragments
Browse files Browse the repository at this point in the history
fix: Transformer should uses getFragmentDependenciesForAST
  • Loading branch information
Quramy committed Mar 15, 2024
2 parents 7f8a49c + e2782ef commit c169298
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 8 deletions.
25 changes: 24 additions & 1 deletion e2e/webpack-specs/transform.js
Expand Up @@ -4,7 +4,7 @@ const { execSync } = require('child_process');
const webpack = require('webpack');
const { print } = require('graphql/language');

async function run() {
async function specWithoutGlobalFragments() {
const config = require('../../project-fixtures/transformation-prj/webpack.config.js');
const compiler = webpack({ ...config, mode: 'production' });
const stats = await new Promise((res, rej) => {
Expand All @@ -17,6 +17,29 @@ async function run() {
const distFilePath = path.resolve(stats.toJson().outputPath, 'main.js');
const result = execSync(`node ${distFilePath}`);
assert.equal(typeof print(JSON.parse(result.toString())), 'string');
assert(print(JSON.parse(result.toString())).indexOf('MyQuery') !== -1);
assert(print(JSON.parse(result.toString())).indexOf('fragment FragmentLeaf') !== -1);
}

async function specWithGlobalFragments() {
const config = require('../../project-fixtures/transformation-global-fag-prj/webpack.config.js');
const compiler = webpack({ ...config, mode: 'production' });
const stats = await new Promise((res, rej) => {
compiler.run((err, stats) => {
if (err) return rej(err);
return res(stats);
});
});
assert(!stats.hasErrors());
const distFilePath = path.resolve(stats.toJson().outputPath, 'main.js');
const result = execSync(`node ${distFilePath}`);
assert(print(JSON.parse(result.toString())).indexOf('MyQuery') !== -1);
assert(print(JSON.parse(result.toString())).indexOf('fragment FragmentLeaf') !== -1);
}

async function run() {
await specWithoutGlobalFragments();
await specWithGlobalFragments();
}

module.exports = run;
1 change: 1 addition & 0 deletions project-fixtures/transformation-global-fag-prj/.gitignore
@@ -0,0 +1 @@
dist/
@@ -0,0 +1,7 @@
import gql from './tag';

const fragmentLeaf = gql`
fragment FragmentLeaf on Query {
hello
}
`;
@@ -0,0 +1,8 @@
import gql from './tag';

const fragmentNode = gql`
fragment FragmentNode on Query {
bye
...FragmentLeaf
}
`;
11 changes: 11 additions & 0 deletions project-fixtures/transformation-global-fag-prj/query.ts
@@ -0,0 +1,11 @@
import gql from './tag';
import { fragmentNode } from './fragment-node';

const query = gql`
query MyQuery {
__typename
...FragmentNode
}
`;

console.log(JSON.stringify(query, null, 2));
4 changes: 4 additions & 0 deletions project-fixtures/transformation-global-fag-prj/schema.graphql
@@ -0,0 +1,4 @@
type Query {
hello: String!
bye: String!
}
4 changes: 4 additions & 0 deletions project-fixtures/transformation-global-fag-prj/tag.ts
@@ -0,0 +1,4 @@
export default function gql(literals: TemplateStringsArray, ...args: unknown[]) {
// dummy impl
return '';
}
16 changes: 16 additions & 0 deletions project-fixtures/transformation-global-fag-prj/tsconfig.json
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"plugins": [
{
"name": "ts-graphql-plugin",
"tag": "gql",
"schema": "schema.graphql",
"enabledGlobalFragments": true
}
]
}
}
35 changes: 35 additions & 0 deletions project-fixtures/transformation-global-fag-prj/webpack.config.js
@@ -0,0 +1,35 @@
const path = require('path');

const TsGraphQLPlugin = require('../../webpack');

const tsgqlPlugin = new TsGraphQLPlugin({ tsconfigPath: __dirname });

module.exports = {
resolve: {
extensions: ['.ts', '.js'],
},
entry: {
main: path.resolve(__dirname, 'query.ts'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader',
options: {
transpileOnly: true,
getCustomTransformers: () => ({
before: [tsgqlPlugin.getTransformer()],
}),
},
},
],
},
plugins: [tsgqlPlugin],
devtool: false,
};
27 changes: 20 additions & 7 deletions src/transformer/transformer-host.ts
@@ -1,11 +1,14 @@
import ts from 'typescript';
import { DocumentNode } from 'graphql';
import { Kind, type DocumentNode, FragmentDefinitionNode } from 'graphql';
import { getFragmentDependenciesForAST } from 'graphql-language-service';
import { Analyzer, AnalyzerFactory, ExtractFileResult } from '../analyzer';
import { getTransformer, DocumentTransformer } from './transformer';
import { parseTagConfig } from '../ts-ast-util';
import { cloneFragmentMap, getFragmentNamesInDocument } from '../gql-ast-util';

class DocumentNodeRegistory {
protected readonly _map = new Map<string, Map<number, DocumentNode>>();
private _externalFragmentMap = new Map<string, FragmentDefinitionNode>();

constructor() {}

Expand All @@ -16,10 +19,20 @@ class DocumentNodeRegistory {
getDocumentNode(templateNode: ts.TemplateExpression | ts.NoSubstitutionTemplateLiteral) {
const positionMap = this._map.get(templateNode.getSourceFile().fileName);
if (!positionMap) return;
return positionMap.get(templateNode.getStart());
const docNode = positionMap.get(templateNode.getStart());
if (!docNode) return;
const externalFragments = getFragmentDependenciesForAST(
docNode,
cloneFragmentMap(this._externalFragmentMap, getFragmentNamesInDocument(docNode)),
);
return {
kind: Kind.DOCUMENT,
definitions: [...docNode.definitions, ...externalFragments],
} satisfies DocumentNode;
}

update(extractedResults: ExtractFileResult[]) {
update(extractedResults: ExtractFileResult[], externalFragmentMap: Map<string, FragmentDefinitionNode>) {
this._externalFragmentMap = externalFragmentMap;
extractedResults.forEach(result => {
if (!result.documentNode) return;
let positionMap = this._map.get(result.fileName);
Expand Down Expand Up @@ -55,8 +68,8 @@ export class TransformerHost {
}

loadProject() {
const [, { fileEntries }] = this._analyzer.extract();
this._documentNodeRegistory.update(fileEntries);
const [, { fileEntries, globalFragments }] = this._analyzer.extract();
this._documentNodeRegistory.update(fileEntries, globalFragments.definitionMap);
}

updateFiles(fileNameList: string[]) {
Expand All @@ -71,10 +84,10 @@ export class TransformerHost {
// other-opened-file.ts: declare `query { ...X }` importing fragment from changed-file.ts
//
// In the above case, the transformed output of other-opened-file.ts should have GraphQL docuemnt corresponding to `fragment X on Query { fieldA } query { ...X }`
const [, { fileEntries }] = this._analyzer.extract([
const [, { fileEntries, globalFragments }] = this._analyzer.extract([
...new Set([...fileNameList, ...this._documentNodeRegistory.getFiles()]),
]);
this._documentNodeRegistory.update(fileEntries);
this._documentNodeRegistory.update(fileEntries, globalFragments.definitionMap);
}

getTransformer({
Expand Down

0 comments on commit c169298

Please sign in to comment.