UNPKG

15.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getBlockStringIndentation = exports.dedentBlockStringValue = exports.getLeadingCommentBlock = exports.getComment = exports.getDescription = exports.printWithComments = exports.printComment = exports.pushComment = exports.collectComment = exports.resetComments = void 0;
4const graphql_1 = require("graphql");
5const MAX_LINE_LENGTH = 80;
6let commentsRegistry = {};
7function resetComments() {
8 commentsRegistry = {};
9}
10exports.resetComments = resetComments;
11function collectComment(node) {
12 var _a;
13 const entityName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
14 if (entityName == null) {
15 return;
16 }
17 pushComment(node, entityName);
18 switch (node.kind) {
19 case 'EnumTypeDefinition':
20 if (node.values) {
21 for (const value of node.values) {
22 pushComment(value, entityName, value.name.value);
23 }
24 }
25 break;
26 case 'ObjectTypeDefinition':
27 case 'InputObjectTypeDefinition':
28 case 'InterfaceTypeDefinition':
29 if (node.fields) {
30 for (const field of node.fields) {
31 pushComment(field, entityName, field.name.value);
32 if (isFieldDefinitionNode(field) && field.arguments) {
33 for (const arg of field.arguments) {
34 pushComment(arg, entityName, field.name.value, arg.name.value);
35 }
36 }
37 }
38 }
39 break;
40 }
41}
42exports.collectComment = collectComment;
43function pushComment(node, entity, field, argument) {
44 const comment = getComment(node);
45 if (typeof comment !== 'string' || comment.length === 0) {
46 return;
47 }
48 const keys = [entity];
49 if (field) {
50 keys.push(field);
51 if (argument) {
52 keys.push(argument);
53 }
54 }
55 const path = keys.join('.');
56 if (!commentsRegistry[path]) {
57 commentsRegistry[path] = [];
58 }
59 commentsRegistry[path].push(comment);
60}
61exports.pushComment = pushComment;
62function printComment(comment) {
63 return '\n# ' + comment.replace(/\n/g, '\n# ');
64}
65exports.printComment = printComment;
66/**
67 * Copyright (c) 2015-present, Facebook, Inc.
68 *
69 * This source code is licensed under the MIT license found in the
70 * LICENSE file in the root directory of this source tree.
71 */
72/**
73 * NOTE: ==> This file has been modified just to add comments to the printed AST
74 * This is a temp measure, we will move to using the original non modified printer.js ASAP.
75 */
76/**
77 * Given maybeArray, print an empty string if it is null or empty, otherwise
78 * print all items together separated by separator if provided
79 */
80function join(maybeArray, separator) {
81 return maybeArray ? maybeArray.filter(x => x).join(separator || '') : '';
82}
83function hasMultilineItems(maybeArray) {
84 var _a;
85 return (_a = maybeArray === null || maybeArray === void 0 ? void 0 : maybeArray.some(str => str.includes('\n'))) !== null && _a !== void 0 ? _a : false;
86}
87function addDescription(cb) {
88 return (node, _key, _parent, path, ancestors) => {
89 var _a;
90 const keys = [];
91 const parent = path.reduce((prev, key) => {
92 if (['fields', 'arguments', 'values'].includes(key) && prev.name) {
93 keys.push(prev.name.value);
94 }
95 return prev[key];
96 }, ancestors[0]);
97 const key = [...keys, (_a = parent === null || parent === void 0 ? void 0 : parent.name) === null || _a === void 0 ? void 0 : _a.value].filter(Boolean).join('.');
98 const items = [];
99 if (node.kind.includes('Definition') && commentsRegistry[key]) {
100 items.push(...commentsRegistry[key]);
101 }
102 return join([...items.map(printComment), node.description, cb(node, _key, _parent, path, ancestors)], '\n');
103 };
104}
105function indent(maybeString) {
106 return maybeString && ` ${maybeString.replace(/\n/g, '\n ')}`;
107}
108/**
109 * Given array, print each item on its own line, wrapped in an
110 * indented "{ }" block.
111 */
112function block(array) {
113 return array && array.length !== 0 ? `{\n${indent(join(array, '\n'))}\n}` : '';
114}
115/**
116 * If maybeString is not null or empty, then wrap with start and end, otherwise
117 * print an empty string.
118 */
119function wrap(start, maybeString, end) {
120 return maybeString ? start + maybeString + (end || '') : '';
121}
122/**
123 * Print a block string in the indented block form by adding a leading and
124 * trailing blank line. However, if a block string starts with whitespace and is
125 * a single-line, adding a leading blank line would strip that whitespace.
126 */
127function printBlockString(value, isDescription = false) {
128 const escaped = value.replace(/"""/g, '\\"""');
129 return (value[0] === ' ' || value[0] === '\t') && value.indexOf('\n') === -1
130 ? `"""${escaped.replace(/"$/, '"\n')}"""`
131 : `"""\n${isDescription ? escaped : indent(escaped)}\n"""`;
132}
133const printDocASTReducer = {
134 Name: { leave: node => node.value },
135 Variable: { leave: node => '$' + node.name },
136 // Document
137 Document: {
138 leave: node => join(node.definitions, '\n\n'),
139 },
140 OperationDefinition: {
141 leave: node => {
142 const varDefs = wrap('(', join(node.variableDefinitions, ', '), ')');
143 const prefix = join([node.operation, join([node.name, varDefs]), join(node.directives, ' ')], ' ');
144 // the query short form.
145 return prefix + ' ' + node.selectionSet;
146 },
147 },
148 VariableDefinition: {
149 leave: ({ variable, type, defaultValue, directives }) => variable + ': ' + type + wrap(' = ', defaultValue) + wrap(' ', join(directives, ' ')),
150 },
151 SelectionSet: { leave: ({ selections }) => block(selections) },
152 Field: {
153 leave({ alias, name, arguments: args, directives, selectionSet }) {
154 const prefix = wrap('', alias, ': ') + name;
155 let argsLine = prefix + wrap('(', join(args, ', '), ')');
156 if (argsLine.length > MAX_LINE_LENGTH) {
157 argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)');
158 }
159 return join([argsLine, join(directives, ' '), selectionSet], ' ');
160 },
161 },
162 Argument: { leave: ({ name, value }) => name + ': ' + value },
163 // Fragments
164 FragmentSpread: {
165 leave: ({ name, directives }) => '...' + name + wrap(' ', join(directives, ' ')),
166 },
167 InlineFragment: {
168 leave: ({ typeCondition, directives, selectionSet }) => join(['...', wrap('on ', typeCondition), join(directives, ' '), selectionSet], ' '),
169 },
170 FragmentDefinition: {
171 leave: ({ name, typeCondition, variableDefinitions, directives, selectionSet }) =>
172 // Note: fragment variable definitions are experimental and may be changed
173 // or removed in the future.
174 `fragment ${name}${wrap('(', join(variableDefinitions, ', '), ')')} ` +
175 `on ${typeCondition} ${wrap('', join(directives, ' '), ' ')}` +
176 selectionSet,
177 },
178 // Value
179 IntValue: { leave: ({ value }) => value },
180 FloatValue: { leave: ({ value }) => value },
181 StringValue: {
182 leave: ({ value, block: isBlockString }) => {
183 if (isBlockString) {
184 return printBlockString(value);
185 }
186 return JSON.stringify(value);
187 },
188 },
189 BooleanValue: { leave: ({ value }) => (value ? 'true' : 'false') },
190 NullValue: { leave: () => 'null' },
191 EnumValue: { leave: ({ value }) => value },
192 ListValue: { leave: ({ values }) => '[' + join(values, ', ') + ']' },
193 ObjectValue: { leave: ({ fields }) => '{' + join(fields, ', ') + '}' },
194 ObjectField: { leave: ({ name, value }) => name + ': ' + value },
195 // Directive
196 Directive: {
197 leave: ({ name, arguments: args }) => '@' + name + wrap('(', join(args, ', '), ')'),
198 },
199 // Type
200 NamedType: { leave: ({ name }) => name },
201 ListType: { leave: ({ type }) => '[' + type + ']' },
202 NonNullType: { leave: ({ type }) => type + '!' },
203 // Type System Definitions
204 SchemaDefinition: {
205 leave: ({ directives, operationTypes }) => join(['schema', join(directives, ' '), block(operationTypes)], ' '),
206 },
207 OperationTypeDefinition: {
208 leave: ({ operation, type }) => operation + ': ' + type,
209 },
210 ScalarTypeDefinition: {
211 leave: ({ name, directives }) => join(['scalar', name, join(directives, ' ')], ' '),
212 },
213 ObjectTypeDefinition: {
214 leave: ({ name, interfaces, directives, fields }) => join(['type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '),
215 },
216 FieldDefinition: {
217 leave: ({ name, arguments: args, type, directives }) => name +
218 (hasMultilineItems(args)
219 ? wrap('(\n', indent(join(args, '\n')), '\n)')
220 : wrap('(', join(args, ', '), ')')) +
221 ': ' +
222 type +
223 wrap(' ', join(directives, ' ')),
224 },
225 InputValueDefinition: {
226 leave: ({ name, type, defaultValue, directives }) => join([name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')], ' '),
227 },
228 InterfaceTypeDefinition: {
229 leave: ({ name, interfaces, directives, fields }) => join(['interface', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '),
230 },
231 UnionTypeDefinition: {
232 leave: ({ name, directives, types }) => join(['union', name, join(directives, ' '), wrap('= ', join(types, ' | '))], ' '),
233 },
234 EnumTypeDefinition: {
235 leave: ({ name, directives, values }) => join(['enum', name, join(directives, ' '), block(values)], ' '),
236 },
237 EnumValueDefinition: {
238 leave: ({ name, directives }) => join([name, join(directives, ' ')], ' '),
239 },
240 InputObjectTypeDefinition: {
241 leave: ({ name, directives, fields }) => join(['input', name, join(directives, ' '), block(fields)], ' '),
242 },
243 DirectiveDefinition: {
244 leave: ({ name, arguments: args, repeatable, locations }) => 'directive @' +
245 name +
246 (hasMultilineItems(args)
247 ? wrap('(\n', indent(join(args, '\n')), '\n)')
248 : wrap('(', join(args, ', '), ')')) +
249 (repeatable ? ' repeatable' : '') +
250 ' on ' +
251 join(locations, ' | '),
252 },
253 SchemaExtension: {
254 leave: ({ directives, operationTypes }) => join(['extend schema', join(directives, ' '), block(operationTypes)], ' '),
255 },
256 ScalarTypeExtension: {
257 leave: ({ name, directives }) => join(['extend scalar', name, join(directives, ' ')], ' '),
258 },
259 ObjectTypeExtension: {
260 leave: ({ name, interfaces, directives, fields }) => join(['extend type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '),
261 },
262 InterfaceTypeExtension: {
263 leave: ({ name, interfaces, directives, fields }) => join(['extend interface', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '),
264 },
265 UnionTypeExtension: {
266 leave: ({ name, directives, types }) => join(['extend union', name, join(directives, ' '), wrap('= ', join(types, ' | '))], ' '),
267 },
268 EnumTypeExtension: {
269 leave: ({ name, directives, values }) => join(['extend enum', name, join(directives, ' '), block(values)], ' '),
270 },
271 InputObjectTypeExtension: {
272 leave: ({ name, directives, fields }) => join(['extend input', name, join(directives, ' '), block(fields)], ' '),
273 },
274};
275const printDocASTReducerWithComments = Object.keys(printDocASTReducer).reduce((prev, key) => ({
276 ...prev,
277 [key]: {
278 leave: addDescription(printDocASTReducer[key].leave),
279 },
280}), {});
281/**
282 * Converts an AST into a string, using one set of reasonable
283 * formatting rules.
284 */
285function printWithComments(ast) {
286 return (0, graphql_1.visit)(ast, printDocASTReducerWithComments);
287}
288exports.printWithComments = printWithComments;
289function isFieldDefinitionNode(node) {
290 return node.kind === 'FieldDefinition';
291}
292// graphql < v13 and > v15 does not export getDescription
293function getDescription(node, options) {
294 if (node.description != null) {
295 return node.description.value;
296 }
297 if (options === null || options === void 0 ? void 0 : options.commentDescriptions) {
298 return getComment(node);
299 }
300}
301exports.getDescription = getDescription;
302function getComment(node) {
303 const rawValue = getLeadingCommentBlock(node);
304 if (rawValue !== undefined) {
305 return dedentBlockStringValue(`\n${rawValue}`);
306 }
307}
308exports.getComment = getComment;
309function getLeadingCommentBlock(node) {
310 const loc = node.loc;
311 if (!loc) {
312 return;
313 }
314 const comments = [];
315 let token = loc.startToken.prev;
316 while (token != null &&
317 token.kind === graphql_1.TokenKind.COMMENT &&
318 token.next != null &&
319 token.prev != null &&
320 token.line + 1 === token.next.line &&
321 token.line !== token.prev.line) {
322 const value = String(token.value);
323 comments.push(value);
324 token = token.prev;
325 }
326 return comments.length > 0 ? comments.reverse().join('\n') : undefined;
327}
328exports.getLeadingCommentBlock = getLeadingCommentBlock;
329function dedentBlockStringValue(rawString) {
330 // Expand a block string's raw value into independent lines.
331 const lines = rawString.split(/\r\n|[\n\r]/g);
332 // Remove common indentation from all lines but first.
333 const commonIndent = getBlockStringIndentation(lines);
334 if (commonIndent !== 0) {
335 for (let i = 1; i < lines.length; i++) {
336 lines[i] = lines[i].slice(commonIndent);
337 }
338 }
339 // Remove leading and trailing blank lines.
340 while (lines.length > 0 && isBlank(lines[0])) {
341 lines.shift();
342 }
343 while (lines.length > 0 && isBlank(lines[lines.length - 1])) {
344 lines.pop();
345 }
346 // Return a string of the lines joined with U+000A.
347 return lines.join('\n');
348}
349exports.dedentBlockStringValue = dedentBlockStringValue;
350/**
351 * @internal
352 */
353function getBlockStringIndentation(lines) {
354 let commonIndent = null;
355 for (let i = 1; i < lines.length; i++) {
356 const line = lines[i];
357 const indent = leadingWhitespace(line);
358 if (indent === line.length) {
359 continue; // skip empty lines
360 }
361 if (commonIndent === null || indent < commonIndent) {
362 commonIndent = indent;
363 if (commonIndent === 0) {
364 break;
365 }
366 }
367 }
368 return commonIndent === null ? 0 : commonIndent;
369}
370exports.getBlockStringIndentation = getBlockStringIndentation;
371function leadingWhitespace(str) {
372 let i = 0;
373 while (i < str.length && (str[i] === ' ' || str[i] === '\t')) {
374 i++;
375 }
376 return i;
377}
378function isBlank(str) {
379 return leadingWhitespace(str) === str.length;
380}