Frontend Development 22 min read

Building a Powerful Docs Site for a Front‑End Graphics Engine Using Gatsby

This article walks through the challenges and solutions of creating an integrated, searchable, multilingual documentation website for the Oasis Engine graphics engine, covering the selection of static site generators, the use of Gatsby with TypeDoc and GraphQL, custom plugins for embedding demos, and implementing global search with Algolia DocSearch.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Building a Powerful Docs Site for a Front‑End Graphics Engine Using Gatsby

If you have watched the Vue.js documentary, you know that an open‑source product’s success depends not only on quality code but also on clear documentation, appealing design, continuous iteration, regular evangelism, and strong backing.

Writing code and unit tests guarantees functionality, but for users the most important thing is documentation. Just as a vacuum cleaner comes with a manual, software should provide clear docs instead of forcing users to read source code.

When we started operating the open‑source graphics engine Oasis Engine, we discovered that creating a clean, usable documentation site is far harder than expected.

Oasis Engine: https://oasisengine.cn/

Selection

Before open‑sourcing we stored docs on Yuque, a good knowledge‑management platform, but it cannot handle complex API docs or custom designs. An open‑source product without its own site feels incomplete.

Most well‑known open‑source projects host their sites on GitHub Pages. We researched static site generators that can be deployed on GitHub Pages:

Jekyll : Officially recommended but depends on Ruby, which is cumbersome on macOS.

Vuepress : A Vue‑based framework developed by a colleague, but its data source is limited to Markdown.

Dumi : A React‑based framework for component development; its dumi‑theme‑mobile is attractive, yet its demo preview is tightly coupled with components and its default style is rough.

Docsify : A lightweight site generator that can parse Markdown at runtime without a build step, offering Gitbook‑like features.

We ultimately chose Docsify . The site combines three pipelines:

Docsify for documentation (Markdown).

Typedoc for API documentation (TypeScript).

Demosify for demo pages (TypeScript).

Because the API, tutorials, and demos live in three separate GitHub repositories, each update required copying content across repos, leading to high maintenance cost and developer fatigue.

Original Intent

Two months after Oasis Engine was open‑sourced, the hype faded and users reported slow loading on the GitHub Pages site, especially for demo pages that were inaccessible without a VPN. We later mirrored the site on Gitee to improve access in China.

Looking at successful engine sites such as Unity, Unreal, Cocos, LayaAir, ThreeJS, and BabylonJS, we realized each presents information architecture and style according to its positioning and commercial strategy. Our goal is to make Oasis Engine’s site simple, reliable, and fast for front‑end developers.

We defined four core requirements:

Integration : Combine API docs (Typedoc), tutorial docs (Markdown), and demos (TypeScript) into a single site with global search.

Demo Embedding : Allow demos to be embedded in tutorials and link to online editors like CodePen.

Multi‑Version : Support simultaneous documentation for multiple engine versions with version switching.

Internationalization : Provide both Chinese and English content.

In essence, we wanted a site similar to Ant Design’s documentation.

Getting Started with Gatsby

The key difference of Gatsby is its GraphQL data layer. Any input that can be transformed into GraphQL can be queried and rendered as React components. For Oasis Engine we needed to convert Typedoc, Markdown, and TypeScript files into React components.

This decouples data from style: instead of compiling each format to HTML separately, a single tool transforms everything into React components, allowing unified styling.

Processing TypeDoc Data

TypeScript is the primary language for Oasis Engine. Typedoc reads TypeScript declaration data and generates HTML API docs, but it also offers a Node API for programmatic access.

<code>export const pageQuery = graphql
  typedoc(typedocId: { eq: "default" }) {
    internal {
      content
    }
  }

export default function MyPage({ data: { typedoc } }) {
  const typedDocContent = JSON.parse(typedoc?.internal.content);
  // do something with that data...
}
</code>

Typedoc defines many kinds (MODULE, ENUM, CLASS, etc.) which makes custom rendering complex:

<code>export enum Kinds {
  MODULE = 1,
  ENUM = 4,
  CLASS = 128,
  INTERFACE = 256,
  TYPE_ALIAS = 4194304,
  FUNCTION = 64,
  PROPERTY = 1024,
  CONSTRUCTOR = 512,
  ACCESSOR = 262144,
  METHOD = 2048,
  GET_SIGNATURE = 524288,
  SET_SIGNATURE = 1048576,
  PARAMETER = 32768,
  TYPE_PARAMETER = 131072,
}
</code>

Steps to integrate Typedoc data:

Collect entry

index.ts

files from each package in the monorepo and write them to a temporary

tsfiles.js

file.

<code>const glob = require('glob');
const fs = require('fs');

glob(`${EngineRepoPath}/packages/**/src/index.ts`, { realpath: true }, function (er, files) {
  var re = new RegExp(/([^test]+).ts/);
  var tsFiles = [];
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    var res = re.exec(file);
    console.log('[Typedoc entry file]:', file);
    if (!res) continue;
    tsFiles.push(`"${file}"`);
  }
  fs.writeFile('./scripts/typedoc/tsfiles.js', `module.exports = [${tsFiles.join(',')}];`, function (err) {});
});
</code>

Configure the gatsby-source-typedoc plugin in

gatsby-config.js

.

