使用npm安装jsdoc后,其文件目录结构以下:
其中templates放置了默认的主题模板。其中default主题的文件结构以下:
其中,publish.js是最重要的文件,它导出一个publish
函数,在cli.js
中被调用html
// cli.js publishPromise = template.publish( // 保存全部数据的Taffy对象,见下面 taffy(props.docs), // env是一个保存运行环境信息的对象 // 参考:lib/jsdoc/env.js env.opts, // 暂时未 resolver.root ); // publish.js // 这里的publish就是上面的template.publish exports.publish = function (taffyData, opts, tutorials) { // ... }
参考:taffydb
而static文件夹放置静态文件,它会被原封不动地复制到生成的文件夹中,tmpl是模板文件npm
function (taffyData, opts, tutorials)
taffyData是包含全部doclet对象的taffy对象,参考doclet.js,
多数和@description,@example等对应。name指代当前@kind的名字json
和模板相关的模块是:函数
const template = require('jsdoc/template'); // 模板定义 const helper = require('jsdoc/util/templateHelper'); // 模板的辅助函数库
生成template的语句是ui
// templatePath是配置文件中的opts.template或者使用cli时的template选项的值 // 它应该是模板文件夹`tmpl`的所在文件夹的路径`(主题文件夹的根目录)` view = new template.Template( path.join(templatePath, 'tmpl') );
再看template.js导出类的构造函数this
constructor(filepath) { // 模板文件所在路径 this.path = filepath; // layout的文件名 this.layout = null; // 代表使用的模板语法 this.settings = { evaluate: /<\?js([\s\S]+?)\?>/g, interpolate: /<\?js=([\s\S]+?)\?>/g, escape: /<\?js~([\s\S]+?)\?>/g }; }
layout的初始化语句:lua
// env.conf是配置文件导出的对象 conf = env.conf.templates || {}; conf.default = conf.default || {}; // .... // 设置layout的路径 view.layout = conf.default.layoutFile ? path.getResourcePath(path.dirname(conf.default.layoutFile), path.basename(conf.default.layoutFile) ) : 'layout.tmpl';
因此,若是要自定义模板(假设为selfTheme,如下都以其为例)的配置项,只须要使用env.conf.templates
获取便可。例如:url
// conf.json { templates: { selfProp: 'selfValue' } } // publish.js conf = env.conf.templates || {}; conf.selfProp = conf.selfProp; // get selfProp
template.js导出类的方法以下:spa
重点看render代码:code
render(file, data) { // 渲染当前模板文件 let content = this.partial(file, data); if (this.layout) { // 将当前内容赋值给data.content,传递给layout模板 data.content = content; content = this.partial(this.layout, data); } return content; }
所以,在layout.tmpl文件中,有content变量表示当前页内容
<?js= content ?>
固然,同时具备data包含的其它属性变量
在publish.js中,直接使用render函数的有两处。
// function generate(title, docs, filename, resolveLinks) docData = { env: env, title: title, // 标题 docs: docs // 见下面docs }; html = view.render('container.tmpl', docData); // function generateTutorial(title, tutorial, filename) const tutorialData = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; const tutorialPath = path.join(outdir, filename); let html = view.render('tutorial.tmpl', tutorialData);
因此,要修改各页面的标题,只须要将调用这两个函数的title函数换掉,好比:
generate('Global', [{kind: 'globalobj'}], globalUrl) // 改为 generate('MyGlobal', [{kind: 'globalobj'}], globalUrl)
不太重点仍是添加模板变量和方法
// 添加变量,只须要改变render调用时的data,好比如下改变container.tmpl的变量 docData = { env: env, title: title, // 标题 docs: docs, myVar: 'myVar' }; html = view.render('container.tmpl', docData); // 添加方法 view.find = find; view.linkto = linkto; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.outputSourceFiles = outputSourceFiles; view.nav = buildNav(members); // 在模板中可使用`this.method`来调用,由于模板的this指向view // 好比 // <?js this.find() ?>
添加到view中的方法/属性:
经过find({kind: 'class'})来获取全部class,也能够添加其它doclet:find({kind, 'class', memberOf: 'Class1' })
渲染一个跳转到longname
)即namepath所指代的url的连接(a标签),文本是linkText
resolveAuthorLinks
渲染形如Jane Doe <jdoe@example.org>
的文本为<a href="mailto:jdoe@example.org">Jane Doe</a>
docs是一个包含数据的Taffy对象,它的来源是:
data = taffyData // 获取各部分的数据taffy对象 members = helper.getMembers(data) classes = taffy(members.classes); modules = taffy(members.modules); namespaces = taffy(members.namespaces); mixins = taffy(members.mixins); externals = taffy(members.externals); interfaces = taffy(members.interfaces); // 获取对应longname的docs const myClasses = helper.find(classes, {longname: longname}); const myExternals = helper.find(externals, {longname: longname}); const myInterfaces = helper.find(interfaces, {longname: longname}); const myMixins = helper.find(mixins, {longname: longname}); const myModules = helper.find(modules, {longname: longname}); const myNamespaces = helper.find(namespaces, {longname: longname});
// myClasses等就是传入模板文件的docs变量
if (myModules.length) { generate(`Module: ${myModules[0].name}`, myModules, helper.longnameToUrl[longname]); } if (myClasses.length) { generate(`Class: ${myClasses[0].name}`, myClasses, helper.longnameToUrl[longname]); } if (myNamespaces.length) { generate(`Namespace: ${myNamespaces[0].name}`, myNamespaces, helper.longnameToUrl[longname]); } if (myMixins.length) { generate(`Mixin: ${myMixins[0].name}`, myMixins, helper.longnameToUrl[longname]); } if (myExternals.length) { generate(`External: ${myExternals[0].name}`, myExternals, helper.longnameToUrl[longname]); } if (myInterfaces.length) { generate(`Interface: ${myInterfaces[0].name}`, myInterfaces, helper.longnameToUrl[longname]); }
在模板中则用this.find({kind: 'class'})代替(view.find)
docs的元素内容是:
// 参考doclet.js { comment: '' name: '',, kind: '', // ... }
特别的:
// source { kind: 'source', code: '' } // main page { kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page' } // global { kind: 'globalobj' }
tutorials相关待续