- 文档
- CLI
【免费下载链接】documentation
:book: documentation for modern JavaScript
本文围绕 documentation.js(本项目即其主仓库,当前版本 14.0.3)的主题机制展开,系统讲解 HTML 输出主题的接口契约、默认主题的目录结构与渲染流程、--theme命令行接入方式、基于默认主题二次开发的完整步骤,以及如何通过documentation.yml注入样式、如何理解 Markdown 输出为何不可主题化。读完本文,你将能够从零搭建自己的 documentation.js 主题,并将其接入documentation build -f html流水线。
主题的本质:一个返回 vinyl File 数组的 Node.js 模块
documentation.js 对"主题"的定义非常开放:主题就是一个 Node.js 模块,导出单个函数,其签名与回调约定如下(源自 docs/THEMING.md 的原始契约描述):
/** * @function * @param {Array<Object>} comments - an array of comments to be output * @param {Object} options - theme options * @param {ThemeCallback} callback - see below */ /** * @callback ThemeCallback * @param {?Error} error * @param {?Array<vinyl.File>} output */要点拆解:
- comments:由 documentation.js 解析、推断并分层(nest)后的注释对象数组。它来自 src/index.js 中
build()构建的完整管线——inferName、inferKind、inferParams、nest、filterAccess、hierarchy等步骤处理完毕的最终数据,主题拿到的是"可直接渲染"的语义结构,而非原始源码。 - options:主题选项,即当前构建的完整配置对象(由 src/merge_config.js 合并 CLI 参数、
documentation.yml与package.json推断字段后的结果)。 - callback:错误优先回调,成功时第二个参数必须是一个 vinyl
File对象数组。每个File代表一个待输出文件(如index.html、assets/xxx.css),带有path、base、contents等属性。
主题完全可以自由选择 HTML 的生成方式:可以用模板字符串拼接、用 underscore/lodash 模板、用 JSX、用任何你喜欢的渲染库。默认主题就采用了 lodash 模板方案,可作为最佳参考实现。
从当前仓库的源码看,src/output/html.js 的实际加载逻辑为:主题路径解析后通过动态import()加载,并调用其default 导出:
let themePath = config.theme && path.resolve(process.cwd(), config.theme); if (themePath) { if (process.platform === 'win32'){ // On Windows, absolute paths must be prefixed with 'file:///' to avoid the ERR_UNSUPPORTED_ESM_URL_SCHEMA error from import(). themePath = 'file:///' + themePath; } return (await import(themePath)).default(comments, config); } return (await import('../default_theme/index.js')).default(comments, config);也就是说:只要你的模块export default一个可调用函数,并能消费(comments, config)两个参数,就能被当作主题使用。值得注意的细节:
- 主题路径基于
process.cwd()解析,所以相对路径是相对你运行命令的目录,而不是相对配置文件。 - Windows 平台下路径会被自动加上
file:///前缀,规避 ESM 动态导入的协议错误。 - 未指定
--theme时,回退到仓库内置的 src/default_theme/index.js。
默认主题的目录解剖
默认主题位于 src/default_theme/,其构成在 src/default_theme/README.md 中有明确说明:
| 文件/目录 | 作用 |
|---|---|
index._ | 主模板,定义整页 HTML 骨架(<head>、左侧 TOC 导航、右侧内容区) |
section._ | 局部模板,渲染每一条 API 文档块(签名、类型、参数、返回值、示例等) |
note._ | 局部模板,渲染叙事型 note 区块 |
section_list._ | 局部模板,渲染成员列表(Static / Instance / Inner / Events) |
paramProperty._ | 局部模板,渲染参数属性表格 |
assets/ | 静态资源:bass.css、github.css、style.css、split.css、anchor.js、split.js、site.js,以及内置的 SourceCodePro 字体族 |
渲染主流程(源码级)
src/default_theme/index.js 的核心逻辑可概括为四步:
- 准备共享工具:创建
LinkerStack(命名空间到锚点的解析器)与createFormatters(类型格式化、参数签名、Markdown 渲染等工具);通过hljs.configure(config.hljs || {})配置 highlight.js 代码高亮。 - 编译模板:用
lodash/template依次读取并编译section_list._、section._、note._、paramProperty._,再以它们为局部函数编译index._主模板。所有模板共享一组imports,包括slug()(GitHub 风格锚点)、signature()/shortSignature()(函数签名)、md()(AST → Markdown)、formatType、autolink、highlight()等。 - 渲染整页:
pageTemplate({ docs: comments, config })生成完整 HTML 字符串。 - 落盘:若配置了
config.output,则将assets/目录整体复制到config.output/assets/,并写入config.output/index.html;否则直接返回字符串。
index._的页面结构值得关注:左侧是带Filter搜索框的 TOC(按 namespace 生成锚点,静态/实例/内部/事件成员分组折叠),右侧是正文区;底部挂载anchor.js、split.js、site.js实现锚点与左右分栏交互。section._则负责单条注释的展示:名称 + GitHub 源码链接、签名代码块、类型、Extends、参数表、属性、Returns、Related、Throws、示例(经highlight()高亮)、以及各级成员区块。
命令行接入:--theme/-t
主题通过 CLI 选项接入构建流程。在 src/commands/shared_options.js 中:
theme: { describe: 'specify a theme: this must be a valid theme module', alias: 't' }配合documentation build命令(src/commands/build.js),典型用法为:
documentation build index.js -f html -o docs --theme node_modules/docjs-theme几个关联注意点:
- HTML 输出必须指定输出目录:
build命令在-f html且-o缺省(stdout)时会直接报错The HTML output mode requires a destination directory set with -o。因为 HTML 是多文件输出(index.html+assets/),无法写进 stdout。 - 未指定
--theme时走内置默认主题,因此该选项只是"换皮肤"的入口,默认主题就是 src/default_theme。 - 主题模块既可以指向仓库内路径(如
--theme node_modules/docjs-theme),也可以指向本地目录(如--theme my-theme)。
仓库自带的测试 fixturetests/fixture/custom_theme/index.js 给出了一个最小可运行主题:
var File = require('vinyl'); /** * This is a theme only used by documentation to test custom theme * support. */ module.exports = function(comments, options, callback) { return Promise.resolve([ new File({ base: '/', path: '/index.html', contents: Buffer.from('Hello world') }) ]); };它展示了主题的最低实现形态:接收comments与options,产出一个vinyl.File实例(路径index.html、内容为二进制 Buffer)。对应测试见tests/bin.js 中的write to html with custom theme用例(通过-t fixture/custom_theme --shallow fixture/internal.input.js -f html -o <目录>触发)。
基于默认主题进行二次开发(官方推荐路径)
原文档给出了一条非常实用的"复制改造"路线,适用于不想从零编写模板、只想微调默认主题视觉或布局的场景:
- 复制主题源码:把仓库内
src/default_theme文件夹的全部内容复制到你项目的某个新目录(例如docjs-theme/)。 - 修正内部依赖引用:在你新建的主题
index.js中,把原先指向仓库内部模块的require('../')(原文档描述的是该文件第 8、9 行的引用,对应旧版 CommonJS 结构)替换为require('documentation'),然后保存。这样你的主题就与文档生成器本体解耦,可独立安装使用。- 提示:当前仓库已迁移为 ESM 模块(
"type": "module",见 package.json),内置主题 src/default_theme/index.js 通过import { util } from '../index.js'获取LinkerStack与createFormatters。在自建主题中,等价的做法是从documentation包导入util或自行实现模板辅助函数。以仓库当前源码为准:主题只需要对外提供 default 导出函数即可被 src/output/html.js 正确加载。
- 提示:当前仓库已迁移为 ESM 模块(
- 将主题作为依赖安装:把该目录放进一个 git 仓库,然后在项目
package.json的devDependencies中声明:"devDependencies": { "docjs-theme": "my-gh-username/reponame" }这样执行依赖安装后,你的主题会出现在项目的
node_modules目录中,可被--theme直接引用。 - 生成文档验证:在
package.json的scripts中配置:"scripts": { "docs": "documentation build index.js -f html -o docs --theme node_modules/docjs-theme" }之后对主题模板或样式所做的任何修改,都会在你重新运行
npm run docs时反映到生成的文档中。
这套流程的妙处在于:主题作为独立 npm 依赖被解析,--theme只是指向node_modules下的模块路径,因此可以跨项目复用、版本化管理。
通过 documentation.yml 注入样式:轻量改版
如果只是想做小范围的样式调整,不必复制整套主题——可以借助documentation.yml的叙事区块在文档 HTML 中注入<style>。
documentation.yml本身用于组织文档目录结构(详见 docs/CONFIG.md):toc数组按顺序排列 API 条目,并允许插入带name+description(Markdown 解释)的叙事区块,还支持file属性引用外部 Markdown 文件、children属性做分组。样式注入正是借助叙事区块的description完成的:
toc: - name: Section Header Name description: | <head> <style> h2{ color:black; } code.black{ background-color: #295377; overflow: hidden; padding: 0.5rem; color: white; font: 0.8rem Inconsolata, monospace; width:100%; } </style> </head> ### Sub Section header Text that describes the section and sub-section here.运行documentation build --config documentation.yml -f html -o docs后,上述<style>会随该区块进入生成的 HTML。
使用时有两点重要提醒(原文档明确强调):
- 覆盖优先级:凡是在标准主题中已存在的元素与类,其样式会被
documentation.yml中的定义覆盖——也就是说你的自定义 CSS 会与默认主题 CSS 同时存在并发生竞争,容易造成"同一套 CSS 被定义两遍"的困惑。 - 推荐做法:只用默认 documentation.js 主题中不存在的类来注入样式(例如上面示例里的
code.black),避免覆盖行为引发样式漂移;若需要系统性改版,仍应走"复制默认主题"的完整路线。
Markdown 输出为什么不能主题化
原文档明确指出:documentation.js 的默认 Markdown 生成器是不可主题化的。与 HTML 主题(自由模板)不同,Markdown 的输出链路是:
- src/output/markdown_ast.js 将注释数据构建为 remark 兼容的抽象语法树(AST)——从源码结构看,它按注释逐条生成标题、类型、参数列表、属性列表、示例代码块、Returns、Throws、Meta 等节点,并支持通过
config.hljs配置代码高亮、通过markdownToc/markdownTocMaxDepth控制目录; - src/output/markdown.js 再用
remark().use(remarkGfm).stringify(ast)把 AST 序列化成 Markdown 字符串。
这意味着:Markdown 的"主题"实际上是固定管线,没有对外开放的模板钩子。如果你需要 Markdown 里额外的内容或版式,原文档给出的两条出路是:
- 向默认主题提需求:推动某个能力被合入官方默认 Markdown 生成器;
- 曲线救国:写一个输出 Markdown 的HTML 主题——因为 HTML 主题对输出内容有完全控制权,你可以在主题内部生成 Markdown 文本(甚至用 vinyl File 输出
.md文件),从而绕过固定管线的限制。
如果你只是想要"类似主题"的轻量控制,CLI 层面还有--markdown-toc(是否生成目录,默认 true)与--markdown-toc-max-depth(目录最大深度,默认 6)两个选项可用(见 src/commands/shared_options.js),它们由 src/output/markdown_ast.js 消费。
一张速查表:三种"改外观"手段怎么选
| 需求 | 手段 | 涉及配置/路径 |
|---|---|---|
| 整体换肤、改布局、完全掌控 HTML | 自建主题,--theme指向主题模块 | src/output/html.js、src/commands/shared_options.js |
| 在默认主题基础上小幅调整样式 | 复制 src/default_theme 二次开发,发布为依赖 | 上文"二次开发"四步 |
| 只在个别叙事区块做小样式微调 | documentation.yml中注入<style> | docs/CONFIG.md、src/merge_config.js |
| Markdown 输出的目录/格式微调 | --markdown-toc、--markdown-toc-max-depth | src/output/markdown_ast.js |
| Markdown 深度定制 | 写一个输出 Markdown 的 HTML 主题 | 主题接口同上 |
最后再强调一遍主题契约的核心:一个导出函数的模块,接收comments与配置,产出 vinylFile数组或直接返回 HTML 字符串。理解了这一点,无论你是复制默认主题微调,还是从零实现一套自己的渲染引擎,都能顺利接入 documentation.js 的 HTML 输出流水线。
- 文档
- CLI
【免费下载链接】documentation
:book: documentation for modern JavaScript
相关推荐
VitePress主题定制终极指南:从默认主题到完全自定义
VitePress主题定制终极指南:从默认主题到完全自定义 VitePress是一个基于Vite和Vue的静态站点生成器,专为技术文档设计。它提供了一个开箱即用
前端文档mdBook 主题定制完全指南:从默认主题到自定义模板与资源覆盖
mdBook 主题定制完全指南:从默认主题到自定义模板与资源覆盖 本篇指南以 mdBook 的默认 HTML 渲染器为主题,系统讲解如何通过 theme 目录按
开发工具文档TypeDoc 主题体系完全指南:从内置默认主题到自定义主题开发
TypeDoc 主题体系完全指南:从内置默认主题到自定义主题开发 TypeDoc 作为 TypeScript 项目的文档生成器,通过主题(Themes)机制控制
开发工具文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考