Skip to content

Commit

Permalink
Merge pull request #1209 from Quramy/fragment_registry
Browse files Browse the repository at this point in the history
Global fragment registry
  • Loading branch information
Quramy committed Mar 14, 2024
2 parents c5544f4 + cc282de commit d9e704c
Show file tree
Hide file tree
Showing 56 changed files with 2,524 additions and 241 deletions.
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -35,6 +35,8 @@ This plugin has the following features:
- [Plugin options](#plugin-options)
- [`schema`](#schema)
- [`tag`](#tag)
- [`exclude`](#exclude)
- [`enabledGlobalFragments`](#enabledglobalfragments)
- [`localSchemaExtensions`](#localschemaextensions)
- [`typegen.addons`](#typegenaddons)
- [`removeDuplicatedFragments`](#removeduplicatedfragments)
Expand Down Expand Up @@ -121,6 +123,7 @@ Pass plugin options to your tsconfig.json to configure this plugin.
/* plugin options */
"schema": "path-or-url-to-your-schema.graphql",
"tag": "gql",
"exclude": ["__generated__"],
...
}
]
Expand Down Expand Up @@ -270,6 +273,22 @@ const str3 = otherTagFn`foooo`; // don't work
It's useful to write multiple kinds template strings(e.g. one is Angular Component template, another is Apollo GraphQL query).
### `exclude`
It's optional. Specify an array of file or directory names when you want to exclude specific TypeScript sources from the plugin's analysis.
It's useful if other code generator copies your GraphQL Template Strings.
> [!NOTE]
> Currently, the `exclude` option only accepts file or directory names. Wildcard characters such as `*` and `**` are not allowed.
### `enabledGlobalFragments`
It's optional and the default value is `false`. If enabled, the plugin automatically searches for and merges the dependent fragments for the target GraphQL operation in the TypeScript project.
> [!IMPORTANT]
> Fragments must be given a unique name if this option is enabled.
### `localSchemaExtensions`
It's optional. If you want to extend server-side schema, derived from `schema` option, you can set path of SDL file of your local extension.
Expand Down
5 changes: 2 additions & 3 deletions e2e/lang-server-specs/diagnostics-syntax.js
Expand Up @@ -7,8 +7,7 @@ function findResponse(responses, eventName) {

const fileContent = `
import gql from 'graphql-tag';
const q = gql\`
\`;
const q = gql\`{\`;
`;

async function run(server) {
Expand All @@ -21,7 +20,7 @@ async function run(server) {
const semanticDiagEvent = findResponse(server.responses, 'semanticDiag');
assert(!!semanticDiagEvent);
assert.strictEqual(semanticDiagEvent.body.diagnostics.length, 1);
assert.strictEqual(semanticDiagEvent.body.diagnostics[0].text, 'Syntax Error: Unexpected <EOF>.');
assert.strictEqual(semanticDiagEvent.body.diagnostics[0].text, 'Syntax Error: Expected Name, found <EOF>.');
});
}

Expand Down
69 changes: 69 additions & 0 deletions e2e/lang-server-specs/diagnostics-with-update.js
@@ -0,0 +1,69 @@
const assert = require('assert');
const path = require('path');
const { mark } = require('fretted-strings');

function findResponse(responses, eventName) {
return responses.find(response => response.event === eventName);
}

async function run(server) {
const fileFragments = path.resolve(__dirname, '../../project-fixtures/simple-prj/fragments.ts');
const fileFragmentsContent = `
import gql from 'graphql-tag';
const f = gql\`fragment MyFragment on Query { hello }\`;
`;

const fileMain = path.resolve(__dirname, '../../project-fixtures/simple-prj/main.ts');
const frets = {};
const fileMainContent = mark(
`
import gql from 'graphql-tag';
const q = gql\`query MyQuery { }\`;
%%% \\ ^ %%%
%%% \\ p %%%
`,
frets,
);

server.send({
command: 'open',
arguments: { file: fileFragments, fileContent: fileFragmentsContent, scriptKindName: 'TS' },
});
server.send({ command: 'open', arguments: { file: fileMain, fileContent: fileMainContent, scriptKindName: 'TS' } });

await server.waitEvent('projectLoadingFinish');

server.send({
command: 'updateOpen',
arguments: {
changedFiles: [
{
fileName: fileMain,
textChanges: [
{
newText: '...MyFragment',
start: {
line: frets.p.line + 1,
offset: frets.p.character + 1,
},
end: {
line: frets.p.line + 1,
offset: frets.p.character + 1,
},
},
],
},
],
},
});
await server.waitResponse('updateOpen');
server.send({ command: 'geterr', arguments: { files: [fileMain], delay: 0 } });
await server.waitEvent('semanticDiag');
return server.close().then(() => {
const semanticDiagEvent = findResponse(server.responses, 'semanticDiag');
assert(!!semanticDiagEvent);
assert.equal(semanticDiagEvent.body.diagnostics.length, 0);
});
}

module.exports = run;
12 changes: 8 additions & 4 deletions project-fixtures/gql-errors-prj/main.ts
Expand Up @@ -13,10 +13,14 @@ const tooComplexExpressionQuery = gql`
${getField()}
}
`
const semanticErrorFragment = gql`
fragment MyFragment on Query {
hoge
}
`;

const semanticErrorQUery = gql`
query {
helo
helloWorld
const duplicatedFragment = gql`
fragment MyFragment on Query {
hoge
}
`;
1 change: 1 addition & 0 deletions project-fixtures/gql-errors-prj/tsconfig.json
Expand Up @@ -7,6 +7,7 @@
"name": "ts-graphql-plugin",
"tag": "gql",
"schema": "schema.graphql",
"enabledGlobalFragments": true,
"localSchemaExtensions": ["local-extension.graphql"]
}
]
Expand Down
12 changes: 6 additions & 6 deletions project-fixtures/react-apollo-prj/GRAPHQL_OPERATIONS.md
Expand Up @@ -4,10 +4,6 @@
### GitHubQuery

```graphql
fragment RepositoryFragment on Repository {
description
}

query GitHubQuery($first: Int!) {
viewer {
repositories(first: $first) {
Expand All @@ -18,9 +14,13 @@ query GitHubQuery($first: Int!) {
}
}
}

fragment RepositoryFragment on Repository {
description
}
```

From [src/index.tsx:11:19](src/index.tsx#L11-L23)
From [src/index.tsx:11:19](src/index.tsx#L11-L22)

## Mutations

Expand All @@ -34,7 +34,7 @@ mutation UpdateMyRepository($repositoryId: ID!) {
}
```

From [src/index.tsx:25:22](src/index.tsx#L25-L31)
From [src/index.tsx:24:22](src/index.tsx#L24-L30)

## Fragments

Expand Down
3 changes: 2 additions & 1 deletion project-fixtures/react-apollo-prj/tsconfig.json
Expand Up @@ -12,8 +12,9 @@
"name": "ts-graphql-plugin",
"schema": "schema.graphql",
"tag": "gql",
"exclude": ["src/__generated__"],
"typegen": {
"addons": ["../../addons/typed-query-document"]
"addons": ["ts-graphql-plugin/addons/typed-query-document"]
}
}
]
Expand Down
Empty file.
1 change: 1 addition & 0 deletions project-fixtures/simple-prj/tsconfig.json
Expand Up @@ -7,6 +7,7 @@
"name": "ts-graphql-plugin",
"tag": "gql",
"schema": "schema.graphql",
"enabledGlobalFragments": true,
"localSchemaExtensions": ["local-extension.graphql"],
"typegen": {
"addons": ["./addon"]
Expand Down
1 change: 1 addition & 0 deletions project-fixtures/typegen-addon-prj/tsconfig.json
Expand Up @@ -9,6 +9,7 @@
"_name": "ts-graphql-plugin",
"tag": "gql",
"schema": "schema.graphql",
"enabledGlobalFragments": true,
"typegen": {
"addons": ["./addon"]
}
Expand Down
59 changes: 56 additions & 3 deletions src/analyzer/__snapshots__/analyzer.test.ts.snap
Expand Up @@ -5,12 +5,42 @@ exports[`Analyzer extractToManifest should extract manifest 1`] = `
[],
{
"documents": [
{
"body": "fragment MyFragment on Query {
hello
}",
"documentEnd": {
"character": 59,
"line": 0,
},
"documentStart": {
"character": 21,
"line": 0,
},
"fileName": "fragment.ts",
"fragmentName": "MyFragment",
"operationName": undefined,
"tag": "gql",
"templateLiteralNodeEnd": {
"character": 60,
"line": 0,
},
"templateLiteralNodeStart": {
"character": 20,
"line": 0,
},
"type": "fragment",
},
{
"body": "query MyQuery {
...MyFragment
}
fragment MyFragment on Query {
hello
}",
"documentEnd": {
"character": 41,
"character": 49,
"line": 0,
},
"documentStart": {
Expand All @@ -22,7 +52,7 @@ exports[`Analyzer extractToManifest should extract manifest 1`] = `
"operationName": "MyQuery",
"tag": "gql",
"templateLiteralNodeEnd": {
"character": 42,
"character": 50,
"line": 0,
},
"templateLiteralNodeStart": {
Expand All @@ -44,6 +74,10 @@ exports[`Analyzer report should create markdown report 1`] = `
\`\`\`graphql
query MyQuery {
...MyFragment
}
fragment MyFragment on Query {
hello
}
\`\`\`
Expand Down Expand Up @@ -75,15 +109,34 @@ Extracted by [ts-graphql-plugin](https://github.com/Quramy/ts-graphql-plugin)"
exports[`Analyzer typegen should create type files 1`] = `
"/* eslint-disable */
/* This is an autogenerated file. Do not edit this file directly! */
export type MyQuery = {
export type MyFragment = {
hello: string;
};
"
`;

exports[`Analyzer typegen should create type files 2`] = `
"/* eslint-disable */
/* This is an autogenerated file. Do not edit this file directly! */
export type MyQuery = MyFragment;
export type MyQueryVariables = {};
export type MyFragment = {
hello: string;
};
"
`;

exports[`Analyzer typegen should report error when no schema 1`] = `"No GraphQL schema. Confirm your ts-graphql-plugin's "schema" configuration at tsconfig.json's compilerOptions.plugins section."`;

exports[`Analyzer validate should report duplicatedFragmentDefinitions error 1`] = `
[
[ErrorWithLocation: All fragments must have an unique name.],
[ErrorWithLocation: All fragments must have an unique name.],
]
`;

exports[`Analyzer validate should report error when no schema 1`] = `"No GraphQL schema. Confirm your ts-graphql-plugin's "schema" configuration at tsconfig.json's compilerOptions.plugins section."`;

exports[`Analyzer validate should report missing external fragment refference 1`] = `"Unknown fragment "F2"."`;

exports[`Analyzer validate should validate project with schema error project 1`] = `"Syntax Error: Unexpected Name "hogehoge"."`;

0 comments on commit d9e704c

Please sign in to comment.