blob: 16e9e0c10bccf7c9bae6197b138f727724eac189 [file] [log] [blame]
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.processors = exports.rules = undefined;
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 = require("fs");
var _fs2 = _interopRequireDefault(_fs);
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
var _graphql = require("graphql");
var _lodash = require("lodash");
var _graphqlConfig = require("graphql-config");
var _customGraphQLValidationRules = require("./customGraphQLValidationRules");
var customRules = _interopRequireWildcard(_customGraphQLValidationRules);
var _constants = require("./constants");
var _createRule = require("./createRule");
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; } }
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")
};
var gqlFiles = ["gql", "graphql"];
var defaultRuleProperties = {
env: {
enum: ["lokka", "fraql", "relay", "apollo", "literal"]
},
schemaJson: {
type: "object"
},
schemaJsonFilepath: {
type: "string"
},
schemaString: {
type: "string"
},
tagName: {
type: "string",
pattern: "^[$_a-zA-Z$_][a-zA-Z0-9$_]+(\\.[a-zA-Z0-9$_]+)?$"
},
projectName: {
type: "string"
}
};
// schemaJson, schemaJsonFilepath, schemaString and projectName are mutually exclusive:
var schemaPropsExclusiveness = {
oneOf: [{
required: ["schemaJson"],
not: { required: ["schemaString", "schemaJsonFilepath", "projectName"] }
}, {
required: ["schemaJsonFilepath"],
not: { required: ["schemaJson", "schemaString", "projectName"] }
}, {
required: ["schemaString"],
not: { required: ["schemaJson", "schemaJsonFilepath", "projectName"] }
}, {
not: {
anyOf: [{ required: ["schemaString"] }, { required: ["schemaJson"] }, { required: ["schemaJsonFilepath"] }]
}
}]
};
var rules = exports.rules = {
"template-strings": {
meta: {
schema: {
type: "array",
items: _extends({
additionalProperties: false,
properties: _extends({}, defaultRuleProperties, {
validators: {
oneOf: [{
type: "array",
uniqueItems: true,
items: {
enum: allGraphQLValidatorNames
}
}, {
enum: ["all"]
}]
}
})
}, schemaPropsExclusiveness)
}
},
create: function create(context) {
return (0, _createRule.createRule)(context, function (optionGroup) {
return parseOptions(optionGroup, context);
});
}
},
"named-operations": {
meta: {
schema: {
type: "array",
items: _extends({
additionalProperties: false,
properties: _extends({}, defaultRuleProperties)
}, schemaPropsExclusiveness)
}
},
create: function create(context) {
return (0, _createRule.createRule)(context, function (optionGroup) {
return parseOptions(_extends({
validators: ["OperationsMustHaveNames"]
}, optionGroup), context);
});
}
},
"required-fields": {
meta: {
schema: {
type: "array",
minItems: 1,
items: _extends({
additionalProperties: false,
properties: _extends({}, defaultRuleProperties, {
requiredFields: {
type: "array",
items: {
type: "string"
}
}
}),
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);
});
}
},
"capitalized-type-name": {
meta: {
schema: {
type: "array",
items: _extends({
additionalProperties: false,
properties: _extends({}, defaultRuleProperties)
}, schemaPropsExclusiveness)
}
},
create: function create(context) {
return (0, _createRule.createRule)(context, function (optionGroup) {
return parseOptions(_extends({
validators: ["typeNamesShouldBeCapitalized"]
}, optionGroup), context);
});
}
},
"no-deprecated-fields": {
meta: {
schema: {
type: "array",
items: _extends({
additionalProperties: false,
properties: _extends({}, defaultRuleProperties)
}, schemaPropsExclusiveness)
}
},
create: function create(context) {
return (0, _createRule.createRule)(context, function (optionGroup) {
return parseOptions(_extends({
validators: ["noDeprecatedFields"]
}, optionGroup), context);
});
}
}
};
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;
var cacheHit = schemaCache[JSON.stringify(optionGroup)];
if (cacheHit && env !== "literal") {
return cacheHit;
}
// Validate and unpack schema
var schema = void 0;
if (schemaJson) {
schema = initSchema(schemaJson);
} else if (schemaJsonFilepath) {
schema = initSchemaFromFile(schemaJsonFilepath);
} else if (schemaString) {
schema = initSchemaFromString(schemaString);
} else {
try {
var config = (0, _graphqlConfig.getGraphQLConfig)(_path2.default.dirname(context.getFilename()));
var projectConfig = void 0;
if (projectName) {
projectConfig = config.getProjects()[projectName];
if (!projectConfig) {
throw new Error("Project with name \"" + projectName + "\" not found in " + config.configPath + ".");
}
} else {
projectConfig = config.getConfigForFile(context.getFilename());
}
if (projectConfig) {
var key = config.configPath + "[" + projectConfig.projectName + "]";
schema = projectCache[key];
if (!schema) {
schema = projectConfig.getSchema();
projectCache[key] = schema;
}
}
if (cacheHit) {
return _extends({}, cacheHit, { schema: 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 e;
}
}
// 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
var tagName = void 0;
if (tagNameOption) {
tagName = tagNameOption;
} else if (env === "relay") {
tagName = "Relay.QL";
} else if (env === "literal") {
tagName = _constants.internalTag;
} else {
tagName = "gql";
}
// 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;
if (validatorNamesOption === "all") {
validatorNames = allGraphQLValidatorNames;
} else if (validatorNamesOption) {
validatorNames = validatorNamesOption;
} else {
validatorNames = envGraphQLValidatorNames[env] || allGraphQLValidatorNames;
}
var validators = validatorNames.map(function (name) {
if (name in customRules) {
return customRules[name];
} else {
return require("graphql/validation/rules/" + name)[name];
}
});
var results = { schema: schema, env: env, tagName: tagName, validators: validators };
schemaCache[JSON.stringify(optionGroup)] = results;
return results;
}
function initSchema(json) {
var 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);
}
function initSchemaFromFile(jsonFile) {
return initSchema(JSON.parse(_fs2.default.readFileSync(jsonFile, "utf8")));
}
function initSchemaFromString(source) {
return (0, _graphql.buildSchema)(source);
}
var gqlProcessor = {
preprocess: function preprocess(text) {
// Wrap the text in backticks and prepend the internal tag. First the text
// must be escaped, because of the three sequences that have special
// meaning in JavaScript template literals, and could change the meaning of
// the text or cause syntax errors.
// https://tc39.github.io/ecma262/#prod-TemplateCharacter
//
// - "`" would end the template literal.
// - "\" would start an escape sequence.
// - "${" would start an interpolation.
var escaped = text.replace(/[`\\]|\$\{/g, "\\$&");
return [_constants.internalTag + "`" + escaped + "`"];
},
postprocess: function postprocess(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);
});
}
};
var processors = exports.processors = (0, _lodash.reduce)(gqlFiles, function (result, value) {
return _extends({}, result, _defineProperty({}, "." + value, gqlProcessor));
}, {});
exports.default = {
rules: rules,
processors: processors
};