Skip to content

Commit

Permalink
Merge pull request #19 from rescript-lang/api-docs
Browse files Browse the repository at this point in the history
API docs
  • Loading branch information
nojaf authored Nov 23, 2024
2 parents e5386f8 + cbe8118 commit 91d8789
Show file tree
Hide file tree
Showing 14 changed files with 591 additions and 14 deletions.
13 changes: 13 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @type {import("prettier").Config} */
export default {
plugins: ["prettier-plugin-astro"],
overrides: [
{
files: "*.astro",
options: {
parser: "astro",
},
},
],
endOfLine: "lf",
};
46 changes: 39 additions & 7 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import { readFileSync } from "node:fs";
import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";
import { apiModules } from "./docs/utils";

const rescriptTM = JSON.parse(
readFileSync("./docs/assets/rescript.tmLanguage.json", "utf-8"),
);

const apiSidebarItems = apiModules.map(({ moduleName, link, items }) => {
const nestedItems = Object.values(items).map(({ moduleName, link }) => ({
label: moduleName,
link
}));

return ({
label: moduleName,
collapsed: true,
items: [
{
label: `Overview`,
link
},
...nestedItems
]
});
});

export default defineConfig({
srcDir: "docs",
publicDir: "docs/public",
Expand All @@ -18,24 +38,36 @@ export default defineConfig({
src: "./docs/assets/rescript-logo.svg",
},
social: {
github: 'https://github.com/rescript-lang/experimental-rescript-webapi',
github: "https://github.com/rescript-lang/experimental-rescript-webapi",
},
editLink: {
baseUrl: 'https://github.com/rescript-lang/experimental-rescript-webapi/edit/main/',
baseUrl:
"https://github.com/rescript-lang/experimental-rescript-webapi/edit/main/",
},
sidebar: [
{
slug: '',
slug: "",
},
{
slug: "design-philosophy",
},
{
slug: 'design-philosophy',
slug: "project-status",
},
{
slug: 'project-status',
label: "Contributing",
autogenerate: { directory: "contributing" },
},
{
label: 'Contributing',
autogenerate: { directory: 'contributing' },
label: "API Documentation",
collapsed: true,
items: [
{
label: "Overview",
link: "apidocs",
},
...apiSidebarItems,
],
},
],
customCss: ["./docs/styles/fonts.css", "./docs/styles/theme.css"],
Expand Down
48 changes: 48 additions & 0 deletions docs/components/record.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
import { micromark } from "micromark";
import Signature from "./signature.astro";
const { name, items, typesInOwnModule } = Astro.props;
---

<h4>Record fields</h4>
{
items.map((item) => {
const documentation =
item.docstrings && micromark(item.docstrings.join("\n"));

return (
<div class="record_field">
<h5 id={`${name}_${item.name}`}>{item.name}</h5>
<div class="type">
<Signature
signature={item.signature}
typesInOwnModule={typesInOwnModule}
/>
</div>
{documentation && <div class="doc" set:html={documentation} />}
</div>
);
})
}

<style>
.record_field {
display: grid;
grid-template-rows: repeat(2, auto);
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
border-bottom: 1px solid var(--sl-color-gray-7);

& .type {
justify-self: end;
color: var(--sl-color-accent-low);
margin-top: 0;
}

& .doc {
padding-bottom: 0.5rem;
grid-column: 1 / -1;
grid-row: 2;
}
}
</style>
49 changes: 49 additions & 0 deletions docs/components/signature.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
import SignatureItem from "./signatureItem.astro";
function tokenize(input) {
// Split by special characters while keeping them
const regex = /([<>,])/g;
return input
.split(regex)
.map((token) => token.trim())
.filter(Boolean);
}
function parseTokens(tokens) {
let index = 0;
const parseNode = () => {
const path = tokens[index++]; // Read the current token and increment index
let genericTypeParameters = [];
if (tokens[index] === "<") {
// Check for generics
index++; // Consume "<"
while (tokens[index] !== ">") {
genericTypeParameters.push(parseNode());
if (tokens[index] === ",") index++; // Consume ","
}
index++; // Consume ">"
}
return { path, genericTypeParameters };
};
return parseNode();
}
function parse(input) {
const tokens = tokenize(input);
return parseTokens(tokens);
}
const { signature, typesInOwnModule } = Astro.props;
const tree = parse(signature);
---

<div class="signature">
{(<SignatureItem typesInOwnModule={typesInOwnModule} item={tree} />)}
</div>
38 changes: 38 additions & 0 deletions docs/components/signatureItem.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
import { createAPIModuleLink } from "../utils.js";
const { item, typesInOwnModule } = Astro.props;
let link;
if (typesInOwnModule && typesInOwnModule.has(item.path)) {
link = `#${item.path}`;
}
if (item.path.startsWith("WebAPI.")) {
const paths = item.path.split(".");
if (paths.length === 3) {
link = `${import.meta.env.BASE_URL}/${createAPIModuleLink(paths[1])}#${paths[2]}`;
}
}
const genericTypeParameters = item.genericTypeParameters || [];
---

<span>
{link ? <a href={link}>{item.path}</a> : item.path}{
genericTypeParameters.length > 0 && (
<>
{"<"}
{genericTypeParameters.map((subItem) => (
<Astro.self typesInOwnModule={typesInOwnModule} item={subItem} />
))}
{">"}
</>
)
}
</span>
<style>
span {
display: inline-flex;
color: var(--sl-color-accent-low);
}
</style>
27 changes: 27 additions & 0 deletions docs/components/value.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
import SignatureItem from "./signatureItem.astro";
const { parameters, returnType } = Astro.props;
---

<div class="value">
<h4>Parameters</h4>
{
parameters.map((p) => (
<SignatureItem item={p} />
))
}
<h4>Return type</h4>
<SignatureItem item={returnType} />
</div>
<style>
.value {
display: flex;
flex-direction: column;
padding-bottom: 1rem;
border-bottom: 1px solid var(--sl-color-gray-7);

.signature {
margin-top: 0rem;
}
}
</style>
114 changes: 114 additions & 0 deletions docs/pages/apidocs/[API].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
import * as path from "node:path";
import { existsSync } from "fs";
import { apiModules, getDoc, createTypeModuleLink } from "../../utils";
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import { Code } from "@astrojs/starlight/components";
import { micromark } from "micromark";
import Record from "../../components/record.astro";
export async function getStaticPaths() {
return apiModules.map((apiModule) => {
return {
params: {
API: apiModule.apiRouteParameter,
},
props: apiModule,
};
});
}
function showRecord(details) {
return details && details.kind === "record" && details.items.length > 0;
}
function getModuleFileName(typeName) {
return `${typeName[0].toUpperCase()}${typeName.slice(1)}`;
}
function showModule(typeName, filePath) {
const moduleFileName = `${getModuleFileName(typeName)}.res`;
const potentialPath = path.join(filePath.replace(".res", ""), moduleFileName);
return existsSync(potentialPath);
}
const { moduleName, filePath, link } = Astro.props;
const docInfo = await getDoc(filePath);
const types = docInfo.items
.filter((item) => item.kind === "type")
.sort((a, b) => a.name.localeCompare(b.name))
.map((type) => {
const documentation =
type.docstrings && micromark(type.docstrings.join("\n"));
return {
name: type.name,
documentation,
signature: type.signature,
detail: type.detail,
};
});
const typesInOwnModule = new Set(types.map((t) => t.name));
const typeHeadings = types.map((type) => ({
depth: 3,
slug: type.name,
text: type.name,
}));
const frontmatter = {
title: moduleName,
};
const headings = [
{
depth: 2,
slug: "types",
text: "Types",
},
...typeHeadings,
];
---

<StarlightPage frontmatter={frontmatter} headings={headings}>
<div id="apidocs">
<h2 id="types">Types</h2>
{
types.map((type) => (
<div class="rescript_type">
<h3 id={type.name}>{type.name}</h3>
<div set:html={type.documentation} />
<Code lang="ReScript" code={type.signature} />
{showRecord(type.detail) ? (
<Record
name={type.name}
typesInOwnModule={typesInOwnModule}
{...type.detail}
/>
) : null}
{showModule(type.name, filePath) && (
<>
<h4>Module</h4>
<p>
There are methods and helpers defined in{" "}
<a
href={`${import.meta.env.BASE_URL}/${createTypeModuleLink(link, type.name)}`}
>
{getModuleFileName(type.name)}
</a>
.
</p>
</>
)}
</div>
))
}
</div>
</StarlightPage>
<style>
#apidocs .rescript_type {
margin-block: 2rem;
}
</style>
Loading

0 comments on commit 91d8789

Please sign in to comment.