diff --git a/src/rule-unused-fields.js b/src/rule-unused-fields.js index 71b0cc0..83b81c2 100644 --- a/src/rule-unused-fields.js +++ b/src/rule-unused-fields.js @@ -11,11 +11,30 @@ const utils = require('./utils'); const getGraphQLAST = utils.getGraphQLAST; -function getGraphQLFieldNames(graphQLAst) { +const DEFAULT_OPTIONS = { + ignoreFields: [], + leavesOnly: false +}; + +function getOptions(optionValue) { + if (optionValue) { + return { + ignoreFields: optionValue.ignoreFields || DEFAULT_OPTIONS.ignoreFields, + leavesOnly: optionValue.leavesOnly || DEFAULT_OPTIONS.leavesOnly + }; + } + return DEFAULT_OPTIONS; +} + +function getGraphQLFieldNames(graphQLAst, leavesOnly) { const fieldNames = {}; function walkAST(node, ignoreLevel) { - if (node.kind === 'Field' && !ignoreLevel) { + if ( + node.kind === 'Field' && + !ignoreLevel && + (!leavesOnly || node.selectionSet == null) + ) { const nameNode = node.alias || node.name; fieldNames[nameNode.value] = nameNode; } @@ -82,6 +101,8 @@ function isPageInfoField(field) { } function rule(context) { + const options = getOptions(context.options[0]); + let currentMethod = []; let foundMemberAccesses = {}; let templateLiterals = []; @@ -131,14 +152,18 @@ function rule(context) { return; } - const queriedFields = getGraphQLFieldNames(graphQLAst); + const queriedFields = getGraphQLFieldNames( + graphQLAst, + options.leavesOnly + ); for (const field in queriedFields) { if ( !foundMemberAccesses[field] && !isPageInfoField(field) && // Do not warn for unused __typename which can be a workaround // when only interested in existence of an object. - field !== '__typename' + field !== '__typename' && + !options.ignoreFields.includes(field) ) { context.report({ node: templateLiteral, diff --git a/test/unused-fields.js b/test/unused-fields.js index 23207f8..48416af 100644 --- a/test/unused-fields.js +++ b/test/unused-fields.js @@ -83,7 +83,21 @@ ruleTester.run('unused-fields', rules['unused-fields'], { } } \`; - ` + `, + { + code: ` + graphql\`fragment Test on Page { id }\`; + `, + options: [{ignoreFields: ['id']}] + }, + { + code: ` + graphql\`fragment Test on Page { connection { edges { node { id } } } }\`; + const nodes = useConnectionArray(props.connection); + node.id; + `, + options: [{leavesOnly: true}] + } ], invalid: [ { @@ -109,6 +123,26 @@ ruleTester.run('unused-fields', rules['unused-fields'], { `, errors: [unusedFieldsWarning('unused1'), unusedFieldsWarning('unused2')] }, + { + code: ` + graphql\`fragment Test on Page { unused1 { unused2 } }\`; + `, + errors: [unusedFieldsWarning('unused1'), unusedFieldsWarning('unused2')] + }, + { + code: ` + graphql\`fragment Test on Page { unused1 { unused2 } }\`; + `, + options: [{leavesOnly: true}], + errors: [unusedFieldsWarning('unused2')] + }, + { + code: ` + graphql\`fragment Test on Page { unused1, unused2 }\`; + `, + options: [{ignoreFields: ['unused1']}], + errors: [unusedFieldsWarning('unused2')] + }, { code: ` const getByPath = require('getByPath');