Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.

feat: Extract implementation to @0no-co/graphql.web (#11)

* Add @0no-co/graphql.web
* Use exports from @0no-co/graphql.web

+1 -66
alias/error/GraphQLError.mjs
···
-
import { getLocation } from 'graphql/language/location';
-
-
import { printLocation, printSourceLocation } from '../language/printLocation';
-
-
export class GraphQLError extends Error {
-
constructor(
-
message,
-
nodes,
-
source,
-
positions,
-
path,
-
originalError,
-
extensions
-
) {
-
super(message);
-
-
this.name = 'GraphQLError';
-
this.message = message;
-
-
if (path) this.path = path;
-
if (nodes) this.nodes = nodes;
-
if (source) this.source = source;
-
if (positions) this.positions = positions;
-
if (originalError) this.originalError = originalError;
-
-
let _extensions = extensions;
-
if (_extensions == null && originalError != null) {
-
const originalExtensions = originalError.extensions;
-
if (isObjectLike(originalExtensions)) {
-
_extensions = originalExtensions;
-
}
-
}
-
-
if (_extensions) {
-
this.extensions = _extensions;
-
}
-
}
-
-
toJSON() {
-
const formattedError = { message: this.message };
-
-
if (this.locations != null) formattedError.locations = this.locations;
-
if (this.path != null) formattedError.path = this.path;
-
if (this.extensions != null && Object.keys(this.extensions).length > 0)
-
formattedError.extensions = this.extensions;
-
return formattedError;
-
}
-
-
toString() {
-
let output = error.message;
-
-
if (error.nodes) {
-
for (const node of error.nodes) {
-
if (node.loc) {
-
output += '\n\n' + printLocation(node.loc);
-
}
-
}
-
} else if (error.source && error.locations) {
-
for (const location of error.locations) {
-
output += '\n\n' + printSourceLocation(error.source, location);
-
}
-
}
-
-
return output;
-
}
-
}
+
export { GraphQLError } from '@0no-co/graphql.web';
/**
* Prints a GraphQLError to a string, representing useful location information
+1 -3
alias/language/blockString.mjs
···
-
export function printBlockString(str) {
-
return '"""\n' + JSON.stringify(str).slice(1, -1) + '\n"""';
-
}
+
export { printBlockString } from '@0no-co/graphql.web';
export function isPrintableAsBlockString(value) {
return true;
+1
alias/language/kinds.mjs
···
+
export { Kind } from '@0no-co/graphql.web';
+1 -339
alias/language/parser.mjs
···
-
/**
-
* This is a spec-compliant implementation of a GraphQL query language parser,
-
* up-to-date with the June 2018 Edition. Unlike the reference implementation
-
* in graphql.js it will only parse the query language, but not the schema
-
* language.
-
*/
-
import { Kind } from 'graphql';
-
import { GraphQLError } from '../error/GraphQLError';
-
import { match, parse as makeParser } from 'reghex';
-
-
// 2.1.7: Includes commas, and line comments
-
const ignored = /([\s,]|#[^\n\r]+)+/;
-
-
// 2.1.9: Limited to ASCII character set, so regex shortcodes are fine
-
const name = match(Kind.NAME, (x) => ({
-
kind: x.tag,
-
value: x[0],
-
}))`
-
${/[_\w][_\d\w]*/}
-
`;
-
-
const null_ = match(Kind.NULL, (x) => ({
-
kind: x.tag,
-
}))`
-
${'null'}
-
`;
-
-
const bool = match(Kind.BOOLEAN, (x) => ({
-
kind: x.tag,
-
value: x[0] === 'true',
-
}))`
-
${/true|false/}
-
`;
-
-
const variable = match(Kind.VARIABLE, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
}))`
-
:${'$'} ${name}
-
`;
-
-
// 2.9.6: Technically, this parser doesn't need to check that true, false, and null
-
// aren't used as enums, but this prevents mistakes and follows the spec closely
-
const enum_ = match(Kind.ENUM, (x) => ({
-
kind: x.tag,
-
value: x[0].value,
-
}))`
-
${name}
-
`;
-
-
// 2.9.1-2: These combine both number values for the sake of simplicity.
-
// It allows for leading zeroes, unlike graphql.js, which shouldn't matter;
-
const number = match(null, (x) => ({
-
kind: x.length === 1 ? Kind.INT : Kind.FLOAT,
-
value: x.join(''),
-
}))`
-
${/[-]?\d+/}
-
${/[.]\d+/}?
-
${/[eE][+-]?\d+/}?
-
`;
-
-
// 2.9.4: Notably, this skips checks for unicode escape sequences and escaped quotes.
-
const string = match(Kind.STRING, (x) => ({
-
kind: x.tag,
-
value: x[0],
-
}))`
-
(:${'"""'} ${/[\s\S]+?(?=""")/} :${'"""'})
-
| (:${'"'} ${/[^"\r\n]*/} :${'"'})
-
`;
-
-
const list = match(Kind.LIST, (x) => ({
-
kind: x.tag,
-
values: x.slice(),
-
}))`
-
:${'['}
-
${() => value}*
-
(?: ${ignored}? ${']'} ${ignored}?)
-
`;
-
-
const objectField = match(Kind.OBJECT_FIELD, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
value: x[1],
-
}))`
-
:${ignored}?
-
${name}
-
(?: ${ignored}? ${':'})
-
${() => value}
-
`;
-
-
const object = match(Kind.OBJECT, (x) => ({
-
kind: x.tag,
-
fields: x.slice(),
-
}))`
-
:${'{'}
-
${objectField}*
-
(?: ${'}'} ${ignored}?)
-
`;
-
-
// 2.9: This matches the spec closely and is complete
-
const value = match(null, (x) => x[0])`
-
:${ignored}?
-
(
-
${null_}
-
| ${bool}
-
| ${variable}
-
| ${string}
-
| ${number}
-
| ${enum_}
-
| ${list}
-
| ${object}
-
)
-
:${ignored}?
-
`;
-
-
const arg = match(Kind.ARGUMENT, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
value: x[1],
-
}))`
-
${name}
-
(?: ${ignored}? ${':'} ${ignored}?)
-
${value}
-
`;
-
-
const args = match()`
-
:${ignored}?
-
(
-
(?: ${'('} ${ignored}?)
-
${arg}+
-
(?: ${')'} ${ignored}?)
-
)?
-
`;
-
-
const directive = match(Kind.DIRECTIVE, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
arguments: x[1],
-
}))`
-
:${'@'} ${name} :${ignored}?
-
${args}?
-
:${ignored}?
-
`;
-
-
const directives = match()`
-
:${ignored}?
-
${directive}*
-
`;
-
-
const field = match(Kind.FIELD, (x) => {
-
let i = 0;
-
return {
-
kind: x.tag,
-
alias: x[1].kind === Kind.NAME ? x[i++] : undefined,
-
name: x[i++],
-
arguments: x[i++],
-
directives: x[i++],
-
selectionSet: x[i++],
-
};
-
})`
-
:${ignored}?
-
${name}
-
(
-
(?: ${ignored}? ${':'} ${ignored}?)
-
${name}
-
)?
-
${args}
-
${directives}
-
${() => selectionSet}?
-
`;
-
-
// 2.11: The type declarations may be simplified since there's little room
-
// for error in this limited type system.
-
const type = match(null, (x) => {
-
const node =
-
x[0].kind === 'Name'
-
? { kind: Kind.NAMED_TYPE, name: x[0] }
-
: { kind: Kind.LIST_TYPE, type: x[0] };
-
return x[1] === '!' ? { kind: Kind.NON_NULL_TYPE, type: node } : node;
-
})`
-
(
-
(
-
(?: ${'['} ${ignored}?)
-
${() => type}
-
(?: ${ignored}? ${']'} ${ignored}?)
-
) | ${name}
-
)
-
${'!'}?
-
:${ignored}?
-
`;
-
-
const typeCondition = match(null, (x) => ({
-
kind: Kind.NAMED_TYPE,
-
name: x[0],
-
}))`
-
(?: ${ignored}? ${'on'} ${ignored})
-
${name}
-
:${ignored}?
-
`;
-
-
const inlineFragment = match(Kind.INLINE_FRAGMENT, (x) => {
-
let i = 0;
-
return {
-
kind: x.tag,
-
typeCondition: x[i].kind === Kind.NAMED_TYPE ? x[i++] : undefined,
-
directives: x[i++],
-
selectionSet: x[i],
-
};
-
})`
-
:${'...'}
-
${typeCondition}?
-
${directives}
-
${() => selectionSet}
-
`;
-
-
const fragmentSpread = match(Kind.FRAGMENT_SPREAD, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
directives: x[1],
-
}))`
-
(?: ${'...'} ${ignored}?)
-
!${'on'}
-
${name}
-
:${ignored}?
-
${directives}
-
`;
-
-
const selectionSet = match(Kind.SELECTION_SET, (x) => ({
-
kind: x.tag,
-
selections: x.slice(),
-
}))`
-
:${ignored}?
-
(?: ${'{'} ${ignored}?)
-
(
-
${inlineFragment} |
-
${fragmentSpread} |
-
${field}
-
)+
-
(?: ${'}'} ${ignored}?)
-
`;
-
-
const varDefinitionDefault = match(null, (x) => x[0])`
-
(?: ${'='} ${ignored}?)
-
${value}
-
`;
-
-
const varDefinition = match(Kind.VARIABLE_DEFINITION, (x) => ({
-
kind: x.tag,
-
variable: x[0],
-
type: x[1],
-
defaultValue: x[2].kind ? x[2] : undefined,
-
directives: !x[2].kind ? x[2] : x[3],
-
}))`
-
${variable}
-
(?: ${ignored}? ${':'} ${ignored}?)
-
${type}
-
${varDefinitionDefault}?
-
${directives}
-
:${ignored}?
-
`;
-
-
const varDefinitions = match('vars')`
-
:${ignored}?
-
(?: ${'('} ${ignored}?)
-
${varDefinition}+
-
(?: ${')'} ${ignored}?)
-
`;
-
-
const fragmentDefinition = match(Kind.FRAGMENT_DEFINITION, (x) => ({
-
kind: x.tag,
-
name: x[0],
-
typeCondition: x[1],
-
directives: x[2],
-
selectionSet: x[3],
-
}))`
-
(?: ${ignored}? ${'fragment'} ${ignored})
-
${name}
-
${typeCondition}
-
${directives}
-
${selectionSet}
-
`;
-
-
const operationDefinition = match(Kind.OPERATION_DEFINITION, (x) => {
-
let i = 1;
-
return {
-
kind: x.tag,
-
operation: x[0],
-
name: x[i].kind === Kind.NAME ? x[i++] : undefined,
-
variableDefinitions: x[i].tag === 'vars' ? x[i++].slice() : [],
-
directives: x[i++],
-
selectionSet: x[i],
-
};
-
})`
-
:${ignored}?
-
${/query|mutation|subscription/}
-
(:${ignored} ${name})?
-
${varDefinitions}?
-
${directives}
-
${selectionSet}
-
`;
-
-
const queryShorthand = match(Kind.OPERATION_DEFINITION, (x) => ({
-
kind: x.tag,
-
operation: 'query',
-
name: undefined,
-
variableDefinitions: [],
-
directives: [],
-
selectionSet: x[0],
-
}))`
-
${selectionSet}
-
`;
-
-
const root = match(Kind.DOCUMENT, (x) =>
-
x.length ? { kind: x.tag, definitions: x.slice() } : undefined
-
)`
-
(${queryShorthand} | ${operationDefinition} | ${fragmentDefinition})*
-
`;
-
-
const _parse = makeParser(root);
-
const _parseValue = makeParser(value);
-
const _parseType = makeParser(type);
-
-
export function parse(input) {
-
const result = _parse(input);
-
if (result == null) throw new GraphQLError('Syntax Error');
-
return result;
-
}
-
-
export function parseValue(input) {
-
const result = _parseValue(input);
-
if (result == null) throw new GraphQLError('Syntax Error');
-
return result;
-
}
-
-
export function parseType(input) {
-
const result = _parseType(input);
-
if (result == null) throw new GraphQLError('Syntax Error');
-
return result;
-
}
+
export { parse, parseType, parseValue } from '@0no-co/graphql.web';
+1 -1
alias/language/printString.mjs
···
-
export const printString = JSON.stringify;
+
export { printString } from '@0no-co/graphql.web';
+1 -141
alias/language/printer.mjs
···
-
import { Kind } from 'graphql';
-
import { printBlockString } from './blockString';
-
import { printString } from './printString';
-
-
export function print(node) {
-
if (Array.isArray(node)) {
-
return node.map(print);
-
} else if (node == null || typeof node !== 'object') {
-
return node ? '' + node : '';
-
}
-
-
switch (node.kind) {
-
case 'OperationDefinition': {
-
const prefix = join(
-
[
-
node.operation,
-
print(node.name) +
-
wrap('(', join(print(node.variableDefinitions), ', '), ')'),
-
join(print(node.directives), ' '),
-
],
-
' '
-
);
-
-
return (
-
(prefix === 'query' ? '' : prefix + ' ') + print(node.selectionSet)
-
);
-
}
-
-
case 'VariableDefinition':
-
return (
-
print(node.variable) +
-
': ' +
-
print(node.type) +
-
wrap(' = ', print(node.defaultValue)) +
-
wrap(' ', join(print(node.directives), ' '))
-
);
-
-
case 'Field':
-
return join(
-
[
-
wrap('', print(node.alias), ': ') +
-
print(node.name) +
-
wrap('(', join(print(node.arguments), ', '), ')'),
-
join(print(node.directives), ' '),
-
print(node.selectionSet),
-
],
-
' '
-
);
-
-
case 'StringValue':
-
return node.isBlockString
-
? printBlockString(node.value)
-
: printString(node.value);
-
-
case 'BooleanValue':
-
return node.value ? 'true' : 'false';
-
-
case 'NullValue':
-
return 'null';
-
-
case 'IntValue':
-
case 'FloatValue':
-
case 'EnumValue':
-
case 'Name':
-
return node.value;
-
-
case 'ListValue':
-
return '[' + join(print(node.values), ', ') + ']';
-
-
case 'ObjectValue':
-
return '{' + join(print(node.fields), ', ') + '}';
-
-
case 'ObjectField':
-
return node.name.value + ': ' + print(node.value);
-
-
case 'Variable':
-
return '$' + node.name.value;
-
case 'Document':
-
return join(print(node.definitions), '\n\n') + '\n';
-
case 'SelectionSet':
-
return block(print(node.selections));
-
case 'Argument':
-
return node.name.value + ': ' + print(node.value);
-
-
case 'FragmentSpread':
-
return (
-
'...' + print(node.name) + wrap(' ', join(print(node.directives), ' '))
-
);
-
-
case 'InlineFragment':
-
return join(
-
[
-
'...',
-
wrap('on ', print(node.typeCondition)),
-
join(print(node.directives), ' '),
-
print(node.selectionSet),
-
],
-
' '
-
);
-
-
case 'FragmentDefinition':
-
return (
-
'fragment ' +
-
node.name.value +
-
wrap('(', join(print(node.variableDefinitions), ', '), ')') +
-
' ' +
-
'on ' +
-
print(node.typeCondition) +
-
' ' +
-
wrap('', join(print(node.directives), ' '), ' ') +
-
print(node.selectionSet)
-
);
-
-
case 'Directive':
-
return (
-
'@' +
-
node.name.value +
-
wrap('(', join(print(node.arguments), ', '), ')')
-
);
-
-
case 'NamedType':
-
return node.name.value;
-
-
case 'ListType':
-
return '[' + print(node.type) + ']';
-
-
case 'NonNullType':
-
return print(node.type) + '!';
-
-
default:
-
return '';
-
}
-
}
-
-
const join = (array, separator) =>
-
(array && array.filter((x) => x).join(separator || '')) || '';
-
-
const block = (array) =>
-
wrap('{\n ', join(array, '\n').replace(/\n/g, '\n '), '\n}');
-
-
const wrap = (start, value, end) => (value ? start + value + (end || '') : '');
+
export { print } from '@0no-co/graphql.web';
+2 -87
alias/language/visitor.mjs
···
-
import {
+
export {
getEnterLeaveForKind,
getVisitFn,
visitInParallel,
-
BREAK,
} from 'graphql/language/visitor';
-
export { getEnterLeaveForKind, getVisitFn, visitInParallel, BREAK };
-
-
export function visit(node, visitor) {
-
const path = [];
-
const ancestors = [];
-
-
function traverse(node, key, parent) {
-
let hasEdited = false;
-
-
const enter = getVisitFn(visitor, node.kind, false);
-
const resultEnter =
-
enter && enter.call(visitor, node, key, parent, path, ancestors);
-
if (resultEnter === false) {
-
return node;
-
} else if (resultEnter === null) {
-
return null;
-
} else if (resultEnter === BREAK) {
-
throw BREAK;
-
} else if (resultEnter && typeof resultEnter.kind === 'string') {
-
hasEdited = resultEnter !== node;
-
node = resultEnter;
-
}
-
-
if (parent) ancestors.push(parent);
-
-
let result;
-
const copy = { ...node };
-
for (const nodeKey in node) {
-
path.push(nodeKey);
-
let value = node[nodeKey];
-
if (Array.isArray(value)) {
-
const newValue = [];
-
for (let index = 0; index < value.length; index++) {
-
if (value[index] != null && typeof value[index].kind === 'string') {
-
ancestors.push(node);
-
path.push(index);
-
result = traverse(value[index], index, value);
-
path.pop();
-
ancestors.pop();
-
if (result === undefined) {
-
newValue.push(value[index]);
-
} else if (result === null) {
-
hasEdited = true;
-
} else {
-
hasEdited = hasEdited || result !== value[index];
-
newValue.push(result);
-
}
-
}
-
}
-
value = newValue;
-
} else if (value != null && typeof value.kind === 'string') {
-
result = traverse(value, nodeKey, node);
-
if (result !== undefined) {
-
hasEdited = hasEdited || value !== result;
-
value = result;
-
}
-
}
-
-
path.pop();
-
if (hasEdited) copy[nodeKey] = value;
-
}
-
-
if (parent) ancestors.pop();
-
const leave = getVisitFn(visitor, node.kind, true);
-
const resultLeave =
-
leave && leave.call(visitor, node, key, parent, path, ancestors);
-
if (resultLeave === BREAK) {
-
throw BREAK;
-
} else if (resultLeave !== undefined) {
-
return resultLeave;
-
} else if (resultEnter !== undefined) {
-
return hasEdited ? copy : resultEnter;
-
} else {
-
return hasEdited ? copy : node;
-
}
-
}
-
-
try {
-
const result = traverse(node);
-
return result !== undefined && result !== false ? result : node;
-
} catch (error) {
-
if (error !== BREAK) throw error;
-
return node;
-
}
-
}
+
export { BREAK, visit } from '@0no-co/graphql.web';
+1
package.json
···
"url": "https://github.com/kitten/graphql-web-lite.git"
},
"devDependencies": {
+
"@0no-co/graphql.web": "^0.1.2",
"@babel/core": "^7.15.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-buble": "^0.21.3",
+11
tsconfig.json
···
+
{
+
"compilerOptions": {
+
"target": "ESNext",
+
"module": "ESNext",
+
"allowJs": true,
+
"moduleResolution": "node",
+
"strict": true,
+
"skipLibCheck": true,
+
"noEmit": true
+
}
+
}
+5
yarn.lock
···
# yarn lockfile v1
+
"@0no-co/graphql.web@^0.1.2":
+
version "0.1.2"
+
resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-0.1.2.tgz#6a8f71bad35e1db0f1c464bc5aaf7a8d02c03b23"
+
integrity sha512-Jgn4aaiXit6yDL4tBZvv1wV2x9rCEsElqpeNxO80NHUNOPP+QY9VxA1dq9RTSaeaeN6/i994gNV+UvRIX3gQLQ==
+
"@babel/code-frame@7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"