graphql-document-analyzer
Version:
Resilient analyzing and printing of GraphQL documents
1,170 lines (1,146 loc) • 34.1 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
analyze: () => analyze,
interpolate: () => interpolate,
isExtendedDocumentNode: () => isExtendedDocumentNode,
print: () => print2,
visit: () => visit3
});
module.exports = __toCommonJS(src_exports);
// src/analyze.ts
var import_graphql7 = require("graphql");
// src/lib/source.ts
var import_graphql = require("graphql");
function substring(source, location) {
return source.body.substring(location.start, location.end);
}
function splitLines(source) {
source = typeof source === "string" ? new import_graphql.Source(source) : source;
const lines = [];
let lastIndex = 0;
let line = 0;
for (const match of source.body.matchAll(/\r?\n/g)) {
line += 1;
if (match.index == null)
continue;
const start2 = lastIndex;
const end2 = match.index;
const value2 = source.body.substring(start2, end2);
lines.push(new import_graphql.Token(import_graphql.TokenKind.STRING, start2, end2, line, 1, null, value2));
lastIndex = match.index + match[0].length;
}
const start = lastIndex;
const end = source.body.length;
const value = source.body.substring(start, end);
lines.push(new import_graphql.Token(import_graphql.TokenKind.STRING, start, end, line + 1, 1, null, value));
return lines;
}
// src/resilient-parser.ts
var import_graphql6 = require("graphql");
var import_parser = require("graphql/language/parser");
// src/extended-ast.ts
var import_graphql2 = require("graphql");
// src/lib/is-record.ts
function isRecord(value) {
return value != null && typeof value === "object";
}
// src/extended-ast.ts
function isExtendedNode(node) {
return isRecord(node) && (node.kind === "ExtendedDocument" || node.kind === "InvalidOperationDefinition" || node.kind === "InvalidFragmentDefinition" || node.kind === "Invalid" || node.kind === "Ignored");
}
function isExtendedDocumentNode(node) {
return isRecord(node) && node.kind === "ExtendedDocument";
}
function invalidOperationDefinition(source, operation, name, start, end) {
const loc = new import_graphql2.Location(start, end, source);
return {
kind: "InvalidOperationDefinition",
operation,
name: name ? { kind: "Name", value: name } : void 0,
value: substring(source, loc),
loc
};
}
function invalidShorthandOperationDefinition(source, start, end) {
const loc = new import_graphql2.Location(start, end, source);
return {
kind: "InvalidOperationDefinition",
operation: "query",
value: substring(source, loc),
loc
};
}
function invalidFragment(source, name, typeCondition, start, end) {
const loc = new import_graphql2.Location(start, end, source);
return {
kind: "InvalidFragmentDefinition",
name: { kind: "Name", value: name },
typeCondition: {
kind: "NamedType",
name: { kind: "Name", value: typeCondition }
},
value: substring(source, loc),
loc
};
}
function invalid(source, start, end) {
const loc = new import_graphql2.Location(start, end, source);
return { kind: "Invalid", value: substring(source, loc), loc };
}
function ignored(source, start, end) {
const loc = new import_graphql2.Location(start, end, source);
return { kind: "Ignored", value: substring(source, loc), loc };
}
// src/lib/insert-whitespace.ts
var import_graphql3 = require("graphql");
function insertWhitespace(source, lines, sections) {
var _a, _b;
const lastLine = lines.at(-1);
if (!lastLine)
return sections;
const SOF = new import_graphql3.Token(import_graphql3.TokenKind.SOF, 0, 0, 0, 0, null);
const EOF = new import_graphql3.Token(
import_graphql3.TokenKind.EOF,
lastLine.end,
lastLine.end,
lastLine.line + 1,
lastLine.column + ((_b = (_a = lastLine.value) == null ? void 0 : _a.length) != null ? _b : 0),
lastLine
);
if (!sections.length) {
return [ignored(source, SOF, EOF)];
}
const lastSection = sections.at(-1);
const trailingRange = (lastSection == null ? void 0 : lastSection.loc) ? linesBetween(lines, lastSection.loc.endToken, EOF) : void 0;
const trailing = trailingRange ? ignored(source, trailingRange[0], trailingRange[1]) : void 0;
const withWhitespace = [
...sections.map((section, index) => {
var _a2, _b2, _c, _d;
const before = (_c = (_b2 = (_a2 = sections[index - 1]) == null ? void 0 : _a2.loc) == null ? void 0 : _b2.endToken) != null ? _c : SOF;
const leadingRange = section.loc ? linesBetween(lines, before, (_d = section.loc) == null ? void 0 : _d.startToken) : void 0;
const leading = leadingRange ? ignored(source, leadingRange[0], leadingRange[1]) : void 0;
return [leading, section];
}).flat(),
trailing
].filter(Boolean);
return withWhitespace;
}
function linesBetween(lines, before, after) {
const startIndex = before.line - 1 + 1;
const endIndex = after.line - 1 - 1;
if (startIndex < 0 || endIndex < 0)
return void 0;
if (startIndex > endIndex)
return void 0;
const start = lines[startIndex];
const end = lines[endIndex];
return start && end ? [start, end] : void 0;
}
// src/lib/landmarks.ts
var import_graphql5 = require("graphql");
// src/lib/lexer.ts
var import_graphql4 = require("graphql");
function restoreLexer(lexer, snapshot) {
lexer.lastToken = snapshot.lastToken;
lexer.token = snapshot.token;
lexer.line = snapshot.line;
lexer.lineStart = snapshot.lineStart;
}
function snapshotLexer(lexer) {
const { lastToken, token, line, lineStart } = lexer;
return {
lastToken: new import_graphql4.Token(
lastToken.kind,
lastToken.start,
lastToken.end,
lastToken.line,
lastToken.column,
lastToken.prev,
lastToken.value
),
token: new import_graphql4.Token(
token.kind,
token.start,
token.end,
token.line,
token.column,
token.prev,
token.value
),
line,
lineStart
};
}
function safeAdvanceToEOF(lexer) {
while (lexer.token.kind !== import_graphql4.TokenKind.EOF) {
safeAdvance(lexer);
}
}
function strictAdvanceToEOF(lexer) {
while (lexer.token.kind !== import_graphql4.TokenKind.EOF) {
if (lexer.token.kind !== import_graphql4.TokenKind.COMMENT) {
throw new Error(`Unexpected token kind "${lexer.token.kind}"`);
}
lexer.advance();
}
}
function safeAdvance(lexer) {
try {
lexer.advance();
} catch {
const lines = splitLines(lexer.source);
const EOF = new import_graphql4.Token(
import_graphql4.TokenKind.EOF,
lexer.source.body.length,
lexer.source.body.length,
lines.length + 1,
1,
lexer.token
);
lexer.lastToken = EOF;
lexer.token = EOF;
}
}
// src/lib/landmarks.ts
function findLandmarks(source, lines) {
const landmarks = [];
for (const line of lines) {
const operation = tryParseOperation(source, line);
if (operation)
landmarks.push(operation);
const fragment = tryParseFragment(source, line);
if (fragment)
landmarks.push(fragment);
}
return landmarks;
}
function findNextLandmark(landmarks, lineNumber = 1) {
return landmarks.find(
(landmark) => {
var _a;
return landmark.loc && ((_a = landmark.loc) == null ? void 0 : _a.startToken.line) >= lineNumber;
}
);
}
function safeAdvanceToLandmark(lexer, landmark) {
while (landmark.loc ? lexer.token.kind !== import_graphql5.TokenKind.EOF && lexer.token.line < landmark.loc.startToken.line : lexer.token.kind !== import_graphql5.TokenKind.EOF) {
safeAdvance(lexer);
}
}
function strictAdvanceToLandmark(lexer, landmark) {
while ((landmark == null ? void 0 : landmark.loc) ? lexer.token.kind !== import_graphql5.TokenKind.EOF && lexer.token.line < landmark.loc.startToken.line : lexer.token.kind !== import_graphql5.TokenKind.EOF) {
if (lexer.token.kind !== import_graphql5.TokenKind.COMMENT) {
throw new Error(`Unexpected token kind "${lexer.token.kind}"`);
}
lexer.advance();
}
}
var OPERATION = /^\s*(query\s*|mutation\s*|subscription\s*)(\w+)?.*{/;
function tryParseOperation(source, line) {
var _a, _b, _c;
if (!line.value)
return false;
const match = line.value.match(OPERATION);
if (match == null)
return false;
const operation = (_b = (_a = match[1]) == null ? void 0 : _a.trim()) != null ? _b : "query";
const name = (_c = match[2]) == null ? void 0 : _c.trim();
return invalidOperationDefinition(source, operation, name, line, line);
}
var FRAGMENT = /^\s*fragment\s+(\w+)\s+on\s+(\w+)\s*/;
function tryParseFragment(source, line) {
if (!line.value)
return false;
const match = line.value.match(FRAGMENT);
if (match == null)
return false;
const name = match[1];
const typeCondition = match[2];
return invalidFragment(source, name, typeCondition, line, line);
}
// src/resilient-parser.ts
var ResilientParser = class extends import_parser.Parser {
constructor(source, options = {}) {
source = typeof source === "string" ? new import_graphql6.Source(source) : source;
super(source, options);
this._lines = splitLines(source);
this._landmarks = findLandmarks(source, this._lines);
}
parseExtendedDocument() {
const definitions = this.zeroToMany(
import_graphql6.TokenKind.SOF,
this.parseSection,
import_graphql6.TokenKind.EOF
);
const sections = insertWhitespace(
this._lexer.source,
this._lines,
definitions
);
return {
kind: "ExtendedDocument",
sections
};
}
parseSection() {
if (this.peek(import_graphql6.TokenKind.NAME)) {
switch (this._lexer.token.value) {
case "query":
case "mutation":
case "subscription":
return this.tryParseOperationDefinition();
case "fragment":
return this.tryParseFragmentDefinition();
}
} else if (this.peek(import_graphql6.TokenKind.BRACE_L)) {
return this.tryParseOperationDefinition();
}
return this.parseInvalid();
}
tryParseOperationDefinition() {
const snapshot = snapshotLexer(this._lexer);
try {
const definition = this.parseOperationDefinition();
const next = findNextLandmark(this._landmarks, this._lexer.token.line);
if (next) {
strictAdvanceToLandmark(this._lexer, next);
} else {
strictAdvanceToEOF(this._lexer);
}
return definition;
} catch (error) {
restoreLexer(this._lexer, snapshot);
const start = this._lexer.token;
const invalidOperation = start.kind === import_graphql6.TokenKind.BRACE_L ? invalidShorthandOperationDefinition(
this._lexer.source,
start,
start
) : tryParseOperation(this._lexer.source, this._lines[start.line - 1]);
if (!invalidOperation)
return this.parseInvalid();
const next = findNextLandmark(
this._landmarks,
this._lexer.token.line + 1
);
if (next) {
safeAdvanceToLandmark(this._lexer, next);
} else {
safeAdvanceToEOF(this._lexer);
}
const end = this._lexer.lastToken;
const loc = new import_graphql6.Location(start, end, this._lexer.source);
return {
...invalidOperation,
value: substring(this._lexer.source, loc),
loc
};
}
}
tryParseFragmentDefinition() {
const snapshot = snapshotLexer(this._lexer);
try {
const fragment = this.parseFragmentDefinition();
const next = findNextLandmark(this._landmarks, this._lexer.token.line);
if (next) {
strictAdvanceToLandmark(this._lexer, next);
} else {
strictAdvanceToEOF(this._lexer);
}
return fragment;
} catch (error) {
restoreLexer(this._lexer, snapshot);
const start = this._lexer.token;
const invalidFragment2 = tryParseFragment(
this._lexer.source,
this._lines[start.line - 1]
);
if (!invalidFragment2)
return this.parseInvalid();
const next = findNextLandmark(
this._landmarks,
this._lexer.token.line + 1
);
if (next) {
safeAdvanceToLandmark(this._lexer, next);
} else {
safeAdvanceToEOF(this._lexer);
}
const end = this._lexer.lastToken;
const loc = new import_graphql6.Location(start, end, this._lexer.source);
return {
...invalidFragment2,
value: substring(this._lexer.source, loc),
loc
};
}
}
parseInvalid() {
const start = this._lexer.token;
const next = findNextLandmark(this._landmarks, this._lexer.token.line + 1);
if (next) {
safeAdvanceToLandmark(this._lexer, next);
} else {
safeAdvanceToEOF(this._lexer);
}
const end = this._lexer.lastToken;
return invalid(this._lexer.source, start, end);
}
// @override
parseSelectionSet() {
const start = this._lexer.token;
return {
kind: import_graphql6.Kind.SELECTION_SET,
selections: this.zeroToMany(
import_graphql6.TokenKind.BRACE_L,
this.parseSelection,
import_graphql6.TokenKind.BRACE_R
),
loc: this.loc(start)
};
}
/**
* Returns a list of parse nodes that may be empty, determined by the parseFn.
* This list begins with a lex token of openKind and ends with a lex token of closeKind.
* Advances the parser to the next lex token after the closing token.
*/
zeroToMany(openKind, parseFn, closeKind) {
this.expectToken(openKind);
const nodes = [];
while (!this.expectOptionalToken(closeKind)) {
nodes.push(parseFn.call(this));
}
return nodes;
}
};
// src/analyze.ts
function analyze(source, options) {
const parser = new ResilientParser(source, options);
return parser.parseExtendedDocument();
}
// src/interpolate.ts
function interpolate(document, reference) {
const documentOutline = document.sections.filter(isRelevant);
const referenceOutline = reference.sections.filter(isRelevant);
if (documentOutline.length !== referenceOutline.length)
return document;
const sections = document.sections.map((section) => {
if (section.kind === "InvalidOperationDefinition") {
const index = documentOutline.indexOf(section);
const isAnonymous = !section.name;
const replacement = isAnonymous ? referenceOutline.find(findAnonymousOperation(section, index)) : referenceOutline.find(findNamedOperation(section));
if (replacement) {
return { ...replacement, loc: section.loc };
}
}
if (section.kind === "InvalidFragmentDefinition") {
const replacement = referenceOutline.find(findFragment(section));
if (replacement) {
return { ...replacement, loc: section.loc };
}
}
return section;
});
return { kind: "ExtendedDocument", sections };
}
function isRelevant(section) {
return section.kind === "OperationDefinition" || section.kind === "InvalidOperationDefinition" || section.kind === "FragmentDefinition" || section.kind === "InvalidFragmentDefinition";
}
function findAnonymousOperation(operation, index) {
return (relevant, relevantIndex) => {
return index === relevantIndex && relevant.kind === "OperationDefinition" && relevant.operation === operation.operation && !relevant.name;
};
}
function findNamedOperation(operation) {
return (relevant) => {
var _a;
return relevant.kind === "OperationDefinition" && relevant.operation === operation.operation && !!relevant.name && relevant.name.value === ((_a = operation.name) == null ? void 0 : _a.value);
};
}
function findFragment(fragment) {
return (relevant) => {
return relevant.kind === "FragmentDefinition" && relevant.name.value === fragment.name.value;
};
}
// src/print.ts
var import_graphql9 = require("graphql");
// src/graphql-js/language/characterClasses.ts
function isWhiteSpace(code) {
return code === 9 || code === 32;
}
// src/graphql-js/language/blockString.ts
function printBlockString(value, options) {
const escapedValue = value.replace(/"""/g, '\\"""');
const lines = escapedValue.split(/\r\n|[\n\r]/g);
const isSingleLine = lines.length === 1;
const forceLeadingNewLine = lines.length > 1 && lines.slice(1).every((line) => line.length === 0 || isWhiteSpace(line.charCodeAt(0)));
const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""');
const hasTrailingQuote = value.endsWith('"') && !hasTrailingTripleQuotes;
const hasTrailingSlash = value.endsWith("\\");
const forceTrailingNewline = hasTrailingQuote || hasTrailingSlash;
const printAsMultipleLines = !(options == null ? void 0 : options.minimize) && // add leading and trailing new lines only if it improves readability
(!isSingleLine || value.length > 70 || forceTrailingNewline || forceLeadingNewLine || hasTrailingTripleQuotes);
let result = "";
const skipLeadingNewLine = isSingleLine && isWhiteSpace(value.charCodeAt(0));
if (printAsMultipleLines && !skipLeadingNewLine || forceLeadingNewLine) {
result += "\n";
}
result += escapedValue;
if (printAsMultipleLines || forceTrailingNewline) {
result += "\n";
}
return '"""' + result + '"""';
}
// src/graphql-js/language/printString.ts
function printString(str) {
return `"${str.replace(escapedRegExp, escapedReplacer)}"`;
}
var escapedRegExp = /[\x00-\x1f\x22\x5c\x7f-\x9f]/g;
function escapedReplacer(str) {
return escapeSequences[str.charCodeAt(0)];
}
var escapeSequences = [
"\\u0000",
"\\u0001",
"\\u0002",
"\\u0003",
"\\u0004",
"\\u0005",
"\\u0006",
"\\u0007",
"\\b",
"\\t",
"\\n",
"\\u000B",
"\\f",
"\\r",
"\\u000E",
"\\u000F",
"\\u0010",
"\\u0011",
"\\u0012",
"\\u0013",
"\\u0014",
"\\u0015",
"\\u0016",
"\\u0017",
"\\u0018",
"\\u0019",
"\\u001A",
"\\u001B",
"\\u001C",
"\\u001D",
"\\u001E",
"\\u001F",
"",
"",
'\\"',
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 2F
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 3F
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 4F
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"\\\\",
"",
"",
"",
// 5F
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 6F
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"\\u007F",
"\\u0080",
"\\u0081",
"\\u0082",
"\\u0083",
"\\u0084",
"\\u0085",
"\\u0086",
"\\u0087",
"\\u0088",
"\\u0089",
"\\u008A",
"\\u008B",
"\\u008C",
"\\u008D",
"\\u008E",
"\\u008F",
"\\u0090",
"\\u0091",
"\\u0092",
"\\u0093",
"\\u0094",
"\\u0095",
"\\u0096",
"\\u0097",
"\\u0098",
"\\u0099",
"\\u009A",
"\\u009B",
"\\u009C",
"\\u009D",
"\\u009E",
"\\u009F"
];
// src/graphql-js/language/printer.ts
var import_graphql8 = require("graphql");
var import_ast = require("graphql/language/ast.js");
function print(ast) {
return (0, import_graphql8.visit)(ast, printDocASTReducer);
}
var MAX_LINE_LENGTH = 80;
var printDocASTReducer = {
Name: { leave: (node) => node.value },
Variable: { leave: (node) => "$" + node.name },
// Document
Document: {
leave: (node) => join(node.definitions, "\n\n")
},
OperationDefinition: {
leave(node) {
const inlineVarDefs = wrap(
"(",
join(node.variableDefinitions, ", "),
")"
);
const inlinePrefix = join(
[
node.operation,
join([node.name, inlineVarDefs]),
join(node.directives, " ")
],
" "
);
if (inlinePrefix.length + 2 <= MAX_LINE_LENGTH) {
return (inlinePrefix === "query" ? "" : inlinePrefix + " ") + node.selectionSet;
}
const blockVarDefs = node.variableDefinitions ? block(node.variableDefinitions, "(", ")", "") : "";
const blockPrefix = join(
[
node.operation,
join([node.name, blockVarDefs]),
join(node.directives, " ")
],
" "
);
return blockPrefix + " " + node.selectionSet;
}
},
VariableDefinition: {
leave: ({ variable, type, defaultValue, directives }) => variable + ": " + type + wrap(" = ", defaultValue) + wrap(" ", join(directives, " "))
},
SelectionSet: { leave: ({ selections }) => block(selections) },
Field: {
leave({
alias,
name,
arguments: args,
// nullabilityAssertion,
directives,
selectionSet
}) {
const prefix = join([wrap("", alias, ": "), name], "");
let argsLine = prefix + wrap("(", join(args, ", "), ")");
if (argsLine.length > MAX_LINE_LENGTH) {
argsLine = prefix + wrap("(\n", indent(join(args, "\n")), "\n)");
}
return join([
argsLine,
// Note: Client Controlled Nullability is experimental and may be
// changed or removed in the future.
// nullabilityAssertion,
wrap(" ", join(directives, " ")),
wrap(" ", selectionSet)
]);
}
},
Argument: { leave: ({ name, value }) => name + ": " + value },
// Nullability Modifiers
// ListNullabilityOperator: {
// leave({ nullabilityAssertion }) {
// return join(['[', nullabilityAssertion, ']']);
// },
// },
// NonNullAssertion: {
// leave({ nullabilityAssertion }) {
// return join([nullabilityAssertion, '!']);
// },
// },
// ErrorBoundary: {
// leave({ nullabilityAssertion }) {
// return join([nullabilityAssertion, '?']);
// },
// },
// Fragments
FragmentSpread: {
leave: ({ name, directives }) => "..." + name + wrap(" ", join(directives, " "))
},
InlineFragment: {
leave: ({ typeCondition, directives, selectionSet }) => join(
[
"...",
wrap("on ", typeCondition),
join(directives, " "),
selectionSet
],
" "
)
},
FragmentDefinition: {
leave: ({
name,
typeCondition,
variableDefinitions,
directives,
selectionSet
}) => (
// Note: fragment variable definitions are experimental and may be changed
// or removed in the future.
`fragment ${name}${wrap("(", join(variableDefinitions, ", "), ")")} on ${typeCondition} ${wrap("", join(directives, " "), " ")}` + selectionSet
)
},
// Value
IntValue: { leave: ({ value }) => value },
FloatValue: { leave: ({ value }) => value },
StringValue: {
leave: ({ value, block: isBlockString }) => isBlockString === true ? printBlockString(value) : printString(value)
},
BooleanValue: { leave: ({ value }) => value ? "true" : "false" },
NullValue: { leave: () => "null" },
EnumValue: { leave: ({ value }) => value },
ListValue: {
leave: ({ values }) => {
const valuesLine = "[" + join(values, ", ") + "]";
if (valuesLine.length > MAX_LINE_LENGTH) {
return "[\n" + indent(join(values, "\n")) + "\n]";
}
return valuesLine;
}
},
ObjectValue: {
leave: ({ fields }, key, parent, path, ancestors) => {
const depth = [...ancestors, parent].filter(
(ancestor) => (0, import_ast.isNode)(ancestor) && ancestor.kind === "ObjectValue"
).length;
const parentKey = (0, import_ast.isNode)(parent) && parent.kind === "ObjectField" ? parent.name.value : void 0;
const fieldsLine = "{ " + join(fields, ", ") + " }";
const isWrapped = fieldsLine.indexOf("\n") >= 0;
const TOP_LEVEL_DEPTH = 2;
const INDENT = 2;
const approximateInset = (depth + TOP_LEVEL_DEPTH) * INDENT + (parentKey ? parentKey.length : 0);
return isWrapped || approximateInset + fieldsLine.length > MAX_LINE_LENGTH ? block(fields) : fieldsLine;
}
},
ObjectField: { leave: ({ name, value }) => name + ": " + value },
// Directive
Directive: {
leave: ({ name, arguments: args }) => "@" + name + wrap("(", join(args, ", "), ")")
},
// Type
NamedType: { leave: ({ name }) => name },
ListType: { leave: ({ type }) => "[" + type + "]" },
NonNullType: { leave: ({ type }) => type + "!" },
// Type System Definitions
SchemaDefinition: {
leave: ({ description, directives, operationTypes }) => wrap("", description, "\n") + join(["schema", join(directives, " "), block(operationTypes)], " ")
},
OperationTypeDefinition: {
leave: ({ operation, type }) => operation + ": " + type
},
ScalarTypeDefinition: {
leave: ({ description, name, directives }) => wrap("", description, "\n") + join(["scalar", name, join(directives, " ")], " ")
},
ObjectTypeDefinition: {
leave: ({ description, name, interfaces, directives, fields }) => wrap("", description, "\n") + join(
[
"type",
name,
wrap("implements ", join(interfaces, " & ")),
join(directives, " "),
block(fields)
],
" "
)
},
FieldDefinition: {
leave: ({ description, name, arguments: args, type, directives }) => wrap("", description, "\n") + name + (hasMultilineItems(args) ? wrap("(\n", indent(join(args, "\n")), "\n)") : wrap("(", join(args, ", "), ")")) + ": " + type + wrap(" ", join(directives, " "))
},
InputValueDefinition: {
leave: ({ description, name, type, defaultValue, directives }) => wrap("", description, "\n") + join(
[name + ": " + type, wrap("= ", defaultValue), join(directives, " ")],
" "
)
},
InterfaceTypeDefinition: {
leave: ({ description, name, interfaces, directives, fields }) => wrap("", description, "\n") + join(
[
"interface",
name,
wrap("implements ", join(interfaces, " & ")),
join(directives, " "),
block(fields)
],
" "
)
},
UnionTypeDefinition: {
leave: ({ description, name, directives, types }) => wrap("", description, "\n") + join(
["union", name, join(directives, " "), wrap("= ", join(types, " | "))],
" "
)
},
EnumTypeDefinition: {
leave: ({ description, name, directives, values }) => wrap("", description, "\n") + join(["enum", name, join(directives, " "), block(values)], " ")
},
EnumValueDefinition: {
leave: ({ description, name, directives }) => wrap("", description, "\n") + join([name, join(directives, " ")], " ")
},
InputObjectTypeDefinition: {
leave: ({ description, name, directives, fields }) => wrap("", description, "\n") + join(["input", name, join(directives, " "), block(fields)], " ")
},
DirectiveDefinition: {
leave: ({ description, name, arguments: args, repeatable, locations }) => wrap("", description, "\n") + "directive @" + name + (hasMultilineItems(args) ? wrap("(\n", indent(join(args, "\n")), "\n)") : wrap("(", join(args, ", "), ")")) + (repeatable ? " repeatable" : "") + " on " + join(locations, " | ")
},
SchemaExtension: {
leave: ({ directives, operationTypes }) => join(
["extend schema", join(directives, " "), block(operationTypes)],
" "
)
},
ScalarTypeExtension: {
leave: ({ name, directives }) => join(["extend scalar", name, join(directives, " ")], " ")
},
ObjectTypeExtension: {
leave: ({ name, interfaces, directives, fields }) => join(
[
"extend type",
name,
wrap("implements ", join(interfaces, " & ")),
join(directives, " "),
block(fields)
],
" "
)
},
InterfaceTypeExtension: {
leave: ({ name, interfaces, directives, fields }) => join(
[
"extend interface",
name,
wrap("implements ", join(interfaces, " & ")),
join(directives, " "),
block(fields)
],
" "
)
},
UnionTypeExtension: {
leave: ({ name, directives, types }) => join(
[
"extend union",
name,
join(directives, " "),
wrap("= ", join(types, " | "))
],
" "
)
},
EnumTypeExtension: {
leave: ({ name, directives, values }) => join(["extend enum", name, join(directives, " "), block(values)], " ")
},
InputObjectTypeExtension: {
leave: ({ name, directives, fields }) => join(["extend input", name, join(directives, " "), block(fields)], " ")
}
};
function join(maybeArray, separator = "") {
var _a;
return (_a = maybeArray == null ? void 0 : maybeArray.filter((x) => x).join(separator)) != null ? _a : "";
}
function block(array, start = "{", end = "}", separator = "") {
return wrap(`${start}
`, indent(join(array, `${separator}
`)), `
${end}`);
}
function wrap(start, maybeString, end = "") {
return maybeString != null && maybeString !== "" ? start + maybeString + end : "";
}
function indent(str) {
return wrap(" ", str.replace(/\n/g, "\n "));
}
function hasMultilineItems(maybeArray) {
var _a;
return (_a = maybeArray == null ? void 0 : maybeArray.some((str) => str.includes("\n"))) != null ? _a : false;
}
// src/lib/trailing-newline.ts
function ensureTrailingNewline(value) {
return value.endsWith("\n") ? value : `${value}
`;
}
function trimTrailingNewlines(value) {
const match = value.match(/\n+$/);
if (match == null)
return value;
const trimmed = value.substring(0, match.index);
return trimmed;
}
// src/lib/trim-trailing-whitespace.ts
function trimTrailingWhitespace(value) {
return value.split(/\r?\n/g).map((line) => line.trimEnd()).join("\n");
}
// src/print.ts
function print2(ast) {
if (!isExtendedDocumentNode(ast)) {
return ensureTrailingNewline(
ast.kind === import_graphql9.Kind.DOCUMENT ? resilientPrint(ast) : print(ast)
);
}
const output = [];
for (const section of ast.sections) {
if (section.kind === "Ignored" || section.kind === "Invalid" || section.kind === "InvalidOperationDefinition" || section.kind === "InvalidFragmentDefinition") {
output.push(section.value);
continue;
}
output.push(resilientPrint(section));
}
return ensureTrailingNewline(output.join("\n"));
}
var TEMPORARY_FIELD = {
kind: import_graphql9.Kind.FIELD,
name: { kind: import_graphql9.Kind.NAME, value: "TEMPORARY_FIELD" }
};
function resilientPrint(node) {
const document = node.kind === import_graphql9.Kind.DOCUMENT ? node : {
kind: import_graphql9.Kind.DOCUMENT,
definitions: [node]
};
const temporaryDocument = (0, import_graphql9.visit)(document, {
OperationDefinition(node2) {
if (node2.selectionSet.selections.length > 0)
return;
return {
...node2,
selectionSet: {
kind: import_graphql9.Kind.SELECTION_SET,
selections: [TEMPORARY_FIELD]
}
};
},
Field(node2) {
var _a;
if (((_a = node2.selectionSet) == null ? void 0 : _a.selections.length) !== 0)
return;
return {
...node2,
selectionSet: {
kind: import_graphql9.Kind.SELECTION_SET,
selections: [TEMPORARY_FIELD]
}
};
},
InlineFragment(node2) {
if (node2.selectionSet.selections.length > 0)
return;
return {
...node2,
selectionSet: {
kind: import_graphql9.Kind.SELECTION_SET,
selections: [TEMPORARY_FIELD]
}
};
}
});
const valid = print(temporaryDocument);
const value = trimTrailingNewlines(
trimTrailingWhitespace(valid.replace(/TEMPORARY_FIELD/g, ""))
);
return value;
}
// src/visit.ts
var import_graphql10 = require("graphql");
function visit3(root, visitor) {
var _a, _b;
if (!isExtendedNode(root)) {
return (0, import_graphql10.visit)(root, visitor);
}
let node = root;
const enterLeave = getEnterLeaveForKind(visitor, root.kind);
const entered = (_a = enterLeave.enter) == null ? void 0 : _a.call(
enterLeave,
isExtendedDocumentNode(node) ? cloneDocument(node) : node,
void 0,
void 0,
[],
[]
);
if (entered === import_graphql10.BREAK) {
return node;
}
if (entered === null) {
return null;
}
if (entered !== void 0) {
node = entered;
}
if (isExtendedDocumentNode(node)) {
const visited = [];
let broke = false;
for (const section of node.sections) {
const result = visit3(section, visitor);
if (result === import_graphql10.BREAK) {
broke = true;
break;
}
visited.push(result);
}
const sections = visited.flat().filter((section) => section !== null);
node = { kind: "ExtendedDocument", sections };
if (broke) {
return node;
}
}
const left = (_b = enterLeave.leave) == null ? void 0 : _b.call(
enterLeave,
isExtendedDocumentNode(node) ? cloneDocument(node) : node,
void 0,
void 0,
[],
[]
);
if (left === import_graphql10.BREAK) {
return node;
}
if (left === null) {
return null;
}
if (left !== void 0) {
node = left;
}
return node;
}
function cloneDocument(document) {
return {
kind: "ExtendedDocument",
sections: [...document.sections]
};
}
function getEnterLeaveForKind(visitor, kind) {
const kindVisitor = visitor[kind];
if (typeof kindVisitor === "object") {
return kindVisitor;
} else if (typeof kindVisitor === "function") {
return { enter: kindVisitor, leave: void 0 };
}
return { enter: visitor.enter, leave: visitor.leave };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
analyze,
interpolate,
isExtendedDocumentNode,
print,
visit: visit2
});
// @license MIT