Skip to content

Commit

Permalink
NEW RULE: avoid-typename-prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
dotansimha committed May 18, 2021
1 parent 61251e7 commit 63dc00a
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-tables-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-eslint/eslint-plugin': minor
---

NEW RULE: avoid-typename-prefix
81 changes: 81 additions & 0 deletions packages/plugin/src/rules/avoid-typename-prefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { FieldDefinitionNode } from 'graphql';
import { GraphQLESTreeNode } from '../estree-parser';
import { GraphQLESLintRule, GraphQLESlintRuleContext } from '../types';

const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';

function checkNode(
context: GraphQLESlintRuleContext<any>,
typeName: string,
fields: GraphQLESTreeNode<FieldDefinitionNode>[]
) {
const lowerTypeName = (typeName || '').toLowerCase();

for (const field of fields) {
const fieldName = field.name.value || '';

if (fieldName && lowerTypeName && fieldName.toLowerCase().startsWith(lowerTypeName)) {
context.report({
node: field.name,
data: {
fieldName,
typeName,
},
messageId: AVOID_TYPENAME_PREFIX,
});
}
}
}

const rule: GraphQLESLintRule = {
meta: {
type: 'suggestion',
docs: {
category: 'Best Practices',
description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
recommended: true,
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
requiresSiblings: false,
requiresSchema: false,
examples: [
{
title: 'Incorrect',
code: /* GraphQL */ `
type User {
userId: ID!
}
`,
},
{
title: 'Correct',
code: /* GraphQL */ `
type User {
id: ID!
}
`,
},
],
},
messages: {
[AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
},
},
create(context) {
return {
ObjectTypeDefinition(node) {
checkNode(context, node.name.value, node.fields);
},
ObjectTypeExtension(node) {
checkNode(context, node.name.value, node.fields);
},
InterfaceTypeDefinition(node) {
checkNode(context, node.name.value, node.fields);
},
InterfaceTypeExtension(node) {
checkNode(context, node.name.value, node.fields);
},
};
},
};

export default rule;
2 changes: 2 additions & 0 deletions packages/plugin/src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import noHashtagDescription from './no-hashtag-description';
import selectionSetDepth from './selection-set-depth';
import avoidDuplicateFields from './avoid-duplicate-fields';
import strictIdInTypes from './strict-id-in-types';
import avoidTypenamePrefix from './avoid-typename-prefix';
import { GRAPHQL_JS_VALIDATIONS } from './graphql-js-validation';

export const rules = {
'avoid-typename-prefix': avoidTypenamePrefix,
'no-unreachable-types': noUnreachableTypes,
'no-deprecated': noDeprecated,
'unique-fragment-name': uniqueFragmentName,
Expand Down
61 changes: 61 additions & 0 deletions packages/plugin/tests/avoid-typename-prefix.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { GraphQLRuleTester } from '../src/testkit';
import rule from '../src/rules/avoid-typename-prefix';

const ruleTester = new GraphQLRuleTester();

ruleTester.runGraphQLTests('avoid-typename-prefix', rule, {
valid: [
{
code: /* GraphQL */ `
type User {
id: ID!
}
`,
},
{
code: /* GraphQL */ `
interface Node {
id: ID!
}
`,
},
{
code: /* GraphQL */ `
type User {
# eslint-disable-next-line
userId: ID!
}
`,
},
],
invalid: [
{
code: /* GraphQL */ `
type User {
userId: ID!
}
`,
errors: [{ message: 'Field "userId" starts with the name of the parent type "User"' }],
},
{
code: /* GraphQL */ `
type User {
userId: ID!
userName: String!
}
`,
errors: [
{ message: 'Field "userId" starts with the name of the parent type "User"' },
{ message: 'Field "userName" starts with the name of the parent type "User"' },
],
},
{
code: /* GraphQL */ `
interface Node {
nodeId: ID!
}
`,
errors: [{ message: 'Field "nodeId" starts with the name of the parent type "Node"' }],
},
],
});

0 comments on commit 63dc00a

Please sign in to comment.