eslint-plugin-graphql
Advanced tools
Comparing version 3.1.1 to 4.0.0
@@ -5,2 +5,11 @@ # Change log | ||
- _Nothing yet!_ | ||
### v4.0.0 | ||
- Improve identity template literal tag docs. [PR #254](https://github.com/apollographql/eslint-plugin-graphql/pull/254) by [Jayden Seric](https://github.com/jaydenseric). | ||
- Add support for GraphQL 15. [PR #271](https://github.com/apollographql/eslint-plugin-graphql/pull/271) by [Scott Taylor](https://github.com/staylor). | ||
- Update all `devDependencies` - upgrades the project to use Babel 7 and ESLint 6. [PR #271](https://github.com/apollographql/eslint-plugin-graphql/pull/271) by [Scott Taylor](https://github.com/staylor). | ||
- **BREAKING**: Minimum supported Node.js version is now Node.js 10; Dropped support for Node.js 8. [PR #271](https://github.com/apollographql/eslint-plugin-graphql/pull/271) by [Scott Taylor](https://github.com/staylor). | ||
### v3.1.1 | ||
@@ -7,0 +16,0 @@ |
@@ -6,2 +6,4 @@ "use strict"; | ||
}); | ||
var internalTag = exports.internalTag = "ESLintPluginGraphQLFile"; | ||
exports.internalTag = void 0; | ||
const internalTag = "ESLintPluginGraphQLFile"; | ||
exports.internalTag = internalTag; |
@@ -18,8 +18,6 @@ "use strict"; | ||
function replaceExpressions(node, context, env) { | ||
var chunks = []; | ||
node.quasis.forEach(function (element, i) { | ||
var chunk = element.value.cooked; | ||
var value = node.expressions[i]; | ||
const chunks = []; | ||
node.quasis.forEach((element, i) => { | ||
const chunk = element.value.cooked; | ||
const value = node.expressions[i]; | ||
chunks.push(chunk); | ||
@@ -42,3 +40,3 @@ | ||
// Preserve location of errors by replacing with exactly the same length | ||
var nameLength = value.end - value.start; | ||
const nameLength = value.end - value.start; | ||
@@ -48,17 +46,15 @@ if (env === "relay" && /:\s*$/.test(chunk)) { | ||
// is a variable | ||
// Add 2 for brackets in the interpolation | ||
var placeholder = strWithLen(nameLength + 2); | ||
const placeholder = strWithLen(nameLength + 2); | ||
chunks.push("$" + placeholder); | ||
} else if (env === "lokka" && /\.\.\.\s*$/.test(chunk)) { | ||
// This is Lokka-style fragment interpolation where you actually type the '...' yourself | ||
var _placeholder = strWithLen(nameLength + 3); | ||
chunks.push(_placeholder); | ||
const placeholder = strWithLen(nameLength + 3); | ||
chunks.push(placeholder); | ||
} else if (env === "relay") { | ||
// This is Relay-style fragment interpolation where you don't type '...' | ||
// Ellipsis cancels out extra characters | ||
var _placeholder2 = strWithLen(nameLength); | ||
chunks.push("..." + _placeholder2); | ||
} else if (!env || env === "apollo") { | ||
// In Apollo, fragment interpolation is only valid outside of brackets | ||
const placeholder = strWithLen(nameLength); | ||
chunks.push("..." + placeholder); | ||
} else if (!env || env === "apollo") {// In Apollo, fragment interpolation is only valid outside of brackets | ||
// Since we don't know what we'd interpolate here (that occurs at runtime), | ||
@@ -80,3 +76,2 @@ // we're not going to do anything with this interpolation. | ||
}); | ||
return chunks.join(""); | ||
@@ -89,6 +84,7 @@ } | ||
} | ||
var location = error.locations[0]; | ||
var line = void 0; | ||
var column = void 0; | ||
const location = error.locations[0]; | ||
let line; | ||
let column; | ||
if (location.line === 1 && node.tag.name !== _constants.internalTag) { | ||
@@ -103,4 +99,4 @@ line = node.loc.start.line; | ||
return { | ||
line: line, | ||
column: column | ||
line, | ||
column | ||
}; | ||
@@ -110,3 +106,4 @@ } | ||
function handleTemplateTag(node, context, schema, env, validators) { | ||
var text = void 0; | ||
let text; | ||
try { | ||
@@ -118,12 +115,13 @@ text = replaceExpressions(node.quasi, context, env); | ||
} | ||
return; | ||
} | ||
} // Re-implement syntax sugar for fragment names, which is technically not valid | ||
// graphql | ||
// Re-implement syntax sugar for fragment names, which is technically not valid | ||
// graphql | ||
if ((env === "lokka" || env === "relay" || env === "fraql") && /fragment\s+on/.test(text)) { | ||
text = text.replace("fragment", "fragment _"); | ||
text = text.replace("fragment", `fragment _`); | ||
} | ||
var ast = void 0; | ||
let ast; | ||
@@ -134,3 +132,3 @@ try { | ||
context.report({ | ||
node: node, | ||
node, | ||
message: error.message.split("\n")[0], | ||
@@ -142,6 +140,7 @@ loc: locFrom(node, error) | ||
var validationErrors = schema ? (0, _graphql.validate)(schema, ast, validators) : []; | ||
const validationErrors = schema ? (0, _graphql.validate)(schema, ast, validators) : []; | ||
if (validationErrors && validationErrors.length > 0) { | ||
context.report({ | ||
node: node, | ||
node, | ||
message: validationErrors[0].message, | ||
@@ -155,3 +154,4 @@ loc: locFrom(node, validationErrors[0]) | ||
function templateExpressionMatchesTag(tagName, node) { | ||
var tagNameSegments = tagName.split(".").length; | ||
const tagNameSegments = tagName.split(".").length; | ||
if (tagNameSegments === 1) { | ||
@@ -171,2 +171,3 @@ // Check for single identifier, like 'gql' | ||
} | ||
return true; | ||
@@ -176,83 +177,43 @@ } | ||
function createRule(context, optionParser) { | ||
var tagNames = new Set(); | ||
var tagRules = []; | ||
var options = context.options.length === 0 ? [{}] : context.options; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
const tagNames = new Set(); | ||
const tagRules = []; | ||
const options = context.options.length === 0 ? [{}] : context.options; | ||
try { | ||
var _loop = function _loop() { | ||
var optionGroup = _step.value; | ||
for (const optionGroup of options) { | ||
const { | ||
schema, | ||
env, | ||
tagName, | ||
validators | ||
} = optionParser(optionGroup); | ||
const boundValidators = validators.map(v => ctx => v(ctx, optionGroup)); | ||
var _optionParser = optionParser(optionGroup), | ||
schema = _optionParser.schema, | ||
env = _optionParser.env, | ||
tagName = _optionParser.tagName, | ||
validators = _optionParser.validators; | ||
if (tagNames.has(tagName)) { | ||
throw new Error("Multiple options for GraphQL tag " + tagName); | ||
} | ||
var boundValidators = validators.map(function (v) { | ||
return function (ctx) { | ||
return v(ctx, optionGroup); | ||
}; | ||
}); | ||
if (tagNames.has(tagName)) { | ||
throw new Error("Multiple options for GraphQL tag " + tagName); | ||
} | ||
tagNames.add(tagName); | ||
tagRules.push({ schema: schema, env: env, tagName: tagName, validators: boundValidators }); | ||
}; | ||
for (var _iterator = options[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
_loop(); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
tagNames.add(tagName); | ||
tagRules.push({ | ||
schema, | ||
env, | ||
tagName, | ||
validators: boundValidators | ||
}); | ||
} | ||
return { | ||
TaggedTemplateExpression: function TaggedTemplateExpression(node) { | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = tagRules[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _ref2 = _step2.value; | ||
var schema = _ref2.schema, | ||
env = _ref2.env, | ||
tagName = _ref2.tagName, | ||
validators = _ref2.validators; | ||
if (templateExpressionMatchesTag(tagName, node)) { | ||
return handleTemplateTag(node, context, schema, env, validators); | ||
} | ||
TaggedTemplateExpression(node) { | ||
for (const { | ||
schema, | ||
env, | ||
tagName, | ||
validators | ||
} of tagRules) { | ||
if (templateExpressionMatchesTag(tagName, node)) { | ||
return handleTemplateTag(node, context, schema, env, validators); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
} |
@@ -13,7 +13,5 @@ "use strict"; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function OperationsMustHaveNames(context) { | ||
return { | ||
OperationDefinition: function OperationDefinition(node) { | ||
OperationDefinition(node) { | ||
if (!node.name) { | ||
@@ -23,2 +21,3 @@ context.reportError(new _graphql.GraphQLError("All operations must be named", [node])); | ||
} | ||
}; | ||
@@ -28,3 +27,3 @@ } | ||
function getFieldWasRequestedOnNode(node, field) { | ||
return node.selectionSet.selections.some(function (n) { | ||
return node.selectionSet.selections.some(n => { | ||
return n.kind === "Field" && n.name.value === field; | ||
@@ -43,14 +42,15 @@ }); | ||
function RequiredFields(context, options) { | ||
var requiredFields = options.requiredFields; | ||
const { | ||
requiredFields | ||
} = options; | ||
return { | ||
FragmentDefinition: function FragmentDefinition(node) { | ||
requiredFields.forEach(function (field) { | ||
var type = context.getType(); | ||
FragmentDefinition(node) { | ||
requiredFields.forEach(field => { | ||
const type = context.getType(); | ||
if (fieldAvailableOnType(type, field)) { | ||
var fieldWasRequested = getFieldWasRequestedOnNode(node, field); | ||
const fieldWasRequested = getFieldWasRequestedOnNode(node, field); | ||
if (!fieldWasRequested) { | ||
context.reportError(new _graphql.GraphQLError("'" + field + "' field required on 'fragment " + node.name.value + " on " + node.typeCondition.name.value + "'", [node])); | ||
context.reportError(new _graphql.GraphQLError(`'${field}' field required on 'fragment ${node.name.value} on ${node.typeCondition.name.value}'`, [node])); | ||
} | ||
@@ -61,8 +61,7 @@ } | ||
// Every inline fragment must have the required field specified inside | ||
// itself or in some parent selection set. | ||
InlineFragment: function InlineFragment(node, key, parent, path, ancestors) { | ||
requiredFields.forEach(function (field) { | ||
var type = context.getType(); | ||
InlineFragment(node, key, parent, path, ancestors) { | ||
requiredFields.forEach(field => { | ||
const type = context.getType(); | ||
@@ -75,8 +74,6 @@ if (fieldAvailableOnType(type, field)) { | ||
var ancestorClone = [].concat(_toConsumableArray(ancestors)); | ||
const ancestorClone = [...ancestors]; | ||
let nearestFieldOrExecutableDefinition; | ||
let nextAncestor; // Now, walk up the ancestors, until you see a field or executable definition. | ||
var nearestFieldOrExecutableDefinition = void 0; | ||
var nextAncestor = void 0; | ||
// Now, walk up the ancestors, until you see a field or executable definition. | ||
while (!nearestFieldOrExecutableDefinition) { | ||
@@ -92,12 +89,12 @@ nextAncestor = ancestorClone.pop(); | ||
} | ||
} | ||
} // If we never found a field or executable definition, the query is malformed | ||
// If we never found a field or executable definition, the query is malformed | ||
if (!nearestFieldOrExecutableDefinition) { | ||
throw new Error("Inline fragment found inside document without a parent field, fragment definition, or operation definition."); | ||
} | ||
} // We found a field or executable definition, but we never saw the field we were looking for in | ||
// the intermediate selection sets. | ||
// We found a field or executable definition, but we never saw the field we were looking for in | ||
// the intermediate selection sets. | ||
context.reportError(new _graphql.GraphQLError("'" + field + "' field required on '... on " + node.typeCondition.name.value + "'", [node])); | ||
context.reportError(new _graphql.GraphQLError(`'${field}' field required on '... on ${node.typeCondition.name.value}'`, [node])); | ||
} | ||
@@ -107,8 +104,8 @@ }); | ||
// Every field that can have the field directly on it, should. It's not | ||
// enough to have some child fragment to include the field, since we don't | ||
// know if that fragment covers all of the possible type options. | ||
Field: function Field(node) { | ||
var def = context.getFieldDef(); | ||
Field(node) { | ||
const def = context.getFieldDef(); | ||
if (!def) { | ||
@@ -118,7 +115,8 @@ return; | ||
requiredFields.forEach(function (field) { | ||
requiredFields.forEach(field => { | ||
if (fieldAvailableOnType(def.type, field)) { | ||
var fieldWasRequested = getFieldWasRequestedOnNode(node, field); | ||
const fieldWasRequested = getFieldWasRequestedOnNode(node, field); | ||
if (!fieldWasRequested) { | ||
context.reportError(new _graphql.GraphQLError("'" + field + "' field required on '" + node.name.value + "'", [node])); | ||
context.reportError(new _graphql.GraphQLError(`'${field}' field required on '${node.name.value}'`, [node])); | ||
} | ||
@@ -128,2 +126,3 @@ } | ||
} | ||
}; | ||
@@ -134,4 +133,5 @@ } | ||
return { | ||
NamedType: function NamedType(node) { | ||
var typeName = node.name.value; | ||
NamedType(node) { | ||
const typeName = node.name.value; | ||
if (typeName[0] == typeName[0].toLowerCase()) { | ||
@@ -141,25 +141,31 @@ context.reportError(new _graphql.GraphQLError("All type names should start with a capital letter", [node])); | ||
} | ||
}; | ||
} | ||
} // Mostly taken from https://github.com/graphql/graphql-js/blob/063148de039b02670a760b8d3dfaf2a04a467169/src/utilities/findDeprecatedUsages.js | ||
// See explanation in [#93](https://github.com/apollographql/eslint-plugin-graphql/pull/93) | ||
// Mostly taken from https://github.com/graphql/graphql-js/blob/063148de039b02670a760b8d3dfaf2a04a467169/src/utilities/findDeprecatedUsages.js | ||
// See explanation in [#93](https://github.com/apollographql/eslint-plugin-graphql/pull/93) | ||
function noDeprecatedFields(context) { | ||
return { | ||
Field: function Field(node) { | ||
var fieldDef = context.getFieldDef(); | ||
Field(node) { | ||
const fieldDef = context.getFieldDef(); | ||
if (fieldDef && fieldDef.isDeprecated) { | ||
var parentType = context.getParentType(); | ||
const parentType = context.getParentType(); | ||
if (parentType) { | ||
var reason = fieldDef.deprecationReason; | ||
context.reportError(new _graphql.GraphQLError("The field " + parentType.name + "." + fieldDef.name + " is deprecated." + (reason ? " " + reason : ""), [node])); | ||
const reason = fieldDef.deprecationReason; | ||
context.reportError(new _graphql.GraphQLError(`The field ${parentType.name}.${fieldDef.name} is deprecated.` + (reason ? " " + reason : ""), [node])); | ||
} | ||
} | ||
}, | ||
EnumValue: function EnumValue(node) { | ||
EnumValue(node) { | ||
// context is of type ValidationContext which doesn't export getEnumValue. | ||
// Bypass the public API to grab that information directly from _typeInfo. | ||
var enumVal = context._typeInfo.getEnumValue(); | ||
const enumVal = context._typeInfo.getEnumValue(); | ||
if (enumVal && enumVal.isDeprecated) { | ||
var type = (0, _graphql.getNamedType)(context.getInputType()); | ||
const type = (0, _graphql.getNamedType)(context.getInputType()); | ||
if (!type) { | ||
@@ -169,7 +175,8 @@ return; | ||
var reason = enumVal.deprecationReason; | ||
context.reportError(new _graphql.GraphQLError("The enum value " + type.name + "." + enumVal.name + " is deprecated." + (reason ? " " + reason : ""), [node])); | ||
const reason = enumVal.deprecationReason; | ||
context.reportError(new _graphql.GraphQLError(`The enum value ${type.name}.${enumVal.name} is deprecated.` + (reason ? " " + reason : ""), [node])); | ||
} | ||
} | ||
}; | ||
} |
330
lib/index.js
"use strict"; | ||
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.processors = exports.rules = undefined; | ||
exports.default = exports.processors = exports.rules = void 0; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var _fs = _interopRequireDefault(require("fs")); | ||
var _fs = require("fs"); | ||
var _path = _interopRequireDefault(require("path")); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
var _graphql = require("graphql"); | ||
var _path = require("path"); | ||
var _lodash = _interopRequireDefault(require("lodash.flatten")); | ||
var _path2 = _interopRequireDefault(_path); | ||
var _lodash2 = _interopRequireDefault(require("lodash.without")); | ||
var _graphql = require("graphql"); | ||
var _lodash = require("lodash"); | ||
var _graphqlConfig = require("graphql-config"); | ||
var _customGraphQLValidationRules = require("./customGraphQLValidationRules"); | ||
var customRules = _interopRequireWildcard(require("./customGraphQLValidationRules")); | ||
var customRules = _interopRequireWildcard(_customGraphQLValidationRules); | ||
var _constants = require("./constants"); | ||
@@ -32,28 +30,22 @@ | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
const allGraphQLValidatorNames = _graphql.specifiedRules.map(rule => rule.name); // Map of env name to list of rule names. | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var allGraphQLValidatorNames = _graphql.specifiedRules.map(function (rule) { | ||
return rule.name; | ||
}); | ||
// Map of env name to list of rule names. | ||
var envGraphQLValidatorNames = { | ||
apollo: (0, _lodash.without)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments"), | ||
lokka: (0, _lodash.without)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments"), | ||
fraql: (0, _lodash.without)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments"), | ||
relay: (0, _lodash.without)(allGraphQLValidatorNames, "KnownDirectives", "KnownFragmentNames", "NoUndefinedVariables", "NoUnusedFragments", | ||
// `graphql` < 14 | ||
"ProvidedNonNullArguments", | ||
// `graphql`@14 | ||
"ProvidedRequiredArguments", "ScalarLeafs"), | ||
literal: (0, _lodash.without)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments") | ||
const envGraphQLValidatorNames = { | ||
apollo: (0, _lodash2.default)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments", // `graphql`@15 | ||
"KnownFragmentNamesRule", "NoUnusedFragmentsRule"), | ||
lokka: (0, _lodash2.default)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments", // `graphql`@15 | ||
"KnownFragmentNamesRule", "NoUnusedFragmentsRule"), | ||
fraql: (0, _lodash2.default)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments", // `graphql`@15 | ||
"KnownFragmentNamesRule", "NoUnusedFragmentsRule"), | ||
relay: (0, _lodash2.default)(allGraphQLValidatorNames, "KnownDirectives", "KnownFragmentNames", "NoUndefinedVariables", "NoUnusedFragments", // `graphql`@15 | ||
"KnownDirectivesRule", "KnownFragmentNamesRule", "NoUndefinedVariablesRule", "NoUnusedFragmentsRule", // `graphql` < 14 | ||
"ProvidedNonNullArguments", // `graphql`@14 | ||
"ProvidedRequiredArguments", "ScalarLeafs", // `graphql`@15 | ||
"ProvidedRequiredArgumentsRule", "ScalarLeafsRule"), | ||
literal: (0, _lodash2.default)(allGraphQLValidatorNames, "KnownFragmentNames", "NoUnusedFragments", // `graphql`@15 | ||
"KnownFragmentNamesRule", "NoUnusedFragmentsRule") | ||
}; | ||
var gqlFiles = ["gql", "graphql"]; | ||
var defaultRuleProperties = { | ||
const gqlFiles = ["gql", "graphql"]; | ||
const defaultRuleProperties = { | ||
env: { | ||
@@ -78,23 +70,33 @@ enum: ["lokka", "fraql", "relay", "apollo", "literal"] | ||
} | ||
}; | ||
}; // schemaJson, schemaJsonFilepath, schemaString and projectName are mutually exclusive: | ||
// schemaJson, schemaJsonFilepath, schemaString and projectName are mutually exclusive: | ||
var schemaPropsExclusiveness = { | ||
const schemaPropsExclusiveness = { | ||
oneOf: [{ | ||
required: ["schemaJson"], | ||
not: { required: ["schemaString", "schemaJsonFilepath", "projectName"] } | ||
not: { | ||
required: ["schemaString", "schemaJsonFilepath", "projectName"] | ||
} | ||
}, { | ||
required: ["schemaJsonFilepath"], | ||
not: { required: ["schemaJson", "schemaString", "projectName"] } | ||
not: { | ||
required: ["schemaJson", "schemaString", "projectName"] | ||
} | ||
}, { | ||
required: ["schemaString"], | ||
not: { required: ["schemaJson", "schemaJsonFilepath", "projectName"] } | ||
not: { | ||
required: ["schemaJson", "schemaJsonFilepath", "projectName"] | ||
} | ||
}, { | ||
not: { | ||
anyOf: [{ required: ["schemaString"] }, { required: ["schemaJson"] }, { required: ["schemaJsonFilepath"] }] | ||
anyOf: [{ | ||
required: ["schemaString"] | ||
}, { | ||
required: ["schemaJson"] | ||
}, { | ||
required: ["schemaJsonFilepath"] | ||
}] | ||
} | ||
}] | ||
}; | ||
var rules = exports.rules = { | ||
const rules = { | ||
"template-strings": { | ||
@@ -104,5 +106,5 @@ meta: { | ||
type: "array", | ||
items: _extends({ | ||
items: { | ||
additionalProperties: false, | ||
properties: _extends({}, defaultRuleProperties, { | ||
properties: { ...defaultRuleProperties, | ||
validators: { | ||
@@ -119,11 +121,8 @@ oneOf: [{ | ||
} | ||
}) | ||
}, schemaPropsExclusiveness) | ||
}, | ||
...schemaPropsExclusiveness | ||
} | ||
} | ||
}, | ||
create: function create(context) { | ||
return (0, _createRule.createRule)(context, function (optionGroup) { | ||
return parseOptions(optionGroup, context); | ||
}); | ||
} | ||
create: context => (0, _createRule.createRule)(context, optionGroup => parseOptions(optionGroup, context)) | ||
}, | ||
@@ -134,14 +133,15 @@ "named-operations": { | ||
type: "array", | ||
items: _extends({ | ||
items: { | ||
additionalProperties: false, | ||
properties: _extends({}, defaultRuleProperties) | ||
}, schemaPropsExclusiveness) | ||
properties: { ...defaultRuleProperties | ||
}, | ||
...schemaPropsExclusiveness | ||
} | ||
} | ||
}, | ||
create: function create(context) { | ||
return (0, _createRule.createRule)(context, function (optionGroup) { | ||
return parseOptions(_extends({ | ||
validators: ["OperationsMustHaveNames"] | ||
}, optionGroup), context); | ||
}); | ||
create: context => { | ||
return (0, _createRule.createRule)(context, optionGroup => parseOptions({ | ||
validators: ["OperationsMustHaveNames"], | ||
...optionGroup | ||
}, context)); | ||
} | ||
@@ -154,5 +154,5 @@ }, | ||
minItems: 1, | ||
items: _extends({ | ||
items: { | ||
additionalProperties: false, | ||
properties: _extends({}, defaultRuleProperties, { | ||
properties: { ...defaultRuleProperties, | ||
requiredFields: { | ||
@@ -164,14 +164,16 @@ type: "array", | ||
} | ||
}), | ||
required: ["requiredFields"] | ||
}, schemaPropsExclusiveness) | ||
}, | ||
required: ["requiredFields"], | ||
...schemaPropsExclusiveness | ||
} | ||
} | ||
}, | ||
create: function create(context) { | ||
return (0, _createRule.createRule)(context, function (optionGroup) { | ||
return parseOptions(_extends({ | ||
validators: ["RequiredFields"], | ||
options: { requiredFields: optionGroup.requiredFields } | ||
}, optionGroup), context); | ||
}); | ||
create: context => { | ||
return (0, _createRule.createRule)(context, optionGroup => parseOptions({ | ||
validators: ["RequiredFields"], | ||
options: { | ||
requiredFields: optionGroup.requiredFields | ||
}, | ||
...optionGroup | ||
}, context)); | ||
} | ||
@@ -183,14 +185,15 @@ }, | ||
type: "array", | ||
items: _extends({ | ||
items: { | ||
additionalProperties: false, | ||
properties: _extends({}, defaultRuleProperties) | ||
}, schemaPropsExclusiveness) | ||
properties: { ...defaultRuleProperties | ||
}, | ||
...schemaPropsExclusiveness | ||
} | ||
} | ||
}, | ||
create: function create(context) { | ||
return (0, _createRule.createRule)(context, function (optionGroup) { | ||
return parseOptions(_extends({ | ||
validators: ["typeNamesShouldBeCapitalized"] | ||
}, optionGroup), context); | ||
}); | ||
create: context => { | ||
return (0, _createRule.createRule)(context, optionGroup => parseOptions({ | ||
validators: ["typeNamesShouldBeCapitalized"], | ||
...optionGroup | ||
}, context)); | ||
} | ||
@@ -202,38 +205,44 @@ }, | ||
type: "array", | ||
items: _extends({ | ||
items: { | ||
additionalProperties: false, | ||
properties: _extends({}, defaultRuleProperties) | ||
}, schemaPropsExclusiveness) | ||
properties: { ...defaultRuleProperties | ||
}, | ||
...schemaPropsExclusiveness | ||
} | ||
} | ||
}, | ||
create: function create(context) { | ||
return (0, _createRule.createRule)(context, function (optionGroup) { | ||
return parseOptions(_extends({ | ||
validators: ["noDeprecatedFields"] | ||
}, optionGroup), context); | ||
}); | ||
create: context => { | ||
return (0, _createRule.createRule)(context, optionGroup => parseOptions({ | ||
validators: ["noDeprecatedFields"], | ||
...optionGroup | ||
}, context)); | ||
} | ||
} | ||
}; | ||
exports.rules = rules; | ||
const schemaCache = {}; | ||
const projectCache = {}; | ||
var schemaCache = {}; | ||
var projectCache = {}; | ||
function parseOptions(optionGroup, context) { | ||
var schemaJson = optionGroup.schemaJson, | ||
schemaJsonFilepath = optionGroup.schemaJsonFilepath, | ||
schemaString = optionGroup.schemaString, | ||
env = optionGroup.env, | ||
projectName = optionGroup.projectName, | ||
tagNameOption = optionGroup.tagName, | ||
validatorNamesOption = optionGroup.validators; | ||
const { | ||
schemaJson, | ||
// Schema via JSON object | ||
schemaJsonFilepath, | ||
// Or Schema via absolute filepath | ||
schemaString, | ||
// Or Schema as string, | ||
env, | ||
projectName, | ||
tagName: tagNameOption, | ||
validators: validatorNamesOption | ||
} = optionGroup; | ||
const cacheHit = schemaCache[JSON.stringify(optionGroup)]; | ||
var cacheHit = schemaCache[JSON.stringify(optionGroup)]; | ||
if (cacheHit && env !== "literal") { | ||
return cacheHit; | ||
} | ||
} // Validate and unpack schema | ||
// Validate and unpack schema | ||
var schema = void 0; | ||
let schema; | ||
if (schemaJson) { | ||
@@ -247,38 +256,55 @@ schema = initSchema(schemaJson); | ||
try { | ||
var config = (0, _graphqlConfig.getGraphQLConfig)(_path2.default.dirname(context.getFilename())); | ||
var projectConfig = void 0; | ||
const config = (0, _graphqlConfig.loadConfigSync)({ | ||
rootDir: _path.default.resolve(process.cwd(), _path.default.dirname(context.getFilename())) | ||
}); | ||
let projectConfig; | ||
if (projectName) { | ||
projectConfig = config.getProjects()[projectName]; | ||
projectConfig = config.getProject(projectName); | ||
if (!projectConfig) { | ||
throw new Error("Project with name \"" + projectName + "\" not found in " + config.configPath + "."); | ||
throw new Error(`Project with name "${projectName}" not found in ${config.filepath}.`); | ||
} | ||
} else { | ||
projectConfig = config.getConfigForFile(context.getFilename()); | ||
try { | ||
projectConfig = config.getProjectForFile(context.getFilename()); | ||
} catch (e) { | ||
if (!(e instanceof _graphqlConfig.ProjectNotFoundError)) { | ||
throw e; | ||
} | ||
} | ||
} | ||
if (projectConfig) { | ||
var key = config.configPath + "[" + projectConfig.projectName + "]"; | ||
const key = `${config.filepath}[${projectConfig.name}]`; | ||
schema = projectCache[key]; | ||
if (!schema) { | ||
schema = projectConfig.getSchema(); | ||
schema = projectConfig.getSchemaSync(); | ||
projectCache[key] = schema; | ||
} | ||
} | ||
if (cacheHit) { | ||
return _extends({}, cacheHit, { schema: schema }); | ||
return { ...cacheHit, | ||
schema | ||
}; | ||
} | ||
} catch (e) { | ||
if (e instanceof _graphqlConfig.ConfigNotFoundError) { | ||
throw new Error("Must provide .graphqlconfig file or pass in `schemaJson` option " + "with schema object or `schemaJsonFilepath` with absolute path to the json file."); | ||
throw new Error("Must provide GraphQL Config file or pass in `schemaJson` option " + "with schema object or `schemaJsonFilepath` with absolute path to the json file."); | ||
} | ||
throw e; | ||
} | ||
} | ||
} // Validate env | ||
// Validate env | ||
if (env && env !== "lokka" && env !== "fraql" && env !== "relay" && env !== "apollo" && env !== "literal") { | ||
throw new Error("Invalid option for env, only `apollo`, `lokka`, `fraql`, `relay`, and `literal` supported."); | ||
} | ||
} // Validate tagName and set default | ||
// Validate tagName and set default | ||
var tagName = void 0; | ||
let tagName; | ||
if (tagNameOption) { | ||
@@ -292,9 +318,10 @@ tagName = tagNameOption; | ||
tagName = "gql"; | ||
} | ||
// The validator list may be: | ||
} // The validator list may be: | ||
// The string 'all' to use all rules. | ||
// An array of rule names. | ||
// null/undefined to use the default rule set of the environment, or all rules. | ||
var validatorNames = void 0; | ||
let validatorNames; | ||
if (validatorNamesOption === "all") { | ||
@@ -308,10 +335,15 @@ validatorNames = allGraphQLValidatorNames; | ||
var validators = validatorNames.map(function (name) { | ||
const validators = validatorNames.map(name => { | ||
if (name in customRules) { | ||
return customRules[name]; | ||
} else { | ||
return require("graphql/validation/rules/" + name)[name]; | ||
return require(`graphql/validation/rules/${name}`)[name]; | ||
} | ||
}); | ||
var results = { schema: schema, env: env, tagName: tagName, validators: validators }; | ||
const results = { | ||
schema, | ||
env, | ||
tagName, | ||
validators | ||
}; | ||
schemaCache[JSON.stringify(optionGroup)] = results; | ||
@@ -322,6 +354,8 @@ return results; | ||
function initSchema(json) { | ||
var unpackedSchemaJson = json.data ? json.data : json; | ||
const unpackedSchemaJson = json.data ? json.data : json; | ||
if (!unpackedSchemaJson.__schema) { | ||
throw new Error("Please pass a valid GraphQL introspection query result."); | ||
} | ||
return (0, _graphql.buildClientSchema)(unpackedSchemaJson); | ||
@@ -331,3 +365,3 @@ } | ||
function initSchemaFromFile(jsonFile) { | ||
return initSchema(JSON.parse(_fs2.default.readFileSync(jsonFile, "utf8"))); | ||
return initSchema(JSON.parse(_fs.default.readFileSync(jsonFile, "utf8"))); | ||
} | ||
@@ -339,4 +373,4 @@ | ||
var gqlProcessor = { | ||
preprocess: function preprocess(text) { | ||
const gqlProcessor = { | ||
preprocess: function (text) { | ||
// Wrap the text in backticks and prepend the internal tag. First the text | ||
@@ -351,22 +385,20 @@ // must be escaped, because of the three sequences that have special | ||
// - "${" would start an interpolation. | ||
var escaped = text.replace(/[`\\]|\$\{/g, "\\$&"); | ||
return [_constants.internalTag + "`" + escaped + "`"]; | ||
const escaped = text.replace(/[`\\]|\$\{/g, "\\$&"); | ||
return [`${_constants.internalTag}\`${escaped}\``]; | ||
}, | ||
postprocess: function postprocess(messages) { | ||
postprocess: function (messages) { | ||
// only report graphql-errors | ||
return (0, _lodash.flatten)(messages).filter(function (message) { | ||
return (0, _lodash.includes)((0, _lodash.keys)(rules).map(function (key) { | ||
return "graphql/" + key; | ||
}), message.ruleId); | ||
}); | ||
return (0, _lodash.default)(messages).filter(message => Object.keys(rules).map(key => `graphql/${key}`).includes(message.ruleId)); | ||
} | ||
}; | ||
var processors = exports.processors = (0, _lodash.reduce)(gqlFiles, function (result, value) { | ||
return _extends({}, result, _defineProperty({}, "." + value, gqlProcessor)); | ||
const processors = gqlFiles.reduce((result, value) => { | ||
return { ...result, | ||
[`.${value}`]: gqlProcessor | ||
}; | ||
}, {}); | ||
exports.default = { | ||
rules: rules, | ||
processors: processors | ||
}; | ||
exports.processors = processors; | ||
var _default = { | ||
rules, | ||
processors | ||
}; | ||
exports.default = _default; |
{ | ||
"name": "eslint-plugin-graphql", | ||
"version": "3.1.1", | ||
"version": "4.0.0", | ||
"description": "GraphQL ESLint plugin.", | ||
@@ -10,3 +10,3 @@ "author": "Sashko Stubailo", | ||
"prepublish": "babel ./src --ignore test --out-dir ./lib", | ||
"pretest": "node test/updateSchemaJson.js", | ||
"pretest": "babel-node test/updateSchemaJson.js", | ||
"tav": "tav", | ||
@@ -21,21 +21,15 @@ "lint": "eslint 'src/**/*.js' 'test/**/*.js'" | ||
"devDependencies": { | ||
"babel-cli": "6.26.0", | ||
"babel-core": "6.26.3", | ||
"babel-eslint": "10.0.1", | ||
"babel-plugin-transform-runtime": "6.23.0", | ||
"babel-preset-es2015": "6.24.1", | ||
"babel-preset-stage-0": "6.24.1", | ||
"eslint": "5.16.0", | ||
"graphql": "14.4.2", | ||
"graphql-tools": "4.0.5", | ||
"mocha": "6.2.0", | ||
"pretty-quick": "1.11.1", | ||
"@babel/cli": "7.10.1", | ||
"@babel/core": "7.10.1", | ||
"@babel/node": "7.10.1", | ||
"@babel/plugin-transform-runtime": "7.10.1", | ||
"@babel/preset-env": "7.10.1", | ||
"@babel/register": "7.9.0", | ||
"babel-eslint": "10.1.0", | ||
"eslint": "6.8.0", | ||
"graphql": "15.0.0", | ||
"mocha": "7.2.0", | ||
"pretty-quick": "2.0.1", | ||
"test-all-versions": "4.1.1" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"es2015", | ||
"stage-0" | ||
] | ||
}, | ||
"husky": { | ||
@@ -47,12 +41,15 @@ "hooks": { | ||
"engines": { | ||
"node": ">=6.0" | ||
"node": ">=10.0" | ||
}, | ||
"browserslist": "node 10", | ||
"license": "MIT", | ||
"dependencies": { | ||
"graphql-config": "^2.0.1", | ||
"lodash": "^4.11.1" | ||
"@babel/runtime": "^7.10.0", | ||
"graphql-config": "^3.0.2", | ||
"lodash.flatten": "^4.4.0", | ||
"lodash.without": "^4.4.0" | ||
}, | ||
"peerDependencies": { | ||
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0" | ||
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" | ||
} | ||
} |
@@ -59,25 +59,18 @@ # eslint-plugin-graphql | ||
This plugin relies on GraphQL queries being prefixed with a special tag. In Relay and Apollo, this is always done, but other clients often take query strings without a tag. In this case, you can define an identity tag that doesn't do anything except for tell the linter this is a GraphQL query: | ||
This plugin relies on GraphQL queries being prefixed with a special tag. In Relay and Apollo this is done often, but some other clients take query strings without a tag. In this case, [`fake-tag`](https://npm.im/fake-tag) can be used to define an identity tag that doesn't do anything except for tell the linter this is a GraphQL query: | ||
```js | ||
global.gql = (literals, ...substitutions) => { | ||
let result = ""; | ||
import gql from "fake-tag"; | ||
// run the loop only for the substitution count | ||
for (let i = 0; i < substitutions.length; i++) { | ||
result += literals[i]; | ||
result += substitutions[i]; | ||
const QUERY_VIEWER_NAME = gql` | ||
query ViewerName { | ||
viewer { | ||
name | ||
} | ||
// add the last literal | ||
result += literals[literals.length - 1]; | ||
return result; | ||
} | ||
} | ||
`; | ||
``` | ||
Code snippet taken from: <https://leanpub.com/understandinges6/read#leanpub-auto-multiline-strings> | ||
Fake tags won’t be necessary [once `/* GraphQL */` comment tags are supported](https://github.com/apollographql/eslint-plugin-graphql/issues/224). | ||
Note: The linter rule could be extended to identify calls to various specific APIs to eliminate the need for a template literal tag, but this might just make the implementation a lot more complex for little benefit. | ||
### GraphQL literal files | ||
@@ -84,0 +77,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
16
100394
5
678
577
+ Added@babel/runtime@^7.10.0
+ Addedlodash.flatten@^4.4.0
+ Addedlodash.without@^4.4.0
+ Added@ardatan/aggregate-error@0.0.6(transitive)
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@babel/runtime@7.26.0(transitive)
+ Added@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2(transitive)
+ Added@graphql-tools/batch-execute@7.1.2(transitive)
+ Added@graphql-tools/delegate@7.1.5(transitive)
+ Added@graphql-tools/graphql-file-loader@6.2.7(transitive)
+ Added@graphql-tools/import@6.7.18(transitive)
+ Added@graphql-tools/json-file-loader@6.2.6(transitive)
+ Added@graphql-tools/load@6.2.8(transitive)
+ Added@graphql-tools/merge@6.2.14(transitive)
+ Added@graphql-tools/schema@7.1.5(transitive)
+ Added@graphql-tools/url-loader@6.10.1(transitive)
+ Added@graphql-tools/utils@7.10.09.2.1(transitive)
+ Added@graphql-tools/wrap@7.0.8(transitive)
+ Added@graphql-typed-document-node/core@3.2.0(transitive)
+ Added@iarna/toml@2.2.5(transitive)
+ Added@microsoft/fetch-event-source@2.0.1(transitive)
+ Added@nodelib/fs.scandir@2.1.5(transitive)
+ Added@nodelib/fs.stat@2.0.5(transitive)
+ Added@nodelib/fs.walk@1.2.8(transitive)
+ Added@types/node@22.9.0(transitive)
+ Added@types/parse-json@4.0.2(transitive)
+ Added@types/websocket@1.0.2(transitive)
+ Addedabort-controller@3.0.0(transitive)
+ Addedarg@4.1.3(transitive)
+ Addedarray-union@2.1.0(transitive)
+ Addedasynckit@0.4.0(transitive)
+ Addedbacko2@1.0.2(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbraces@3.0.3(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedcallsites@3.1.0(transitive)
+ Addedcamel-case@4.1.2(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addedcosmiconfig@7.0.0(transitive)
+ Addedcosmiconfig-toml-loader@1.0.0(transitive)
+ Addedcreate-require@1.1.1(transitive)
+ Addedcross-fetch@3.1.4(transitive)
+ Addeddataloader@2.0.0(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addeddiff@4.0.2(transitive)
+ Addeddir-glob@3.0.1(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedevent-target-shim@5.0.1(transitive)
+ Addedeventemitter3@3.1.2(transitive)
+ Addedextract-files@9.0.0(transitive)
+ Addedfast-glob@3.3.2(transitive)
+ Addedfastq@1.17.1(transitive)
+ Addedfill-range@7.1.1(transitive)
+ Addedform-data@4.0.0(transitive)
+ Addedglob-parent@5.1.2(transitive)
+ Addedglobby@11.0.3(transitive)
+ Addedgraphql@15.9.0(transitive)
+ Addedgraphql-config@3.4.1(transitive)
+ Addedgraphql-ws@4.9.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedignore@5.3.2(transitive)
+ Addedimport-fresh@3.3.0(transitive)
+ Addedimport-from@3.0.0(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.1(transitive)
+ Addedis-number@7.0.0(transitive)
+ Addedis-promise@4.0.0(transitive)
+ Addedisomorphic-ws@4.0.1(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedjson-parse-even-better-errors@2.3.1(transitive)
+ Addedlines-and-columns@1.2.4(transitive)
+ Addedlodash.flatten@4.4.0(transitive)
+ Addedlodash.get@4.4.2(transitive)
+ Addedlodash.without@4.4.0(transitive)
+ Addedlower-case@2.0.2(transitive)
+ Addedmake-error@1.3.6(transitive)
+ Addedmerge2@1.4.1(transitive)
+ Addedmeros@1.1.4(transitive)
+ Addedmicromatch@4.0.8(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedminimatch@3.0.4(transitive)
+ Addedno-case@3.0.4(transitive)
+ Addednode-fetch@2.6.1(transitive)
+ Addednormalize-path@2.1.1(transitive)
+ Addedp-limit@3.1.0(transitive)
+ Addedparent-module@1.0.1(transitive)
+ Addedparse-json@5.2.0(transitive)
+ Addedpascal-case@3.1.2(transitive)
+ Addedpath-type@4.0.0(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedqueue-microtask@1.2.3(transitive)
+ Addedregenerator-runtime@0.14.1(transitive)
+ Addedremove-trailing-separator@1.1.0(transitive)
+ Addedresolve-from@5.0.0(transitive)
+ Addedreusify@1.0.4(transitive)
+ Addedrun-parallel@1.2.0(transitive)
+ Addedslash@3.0.0(transitive)
+ Addedsource-map@0.6.1(transitive)
+ Addedsource-map-support@0.5.21(transitive)
+ Addedstring-env-interpolation@1.0.1(transitive)
+ Addedsubscriptions-transport-ws@0.9.19(transitive)
+ Addedsymbol-observable@1.2.0(transitive)
+ Addedsync-fetch@0.3.0(transitive)
+ Addedto-regex-range@5.0.1(transitive)
+ Addedts-node@9.1.1(transitive)
+ Addedtslib@2.0.32.1.02.2.02.8.1(transitive)
+ Addedtypescript@5.6.3(transitive)
+ Addedundici-types@6.19.8(transitive)
+ Addedunixify@1.0.0(transitive)
+ Addedvalid-url@1.0.9(transitive)
+ Addedvalue-or-promise@1.0.6(transitive)
+ Addedws@7.4.5(transitive)
+ Addedyaml@1.10.2(transitive)
+ Addedyn@3.1.1(transitive)
+ Addedyocto-queue@0.1.0(transitive)
- Removedlodash@^4.11.1
- Removedargparse@1.0.10(transitive)
- Removedcross-fetch@2.2.2(transitive)
- Removedesprima@4.0.1(transitive)
- Removedgraphql@14.7.0(transitive)
- Removedgraphql-config@2.2.2(transitive)
- Removedgraphql-import@0.7.1(transitive)
- Removedgraphql-request@1.8.2(transitive)
- Removedjs-yaml@3.14.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removednode-fetch@2.1.2(transitive)
- Removedsprintf-js@1.0.3(transitive)
- Removedwhatwg-fetch@2.0.4(transitive)
Updatedgraphql-config@^3.0.2