UNPKG

arrow-docgen

Version:
263 lines (236 loc) 7.16 kB
// jscs:disable jsDoc var fs = require('fs'), path = require('path'), ejs = require('ejs'), fsextra = require('fs-extra'), async = require('async'), util = require('arrow-util'), _ = require('lodash'), // in case the admin changes which requires a re-gen pkgHash = md5(JSON.stringify(require('./package.json'))); /** * create an <img> tag */ function image_tag(img, width, height) { var html = '<img src="images/' + img + '" '; if (width) { html += 'width="' + width + '" '; } if (height) { html += 'height="' + height + '" '; } html += '>'; return html; } function md5(value) { return require('crypto').createHash('md5').update(value).digest('hex'); } function generate(config, object, outdir, callback) { // check to see if the doc has already been generated from the same object model + config var key = md5(JSON.stringify(config) + JSON.stringify(object) + pkgHash); var cacheFile = path.join(outdir, '.generated'); if (fs.existsSync(cacheFile)) { var cached = fs.readFileSync(cacheFile).toString(); if (cached === key) { return callback(); } } var baseurl = object.baseurl || ('http://127.0.0.1:' + object.server.port), source = path.join(__dirname, 'source'), adminurl = object.adminurl || (baseurl + object.config.admin.prefix), auth = object.server && object.server.auth, layoutEJS = ejs.compile(fs.readFileSync(path.join(__dirname, 'source', 'layouts', 'layout.ejs'), 'utf8')), sections = [ {title: 'Overview', pages: []} ], context = { apiCount: 0, modelCount: 0, blockCount: 0, connectorCount: 0, connectors: {}, blocks: {} }; var staticPages = [ {url: 'introduction', title: 'Introduction', name: 'index'}, {url: 'authentication', title: 'Authentication'}, {url: 'content_formats', title: 'Content Formats'}, {url: 'response', title: 'Response'}, {url: 'errors', title: 'Errors'} ]; for (var i = 0; i < staticPages.length; i++) { var staticPage = staticPages[i]; staticPage.markdown = fs.readFileSync(path.join(source, staticPage.url + '.md'), 'utf8'); if (i === 0 && object.metadata.documentation) { // TODO: Place object.metadata.documentation on its own page. staticPage.markdown = object.metadata.documentation + '\n' + staticPage.markdown; } sections[0].pages.push(staticPage); } sections.push({ url: 'apis', title: 'APIs', generator: require('./lib/apis').generate }); // these are only available in development if (object.config.env === 'development') { sections.push({ url: 'models', title: 'Models', generator: require('./lib/models').generate }); sections.push({ url: 'blocks', title: 'Blocks', generator: require('./lib/blocks').generate }); sections.push({ url: 'connectors', title: 'Connectors', generator: require('./lib/connectors').generate }); } var replaceVars = _.merge(context, { ENDPOINT_URL: baseurl, ADMIN_URL: adminurl, AUTH_TYPE: auth && auth.type || 'none', AUTH_APIKEY_PRODUCTION: auth && auth.apikey_production || '', AUTH_APIKEY_DEVELOPMENT: auth && auth.apikey_development || '', AUTH_APIKEY: auth && auth.apikey || (auth && object.config.env === 'development' ? auth.apikey_development : auth.apikey_production), AUTH_USER: auth && auth.username, AUTH_PASS: auth && auth.password, ENV: object.config.env || '' }); var pageConfig = _.merge({ directory: 'outdir', value: replaceVars }, config); function makeTemplate(value) { if (_.isString(value)) { return value.split(','); } return value; } var pageCacheFn = path.join(outdir, '_pagecache.json'), pageCache = fs.existsSync(pageCacheFn) && JSON.parse(fs.readFileSync(pageCacheFn)) || {}; async.each(sections, function eachSection(section, nextSection) { section.pages = section.pages || section.generator(object, baseurl, adminurl, context); var token = md5(JSON.stringify(section.pages) + key + pkgHash); if (section.url in pageCache) { var cache = pageCache[section.url]; if (cache === token) { return nextSection(); } } pageCache[section.url] = token; async.each(section.pages, function eachPage(page, nextPage) { var pageToken = md5(JSON.stringify(page) + key + pkgHash), name = (section.url ? section.url + '/' : '') + (page.name || page.url); if (name in pageCache) { var cache = pageCache[name]; if (cache === pageToken) { return nextPage(); } } pageCache[name] = pageToken; pageConfig.content = page.markdown; pageConfig.appendJS = makeTemplate(config.js || []).map(stripWebPublic); pageConfig.appendCSS = makeTemplate(config.css || []).map(stripWebPublic); var html = generateHTML(layoutEJS, pageConfig); var fn = path.join(outdir, name.toLowerCase() + '.html'), dir = path.dirname(fn); if (!fs.existsSync(dir)) { fsextra.mkdirsSync(dir); } delete page.markdown; fs.writeFile(fn, html, nextPage); }, nextSection); }, function allDone(err) { if (err) { callback(err); } else { var menu = []; for (var i = 0; i < sections.length; i++) { var section = sections[i], pages = []; for (var j = 0; j < section.pages.length; j++) { var page = section.pages[j]; pages.push({ url: page.url, title: page.title }); } // Sort all pages except for Overview's. if (i !== 0) { pages.sort(function (a, b) { var at = a.title, bt = b.title; var aHasSlash = at.indexOf('/') >= 0, bHasSlash = bt.indexOf('/') >= 0; if (aHasSlash === bHasSlash) { return compareStrings(at, bt); } else if (aHasSlash) { return 1; } else { return -1; } }); } menu.push({ url: section.url, title: section.title, pages: pages }); } // write our page cache of content to hash fs.writeFileSync(pageCacheFn, JSON.stringify(pageCache)); // write our cache file so we don't need to generate if no docs have changed fs.writeFileSync(cacheFile, key); fs.writeFile(path.join(outdir, 'menu.json'), JSON.stringify(menu), callback); } }); } function compareStrings(a, b) { if (a < b) { return -1; } if (a > b) { return 1; } return 0; } function generateHTML(layoutEJS, options) { var page = _.merge({ page_classes: [], title: 'API Documentation', search: true, image_tag: image_tag, toc_footers: [] }, options); // convert markup var generateObj = util.content.generate(page.content, options.value); page.content = generateObj.markup; // pull out the languages we found page.languages = generateObj.languages; var args = _.merge(page, options.value); // generate the main page return layoutEJS(args); } function stripWebPublic(file) { if (file.indexOf('web/public') === 0) { return file.split('web/public').pop(); } else { return file; } } if (module.id === '.') { // for testing var config = {}, object = JSON.parse(fs.readFileSync('./test/fixtures/example.json').toString()); generate(config, object, function (err, result) { console.log(result); }); } exports.generate = generate;