UNPKG

ydoc

Version:

基于 Markdown 的静态站点生成工具

267 lines (235 loc) 7.3 kB
const path = require('path'); const fs = require('fs-extra'); const utils = require('../utils.js'); const ydoc = require('../ydoc.js'); const ydocConfig = ydoc.config; let defaultIndexPageName = 'index'; const defaultSummaryPage = 'SUMMARY.md'; const defaultNavPage = 'NAV.md'; const generate = require('../generate.js').generatePage; const runBatch = require('../generate.js').runBatch; const parseSummary = require('./summary'); const parseMarkdown = require('./markdown').parseMarkdown; const parsePage = require('./page.js'); const parseHtml = require('./html.js'); const parseNav = require('./nav'); const emitHook = require('../plugin.js').emitHook; const url = require('url'); const color = require('bash-color'); function getIndexPath(filepath){ let getIndexPathByType = (type)=> path.resolve(filepath, defaultIndexPageName + '.' + type); let types = ['md', 'jsx', 'html']; let contentFilepath; for(let index in types){ contentFilepath = getIndexPathByType(types[index]); if(utils.fileExist(contentFilepath)){ return contentFilepath; } } } function getBookSummary(filepath){ let summaryFilepath = path.resolve(filepath, defaultSummaryPage); if(!utils.fileExist(summaryFilepath)) return null; let summary = parseMarkdown(summaryFilepath); fs.unlinkSync(summaryFilepath); return parseSummary(summary); } function getNav(filepath){ let navFilepath = path.resolve(filepath, defaultNavPage); if(!utils.fileExist(navFilepath)) return null; let content = parseMarkdown(navFilepath); fs.unlinkSync(navFilepath); return parseNav(content); } function getBookContext(book, page){ const context = utils.extend({}, book); context.page = page; context.config = ydocConfig; return context; } function handleMdPathToHtml(filepath){ let fileObj = path.parse(filepath); if(fileObj.ext === '.md' || fileObj.ext === '.jsx'){ let name = fileObj.name + '.html'; return path.format({ dir: fileObj.dir, base: name }) }else{ return path.format({ dir: fileObj.dir, base: fileObj.base }) } } exports.parseSite = async function(dist){ try{ await emitHook('init'); defaultIndexPageName = 'index' let indexPath = await getIndexPath(dist); if(!indexPath){ return utils.log.error(`The root directory of site didn't find index page.`) } ydocConfig.nav = getNav(dist); let books = [] ydocConfig.nav.menus.forEach(menu=>{ let menuBooks = getBooks(menu.items, dist); books = books.concat(menuBooks) }) books = utils.distinct(books, (item) => { return item.bookpath + item.indexFile }) const generateSitePage = generate(dist); generateSitePage({ title: ydocConfig.title, page: { srcPath: indexPath, distPath: './index.html' }, config: ydocConfig }) await runBatch(); for(let j=0; j< books.length ; j++){ await parseBook(books[j]); } let showpath = color.yellow( dist + '/index.html'); utils.log.ok(`Generate Site "${ydocConfig.title}" ${showpath}`); await emitHook('finish') }catch(err){ utils.log.error(err); } } function getBooks(menus, dist){ let books = []; for(let i=0; i< menus.length; i++){ let item = menus[i]; if( !item.ref || utils.isUrl(item.ref)){ continue; } if(path.isAbsolute(item.ref)){ item.ref = '.' + item.ref; } let bookHomePath = path.resolve(dist, item.ref); if(!utils.fileExist(bookHomePath)) continue; let indexFile = path.basename(bookHomePath); let bookpath = path.dirname(bookHomePath); let stats; try{ stats = fs.statSync(bookpath); }catch(err){ continue; } if(stats.isDirectory() && item[0] !== '_' && item[0] !== 'style' ){ item.ref = handleMdPathToHtml(item.ref); item.absolutePath = path.resolve(dist,item.ref) books.push({ bookpath: bookpath, indexFile: indexFile, title: item.title }) } } return books; } function getBookInfo(filepath){ let page; if(path.extname(filepath) === '.md'){ page = parsePage(parseMarkdown(filepath)); }else if(path.extname(filepath) === '.jsx'){ page = { title: ydocConfig.title } }else{ page = parsePage( parseHtml(filepath)); } return { title: page.title || ydocConfig.title, description: page.description || '' } } // Schema // const bookSchema = { // title: 'string', // description: 'string', // summary: {}, // nav: {}, // bookpath: '当前书籍路径', // indexPath: '首页相对路径', // page: { // title: 'string', // description: 'string', // content: '内容', // prev: '上一页连接', // next: '下一页链接', // releativePath: '相对路径' // srcPath: '源文件路径', // distPath: '生成文件路径' // }, // assets: { // assets 资源 // js: [], // css: [] // }, // config: {} //ydocConfig 配置 // } async function parseBook({bookpath, indexFile, title}){ const book = {}; //书籍公共变量 let extname = path.extname(indexFile); let name = path.basename(indexFile, extname); defaultIndexPageName = name; let indexPath = await getIndexPath(bookpath); if(!indexPath) return ; let summary = getBookSummary(bookpath); let baseInfo = getBookInfo(indexPath); utils.extend(book, baseInfo); book.summary = summary; book.bookpath = path.resolve(bookpath, name + '.html') //优先使用导航定义的 title book.title = title? title: book.title; await emitHook('book:before', { title: book.title, description: book.description, summary: summary }); const generatePage = generate(bookpath); generatePage(getBookContext(book, { title: book.title, srcPath: indexPath, distPath: defaultIndexPageName + '.html' })) if(summary && Array.isArray(summary)) { parseDocuments(bookpath, function(absolutePath, releativeHtmlPath, title=''){ generatePage(getBookContext(book, { title: title, srcPath: absolutePath, distPath: releativeHtmlPath })); })(summary); } await runBatch(); let showpath = color.yellow( bookpath + '/' + defaultIndexPageName + '.html'); utils.log.ok(`Generate book "${book.title}" ${showpath}`); await emitHook('book'); } function parseDocuments(bookpath, callback){ return function _parseDocuments(summary){ for(let index = 0; index< summary.length; index++){ let item = summary[index]; if(item.ref){ let urlObj = url.parse(item.ref); if(urlObj.host) continue; let releativePath = urlObj.pathname; let absolutePath = path.resolve(bookpath, releativePath); if(utils.fileExist(absolutePath)){ let releativeHtmlPath = handleMdPathToHtml(releativePath); urlObj.hash = urlObj.hash ? urlObj.hash.toLowerCase() : ''; item.ref = releativeHtmlPath + urlObj.hash; item.absolutePath = absolutePath; callback(absolutePath, releativeHtmlPath, item.title) } } if(item.articles && Array.isArray(item.articles) && item.articles.length > 0){ _parseDocuments(item.articles) } } } }