@graphql-markdown/docusaurus 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,581 @@
1
+ const {
2
+ isEnumType,
3
+ isUnionType,
4
+ isObjectType,
5
+ isScalarType,
6
+ isOperation,
7
+ getDefaultValue,
8
+ getTypeName,
9
+ getFields,
10
+ isDirectiveType,
11
+ isParametrizedField,
12
+ isInterfaceType,
13
+ getNamedType,
14
+ isInputType,
15
+ isListType,
16
+ isNonNullType,
17
+ isLeafType,
18
+ getRelationOfReturn,
19
+ getRelationOfField,
20
+ getRelationOfImplementation,
21
+ } = require("./graphql");
22
+
23
+ const { toSlug, escapeMDX } = require("../utils/scalars/string");
24
+ const { hasProperty, hasMethod } = require("../utils/scalars/object");
25
+ const { pathUrl } = require("../utils/scalars/url");
26
+
27
+ const {
28
+ ROOT_TYPE_LOCALE,
29
+ HEADER_SECTION_LEVEL,
30
+ HEADER_SECTION_SUB_LEVEL,
31
+ HEADER_SECTION_ITEM_LEVEL,
32
+ NO_DESCRIPTION_TEXT,
33
+ MARKDOWN_EOL,
34
+ MARKDOWN_EOP,
35
+ } = require("../const/strings");
36
+ const mdx = require("../const/mdx");
37
+
38
+ module.exports = class Printer {
39
+ constructor(
40
+ schema,
41
+ baseURL,
42
+ linkRoot = "/",
43
+ { groups, printTypeOptions } = {
44
+ groups: undefined,
45
+ printTypeOptions: undefined,
46
+ },
47
+ ) {
48
+ this.schema = schema;
49
+ this.baseURL = baseURL;
50
+ this.linkRoot = linkRoot;
51
+ this.groups = groups;
52
+ this.parentTypePrefix = printTypeOptions?.parentTypePrefix ?? true;
53
+ this.relatedTypeSection = printTypeOptions?.relatedTypeSection ?? true;
54
+ this.typeBadges = printTypeOptions?.typeBadges ?? true;
55
+ }
56
+
57
+ getRootTypeLocaleFromString(text) {
58
+ for (const [type, props] of Object.entries(ROOT_TYPE_LOCALE)) {
59
+ if (Object.values(props).includes(text)) {
60
+ return ROOT_TYPE_LOCALE[type];
61
+ }
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ getLinkCategory(graphLQLNamedType) {
67
+ switch (true) {
68
+ case isEnumType(graphLQLNamedType):
69
+ return ROOT_TYPE_LOCALE.ENUM;
70
+ case isUnionType(graphLQLNamedType):
71
+ return ROOT_TYPE_LOCALE.UNION;
72
+ case isInterfaceType(graphLQLNamedType):
73
+ return ROOT_TYPE_LOCALE.INTERFACE;
74
+ case isObjectType(graphLQLNamedType):
75
+ return ROOT_TYPE_LOCALE.TYPE;
76
+ case isInputType(graphLQLNamedType):
77
+ return ROOT_TYPE_LOCALE.INPUT;
78
+ case isScalarType(graphLQLNamedType):
79
+ return ROOT_TYPE_LOCALE.SCALAR;
80
+ case isDirectiveType(graphLQLNamedType):
81
+ return ROOT_TYPE_LOCALE.DIRECTIVE;
82
+ case isOperation(graphLQLNamedType):
83
+ return ROOT_TYPE_LOCALE.OPERATION;
84
+ }
85
+ return undefined;
86
+ }
87
+
88
+ getGroup(type) {
89
+ if (typeof this.groups === "undefined") {
90
+ return "";
91
+ }
92
+ const graphLQLNamedType = getNamedType(type);
93
+ const typeName = graphLQLNamedType.name || graphLQLNamedType;
94
+ return hasProperty(this.groups, typeName)
95
+ ? toSlug(this.groups[typeName])
96
+ : "";
97
+ }
98
+
99
+ toLink(type, name, operation) {
100
+ const fallback = {
101
+ text: name,
102
+ url: "#",
103
+ };
104
+
105
+ const graphLQLNamedType = getNamedType(type);
106
+
107
+ let category = this.getLinkCategory(graphLQLNamedType);
108
+
109
+ if (
110
+ typeof category === "undefined" ||
111
+ typeof graphLQLNamedType === "undefined" ||
112
+ graphLQLNamedType === null
113
+ ) {
114
+ return fallback;
115
+ }
116
+
117
+ // special case for support relation map
118
+ if (category === ROOT_TYPE_LOCALE.OPERATION) {
119
+ if (typeof operation === "undefined") {
120
+ return fallback;
121
+ }
122
+ category = operation;
123
+ }
124
+
125
+ const text = graphLQLNamedType.name || graphLQLNamedType;
126
+ const group = this.getGroup(type);
127
+ const url = pathUrl.join(
128
+ this.linkRoot,
129
+ this.baseURL,
130
+ group,
131
+ category.plural,
132
+ toSlug(text),
133
+ );
134
+
135
+ return {
136
+ text: text,
137
+ url: url,
138
+ };
139
+ }
140
+
141
+ getRelationLink(category, type) {
142
+ if (typeof category === "undefined") {
143
+ return "";
144
+ }
145
+ const link = this.toLink(type, type.name, category);
146
+ return `[\`${link.text}\`](${link.url}) <Badge class="secondary" text="${category.singular}"/>`;
147
+ }
148
+
149
+ printSection(
150
+ values,
151
+ section,
152
+ { level, parentType } = {
153
+ level: HEADER_SECTION_LEVEL,
154
+ parentType: undefined,
155
+ },
156
+ ) {
157
+ if (values.length === 0) {
158
+ return "";
159
+ }
160
+
161
+ if (typeof level === "undefined") {
162
+ level = HEADER_SECTION_LEVEL;
163
+ }
164
+
165
+ return `${level} ${section}${MARKDOWN_EOP}${this.printSectionItems(values, {
166
+ parentType,
167
+ })}${MARKDOWN_EOP}`;
168
+ }
169
+
170
+ printSectionItems(
171
+ values,
172
+ { level, parentType } = {
173
+ level: HEADER_SECTION_SUB_LEVEL,
174
+ parentType: undefined,
175
+ },
176
+ ) {
177
+ if (!Array.isArray(values)) {
178
+ return "";
179
+ }
180
+
181
+ if (typeof level === "undefined") {
182
+ level = HEADER_SECTION_SUB_LEVEL;
183
+ }
184
+
185
+ return values
186
+ .map((v) => v && this.printSectionItem(v, { level, parentType }))
187
+ .join(MARKDOWN_EOP);
188
+ }
189
+
190
+ printLinkAttributes(type, text) {
191
+ if (typeof type == "undefined") {
192
+ return text ?? "";
193
+ }
194
+
195
+ if (!isLeafType(type, text) && typeof type.ofType != "undefined") {
196
+ text = this.printLinkAttributes(type.ofType, text);
197
+ }
198
+
199
+ if (isListType(type)) {
200
+ return `[${text}]`;
201
+ }
202
+
203
+ if (isNonNullType(type)) {
204
+ return `${text}!`;
205
+ }
206
+
207
+ return text;
208
+ }
209
+
210
+ printLink(type, withAttributes = false, parentType = undefined) {
211
+ const link = this.toLink(type, getTypeName(type));
212
+
213
+ if (!withAttributes) {
214
+ const printParentType =
215
+ this.parentTypePrefix && typeof parentType !== "undefined";
216
+ const text = printParentType
217
+ ? `<code style={{ fontWeight: 'normal' }}>${parentType}.<b>${link.text}</b></code>`
218
+ : `\`${link.text}\``;
219
+ return `[${text}](${link.url})`;
220
+ }
221
+
222
+ const text = this.printLinkAttributes(type, link.text);
223
+
224
+ return `[\`${text}\`](${link.url})`;
225
+ }
226
+
227
+ getTypeBadges(type) {
228
+ const rootType = hasProperty(type, "type") ? type.type : type;
229
+
230
+ const badges = [];
231
+
232
+ if (type.isDeprecated) {
233
+ badges.push("deprecated");
234
+ }
235
+
236
+ if (isNonNullType(rootType)) {
237
+ badges.push("non-null");
238
+ }
239
+
240
+ if (isListType(rootType)) {
241
+ badges.push("list");
242
+ }
243
+
244
+ const category = this.getLinkCategory(getNamedType(rootType));
245
+ if (typeof category !== "undefined") {
246
+ badges.push(category);
247
+ }
248
+
249
+ const group = this.getGroup(rootType);
250
+ if (group !== "") {
251
+ badges.push(group);
252
+ }
253
+
254
+ return badges;
255
+ }
256
+
257
+ printBadges(type) {
258
+ if (!this.typeBadges) {
259
+ return "";
260
+ }
261
+
262
+ const badges = this.getTypeBadges(type);
263
+
264
+ if (badges.length === 0) {
265
+ return "";
266
+ }
267
+
268
+ return badges
269
+ .map(
270
+ (badge) =>
271
+ `<Badge class="secondary" text="${badge.singular ?? badge}"/>`,
272
+ )
273
+ .join(" ");
274
+ }
275
+
276
+ printParentLink(type) {
277
+ return hasProperty(type, "type")
278
+ ? `<Bullet />${this.printLink(type.type, true)}`
279
+ : "";
280
+ }
281
+
282
+ printSectionItem(
283
+ type,
284
+ { level, parentType } = {
285
+ level: HEADER_SECTION_SUB_LEVEL,
286
+ parentType: undefined,
287
+ },
288
+ ) {
289
+ if (typeof type === "undefined" || type === null) {
290
+ return "";
291
+ }
292
+
293
+ if (typeof level === "undefined") {
294
+ level = HEADER_SECTION_SUB_LEVEL;
295
+ }
296
+
297
+ const typeNameLink = this.printLink(type, false, parentType);
298
+ const description = this.printDescription(type, "");
299
+ const badges = this.printBadges(type);
300
+ const parentTypeLink = this.printParentLink(type);
301
+
302
+ let section = `${level} ${typeNameLink}${parentTypeLink} ${badges}${MARKDOWN_EOL}> ${description}${MARKDOWN_EOL}> `;
303
+ if (isParametrizedField(type)) {
304
+ section += this.printSectionItems(type.args, {
305
+ level: HEADER_SECTION_ITEM_LEVEL,
306
+ parentType:
307
+ typeof parentType === "undefined"
308
+ ? undefined
309
+ : `${parentType}.${type.name}`,
310
+ });
311
+ }
312
+
313
+ return section;
314
+ }
315
+
316
+ printCodeEnum(type) {
317
+ if (!isEnumType(type)) {
318
+ return "";
319
+ }
320
+
321
+ let code = `enum ${getTypeName(type)} {${MARKDOWN_EOL}`;
322
+ code += type
323
+ .getValues()
324
+ .map((v) => ` ${getTypeName(v)}`)
325
+ .join(MARKDOWN_EOL);
326
+ code += `${MARKDOWN_EOL}}`;
327
+
328
+ return code;
329
+ }
330
+
331
+ printCodeUnion(type) {
332
+ if (!isUnionType(type)) {
333
+ return "";
334
+ }
335
+
336
+ let code = `union ${getTypeName(type)} = `;
337
+ code += type
338
+ .getTypes()
339
+ .map((v) => getTypeName(v))
340
+ .join(" | ");
341
+
342
+ return code;
343
+ }
344
+
345
+ printCodeScalar(type) {
346
+ return `scalar ${getTypeName(type)}`;
347
+ }
348
+
349
+ printCodeArguments(type) {
350
+ if (!hasProperty(type, "args") || type.args.length === 0) {
351
+ return "";
352
+ }
353
+
354
+ let code = `(${MARKDOWN_EOL}`;
355
+ code += type.args.reduce((r, v) => {
356
+ const defaultValue = getDefaultValue(v);
357
+ const hasDefaultValue =
358
+ typeof defaultValue !== "undefined" && defaultValue !== null;
359
+ const printedDefault = hasDefaultValue ? ` = ${getDefaultValue(v)}` : "";
360
+ const propType = v.type.toString();
361
+ const propName = v.name.toString();
362
+ return `${r} ${propName}: ${propType}${printedDefault}${MARKDOWN_EOL}`;
363
+ }, "");
364
+ code += `)`;
365
+
366
+ return code;
367
+ }
368
+
369
+ printCodeField(type) {
370
+ let code = `${getTypeName(type)}`;
371
+ code += this.printCodeArguments(type);
372
+ code += `: ${getTypeName(type.type)}${MARKDOWN_EOL}`;
373
+
374
+ return code;
375
+ }
376
+
377
+ printCodeDirective(type) {
378
+ let code = `directive @${getTypeName(type)}`;
379
+ code += this.printCodeArguments(type);
380
+
381
+ return code;
382
+ }
383
+
384
+ printCodeType(type) {
385
+ let entity;
386
+
387
+ switch (true) {
388
+ case isInputType(type):
389
+ entity = "input";
390
+ break;
391
+ case isInterfaceType(type):
392
+ entity = "interface";
393
+ break;
394
+ default:
395
+ entity = "type";
396
+ }
397
+
398
+ const name = getTypeName(type);
399
+ const extendsInterface =
400
+ hasMethod(type, "getInterfaces") && type.getInterfaces().length > 0
401
+ ? ` implements ${type
402
+ .getInterfaces()
403
+ .map((field) => getTypeName(field))
404
+ .join(", ")}`
405
+ : "";
406
+ const typeFields = getFields(type)
407
+ .map((v) => ` ${this.printCodeField(v)}`)
408
+ .join("");
409
+
410
+ return `${entity} ${name}${extendsInterface} {${MARKDOWN_EOL}${typeFields}}`;
411
+ }
412
+
413
+ printHeader(id, title, options) {
414
+ const { toc, pagination } = { toc: true, pagination: true, ...options };
415
+ const pagination_buttons = pagination
416
+ ? ""
417
+ : `pagination_next: null${MARKDOWN_EOL}pagination_prev: null${MARKDOWN_EOL}`;
418
+ return `---${MARKDOWN_EOL}id: ${id}${MARKDOWN_EOL}title: ${title}\nhide_table_of_contents: ${!toc}${MARKDOWN_EOL}${pagination_buttons}---${MARKDOWN_EOL}`;
419
+ }
420
+
421
+ printDeprecation(type) {
422
+ if (!type.isDeprecated) {
423
+ return "";
424
+ }
425
+
426
+ const reason = type.deprecationReason
427
+ ? ": " + escapeMDX(type.deprecationReason)
428
+ : "";
429
+ return `<Badge class="warning" text="DEPRECATED${reason}"/>${MARKDOWN_EOP}`;
430
+ }
431
+
432
+ printDescription(type, noText = NO_DESCRIPTION_TEXT) {
433
+ const description =
434
+ hasProperty(type, "description") && escapeMDX(type.description);
435
+ return `${this.printDeprecation(type)}${description || noText}`;
436
+ }
437
+
438
+ printSpecification(type) {
439
+ if (!type.specifiedByURL && !type.specifiedByUrl) {
440
+ return "";
441
+ }
442
+
443
+ const url = type.specifiedByURL || type.specifiedByUrl;
444
+
445
+ // Needs newline between "export const specifiedByLinkCss" and markdown header to prevent compilation error in docusaurus
446
+ return `${HEADER_SECTION_LEVEL} <SpecifiedBy url="${url}"/>${MARKDOWN_EOP}`;
447
+ }
448
+
449
+ printCode(type) {
450
+ let code = `${MARKDOWN_EOL}\`\`\`graphql${MARKDOWN_EOL}`;
451
+ switch (true) {
452
+ case isOperation(type):
453
+ code += this.printCodeField(type);
454
+ break;
455
+ case isEnumType(type):
456
+ code += this.printCodeEnum(type);
457
+ break;
458
+ case isUnionType(type):
459
+ code += this.printCodeUnion(type);
460
+ break;
461
+ case isInterfaceType(type):
462
+ case isObjectType(type):
463
+ case isInputType(type):
464
+ code += this.printCodeType(type);
465
+ break;
466
+ case isScalarType(type):
467
+ code += this.printCodeScalar(type);
468
+ break;
469
+ case isDirectiveType(type):
470
+ code += this.printCodeDirective(type);
471
+ break;
472
+ default:
473
+ code += `"${getTypeName(type)}" not supported`;
474
+ }
475
+ return code.trim() + `${MARKDOWN_EOL}\`\`\`${MARKDOWN_EOL}`;
476
+ }
477
+
478
+ printTypeMetadata(type) {
479
+ let metadata;
480
+ switch (true) {
481
+ case isScalarType(type):
482
+ return this.printSpecification(type);
483
+ case isEnumType(type):
484
+ return this.printSection(type.getValues(), "Values", {
485
+ parentType: type.name,
486
+ });
487
+ case isUnionType(type):
488
+ return this.printSection(type.getTypes(), "Possible types");
489
+ case isObjectType(type):
490
+ case isInterfaceType(type):
491
+ case isInputType(type):
492
+ metadata = this.printSection(getFields(type), "Fields", {
493
+ parentType: type.name,
494
+ });
495
+ if (hasMethod(type, "getInterfaces")) {
496
+ metadata += this.printSection(type.getInterfaces(), "Interfaces");
497
+ }
498
+ return metadata;
499
+ case isDirectiveType(type):
500
+ case isOperation(type):
501
+ metadata = this.printSection(type.args, "Arguments", {
502
+ parentType: type.name,
503
+ });
504
+
505
+ if (isOperation(type)) {
506
+ const queryType = getTypeName(type.type).replace(/[![\]]*/g, "");
507
+ metadata += this.printSection(
508
+ [this.schema.getType(queryType)],
509
+ "Type",
510
+ );
511
+ }
512
+ return metadata;
513
+ }
514
+ return metadata;
515
+ }
516
+
517
+ printRelations(type) {
518
+ const relations = {
519
+ "Returned by": getRelationOfReturn,
520
+ "Member of": getRelationOfField,
521
+ "Implemented by": getRelationOfImplementation,
522
+ };
523
+
524
+ let data = "";
525
+ for (const [section, getRelation] of Object.entries(relations)) {
526
+ data += this.printRelationOf(type, section, getRelation);
527
+ }
528
+
529
+ return data;
530
+ }
531
+
532
+ printRelationOf(type, section, getRelation) {
533
+ if (
534
+ typeof type === "undefined" ||
535
+ typeof getRelation !== "function" ||
536
+ isOperation(type)
537
+ ) {
538
+ return "";
539
+ }
540
+
541
+ const relations = getRelation(type, this.schema);
542
+
543
+ if (typeof relations === "undefined") {
544
+ return "";
545
+ }
546
+
547
+ let data = [];
548
+ for (const [relation, types] of Object.entries(relations)) {
549
+ if (types.length === 0) {
550
+ continue;
551
+ }
552
+
553
+ const category = this.getRootTypeLocaleFromString(relation);
554
+ data = data.concat(types.map((t) => this.getRelationLink(category, t)));
555
+ }
556
+
557
+ if (data.length === 0) {
558
+ return "";
559
+ }
560
+
561
+ const content = [...data]
562
+ .sort((a, b) => a.localeCompare(b))
563
+ .join("<Bullet />");
564
+
565
+ return `${HEADER_SECTION_LEVEL} ${section}${MARKDOWN_EOP}${content}${MARKDOWN_EOP}`;
566
+ }
567
+
568
+ printType(name, type, options) {
569
+ if (typeof type === "undefined" || type === null) {
570
+ return "";
571
+ }
572
+
573
+ const header = this.printHeader(name, getTypeName(type), options);
574
+ const description = this.printDescription(type);
575
+ const code = this.printCode(type);
576
+ const metadata = this.printTypeMetadata(type);
577
+ const relations = this.relatedTypeSection ? this.printRelations(type) : "";
578
+
579
+ return `${header}${MARKDOWN_EOP}${mdx}${MARKDOWN_EOP}${description}${MARKDOWN_EOP}${code}${MARKDOWN_EOP}${metadata}${MARKDOWN_EOP}${relations}${MARKDOWN_EOP}`;
580
+ }
581
+ };