355 lines
11 KiB
JavaScript
355 lines
11 KiB
JavaScript
'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
|
|
}; |