const fs = require('fs-extra');
const dirname = require('path').dirname;
const Mustache = require('mustache');
const glob = require('glob');
const headerHtml = `
{{{tocHtml}}}
`;
const footerHtmlTemplate = `
`;
const footerHtml = footerHtmlTemplate.replace('YYYY', new Date().getFullYear());
// const screenshotHtml = `
//
//
//
// Mobile
// |
//
// Command line
// |
//
//
//
//
// |
//
//
// joplin:/My notebook$ ls -n 12
// [ ] 8am conference call ☎
// [ ] Make vet appointment
// [ ] Go pick up parcel
// [ ] Pay flat rent 💸
// [X] Book ferry 🚢
// [X] Deploy Joplin app
// Open source stuff
// Swimming pool time table 🏊
// Grocery shopping list 📝
// Work itinerary
// Tuesday random note
// Vacation plans ☀
//
// |
//
//
// `;
const scriptHtml = `
`;
const rootDir = dirname(dirname(__dirname));
function markdownToHtml(md, templateParams) {
const MarkdownIt = require('markdown-it');
const markdownIt = new MarkdownIt({
breaks: true,
linkify: true,
html: true,
});
markdownIt.core.ruler.push('checkbox', state => {
const tokens = state.tokens;
const Token = state.Token;
const doneNames = [];
const headingTextToAnchorName = (text, doneNames) => {
const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let lastWasDash = true;
let output = '';
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (allowed.indexOf(c) < 0) {
if (lastWasDash) continue;
lastWasDash = true;
output += '-';
} else {
lastWasDash = false;
output += c;
}
}
output = output.toLowerCase();
while (output.length && output[output.length - 1] === '-') {
output = output.substr(0, output.length - 1);
}
let temp = output;
let index = 1;
while (doneNames.indexOf(temp) >= 0) {
temp = `${output}-${index}`;
index++;
}
output = temp;
return output;
};
const createAnchorTokens = anchorName => {
const output = [];
{
const token = new Token('heading_anchor_open', 'a', 1);
token.attrs = [
['name', anchorName],
['href', `#${anchorName}`],
['class', 'heading-anchor'],
];
output.push(token);
}
{
const token = new Token('text', '', 0);
token.content = '🔗';
output.push(token);
}
{
const token = new Token('heading_anchor_close', 'a', -1);
output.push(token);
}
return output;
};
let insideHeading = false;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token.type === 'heading_open') {
insideHeading = true;
continue;
}
if (token.type === 'heading_close') {
insideHeading = false;
continue;
}
if (insideHeading && token.type === 'inline') {
const anchorName = headingTextToAnchorName(token.content, doneNames);
doneNames.push(anchorName);
const anchorTokens = createAnchorTokens(anchorName);
// token.children = anchorTokens.concat(token.children);
token.children = token.children.concat(anchorTokens);
}
}
});
const improveDocHtml = `
`;
return Mustache.render(headerHtml, templateParams) + markdownIt.render(md) + Mustache.render(improveDocHtml, templateParams) + scriptHtml + footerHtml;
}
let tocMd_ = null;
let tocHtml_ = null;
const tocRegex_ = /([^]*)/;
function tocMd() {
if (tocMd_) return tocMd_;
const md = fs.readFileSync(`${rootDir}/README.md`, 'utf8');
const toc = md.match(tocRegex_);
tocMd_ = toc[1];
return tocMd_;
}
function replaceGitHubByJoplinAppLinks(md) {
// let output = md.replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/master\/readme\/(.*?)\/index\.md(#[^\s)]+|)/g, 'https://joplinapp.org/$1');
return md.replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/dev\/readme\/(.*?)\.md(#[^\s)]+|)/g, 'https://joplinapp.org/$1/$2');
}
function tocHtml() {
if (tocHtml_) return tocHtml_;
const MarkdownIt = require('markdown-it');
const markdownIt = new MarkdownIt();
let md = tocMd();
md = md.replace(/# Table of contents/, '');
md = replaceGitHubByJoplinAppLinks(md);
tocHtml_ = markdownIt.render(md);
tocHtml_ = `
${tocHtml_}
`;
return tocHtml_;
}
function renderMdToHtml(md, targetPath, templateParams) {
// Remove the header because it's going to be added back as HTML
md = md.replace(/# Joplin\n/, '');
templateParams.baseUrl = 'https://joplinapp.org';
templateParams.imageBaseUrl = `${templateParams.baseUrl}/images`;
templateParams.tocHtml = tocHtml();
const title = [];
if (!templateParams.title) {
title.push('Joplin - an open source note taking and to-do application with synchronisation capabilities');
} else {
title.push(templateParams.title);
title.push('Joplin');
}
md = replaceGitHubByJoplinAppLinks(md);
templateParams.pageTitle = title.join(' | ');
const html = markdownToHtml(md, templateParams);
const folderPath = dirname(targetPath);
fs.mkdirpSync(folderPath);
fs.writeFileSync(targetPath, html);
}
async function readmeFileTitle(sourcePath) {
const md = await fs.readFile(sourcePath, 'utf8');
const r = md.match(/(^|\n)# (.*)/);
if (!r) {
throw new Error(`Could not determine title for Markdown file: ${sourcePath}`);
} else {
return r[2];
}
}
function renderFileToHtml(sourcePath, targetPath, templateParams) {
const md = fs.readFileSync(sourcePath, 'utf8');
return renderMdToHtml(md, targetPath, templateParams);
}
function makeHomePageMd() {
let md = fs.readFileSync(`${rootDir}/README.md`, 'utf8');
md = md.replace(tocRegex_, '');
// HACK: GitHub needs the \| or the inline code won't be displayed correctly inside the table,
// while MarkdownIt doesn't and will in fact display the \. So we remove it here.
md = md.replace(/\\\| bash/g, '| bash');
return md;
}
async function main() {
await fs.remove(`${rootDir}/docs`);
await fs.copy(`${rootDir}/Assets/WebsiteAssets`, `${rootDir}/docs`);
renderMdToHtml(makeHomePageMd(), `${rootDir}/docs/index.html`, { sourceMarkdownFile: 'README.md' });
const mdFiles = glob.sync(`${rootDir}/readme/**/*.md`, {
ignore: [
// '**/node_modules/**',
],
}).map(f => f.substr(rootDir.length + 1));
const sources = [];
for (const mdFile of mdFiles) {
const title = await readmeFileTitle(`${rootDir}/${mdFile}`);
const targetFilePath = `${mdFile.replace(/\.md/, '').replace(/readme\//, 'docs/')}/index.html`;
sources.push([mdFile, targetFilePath, { title: title }]);
}
const path = require('path');
for (const source of sources) {
source[2].sourceMarkdownFile = source[0];
source[2].sourceMarkdownName = path.basename(source[0], path.extname(source[0]));
renderFileToHtml(`${rootDir}/${source[0]}`, `${rootDir}/${source[1]}`, source[2]);
}
}
main().catch((error) => {
console.error(error);
});