| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| exports.createRule = createRule; |
| |
| var _graphql = require("graphql"); |
| |
| var _constants = require("./constants"); |
| |
| function strWithLen(len) { |
| // from http://stackoverflow.com/questions/14343844/create-a-string-of-variable-length-filled-with-a-repeated-character |
| return new Array(len + 1).join("x"); |
| } |
| |
| function replaceExpressions(node, context, env) { |
| var chunks = []; |
| |
| node.quasis.forEach(function (element, i) { |
| var chunk = element.value.cooked; |
| var value = node.expressions[i]; |
| |
| chunks.push(chunk); |
| |
| if (!env || env === "apollo") { |
| // In Apollo, interpolation is only valid outside top-level structures like `query` or `mutation`. |
| // We'll check to make sure there's an equivalent set of opening and closing brackets, otherwise |
| // we're attempting to do an invalid interpolation. |
| if (chunk.split("{").length - 1 !== chunk.split("}").length - 1) { |
| context.report({ |
| node: value, |
| message: "Invalid interpolation - fragment interpolation must occur outside of the brackets." |
| }); |
| throw new Error("Invalid interpolation"); |
| } |
| } |
| |
| if (!element.tail) { |
| // Preserve location of errors by replacing with exactly the same length |
| var nameLength = value.end - value.start; |
| |
| if (env === "relay" && /:\s*$/.test(chunk)) { |
| // The chunk before this one had a colon at the end, so this |
| // is a variable |
| |
| // Add 2 for brackets in the interpolation |
| var 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); |
| } 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 |
| // Since we don't know what we'd interpolate here (that occurs at runtime), |
| // we're not going to do anything with this interpolation. |
| } else if (env === "fraql") { |
| if (chunk.lastIndexOf("{") > chunk.lastIndexOf("}")) { |
| chunks.push("__typename"); |
| } |
| } else { |
| // Invalid interpolation |
| context.report({ |
| node: value, |
| message: "Invalid interpolation - not a valid fragment or variable." |
| }); |
| throw new Error("Invalid interpolation"); |
| } |
| } |
| }); |
| |
| return chunks.join(""); |
| } |
| |
| function locFrom(node, error) { |
| if (!error.locations || !error.locations.length) { |
| return; |
| } |
| var location = error.locations[0]; |
| |
| var line = void 0; |
| var column = void 0; |
| if (location.line === 1 && node.tag.name !== _constants.internalTag) { |
| line = node.loc.start.line; |
| column = node.tag.loc.end.column + location.column; |
| } else { |
| line = node.loc.start.line + location.line - 1; |
| column = location.column - 1; |
| } |
| |
| return { |
| line: line, |
| column: column |
| }; |
| } |
| |
| function handleTemplateTag(node, context, schema, env, validators) { |
| var text = void 0; |
| try { |
| text = replaceExpressions(node.quasi, context, env); |
| } catch (e) { |
| if (e.message !== "Invalid interpolation") { |
| console.log(e); // eslint-disable-line no-console |
| } |
| return; |
| } |
| |
| // 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 _"); |
| } |
| |
| var ast = void 0; |
| |
| try { |
| ast = (0, _graphql.parse)(text); |
| } catch (error) { |
| context.report({ |
| node: node, |
| message: error.message.split("\n")[0], |
| loc: locFrom(node, error) |
| }); |
| return; |
| } |
| |
| var validationErrors = schema ? (0, _graphql.validate)(schema, ast, validators) : []; |
| if (validationErrors && validationErrors.length > 0) { |
| context.report({ |
| node: node, |
| message: validationErrors[0].message, |
| loc: locFrom(node, validationErrors[0]) |
| }); |
| return; |
| } |
| } |
| |
| function templateExpressionMatchesTag(tagName, node) { |
| var tagNameSegments = tagName.split(".").length; |
| if (tagNameSegments === 1) { |
| // Check for single identifier, like 'gql' |
| if (node.tag.type !== "Identifier" || node.tag.name !== tagName) { |
| return false; |
| } |
| } else if (tagNameSegments === 2) { |
| // Check for dotted identifier, like 'Relay.QL' |
| if (node.tag.type !== "MemberExpression" || node.tag.object.name + "." + node.tag.property.name !== tagName) { |
| return false; |
| } |
| } else { |
| // We don't currently support 3 segments so ignore |
| return false; |
| } |
| return true; |
| } |
| |
| 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; |
| |
| try { |
| var _loop = function _loop() { |
| var optionGroup = _step.value; |
| |
| var _optionParser = optionParser(optionGroup), |
| schema = _optionParser.schema, |
| env = _optionParser.env, |
| tagName = _optionParser.tagName, |
| validators = _optionParser.validators; |
| |
| 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; |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |
| } catch (err) { |
| _didIteratorError2 = true; |
| _iteratorError2 = err; |
| } finally { |
| try { |
| if (!_iteratorNormalCompletion2 && _iterator2.return) { |
| _iterator2.return(); |
| } |
| } finally { |
| if (_didIteratorError2) { |
| throw _iteratorError2; |
| } |
| } |
| } |
| } |
| }; |
| } |