diff --git a/README.md b/README.md index a85a642..5171286 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,50 @@ module.exports = { ] } ``` + +### Capitalization of a first letter of Named Operation rule + +The Capitalize Named Operation rule validates that all existing operation names are capitalized. + +**Pass** +``` +query FetchUsername { + viewer { + name + } +} +``` + +**Fail** +``` +query fetchUsername { + viewer { + name + } +} +``` + +The rule is defined as `graphql/capitalized-named-operations`. + +```js +// In a file called .eslintrc.js +module.exports = { + parser: "babel-eslint", + rules: { + "graphql/template-strings": ['error', { + env: 'apollo', + schemaJson: require('./schema.json'), + }], + "graphql/capitalized-named-operations": ['warn', { + schemaJson: require('./schema.json'), + }], + }, + plugins: [ + 'graphql' + ] +} +``` + ### Required Fields Validation Rule The Required Fields rule validates that any specified required field is part of the query, but only if that field is available in schema. This is useful to ensure that query results are cached properly in the client. diff --git a/src/customGraphQLValidationRules.js b/src/customGraphQLValidationRules.js index 0600bba..e9c8402 100644 --- a/src/customGraphQLValidationRules.js +++ b/src/customGraphQLValidationRules.js @@ -1,5 +1,17 @@ import { GraphQLError, getNamedType } from "graphql"; +export function OperationNamesMustBeCapitalized(context) { + return { + OperationDefinition(node) { + if (node.name && node.name.value && (node.name.value[0] == node.name.value[0].toLowerCase())) { + context.reportError( + new GraphQLError("All operations must be capitalized", [node]) + ); + } + } + }; +} + export function OperationsMustHaveNames(context) { return { OperationDefinition(node) { diff --git a/src/index.js b/src/index.js index 144db0b..08af35f 100644 --- a/src/index.js +++ b/src/index.js @@ -178,6 +178,29 @@ export const rules = { ); } }, + "capitalized-named-operations": { + meta: { + schema: { + type: "array", + items: { + additionalProperties: false, + properties: { ...defaultRuleProperties }, + ...schemaPropsExclusiveness + } + } + }, + create: context => { + return createRule(context, optionGroup => + parseOptions( + { + validators: ["OperationNamesMustBeCapitalized"], + ...optionGroup + }, + context + ) + ); + } + }, "required-fields": { meta: { schema: { diff --git a/test/validationRules/capitalized-named-operations.js b/test/validationRules/capitalized-named-operations.js new file mode 100644 index 0000000..b8e86e5 --- /dev/null +++ b/test/validationRules/capitalized-named-operations.js @@ -0,0 +1,41 @@ +import { rules } from "../../src"; +import schemaJson from "../schema.json"; + +import { ruleTester, parserOptions } from "../helpers"; + +const capitalizedNamedOperationsValidatorCases = { + pass: [ + "const x = gql`query Test { sum(a: 1, b: 2) }`", + "const x = gql`query { sum(a: 1, b: 2) }`", + ], + fail: [{ + code: "const x = gql`query test { sum(a: 1, b: 2) }`", + errors: [ + { + message: "All operations must be capitalized", + type: "TaggedTemplateExpression" + } + ] + }] +}; + +// Validate the named-operations rule +const options = [ + { + schemaJson, + tagName: "gql" + } +]; +ruleTester.run("testing capitalized-named-operations rule", rules["capitalized-named-operations"], { + valid: capitalizedNamedOperationsValidatorCases.pass.map(code => ({ + options, + parserOptions, + code + })), + invalid: capitalizedNamedOperationsValidatorCases.fail.map(({ code, errors }) => ({ + options, + parserOptions, + code, + errors + })) +}); diff --git a/test/validationRules/index.js b/test/validationRules/index.js index d7efac4..195c114 100644 --- a/test/validationRules/index.js +++ b/test/validationRules/index.js @@ -1,4 +1,5 @@ import './named-operations'; +import './capitalized-named-operations'; import './required-fields'; import './no-deprecated-fields'; import './capitalized-type-name';