diff --git a/README.md b/README.md index 333a71b..18c26d9 100644 --- a/README.md +++ b/README.md @@ -52,14 +52,42 @@ The following rules support suppression within graphql tags: Supported rules can be suppressed by adding `# eslint-disable-next-line relay/name-of-rule` to the preceding line: ```js -graphql`fragment foo on Page { - # eslint-disable-next-line relay/must-colocate-fragment-spreads - ...unused1 -}` +graphql` + fragment foo on Page { + # eslint-disable-next-line relay/must-colocate-fragment-spreads + ...unused1 + } +`; ``` Note that only the `eslint-disable-next-line` form of suppression works. `eslint-disable-line` doesn't currently work until graphql-js provides support for [parsing Comment nodes](https://github.com/graphql/graphql-js/issues/2241) in their AST. +## Ignoring fields with `unused-fields` + +Sometimes you might have fields that are accessed by library code (outside the current file), for example: + +```js +import getNodes from 'some/lib'; + +graphql` + fragment foo on Page { + edges { # not directly accessed by this file + node { # not directly accessed by this file + name + } + } + } +`; + +getNodes(foo).map(node => node.name); +``` + +You can ignore the `unused-fields` rule for these fields by providing the `ignoredFields` option. + +```js + 'relay/unused-fields': ['warn', {ignoredFields: ['edges', 'node']}], +``` + ## Contribute We actively welcome pull requests, learn how to [contribute](./CONTRIBUTING.md). diff --git a/src/rule-unused-fields.js b/src/rule-unused-fields.js index dfde701..09a19a6 100644 --- a/src/rule-unused-fields.js +++ b/src/rule-unused-fields.js @@ -92,7 +92,17 @@ function isPageInfoField(field) { } } +function getOptions(context) { + return (context.options || [])[0] || {}; +} + +function isIgnoredField(field, {ignoredFields = []}) { + return ignoredFields.includes(field); +} + function rule(context) { + const options = getOptions(context); + let currentMethod = []; let foundMemberAccesses = {}; let templateLiterals = []; @@ -147,6 +157,7 @@ function rule(context) { if ( !foundMemberAccesses[field] && !isPageInfoField(field) && + !isIgnoredField(field, options) && // Do not warn for unused __typename which can be a workaround // when only interested in existence of an object. field !== '__typename' @@ -205,4 +216,20 @@ function rule(context) { }; } -module.exports = rule; +module.exports = module.exports = { + meta: { + schema: [ + { + type: 'object', + properties: { + ignoredFields: { + type: 'array', + items: {type: 'string'} + } + }, + additionalProperties: false + } + ] + }, + create: rule +}; diff --git a/test/unused-fields.js b/test/unused-fields.js index c50d23a..8ff484b 100644 --- a/test/unused-fields.js +++ b/test/unused-fields.js @@ -89,7 +89,16 @@ ruleTester.run('unused-fields', rules['unused-fields'], { # eslint-disable-next-line relay/unused-fields name }\`; - ` + `, + { + code: ` + graphql\`fragment foo on Page { + edges { node { name } } + }\`; + getCollectionNodes(foo)[0]?.name; + `, + options: [{ignoredFields: ['node', 'edges']}] + } ], invalid: [ { @@ -163,6 +172,15 @@ ruleTester.run('unused-fields', rules['unused-fields'], { line: 4 } ] + }, + { + code: ` + graphql\`fragment foo on Page { + edges { node { name } } + }\`; + getCollectionNodes(foo)[0]?.name; + `, + errors: [unusedFieldsWarning('edges'), unusedFieldsWarning('node')] } ] });