时间:2023-08-14 11:57:02 | 来源:网站运营
时间:2023-08-14 11:57:02 来源:网站运营
如何建设一个开源图形引擎的文档网站:如果你看过 Vue.js 的纪录片,就会发现一个开源产品的成功不仅仅是优质的代码,而且还需要:清晰的文档、不土的审美、持续的迭代、定期的布道、大佬的站台……今天来聊聊“文档”这件看似简单却需要精心雕琢的小事。export const pageQuery = graphql` typedoc(typedocId: { eq: "default" }) { internal { content } }`export default function MyPage({ data: { typedoc } }) { const typedocContent = JSON.parse(typedoc?.internal.content); // do something with that data...}
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,}
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) {});});
const DTS = require('./scripts/typedoc/tsfiles');module.exports = { plugins: [ { resolve: "gatsby-source-typedoc", options: { src: DTS, typedoc: { tsconfig: `${typedocSource}/tsconfig.json` } } } ]}
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); // do something with that data... const packages = apis.children.map((p) => { return { id: P.ID | Truly Identity, 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 } }); }) } }); }}
<playground src="pbr-helmet.ts"></playground>
多么简单优雅!可是问题来了:怎么从 Markdown 中“提取”出这行代码并最终渲染成想要的样子呢?不要忘了 Markdown 本来就是 gatsby 的一项数据源,gatsby 正是通过 gatsby-transformer-remark 插件解析数据的,而数据的解析从原理上绕不过抽象语法树,看了一下 graphiQL 果然有 AST 数据:// `gatsby-remark-oasis` plugin: // Extract <playground> from markdown AST and replace the contentconst 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;};
{ resolve: 'gatsby-transformer-remark', options: { plugins: [ // Extract <playground> from html markdwon AST and replace the content { resolve: 'gatsby-remark-oasis', options: { api: `/${version}/api/`, playground: `/${version}/playground/`, docs: `/${version}/docs/`, } }, // convert <playground> to React Componennt { resolve: "gatsby-remark-component-parent2div", options: { components: ["Playground"], verbose: true } }, ], }, },
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<ArticleProps> { render () { return renderAst(this.props.content.htmlAst); }}
// gatsby-node.jsconst babel = require("@babel/core");exports.onCreateNode = module.exports.onCreateNode = async function onCreateNode( { node, loadNodeContent, actions, createNodeId, reporter, createContentDigest }) { const { createNode } = actions const content = await loadNodeContent(node) // 省略了 babel 配置 const result = babel.transformSync(content, {...}); const playgroundNode = { internal: { content: result.code, type: `Playground`, }, } playgroundNode.internal.contentDigest = createContentDigest(playgroundNode) createNode(playgroundNode) return playgroundNode}
{ // 要爬取的页面 url 匹配规则 "start_urls": [ { "url": "https://oasisengine.cn/(?P<version>.*?)/docs/.+?-cn", "variables": { "version": [ "0.3" ] }, "tags": [ "cn" ] }, ], // 爬取页面中哪些 HTML 标签的数据 "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" }}
关键词:引擎,图形,建设