Skip to content

Commit 4b0103e

Browse files
committed
refactor: autogen graphql docs
1 parent 5601b61 commit 4b0103e

File tree

777 files changed

+80131
-629
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

777 files changed

+80131
-629
lines changed

biome.jsonc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
33
"extends": ["@sablier/devkit/biome"],
44
"files": {
5-
"includes": ["**/*.{css,js,json,jsonc,jsx,ts,tsx}", "!node_modules", "!repos", "!src/autogen", "!static"]
5+
"includes": [
6+
"**/*.{css,js,json,jsonc,jsx,ts,tsx}",
7+
"!node_modules/**",
8+
"!repos/**",
9+
"!src/autogen/**",
10+
"!static/**"
11+
]
612
}
713
}

bun.lock

Lines changed: 106 additions & 49 deletions
Large diffs are not rendered by default.

cli/commands/autogen/graphql.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import fs from "node:fs";
2+
import { join } from "node:path";
3+
import { getSablierIndexerEnvio, type Indexer } from "@sablier/indexers";
4+
import { Command } from "commander";
5+
import { $ } from "execa";
6+
import * as yaml from "js-yaml";
7+
import _ from "lodash";
8+
import type { CliOptions } from "../../types";
9+
10+
const CHAIN_ID_SEPOLIA = 11155111;
11+
12+
type Category = {
13+
/** @see https://docusaurus.io/feature-requests/p/hiding-parts-of-docs-in-autogenerated-sidebar */
14+
className?: "hidden";
15+
collapsible: boolean;
16+
collapsed: boolean;
17+
label: string;
18+
position: number;
19+
};
20+
21+
type GraphQLOptions = CliOptions & {
22+
protocol: string;
23+
vendor: string;
24+
};
25+
26+
/* -------------------------------------------------------------------------- */
27+
/* COMMAND */
28+
/* -------------------------------------------------------------------------- */
29+
30+
export function createGraphQLCommand(): Command {
31+
return new Command("graphql")
32+
.description("Generate GraphQL schema documentation")
33+
.requiredOption("-p, --protocol <protocol>", "generate for specific protocol")
34+
.requiredOption("-v, --vendor <vendor>", "generate for specific vendor")
35+
.action(async (options, command: Command) => {
36+
const globalOptions = command.parent?.opts() || {};
37+
const mergedOptions: GraphQLOptions = { ...globalOptions, ...options };
38+
await generateGraphQL(mergedOptions);
39+
});
40+
}
41+
42+
export async function generateGraphQL(options: GraphQLOptions): Promise<void> {
43+
const protocols: Indexer.Protocol[] = ["airdrops", "flow", "lockup"];
44+
const vendors = ["graph", "envio"];
45+
46+
if (options.protocol !== "all" && !protocols.includes(options.protocol as Indexer.Protocol)) {
47+
throw new Error(`Invalid protocol: ${options.protocol}. Valid options: ${protocols.join(", ")}, all`);
48+
}
49+
if (options.vendor !== "all" && !vendors.includes(options.vendor as Indexer.Vendor)) {
50+
throw new Error(`Invalid vendor: ${options.vendor}. Valid options: ${vendors.join(", ")}, all`);
51+
}
52+
53+
let targetProtocols = protocols;
54+
if (options.protocol !== "all") {
55+
targetProtocols = _.filter(protocols, (p) => p === options.protocol);
56+
}
57+
58+
const basePaths: string[] = [];
59+
for (const p of targetProtocols) {
60+
if (options.vendor === "all" || options.vendor === "graph") {
61+
basePaths.push(await generateGraph(p));
62+
}
63+
if (options.vendor === "all" || options.vendor === "envio") {
64+
basePaths.push(await generateEnvio(p));
65+
}
66+
}
67+
68+
cleanupDocs(basePaths);
69+
}
70+
71+
/* -------------------------------------------------------------------------- */
72+
/* INTERNAL LOGIC */
73+
/* -------------------------------------------------------------------------- */
74+
75+
const COLLAPSED = {
76+
collapsed: true,
77+
collapsible: true,
78+
};
79+
80+
const THE_GRAPH_CATEGORY: Category = { ...COLLAPSED, label: "The Graph", position: 2 };
81+
const ENVIO_CATEGORY: Category = { ...COLLAPSED, label: "Envio", position: 3 };
82+
const QUERIES_CATEGORY: Category = { ...COLLAPSED, label: "Queries", position: 2 }; // first is the overview
83+
const OBJECTS_CATEGORY: Category = { ...COLLAPSED, label: "Objects", position: 3 };
84+
const ENUMS_CATEGORY: Category = { ...COLLAPSED, className: "hidden", label: "Enums", position: 4 };
85+
const INPUTS_CATEGORY: Category = { ...COLLAPSED, className: "hidden", label: "Inputs", position: 5 };
86+
const SCALARS_CATEGORY: Category = { ...COLLAPSED, className: "hidden", label: "Scalars", position: 6 };
87+
88+
function cleanupDocs(basePaths: string[]): void {
89+
for (const basePath of basePaths) {
90+
fs.rmSync(join(basePath, "directives"), { force: true, recursive: true });
91+
fs.rmSync(join(basePath, "subscriptions"), { force: true, recursive: true });
92+
93+
// Delete all _category_.yml files
94+
fs.unlinkSync(join(basePath, "queries", "_category_.yml"));
95+
fs.unlinkSync(join(basePath, "enums", "_category_.yml"));
96+
fs.unlinkSync(join(basePath, "inputs", "_category_.yml"));
97+
fs.unlinkSync(join(basePath, "objects", "_category_.yml"));
98+
fs.unlinkSync(join(basePath, "scalars", "_category_.yml"));
99+
100+
// Rewrite _category_.yml files
101+
fs.writeFileSync(join(basePath, "queries", "_category_.yml"), yaml.dump(QUERIES_CATEGORY));
102+
fs.writeFileSync(join(basePath, "objects", "_category_.yml"), yaml.dump(OBJECTS_CATEGORY));
103+
fs.writeFileSync(join(basePath, "enums", "_category_.yml"), yaml.dump(ENUMS_CATEGORY));
104+
fs.writeFileSync(join(basePath, "inputs", "_category_.yml"), yaml.dump(INPUTS_CATEGORY));
105+
fs.writeFileSync(join(basePath, "scalars", "_category_.yml"), yaml.dump(SCALARS_CATEGORY));
106+
107+
// Write vendor category file at base path
108+
if (basePath.includes("envio")) {
109+
fs.writeFileSync(join(basePath, "_category_.yml"), yaml.dump(ENVIO_CATEGORY));
110+
} else if (basePath.includes("the-graph")) {
111+
fs.writeFileSync(join(basePath, "_category_.yml"), yaml.dump(THE_GRAPH_CATEGORY));
112+
}
113+
}
114+
}
115+
116+
async function generateEnvio(protocol: Indexer.Protocol): Promise<string> {
117+
const base = `./docs/api/${protocol}/graphql/envio`;
118+
const schemaURL = getSablierIndexerEnvio({ chainId: CHAIN_ID_SEPOLIA, protocol }).endpoint.url;
119+
120+
await runCommand(base, schemaURL);
121+
console.log(`✔️ Generated GraphQL docs for Envio vendor and ${_.capitalize(protocol)} protocol\n`);
122+
return base;
123+
}
124+
125+
async function generateGraph(protocol: Indexer.Protocol): Promise<string> {
126+
const base = `./docs/api/${protocol}/graphql/the-graph`;
127+
const schemaURL = `https://api.studio.thegraph.com/query/112500/sablier-${protocol}-experimental/version/latest`;
128+
129+
await runCommand(base, schemaURL);
130+
console.log(`✔️ Generated GraphQL docs for The Graph vendor and ${_.capitalize(protocol)} protocol\n`);
131+
return base;
132+
}
133+
134+
/**
135+
* @see https://graphql-markdown.dev/docs/settings#baseurl
136+
* @see {@link file://./../../../config/plugins.ts}
137+
*/
138+
async function runCommand(base: string, schema: string): Promise<void> {
139+
await $({ stdio: "inherit" })`bun docusaurus graphql-to-doc --base ${base} --schema ${schema}`;
140+
}

