// TODO: move the tutorial functions to templateHelper.js function generateTutorial(title, tutorial, filename) { const tutorialData = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; let html = view.render('tutorial.tmpl', tutorialData); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> const tutorialPath = path.join(outdir, filename); fs.writeFileSync(tutorialPath, html, 'utf8'); }
function generate(title, docs, filename, resolveLinks) { resolveLinks = resolveLinks === false ? false : true; const docData = { filename: filename, title: title, docs: docs, packageInfo: (find({kind: 'package'}) || []) [0] }; const outpath = path.join(outdir, filename); let html = view.render('container.tmpl', docData); if (resolveLinks) { html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> } fs.writeFileSync(outpath, html, 'utf8'); }
exports.publish = function (taffyData, opts, tutorials) { data = taffyData; const conf = env.conf.templates || {}; conf['default'] = conf['default'] || {}; const templatePath = opts.template; view = new template.Template(templatePath + '/tmpl'); // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness // doesn't try to hand them out later const indexUrl = helper.getUniqueFilename('index'); // don't call registerLink() on this one! 'index' is also a valid longname const globalUrl = helper.getUniqueFilename('global'); helper.registerLink('global', globalUrl); // set up templating view.layout = 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); data = helper.prune(data); data.sort('longname, version, since'); helper.addEventListeners(data); let sourceFiles = {}; const sourceFilePaths = []; data().each(function (doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function (example) { let caption, code; if (example.match(/^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { caption = RegExp.$1; code = RegExp.$3; } return { caption: caption || '', code: code || example }; }); } if (doclet.see) { doclet.see.forEach(function (seeItem, i) { doclet.see[i] = hashToLink(doclet, seeItem); }); } // build a list of source files let sourcePath; let resolvedSourcePath; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); resolvedSourcePath = resolveSourcePath(sourcePath); sourceFiles[sourcePath] = { resolved: resolvedSourcePath, shortened: null }; sourceFilePaths.push(resolvedSourcePath); } }); fs.mkPath(outdir); // copy the template's static files to outdir const fromDir = path.join(templatePath, 'static'); const staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function (fileName) { const toDir = fs.toDir(fileName.replace(fromDir, outdir)); fs.mkPath(toDir); fs.copyFileSync(fileName, toDir); }); // copy user-specified static files to outdir let staticFilePaths; let staticFileFilter; let staticFileScanner; if (conf['default'].staticFiles) { staticFilePaths = conf['default'].staticFiles.paths || []; staticFileFilter = new(require('jsdoc/lib/jsdoc/src/filter')).Filter(conf['default'].staticFiles); staticFileScanner = new(require('jsdoc/lib/jsdoc/src/scanner')).Scanner(); staticFilePaths.forEach(function (filePath) { const extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); extraStaticFiles.forEach(function (fileName) { const sourcePath = fs.statSync(filePath).isDirectory() ? filePath : path.dirname(filePath); const toDir = fs.toDir(fileName.replace(sourcePath, outdir)); fs.mkPath(toDir); fs.copyFileSync(fileName, toDir); }); }); } if (sourceFilePaths.length) { sourceFiles = shortenPaths(sourceFiles, path.commonPrefix(sourceFilePaths)); } data().each(function (doclet) { const url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); // replace the filename with a shortened version of the full path let docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.filename = docletPath; } } }); data().each(function (doclet) { const url = helper.longnameToUrl[doclet.longname]; if (url.indexOf('#') > -1) { doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); } else { doclet.id = doclet.name; } if (needsSignature(doclet)) { addSignatureParams(doclet); addSignatureReturns(doclet); } }); // do this after the urls have all been generated data().each(function (doclet) { doclet.ancestors = getAncestorLinks(doclet); if (doclet.kind === 'member') { addSignatureTypes(doclet); } if (doclet.kind === 'constant') { addSignatureTypes(doclet); doclet.kind = 'member'; } }); const members = helper.getMembers(data); members.tutorials = tutorials.children; // add template helpers view.find = find; view.linkto = linkto; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.members = members; //@davidshimjs: To make navigation for customizing // once for all view.nav = buildNav(members); attachModuleSymbols(find({ kind: ['class', 'function'], longname: { left: 'module:' } }), members.modules); // only output pretty-printed source files if requested; do this before generating any other // pages, so the other pages can link to the source files if (conf['default'].outputSourceFiles) { generateSourceFiles(sourceFiles); } if (members.globals.length) { generate('Global', [{ kind: 'globalobj' }], globalUrl); } // index page displays information from package.json and lists files const files = find({ kind: 'file' }); generate('Index', [{ kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page' }].concat(files), indexUrl); // set up the lists that we'll use to generate pages const classes = taffy(members.classes); const modules = taffy(members.modules); const namespaces = taffy(members.namespaces); const mixins = taffy(members.mixins); const externals = taffy(members.externals); for (const longname in helper.longnameToUrl) { if (hasOwnProp.call(helper.longnameToUrl, longname)) { const myClasses = helper.find(classes, { longname: longname }); if (myClasses.length) { generate('Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); } const myModules = helper.find(modules, { longname: longname }); if (myModules.length) { generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } const myNamespaces = helper.find(namespaces, { longname: longname }); if (myNamespaces.length) { generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); } const myMixins = helper.find(mixins, { longname: longname }); if (myMixins.length) { generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); } const myExternals = helper.find(externals, { longname: longname }); if (myExternals.length) { generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); } } } // TODO: move the tutorial functions to templateHelper.js function generateTutorial(title, tutorial, filename) { const tutorialData = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children, titleApp, versionApp, }; let html = view.render('tutorial.tmpl', tutorialData); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> const tutorialPath = path.join(outdir, filename); fs.writeFileSync(tutorialPath, html, 'utf8'); } // tutorials can have only one parent so there is no risk for loops function saveChildren(node) { node.children.forEach(function (child) { generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); saveChildren(child); }); } saveChildren(tutorials); };