文件目录

使用npm安装jsdoc后,其文件目录结构如下:
image.png
其中templates放置了默认的主题模板。其中default主题的文件结构如下:
image.png
其中,publish.js是最重要的文件,它导出一个publish函数,在cli.js中被调用

// 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是模板文件

publish.js解析

publish函数参数

function (taffyData, opts, tutorials)

taffyData是包含所有doclet对象的taffy对象,参考doclet.js,
多数和@description,@example等对应。name指代当前@kind的名字

模板

和模板相关的模块是:

const template = require('jsdoc/template'); // 模板定义
const helper = require('jsdoc/util/templateHelper'); // 模板的辅助函数库

生成template的语句是

// templatePath是配置文件中的opts.template或者使用cli时的template选项的值
// 它应该是模板文件夹`tmpl`的所在文件夹的路径`(主题文件夹的根目录)`
view = new template.Template( path.join(templatePath, 'tmpl') );

再看template.js导出类的构造函数

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的初始化语句:

// 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获取即可。例如:

// conf.json
{
  templates: {
    selfProp: 'selfValue'
  }
}

// publish.js
conf = env.conf.templates || {};
conf.selfProp = conf.selfProp; // get selfProp

template.js导出类的方法如下:

  • load(file: string) // 解析模板
  • partial(file: string, data: any) // 引用另一模板文件,data是载入模板的数据
  • render(file: string, data: any) // 渲染模板

重点看render代码:

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(spec)

通过find({kind: 'class'})来获取所有class,也可以添加其它doclet:find({kind, 'class', memberOf: 'Class1' })

  • linkTo(longname, linkText)

渲染一个跳转到longname
)即namepath所指代的url的链接(a标签),文本是linkText
resolveAuthorLinks
渲染形如Jane Doe <jdoe@example.org> 的文本为<a href="mailto:jdoe@example.org">Jane Doe</a>

  • htmlsafe(str) 转义html文本
  • outputSourceFiles 是否导出源文件的标志
  • nav 导航栏标签

docs

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相关待续

小结点
154 声望3 粉丝

« 上一篇
vuex知识点
下一篇 »
axios二次封装

引用和评论

0 条评论