cli/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ async function main() {
1212
// Import and create subcommands
1313
const { createDeploymentsCommand, generateDeployments } = await import("./commands/autogen/deployments.js");
1414
const { createIndexersCommand, generateIndexers } = await import("./commands/autogen/indexers.js");
15+
const { createGraphQLCommand } = await import("./commands/autogen/graphql.js");
1516

1617
// Create autogen parent command
1718
const autogenCommand = new Command("autogen")
18-
.description("Auto-generate documentation tables")
19+
.description("Auto-generate documentation and indexers tables")
1920
.action(async (_options, command) => {
2021
const globalOptions = command.parent?.opts() || {};
2122

@@ -36,6 +37,7 @@ async function main() {
3637
// Add subcommands to autogen
3738
autogenCommand.addCommand(createDeploymentsCommand());
3839
autogenCommand.addCommand(createIndexersCommand());
40+
autogenCommand.addCommand(createGraphQLCommand());
3941

4042
// Add the autogen command to the main program
4143
program.addCommand(autogenCommand);

config/plugins.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import type { Options as ClientRedirectsOptions } from "@docusaurus/plugin-client-redirects";
22
import type { Options as VercelAnalyticsOptions } from "@docusaurus/plugin-vercel-analytics";
3-
import { type DocusaurusConfig } from "@docusaurus/types";
3+
import { type DocusaurusConfig, type PluginOptions } from "@docusaurus/types";
4+
import type { ConfigOptions, GraphQLMarkdownCliOptions, LoaderOption } from "@graphql-markdown/types";
45
import { createRedirects, redirects } from "./redirects";
56

7+
/* -------------------------------------------------------------------------- */
8+
/* Client Redirects */
9+
/* -------------------------------------------------------------------------- */
10+
611
const clientRedirects: [string, ClientRedirectsOptions] = [
712
"@docusaurus/plugin-client-redirects",
813
{
@@ -12,11 +17,45 @@ const clientRedirects: [string, ClientRedirectsOptions] = [
1217
},
1318
];
1419

20+
/* -------------------------------------------------------------------------- */
21+
/* GraphQL Markdown */
22+
/* -------------------------------------------------------------------------- */
23+
type GraphQLMarkdownOptions = GraphQLMarkdownCliOptions & Partial<PluginOptions>;
24+
25+
const graphqlMarkdown: [string, GraphQLMarkdownOptions] = [
26+
"@graphql-markdown/docusaurus",
27+
/**
28+
* Some settings will be overridden by the CLI.
29+
* @see https://graphql-markdown.dev/docs/settings
30+
*/
31+
{
32+
baseURL: "./docs/api/flow/the-graph",
33+
homepage: "static/graphql-overview.md",
34+
loaders: {
35+
UrlLoader: {
36+
module: "@graphql-tools/url-loader",
37+
},
38+
} as LoaderOption,
39+
metatags: [{ content: "noindex", name: "robots" }, { charset: "utf-8" }],
40+
pretty: true,
41+
printTypeOptions: {
42+
hierarchy: "entity",
43+
relatedTypeSection: false,
44+
},
45+
rootPath: ".",
46+
schema: "https://api.studio.thegraph.com/query/112500/sablier-flow-experimental/version/latest",
47+
} satisfies ConfigOptions,
48+
];
49+
50+
/* -------------------------------------------------------------------------- */
51+
/* VERCEL ANALYTICS */
52+
/* -------------------------------------------------------------------------- */
53+
1554
const vercelAnalytics: [string, VercelAnalyticsOptions] = [
1655
"vercel-analytics",
1756
{
1857
mode: "auto",
1958
},
2059
];
2160

22-
export const plugins: DocusaurusConfig["plugins"] = [clientRedirects, vercelAnalytics];
61+
export const plugins: DocusaurusConfig["plugins"] = [clientRedirects, graphqlMarkdown, vercelAnalytics];

0 commit comments

Comments
 (0)