123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- var fs = require('jsdoc/fs');
- var path = require('jsdoc/path');
- var helper = require('jsdoc/util/templateHelper');
- var doop = require('jsdoc/util/doop');
- var handle = require('jsdoc/util/error').handle;
- var template = require('./template');
- var doc = require('./docletHelper');
- var glob = require('glob');
- var extend = require('extend');
- exports.process = function(){
- template.raw.data.sort(template.options.sort);
- template.raw.data().each(function(doclet){
- doclet.ancestors = helper.getAncestorLinks(template.raw.data, doclet);
- doclet.linkText = doc.getLinkText(doclet);
- doclet.summary = doc.getSummary(doclet);
- doclet.params = doc.getParamsOrProps(doclet, 'params');
- doc.checkParamsOrProps(doclet, 'params');
- doclet.properties = doc.getParamsOrProps(doclet, 'properties');
- doc.checkParamsOrProps(doclet, 'properties');
- doclet.attribs = doc.getAttribs(doclet);
- doclet.signature = doc.getSignature(doclet);
- doclet.examples = doc.getExamples(doclet);
- doclet.fires = doc.getFires(doclet);
- doclet.requires = doc.getRequires(doclet);
- doclet.pageTitle = doc.getPageTitle(doclet, true);
- doclet.pageTitleHTML = doc.getPageTitle(doclet, false);
- doclet.symbolTitle = doc.getSymbolTitle(doclet, true);
- doclet.symbolTitleHTML = doc.getSymbolTitle(doclet, false);
- doclet.primaryTitle = doc.getPrimaryTitle(doclet, true);
- doclet.primaryTitleHTML = doc.getPrimaryTitle(doclet, false);
- doclet.listTitle = doc.getListTitle(doclet, true);
- doclet.listTitleHTML = doc.getListTitle(doclet, false);
- doclet.hasDetails = doc.hasDetails(doclet);
- doclet.inherited = doc.isInherited(doclet);
- doclet.access = typeof doclet.access == 'string' ? doclet.access : "public";
- // todo: maybe expose this as an additional option, basically switches the description to a summary if no summary was supplied and the descriptions text is shorter than 120 characters
- //if (!doclet.summary.length && doclet.description && template.sanitize(doclet.description).length <= 120){
- // doclet.summary = doclet.description;
- // doclet.description = '';
- //}
- if (typeof doclet.showTableOfContents != 'boolean'){
- doclet.showTableOfContents = template.options.showTableOfContents;
- }
- });
- template.raw.data().each(function(doclet){
- doclet.symbols = doc.getSymbols(doclet);
- doclet.showAccessFilter = doc.getShowAccessFilter(doclet);
- });
- };
- var hasOwnProp = Object.prototype.hasOwnProperty;
- function getFilename(longname) {
- var fileUrl;
- if ( hasOwnProp.call(helper.longnameToUrl, longname) ) {
- fileUrl = helper.longnameToUrl[longname];
- } else {
- fileUrl = helper.getUniqueFilename(longname);
- helper.registerLink(longname, fileUrl);
- }
- return fileUrl;
- }
- /**
- * @summary Registers the given doclet creating and registering a link for it and appending an id value to use when rendering in HTML.
- * @param doclet - The doclet to register.
- */
- var registerDoclet = exports.registerDoclet = function (doclet) {
- var url;
- if (template.kinds.custom.indexOf(doclet.kind) !== -1) {
- url = helper.getUniqueFilename(doclet.longname);
- } else if (doclet.kind === 'event') {
- url = getFilename(doclet.memberof || helper.globalName);
- if ( (doclet.name !== doclet.longname) || (doclet.scope === helper.globalName) ) {
- url += '#' + (helper.scopeToPunc[doclet.scope] === '#' ? '' : helper.scopeToPunc[doclet.scope]) + 'event:' + doclet.name.replace(/(^"|"$)/g, "");
- }
- } else {
- url = helper.createLink(doclet);
- }
- helper.registerLink(doclet.longname, url);
- if (helper.longnameToUrl[doclet.longname].indexOf('#') > -1) {
- doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop();
- } else {
- doclet.id = doclet.name;
- }
- };
- /**
- * @summary Registers the README.md file as the `readme` doclet.
- * @description This not only registers the doclet but also the link associated with it, reserving it prior to registering any other links.
- *
- * The doclet is built up using the `systemName` and `systemSummary` options as the values for the associated `name` and `summary` properties of a doclet. The `longname` is simply the filename which is `index.html` and the `contents` is the markdown parsed `README.md` supplied as a source to jsdoc.
- * @example {@caption The following shows the basic structure of the generated doclet.}
- * {
- * kind: "readme",
- * longname: "index.html",
- * name: "<option:systemName>",
- * summary: "<option:systemSummary>",
- * contents: "<markdown:README.md>"
- * }
- */
- exports.registerReadme = function () {
- var doclet = {
- kind: 'readme',
- longname: 'index',
- name: template.options.systemName,
- summary: template.options.systemSummary,
- contents: template.raw.opts.readme
- };
- registerDoclet(doclet);
- template.raw.data.insert(doclet);
- };
- exports.registerGlobals = function(){
- var options = template.options.navMembers.find(function(member){ return member.kind === "global"; });
- if (!options){
- options = {
- title: "Globals",
- summary: "All documented globals."
- };
- }
- var doclet = {
- kind: 'global',
- longname: helper.globalName,
- name: options.title,
- summary: options.summary,
- members: template.find({
- kind: template.kinds.global,
- memberof: { isUndefined: true }
- }, "longname, version, since"),
- showTableOfContents: true,
- showAccessFilter: false
- };
- registerDoclet(doclet);
- template.raw.data.insert(doclet);
- };
- exports.registerListeners = function(){
- helper.addEventListeners(template.raw.data);
- };
- /**
- * @summary Registers all default doclets.
- */
- exports.registerDoclets = function(){
- template.raw.data().each(function(doclet){
- if (template.kinds.custom.indexOf(doclet.kind) === -1) {
- registerDoclet(doclet);
- }
- });
- };
- exports.registerModules = function(){
- var mapping = {};
- template.raw.data({
- kind: ['class', 'function'],
- longname: {
- left: 'module:'
- }
- }).each(function(doclet){
- var longname = doclet.longname;
- mapping[longname] = mapping[longname] || [];
- mapping[longname].push(doclet);
- });
- template.raw.data({kind: 'module'}).each(function(module){
- if (mapping[module.longname]){
- mapping[module.longname].filter(function(doclet){
- return doclet.summary || doclet.description || doclet.kind === 'class';
- }).forEach(function(doclet){
- doclet.exported = true;
- doclet.memberof = module.longname;
- var scope = helper.scopeToPunc[doclet.scope] || '>';
- doclet.longname = module.longname + scope + doclet.name;
- });
- }
- });
- };
- /**
- * @summary Registers all source files as a `source` doclet.
- * @description This method takes into account the `sourceRootPath` option and will remove this value from the file path when generating the `longname` value for the doclet. If this option is not supplied the default behavior is to find the common prefix of all file paths and trim that.
- */
- exports.registerSources = function () {
- var sourceFilePaths = [];
- // iterate all default doclets and build the source file path if the file meta exists and add it to the doclet as meta.filepath
- template.raw.data().each(function (doclet) {
- if (template.kinds.custom.indexOf(doclet.kind) === -1) {
- if (doclet.meta) {
- var src = doclet.meta.path && doclet.meta.path !== 'null'
- ? doclet.meta.path + '/' + doclet.meta.filename
- : doclet.meta.filename;
- doclet.linenum = doclet.meta.lineno; // add the linenum to the base doclet so we can sort by it
- doclet.meta.filepath = path.normalize(src);
- // push the full source file path into the array if it doesn't exist
- if (sourceFilePaths.indexOf(doclet.meta.filepath) === -1) {
- sourceFilePaths.push(doclet.meta.filepath);
- }
- }
- }
- });
- var pathToLongname = {};
- if (sourceFilePaths.length) {
- var sources = [], root = template.options.sourceRootPath;
- if (!root) root = path.commonPrefix(sourceFilePaths);
- sourceFilePaths.forEach(function (filePath) {
- var short = filePath.replace(root, '').replace(/\\/g, '/');
- var doclet = {kind: "source", longname: short, name: short, code: "", showTableOfContents: false};
- try {
- doclet.code = helper.htmlsafe(fs.readFileSync(filePath, 'utf8'));
- pathToLongname[filePath] = short;
- registerDoclet(doclet);
- sources.push(doclet);
- } catch (e) {
- handle(e);
- }
- });
- template.raw.data.insert(sources);
- }
- // now that the sources are registered go back and update the default doclets so they contain the relevant source properties
- template.raw.data().each(function (doclet) {
- // only update default kinds with the source file info
- if (template.kinds.custom.indexOf(doclet.kind) === -1) {
- // if there is a registered source file for this doclet
- if (doclet.meta && doclet.meta.filepath && pathToLongname[doclet.meta.filepath]) {
- // build up the plain as well as html text source properties
- doclet.source = pathToLongname[doclet.meta.filepath];
- doclet.sourcelink = helper.linkto(doclet.source);
- if (template.options.linenums) {
- doclet.sourcelink += ', ' + helper.linkto(doclet.source, 'line ' + doclet.meta.lineno, null, 'line-' + doclet.meta.lineno);
- }
- }
- doclet.hasSource = !!(doclet.source && (template.options.outputSourceFiles || template.options.outputSourcePath));
- }
- });
- };
- var flattenTutorialConfig = function(obj, base){
- if (!base) base = {};
- Object.keys(obj).forEach(function(key){
- var config = obj[key];
- base[key] = config;
- if (config && config.children && !config.children.length){
- flattenTutorialConfig(config.children, base);
- }
- });
- return base;
- };
- var getTutorialToConfig = function(){
- if (!template.config.dir.tutorials) return {};
- var files = glob.sync('*.json', {cwd: template.config.dir.tutorials}), json = {};
- files.forEach(function(file){
- var raw = fs.readFileSync(path.join(template.config.dir.tutorials, file), "utf8");
- extend(true, json, JSON.parse(raw));
- });
- return flattenTutorialConfig(json);
- };
- var processTutorial = function(tutorial, tutorials, tutorialToConfig){
- if (!tutorials) tutorials = [];
- tutorial.children.forEach(function(child) {
- child.kind = 'tutorial';
- child.longname = child.name;
- child.title = child.title || child.name;
- child.memberof = tutorial.name;
- child.contents = child.parse();
- child.showTableOfContents = template.options.showTableOfContents;
- var config;
- if (config = tutorialToConfig[child.longname]){
- if (config.summary){
- child.summary = config.summary;
- }
- if (config.description){
- child.description = config.description;
- }
- if (typeof config.showTableOfContents === 'boolean'){
- child.showTableOfContents = config.showTableOfContents;
- }
- }
- tutorials.push(child);
- processTutorial(child, tutorials, tutorialToConfig);
- });
- return tutorials;
- };
- exports.registerTutorials = function(){
- helper.setTutorials(template.raw.tutorials);
- var tutorialToConfig = getTutorialToConfig();
- var tutorials = processTutorial(template.raw.tutorials, false, tutorialToConfig);
- tutorials.forEach(function(tutorial){
- template.raw.data.insert(tutorial);
- });
- };
- exports.registerLists = function(){
- template.options.navMembers.forEach(function(member){
- // the global kind is register just after the index to reserve it's name so don't do it again
- if (member.kind == "global") return;
- var doclet = {
- kind: 'list',
- longname: "list:" + member.kind,
- for: member.kind,
- name: member.title,
- summary: member.summary,
- showTableOfContents: true,
- showAccessFilter: false,
- members: template.find({kind: member.kind}, "longname, version, since")
- };
- if (member.kind == 'tutorial'){
- doclet.members = doclet.members.filter(function(m){ return !m.memberof; });
- }
- registerDoclet(doclet);
- template.raw.data.insert(doclet);
- });
- };
- exports.buildNavbar = function(navbar){
- navbar.index = {
- kind: 'readme',
- link: helper.longnameToUrl['index'],
- title: template.options.systemName,
- summary: template.options.systemSummary,
- members: []
- };
- navbar.topLevel = template.find({kind:['list','global']}, "longname, name").filter(function(doclet){
- return doclet.members.length > 0 && (doclet.kind == 'list' || template.hasNavMember(doclet.kind));
- }).map(function(doclet){
- return {
- title: doclet.name,
- summary: doclet.summary,
- link: helper.longnameToUrl[doclet.longname],
- members: doclet.members.map(function(member){
- return template.linkto(member.longname);
- })
- };
- });
- };
|