<code>const DTS = require('./scripts/typedoc/tsfiles');

module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-typedoc",
      options: {
        src: DTS,
        typedoc: {
          tsconfig: `${typedocSource}/tsconfig.json`
        }
      }
    }
  ]
};
</code>

Verify data ingestion by opening

http://localhost:8000/___graphql

and querying

typedoc { internal { content } }

.

Create pages from the Typedoc data in

gatsby-node.js

.

<code>async function createAPI(graphql, actions) {
  const { createPage } = actions;
  const apiTemplate = resolve(__dirname, '../src/templates/api.tsx');
  const typedocquery = await graphql(`
    {
      typedoc {
        internal { content }
      }
    }
  `);
  let apis = JSON.parse(typedocquery.data.typedoc.internal.content);
  const packages = apis.children.map(p => ({ id: p.id, kind: p.kind, name: p.name.replace('/src', '') }));
  if (apis) {
    apis.children.forEach((node, i) => {
      const name = node.name.replace('/src', '');
      createPage({ path: `${version}/api/${name}/index`, component: apiTemplate, context: { node, type: 'package', packages } });
      if (node.children) {
        node.children.forEach(child => {
          createPage({ path: `${version}/api/${name}/${child.name}`, component: apiTemplate, context: { node: child, type: 'module', packages, packageIndex: i } });
        });
      }
    });
  }
}
</code>

The final site is reachable at https://oasisengine.cn/0.3/api/core/index . The custom styling makes the API docs far more readable than the default Typedoc output.

Embedding Demo in Markdown

We wanted a one‑line Markdown syntax to embed a demo, e.g.

&lt;playground src="pbr-helmet.ts"&gt;&lt;/playground&gt;

. To achieve this we created a Gatsby remark plugin that extracts the

&lt;playground&gt;

tag from the Markdown AST, reads the source file, and replaces the node with highlighted code using Prismjs.

<code>// `gatsby-remark-oasis` plugin:
// Extract &lt;playground&gt; from markdown AST and replace the content
const visit = require('unist-util-visit');
const fs = require('fs');
const Prism = require('prismjs');

module.exports = ({ markdownAST }, { api, playground, docs }) => {
  visit(markdownAST, 'html', node => {
    if (node.value.includes('<playground')) {
      const src = /src="(.+)"/.exec(node.value);
      if (src && src[1]) {
        const name = src[1];
        const path = `playground/${name}`;
        const code = fs.readFileSync(`./${path}`, { encoding: 'utf8' });
        node.value = `<playground name="${name}"><textarea>${code}</textarea>${Prism.highlight(code, Prism.languages.javascript, 'javascript')}</playground>`;
      }
    }
  });
  return markdownAST;
};
</code>

Another plugin, gatsby-remark-component-parent2div , converts the custom

&lt;playground&gt;

element into a React component using rehype‑react :

<code>import RehypeReact from "rehype-react";
import Playground from "../Playground";

const renderAst = new RehypeReact({
  createElement: React.createElement,
  components: { "playground": Playground }
}).Compiler;

export default class Article extends React.PureComponent {
  render() { return renderAst(this.props.content.htmlAst); }
}
</code>

In

gatsby-node.js

we also generate a virtual

Playground

node by transforming the demo TypeScript files with Babel:

<code>const babel = require("@babel/core");

exports.onCreateNode = async function onCreateNode({ node, loadNodeContent, actions, createNodeId, reporter, createContentDigest }) {
  const { createNode } = actions;
  const content = await loadNodeContent(node);
  // Babel configuration omitted for brevity
  const result = babel.transformSync(content, {/* ... */});
  const playgroundNode = {
    internal: { content: result.code, type: `Playground` }
  };
  playgroundNode.internal.contentDigest = createContentDigest(playgroundNode);
  createNode(playgroundNode);
  return playgroundNode;
};
</code>

Additional features such as opening demos in CodePen, CodeSandbox, or Stackblitz were added to improve interactivity.

Global Search

Because the engine has a large API surface, a global search is essential. We integrated Algolia DocSearch, which crawls the site daily and provides a front‑end SDK for querying.

After obtaining an API key, we added a configuration file to the

docsearch-configs

repository:

<code>{
  "start_urls": [
    {
      "url": "https://oasisengine.cn/(?P<version>.*?)/docs/.+?-cn",
      "variables": { "version": ["0.3"] },
      "tags": ["cn"]
    }
  ],
  "selectors": {
    "lvl0": { "selector": ".docsearch-lvl0", "global": true, "default_value": "Documentation" },
    "lvl1": "article h1",
    "lvl2": "article h2",
    "lvl3": "article h3",
    "lvl4": "article h4",
    "lvl5": "article h5",
    "text": "article p, article li"
  }
}
</code>

The maintainer of the

docsearch-configs

repo responded quickly, making the integration smooth.

Conclusion

The documentation build process involved many detours—choosing a site generator, wiring Typedoc through GraphQL, writing custom Gatsby plugins, and adding Algolia search—but each step taught valuable lessons, especially the power of GraphQL. Oasis Engine’s documentation is still in its early stages, and we plan to keep iterating to provide developers with fast, reliable access to information.

frontenddocumentationGraphQLStatic SiteGatsbyTypeDoc
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.