docs.js (227 lines of code) (raw):

const fs = require('fs'); const process = require('process'); const path = require('path'); const YAML = require('yamljs'); const {execSync} = require('child_process'); const {promises} = fs; const docConfig = './data/docs.yml'; const layoutTemplateFile = '/themes/docsy/layouts/projectDoc/baseof.html'; const NEXT = 'next'; init(); async function init() { try { const targetPath = path.join(__dirname, layoutTemplateFile) const docsList = await loadYaml(docConfig) const {tpl, docsInfo} = await traverseDocsList(docsList) await generateLayoutTemplate(targetPath, tpl) handleDocsFiles(docsInfo) } catch (err) { console.log(err); process.exit(1) } } function readDirSync(path, docInfo, replaceMarkdownText) { const pa = fs.readdirSync(path); pa.forEach(function (ele) { const filePath = path + "/" + ele const info = fs.statSync(filePath); if (info.isDirectory()) { readDirSync(filePath, docInfo, replaceMarkdownText); return; } if (isImage(ele)) { const {docName, version} = docInfo; const imgName = `${docName}_${version}_${ele}`; fs.copyFile(filePath, './static/images/' + imgName, function (err) { if (err) { throw err } }); return; } const reg = /\.md/gi; const shouldFormat = reg.test(filePath); if (shouldFormat) { readFile(filePath, docInfo, replaceMarkdownText); } }); } function readFile(filePath, docInfo, replaceMarkdownText) { fs.readFile(filePath, function (err, data) { if (err) { throw err } else { let codeTxt = data.toString(); codeTxt = replaceMarkdownText(codeTxt, docInfo, filePath) writeFile(filePath, codeTxt); } }); } function isImage(file) { var reg = /(.*)\.(jpg|jpeg|png|bmp|gif|ico|pcx|tif|tiff|raw|tga|webp|svg)$/; return reg.test(file) } function replaceMarkdownText(codeTxt, docInfo, filePath) { if (!/^([\s]*)(---[\s\S]*---)/.test(codeTxt)) { const {repoUrl, commitId, docName, version} = docInfo; const prefix = repoUrl.replace('.git', '/tree') + `/${commitId}`; const depth = filePath.split('/docs')[1].match(/\//g).length - 2; let title = codeTxt.trim().split('\n')[0] title = title.match(/(?<=([ ])).*/g)[0]; title = title.replace(/:/g, ':') codeTxt = `--- title: ${title} type: projectDoc layout: baseof ---\n` + codeTxt; codeTxt = codeTxt .replace(/(\[[\s\S]*?\])\(([\s\S]*?)\)/g, function (match, p1, p2) { if (p2 && p2.startsWith('http') || isImage(p2)) { return match } if (p2.startsWith('../')) { const parentDepth = p2.match(/\.\.\//g).length; if (parentDepth >= depth) { const url = p2.replace(/\.\.\//g, '') return `${p1}(${prefix}/${url})` } } const str = p2 .toLowerCase() .replace(/\.md/g, '') if (str.startsWith('#')) { return `${p1}(${str})` } if (str.startsWith('./')) { return `${p1}(./../${str.slice(2)})` } return `${p1}(../${str})` }) .replace(/<img(.*?)src="(.*?)"(.*?)>/g, function (match, p1, p2, p3) { if (p2 && p2.startsWith('http')) { return match } const imgName = `${docName}_${version}_` + p2.split('/').pop(); return `<img${p1}src="/images/${imgName}"${p3}>` }) .replace(/(\!\[[\s\S]*?\])\((.*?)\)/g, function (match, p1, p2,) { if (p2 && p2.startsWith('http')) { return match } const imgName = `${docName}_${version}_` + p2.split('/').pop(); return `${p1}(/images/${imgName})` }) } return codeTxt } function writeFile(filePath, codeTxt) { fs.writeFile(filePath, codeTxt, function (err) { if (err) { throw err } }); } async function traverseDocsList(result) { let tpl = ''; const docsInfo = [] for (const data of result) { for (const item of data.list) { if (!item.docs) { continue; } for (const doc of item.docs) { const {name, repo, repoUrl, docs, description, icon} = item; if (!repoUrl) continue; let {version, commitId} = doc; version = version.toLowerCase(); commitId = commitId || version; const docName = repo === 'skywalking' ? 'main' : repo; const localPath = `/content/docs/${docName}/${version}`; const menuFileName = `${docName.replace(/\-|\./g, '_')}${version.replace(/\-|v|\./g, '_')}`; tpl += `{{ if in .File.Path "${localPath.split('/content/')[1]}" }} <div class="description-wrapper"> <h5> <img width="26" height="26" src="/images/project/${icon}.svg"> ${name} </h5> <p>${description}</p> </div> {{ $currentVersion := lower .Site.Data.docSidebar.${menuFileName}.version }} <div class="version-wrapper">Version: <select class="version-select"> {{range .Site.Data.docSidebar.${menuFileName}.repoDocs}} {{$version := lower .version}} {{$versionName := .versionName}} <option {{ cond (eq $currentVersion $version) "selected" "" }} value="{{$version}}"> {{if $versionName}} {{$versionName}} {{else}} {{$version}} {{end}} </option> {{end}} </select> </div> {{ partial "sidebar-menu.html" .Site.Data.docSidebar.${menuFileName} }} <div class="commit-id">Commit Id: {{.Site.Data.docSidebar.${menuFileName}.commitId}}</div> {{ end }}\n`; execSync(`"./doc.sh" ${repo} ${repoUrl} ${commitId} ${localPath} ${menuFileName}`); const sha = execSync(`git -C ./tmp/${repo} rev-parse HEAD`); if(version === NEXT && sha){ commitId = sha.toString().replace('\n', ''); } docsInfo.push({localPath, repoUrl, commitId, docName, version}) await handleMenuFiles(`./data/docSidebar/${menuFileName}.yml`, { version, commitId, docs, }, `/docs/${docName}/${version}`) } } } return {tpl, docsInfo} } async function generateLayoutTemplate(targetPath, tpl) { let codeTxt = await promises.readFile(targetPath, 'utf8'); codeTxt = codeTxt.toString() codeTxt = codeTxt.replace(/(td-sidebar">)([\s\S]*)(<\/div>[\s\S]*<main)/, function (match, p1, p2, p3) { return `${p1}\n${tpl}\n${p3}` }) await promises.writeFile(targetPath, codeTxt, 'utf8'); } function handleDocsFiles(docsInfo) { docsInfo.forEach((docInfo) => { const {localPath} = docInfo const root = path.join(__dirname, localPath); readDirSync(root, docInfo, replaceMarkdownText); }) } async function handleMenuFiles(menuFilePath, docInfo, localPath) { const nativeObject = await loadYaml(menuFilePath); const {version, commitId, docs} = docInfo nativeObject.version = version; nativeObject.commitId = commitId.slice(0, 7); nativeObject.repoDocs = docs; handleMenuPath(nativeObject.catalog, localPath) const yamlString = YAML.stringify(nativeObject, 2); await promises.writeFile(menuFilePath, yamlString, 'utf8'); } function handleMenuPath(list, localPath) { list.forEach(item => { const pagePath = item.path; if (pagePath) { item.path = pagePath.startsWith('http') ? pagePath : (localPath + pagePath).toLowerCase(); } if (item.catalog) { handleMenuPath(item.catalog, localPath) } }) } function loadYaml(filePath) { return new Promise((resolve) => { YAML.load(filePath, function (result) { resolve(result) }); }) }