Files
30-seconds-of-code/node_modules/@gatsbyjs/relay-compiler/lib/ReaderCodeGenerator.js
2019-08-20 15:52:05 +02:00

332 lines
10 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var CodeMarker = require("./CodeMarker");
var IRVisitor = require("./GraphQLIRVisitor");
var SchemaUtils = require("./GraphQLSchemaUtils");
var _require = require("./RelayCompilerError"),
createCompilerError = _require.createCompilerError,
createUserError = _require.createUserError;
var _require2 = require("graphql"),
GraphQLList = _require2.GraphQLList;
var _require3 = require("relay-runtime"),
getStorageKey = _require3.getStorageKey,
stableCopy = _require3.stableCopy;
var getRawType = SchemaUtils.getRawType,
isAbstractType = SchemaUtils.isAbstractType,
getNullableType = SchemaUtils.getNullableType;
/**
* @public
*
* Converts a GraphQLIR node into a plain JS object representation that can be
* used at runtime.
*/
function generate(node) {
return IRVisitor.visit(node, ReaderCodeGenVisitor);
}
var ReaderCodeGenVisitor = {
leave: {
Request: function Request(node) {
throw createCompilerError('ReaderCodeGenerator: unexpeted Request node.');
},
Fragment: function Fragment(node) {
var metadata = null;
if (node.metadata != null) {
var _node$metadata = node.metadata,
mask = _node$metadata.mask,
plural = _node$metadata.plural,
connection = _node$metadata.connection,
refetch = _node$metadata.refetch;
if (Array.isArray(connection)) {
var _metadata;
metadata = (_metadata = metadata) !== null && _metadata !== void 0 ? _metadata : {};
metadata.connection = connection;
}
if (typeof mask === 'boolean') {
var _metadata2;
metadata = (_metadata2 = metadata) !== null && _metadata2 !== void 0 ? _metadata2 : {};
metadata.mask = mask;
}
if (typeof plural === 'boolean') {
var _metadata3;
metadata = (_metadata3 = metadata) !== null && _metadata3 !== void 0 ? _metadata3 : {};
metadata.plural = plural;
}
if (typeof refetch === 'object') {
var _metadata4;
metadata = (_metadata4 = metadata) !== null && _metadata4 !== void 0 ? _metadata4 : {};
metadata.refetch = {
connection: refetch.connection,
operation: CodeMarker.moduleDependency(refetch.operation + '.graphql'),
fragmentPathInResult: refetch.fragmentPathInResult
};
}
}
return {
kind: 'Fragment',
name: node.name,
type: node.type.toString(),
metadata: metadata,
argumentDefinitions: node.argumentDefinitions,
selections: node.selections
};
},
LocalArgumentDefinition: function LocalArgumentDefinition(node) {
return {
kind: 'LocalArgument',
name: node.name,
type: node.type.toString(),
defaultValue: node.defaultValue
};
},
RootArgumentDefinition: function RootArgumentDefinition(node) {
return {
kind: 'RootArgument',
name: node.name,
type: node.type ? node.type.toString() : null
};
},
Condition: function Condition(node, key, parent, ancestors) {
if (node.condition.kind !== 'Variable') {
throw createCompilerError("ReaderCodeGenerator: Expected 'Condition' with static value to be " + 'pruned or inlined', [node.condition.loc]);
}
return {
kind: 'Condition',
passingValue: node.passingValue,
condition: node.condition.variableName,
selections: node.selections
};
},
FragmentSpread: function FragmentSpread(node) {
return {
kind: 'FragmentSpread',
name: node.name,
args: valuesOrNull(sortByName(node.args))
};
},
InlineFragment: function InlineFragment(node) {
return {
kind: 'InlineFragment',
type: node.typeCondition.toString(),
selections: node.selections
};
},
LinkedField: function LinkedField(node) {
// Note: it is important that the arguments of this field be sorted to
// ensure stable generation of storage keys for equivalent arguments
// which may have originally appeared in different orders across an app.
// TODO(T37646905) enable this invariant after splitting the
// RelayCodeGenerator-test and running the RelayFieldHandleTransform on
// Reader ASTs.
//
// invariant(
// node.handles == null,
// 'ReaderCodeGenerator: unexpected handles',
// );
var type = getRawType(node.type);
var field = {
kind: 'LinkedField',
alias: node.alias,
name: node.name,
storageKey: null,
args: valuesOrNull(sortByName(node.args)),
concreteType: !isAbstractType(type) ? type.toString() : null,
plural: isPlural(node.type),
selections: node.selections
}; // Precompute storageKey if possible
var storageKey = getStaticStorageKey(field, node.metadata);
if (storageKey) {
field = (0, _objectSpread2["default"])({}, field, {
storageKey: storageKey
});
}
return field;
},
MatchField: function MatchField(node, key, parent, ancestors) {
var matchesByType = {};
node.selections.forEach(function (selection) {
var _regExpMatch$;
if (selection.kind === 'ScalarField' && selection.name === '__typename') {
// The RelayGenerateTypename transform will add a __typename selection
// to the selections of the match field.
return;
}
if (selection.kind !== 'MatchBranch') {
throw createCompilerError("ReaderCodeGenerator: Expected selection for MatchField '".concat(node.name, "' to be a 'MatchBranch', got '").concat(selection.kind, "'."), [selection.loc]);
}
if (matchesByType.hasOwnProperty(selection.type)) {
throw createCompilerError('ReaderCodeGenerator: Each @match type can appear at-most once. ' + "Type '".concat(String(selection.type), "' was duplicated."), selection.type, [selection.loc]);
}
var fragmentName = selection.name;
var regExpMatch = fragmentName.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:_([a-zA-Z][_a-zA-Z0-9]*))?$/);
if (!regExpMatch) {
throw createCompilerError('ReaderCodeGenerator: @match fragments should be named ' + "'FragmentName_propName', got '".concat(fragmentName, "'."), [selection.loc]);
}
var fragmentPropName = (_regExpMatch$ = regExpMatch[2]) !== null && _regExpMatch$ !== void 0 ? _regExpMatch$ : 'matchData';
matchesByType[selection.type] = {
fragmentPropName: fragmentPropName,
fragmentName: fragmentName
};
});
var field = {
kind: 'MatchField',
alias: node.alias,
name: node.name,
storageKey: null,
args: valuesOrNull(sortByName(node.args)),
matchesByType: matchesByType
}; // Precompute storageKey if possible
var storageKey = getStaticStorageKey(field, node.metadata);
if (storageKey) {
field = (0, _objectSpread2["default"])({}, field, {
storageKey: storageKey
});
}
return field;
},
ScalarField: function ScalarField(node) {
// Note: it is important that the arguments of this field be sorted to
// ensure stable generation of storage keys for equivalent arguments
// which may have originally appeared in different orders across an app.
// TODO(T37646905) enable this invariant after splitting the
// RelayCodeGenerator-test and running the RelayFieldHandleTransform on
// Reader ASTs.
//
// invariant(
// node.handles == null,
// 'ReaderCodeGenerator: unexpected handles',
var field = {
kind: 'ScalarField',
alias: node.alias,
name: node.name,
args: valuesOrNull(sortByName(node.args)),
storageKey: null
}; // Precompute storageKey if possible
var storageKey = getStaticStorageKey(field, node.metadata);
if (storageKey) {
field = (0, _objectSpread2["default"])({}, field, {
storageKey: storageKey
});
}
return field;
},
SplitOperation: function SplitOperation(node, key, parent) {
return {
kind: 'SplitOperation',
name: node.name,
metadata: null,
selections: node.selections
};
},
Variable: function Variable(node, key, parent) {
return {
kind: 'Variable',
name: parent.name,
variableName: node.variableName,
type: parent.type ? parent.type.toString() : null
};
},
Literal: function Literal(node, key, parent) {
return {
kind: 'Literal',
name: parent.name,
value: stableCopy(node.value),
type: parent.type ? parent.type.toString() : null
};
},
Argument: function Argument(node, key, parent, ancestors) {
if (!['Variable', 'Literal'].includes(node.value.kind)) {
var valueString = JSON.stringify(node.value, null, 2);
throw createUserError('ReaderCodeGenerator: Complex argument values (Lists or ' + 'InputObjects with nested variables) are not supported.', [node.value.loc]);
}
return node.value.value !== null ? node.value : null;
}
}
};
function isPlural(type) {
return getNullableType(type) instanceof GraphQLList;
}
function valuesOrNull(array) {
return !array || array.length === 0 ? null : array;
}
function sortByName(array) {
return array instanceof Array ? array.slice().sort(function (a, b) {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
}) : array;
}
/**
* Pre-computes storage key if possible and advantageous. Storage keys are
* generated for fields with supplied arguments that are all statically known
* (ie. literals, no variables) at build time.
*/
function getStaticStorageKey(field, metadata) {
var metadataStorageKey = metadata === null || metadata === void 0 ? void 0 : metadata.storageKey;
if (typeof metadataStorageKey === 'string') {
return metadataStorageKey;
}
if (!field.args || field.args.length === 0 || field.args.some(function (arg) {
return arg.kind !== 'Literal';
})) {
return null;
}
return getStorageKey(field, {});
}
module.exports = {
generate: generate
};