Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global fragment registry #1209

Merged
merged 41 commits into from Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
519e36a
wip: Global fragment registry
Quramy Mar 5, 2024
518f2af
refactor: Modify FragmentRegistry's methods
Quramy Mar 5, 2024
6a4b5ce
fix: DocumentRegistry event listener
Quramy Mar 5, 2024
3eea17d
fix: Don't re-create DocumentRegistry instance
Quramy Mar 6, 2024
22313c7
feat: Validate command with external fragments
Quramy Mar 6, 2024
f1012a9
fix: Fix logic to extract global fragments
Quramy Mar 6, 2024
7fae66c
test: Add analyzer spec for template expression
Quramy Mar 6, 2024
4739413
refactor: Change extractor result type
Quramy Mar 6, 2024
3a736fc
refactor: Add throwErrorIfOutOfRange option to position converter
Quramy Mar 7, 2024
6535b97
fix: Don't include errors in external fragments
Quramy Mar 7, 2024
044f24a
test: Modify position converter spec
Quramy Mar 7, 2024
b70becf
refactor: Move getExternalFragments to FragmentRegistry
Quramy Mar 11, 2024
8651911
chore: Add LRU Cache class
Quramy Mar 11, 2024
0621900
refactor: Cache for getExternalFragments
Quramy Mar 11, 2024
3d09a3f
test: Add e2e specs to cover onUpdate
Quramy Mar 11, 2024
ceaf364
chore: Set enabledGlobalFragments to fixture tsconfig
Quramy Mar 11, 2024
e4e0df9
refactor: FragmentRegistry caching
Quramy Mar 11, 2024
7889934
refactor: Extract DefinitionFileStore
Quramy Mar 12, 2024
485e4ed
fix: Stop to emit unneeded disappeare record of DefinitionFileStore
Quramy Mar 12, 2024
d1628f6
fix: Don't push multiple items whose text are same to valuesNotChange…
Quramy Mar 12, 2024
371955c
chore: Remove unneeded logger calling
Quramy Mar 13, 2024
877fbba
chore: Remove unused optional arguments
Quramy Mar 13, 2024
68ab48e
feat: Validate command using external fragments
Quramy Mar 13, 2024
3f6502b
feat: Make typegen command use global fragments
Quramy Mar 13, 2024
63565a0
feat: Make report command work with global fragments
Quramy Mar 13, 2024
f8923d7
test: Refactor analyezr specs
Quramy Mar 13, 2024
ecbaa14
chore: Update example
Quramy Mar 13, 2024
3fac3e6
test: Add registerDocumentChangeEvent test
Quramy Mar 13, 2024
7fbbbd4
feat: Add exclude option
Quramy Mar 13, 2024
8ded67c
docs: Write about excluded and enabledGlobalFragments options
Quramy Mar 13, 2024
458d539
chore: Modify FragmentRegistry method name
Quramy Mar 13, 2024
5f56e6d
refactor: DocumentRegisty handlers
Quramy Mar 13, 2024
cc04243
fix: Fix calc position for template expression
Quramy Mar 14, 2024
640256a
feat: Report duplicated fragment error from lang-service
Quramy Mar 14, 2024
9d27fe8
chore: Modify analysis context method type
Quramy Mar 14, 2024
8d89a35
feat: Report duplicated fragment error from CLI
Quramy Mar 14, 2024
80b6995
refactor: Modify fragment registry signature
Quramy Mar 14, 2024
8f00c1f
chore: Modify cache size
Quramy Mar 14, 2024
afe14af
fix: Don't report dependent fragment error
Quramy Mar 14, 2024
eb26ed6
fix: Don't display excluded files to verbose log
Quramy Mar 14, 2024
cc282de
chore: Turn off global fragments for react-apollo example
Quramy Mar 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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"."`;