Skip to content

Commit

Permalink
feat: Support the '...' group for alphabetizing fragment spreads
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavsion committed May 9, 2024
1 parent 0d1d0eb commit b3d0b6f
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-readers-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphql-eslint/eslint-plugin": minor
---

Support the fragment spread group when defining alphabetize rule's groups
194 changes: 194 additions & 0 deletions packages/plugin/__tests__/__snapshots__/alphabetize.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,200 @@ exports[`alphabetize > invalid > should sort selections by group when \`*\` is b
7 | }
`;

exports[`alphabetize > invalid > should sort selections by group when \`...\` is at the end 1`] = `
#### ⌨️ Code

1 | {
2 | ...ChildFragment
3 | zz
4 | updatedAt
5 | createdAt
6 | aa
7 | id
8 | }

#### ⚙️ Options

{
"selections": [
"OperationDefinition"
],
"groups": [
"id",
"*",
"createdAt",
"updatedAt",
"..."
]
}

#### ❌ Error 1/4

2 | ...ChildFragment
> 3 | zz
| ^^ field "zz" should be before fragment spread "ChildFragment"
4 | updatedAt

#### ❌ Error 2/4

4 | updatedAt
> 5 | createdAt
| ^^^^^^^^^ field "createdAt" should be before field "updatedAt"
6 | aa

#### ❌ Error 3/4

5 | createdAt
> 6 | aa
| ^^ field "aa" should be before field "createdAt"
7 | id

#### ❌ Error 4/4

6 | aa
> 7 | id
| ^^ field "id" should be before field "aa"
8 | }

#### 🔧 Autofix output

1 | {
2 | id
3 | aa
4 | zz
5 | createdAt
6 | updatedAt
7 | ...ChildFragment
8 | }
`;

exports[`alphabetize > invalid > should sort selections by group when \`...\` is at the start 1`] = `
#### ⌨️ Code

1 | {
2 | zz
3 | updatedAt
4 | createdAt
5 | aa
6 | id
7 | ...ChildFragment
8 | }

#### ⚙️ Options

{
"selections": [
"OperationDefinition"
],
"groups": [
"...",
"id",
"*",
"createdAt",
"updatedAt"
]
}

#### ❌ Error 1/4

3 | updatedAt
> 4 | createdAt
| ^^^^^^^^^ field "createdAt" should be before field "updatedAt"
5 | aa

#### ❌ Error 2/4

4 | createdAt
> 5 | aa
| ^^ field "aa" should be before field "createdAt"
6 | id

#### ❌ Error 3/4

5 | aa
> 6 | id
| ^^ field "id" should be before field "aa"
7 | ...ChildFragment

#### ❌ Error 4/4

6 | id
> 7 | ...ChildFragment
| ^^^^^^^^^^^^^ fragment spread "ChildFragment" should be before field "id"
8 | }

#### 🔧 Autofix output

1 | {
2 | ...ChildFragment
3 | id
4 | aa
5 | zz
6 | createdAt
7 | updatedAt
8 | }
`;

exports[`alphabetize > invalid > should sort selections by group when \`...\` is between 1`] = `
#### ⌨️ Code

1 | {
2 | zz
3 | ...ChildFragment
4 | updatedAt
5 | createdAt
6 | aa
7 | id
8 | }

#### ⚙️ Options

{
"selections": [
"OperationDefinition"
],
"groups": [
"id",
"*",
"...",
"createdAt",
"updatedAt"
]
}

#### ❌ Error 1/3

4 | updatedAt
> 5 | createdAt
| ^^^^^^^^^ field "createdAt" should be before field "updatedAt"
6 | aa

#### ❌ Error 2/3

5 | createdAt
> 6 | aa
| ^^ field "aa" should be before field "createdAt"
7 | id

#### ❌ Error 3/3

6 | aa
> 7 | id
| ^^ field "id" should be before field "aa"
8 | }

#### 🔧 Autofix output

1 | {
2 | id
3 | aa
4 | zz
5 | ...ChildFragment
6 | createdAt
7 | updatedAt
8 | }
`;

exports[`alphabetize > invalid > should sort when selection is aliased 1`] = `
#### ⌨️ Code

Expand Down
60 changes: 60 additions & 0 deletions packages/plugin/__tests__/alphabetize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,5 +444,65 @@ ruleTester.run<RuleOptions>('alphabetize', rule, {
`,
errors: 3,
},
{
name: 'should sort selections by group when `...` is at the start',
options: [
{
selections: ['OperationDefinition'],
groups: ['...', 'id', '*', 'createdAt', 'updatedAt'],
},
],
code: /* GraphQL */ `
{
zz
updatedAt
createdAt
aa
id
...ChildFragment
}
`,
errors: 4,
},
{
name: 'should sort selections by group when `...` is between',
options: [
{
selections: ['OperationDefinition'],
groups: ['id', '*', '...', 'createdAt', 'updatedAt'],
},
],
code: /* GraphQL */ `
{
zz
...ChildFragment
updatedAt
createdAt
aa
id
}
`,
errors: 3,
},
{
name: 'should sort selections by group when `...` is at the end',
options: [
{
selections: ['OperationDefinition'],
groups: ['id', '*', 'createdAt', 'updatedAt', '...'],
},
],
code: /* GraphQL */ `
{
...ChildFragment
zz
updatedAt
createdAt
aa
id
}
`,
errors: 4,
},
],
});
2 changes: 1 addition & 1 deletion packages/plugin/src/configs/operations-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export = {
selections: ['OperationDefinition', 'FragmentDefinition'],
variables: true,
arguments: ['Field', 'Directive'],
groups: ['id', '*', 'createdAt', 'updatedAt'],
groups: ['id', '*', 'createdAt', 'updatedAt', '...'],
},
],
'@graphql-eslint/lone-executable-definition': 'error',
Expand Down
31 changes: 27 additions & 4 deletions packages/plugin/src/rules/alphabetize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const schema = {
...ARRAY_DEFAULT_OPTIONS,
minItems: 2,
description:
"Custom order group. Example: `['id', '*', 'createdAt', 'updatedAt']` where `*` says for everything else.",
"Custom order group. Example: `['id', '*', 'createdAt', 'updatedAt', '...']` where `...` stands for fragment spreads, and `*` stands for for everything else.",
},
},
},
Expand Down Expand Up @@ -203,7 +203,7 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
selections: selectionsEnum,
variables: true,
arguments: [Kind.FIELD, Kind.DIRECTIVE],
groups: ['id', '*', 'createdAt', 'updatedAt'],
groups: ['id', '*', 'createdAt', 'updatedAt', '...'],
},
],
},
Expand Down Expand Up @@ -283,10 +283,33 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
if (!groups.includes('*')) {
throw new Error('`groups` option should contain `*` string.');
}

// Try an exact match
let indexForPrev = groups.indexOf(prevName);
if (indexForPrev === -1) indexForPrev = groups.indexOf('*');

// Check for the fragment spread group
if (indexForPrev === -1 && prevNode.kind === Kind.FRAGMENT_SPREAD) {
indexForPrev = groups.indexOf('...');
}

// Check for the catch-all group
if (indexForPrev === -1) {
indexForPrev = groups.indexOf('*');
}

// Try an exact match
let indexForCurr = groups.indexOf(currName);
if (indexForCurr === -1) indexForCurr = groups.indexOf('*');

// Check for the fragment spread group
if (indexForCurr === -1 && currNode.kind === Kind.FRAGMENT_SPREAD) {
indexForCurr = groups.indexOf('...');
}

// Check for the catch-all group
if (indexForCurr === -1) {
indexForCurr = groups.indexOf('*');
}

shouldSortByGroup = indexForPrev - indexForCurr > 0;
if (indexForPrev < indexForCurr) {
continue;
Expand Down
Loading

0 comments on commit b3d0b6f

Please sign in to comment.