render-gfm
Version:
Render GitHub Flavoured Markdown, with CSS for each of GitHub's themes.
148 lines (141 loc) • 11.6 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import fs from 'fs';
import path from 'path';
import prettier from 'prettier';
import { Writer } from 'steno';
import { Octokit } from 'octokit';
import getCSS from 'generate-github-markdown-css';
const octokit = new Octokit();
/**
* The theme(s) to use when rendering the Markdown.
*/
export var Theme;
(function (Theme) {
Theme["Auto"] = "auto";
Theme["Light"] = "light";
Theme["Dark"] = "dark";
Theme["DarkDimmed"] = "dark_dimmed";
Theme["DarkHighContrast"] = "dark_high_contrast";
Theme["LightColorblind"] = "light_colorblind";
Theme["DarkColorblind"] = "dark_colorblind";
})(Theme || (Theme = {}));
/**
* A dictionary of functions that return CSS for each theme.
* Auto means that if the user's system is set to dark mode, the dark theme will be used, otherwise the light theme will be used.
* Light means that the light theme will be used, no matter what the user's system asks for (light mode or dark mode).
* Dark, dark_dimmed, dark_high_contrast, light_colorblind, and dark_colorblind are the same thing as light (single mode, the specific theme will be used in both cases, like the user's system requesting light mode or dark mode).
*
* If you want a different combination (e.g. when the user's system/browser requests light mode, serve light_colorblind, but when the user's system/browser requests dark mode, serve dark), you can use the `getCSS` function from the `generate-github-markdown-css` package.
* The `getCSS` function is also exported from this package for convenience.
*/
const themeToCSS = {
auto: () => getCSS({ light: 'light', dark: 'dark' }),
light: () => getCSS({ light: 'light', dark: 'light' }),
dark: () => getCSS({ light: 'dark', dark: 'dark' }),
dark_dimmed: () => getCSS({ light: 'dark_dimmed', dark: 'dark_dimmed' }),
dark_high_contrast: () => getCSS({ light: 'dark_high_contrast', dark: 'dark_high_contrast' }),
light_colorblind: () => getCSS({ light: 'light_colorblind', dark: 'light_colorblind' }),
dark_colorblind: () => getCSS({ light: 'dark_colorblind', dark: 'dark_colorblind' })
};
export { getCSS };
/**
* Writes a file with string data to the filesystem.
* If the file does not exist, it will be created.
* @param {string} file The file path to write to. Absolute or relative.
* @param {string} content The string data to write to the file.
*/
function write(file, content) {
return __awaiter(this, void 0, void 0, function* () {
const fileWriter = new Writer(file);
fs.mkdirSync(path.dirname(file), { recursive: true });
yield fileWriter.write(yield content); // "await content" is needed, despite VSCode's warning. String content might be a promise, and we need to wait for it to resolve.
});
}
/**
* Generates and returns CSS for each requested theme in the `themes` array, as an object
* @param {string} outputDir The directory to write the CSS files to. If unspecified, the CSS will still be returned in an object, but not written to the filesystem.
* @param {Theme[]} themes An array of the themes to generate CSS for. Defaults to all themes.
* @returns {Promise<Record<string, string>>} An object containing the CSS for each theme.
*/
export function generateCSS(outputDir, themes = [Theme.Auto, Theme.Light, Theme.Dark, Theme.DarkDimmed, Theme.DarkHighContrast, Theme.LightColorblind, Theme.DarkColorblind]) {
return __awaiter(this, void 0, void 0, function* () {
let stylesheets = {};
for (const theme of themes) {
stylesheets[theme] = themeToCSS[theme]();
}
if (outputDir) {
fs.mkdirSync(outputDir, { recursive: true });
for (const [name, stylesheet] of Object.entries(stylesheets)) {
yield write(path.join(outputDir, `${name}.css`), stylesheet);
}
}
return stylesheets;
});
}
/**
* Renders Markdown to HTML. If `outputFile` is specified, the HTML will be written to the filesystem.
* The resulting HTML rendered will be wrapped in a default template, unless `includeDefaultTemplate` is set to false.
* This is useful for when you want to use your own HTML template.
* @param {string} markdown The Markdown to render.
* @param {string} outputFile The file to write the rendered HTML to. If unspecified, the HTML will still be returned, but not written to the filesystem.
* @param {boolean} includeDefaultTemplate Whether or not to include the default HTML template. Default: true. If false, the rendered Markdown will not be wrapped in a template.
* @returns {Promise<string>} The rendered HTML.
*/
export default function render(markdown, outputFile, includeDefaultTemplate = true) {
return __awaiter(this, void 0, void 0, function* () {
const request = yield octokit.request('POST /markdown', { text: markdown.toString(), mode: 'gfm' });
let renderedMarkdown = request.data;
if (includeDefaultTemplate) {
// #main adds spacing so it's not right up against the edge of the screen. The margin/padding values are GitHub's defaults when rendering Markdown using GitHub Pages.
// Good max widths: '75em' or '100em' (1012px is GitHub Page's default)
renderedMarkdown = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitHub Markdown</title>
<link id="theme" rel="stylesheet" href="css/auto.css">
<style>
#main {
margin-top: 32px !important;
margin-bottom: 32px !important;
padding-left: 16px !important;
padding-right: 16px !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
</style>
</head>
<body class="markdown-body">
<article id="main" class="markdown-body" style="padding: 1em; max-width: 1012px; margin: 0px auto;">
${request.data}
</article>
<select style="position: fixed; top: 1em; right: 1em; font-size: 16px; border-radius: 10px; padding: 5px;" onchange="theme.href=this.value">
<option value="css/auto.css">auto</option>
<option value="css/light.css">light</option>
<option value="css/dark_dimmed.css">dark dimmed</option>
<option value="css/dark.css">dark</option>
<option value="css/dark_high_contrast.css">dark high contrast</option>
<option value="css/dark_colorblind.css">dark colorblind</option>
<option value="css/light_colorblind.css">light colorblind</option>
</select>
</body>
</html>
`;
}
const render = yield prettier.format(renderedMarkdown, { parser: 'html', singleAttributePerLine: false, tabWidth: 4 });
if (outputFile) {
yield write(outputFile, render);
}
return render;
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUN4QixPQUFPLFFBQVEsTUFBTSxVQUFVLENBQUM7QUFDaEMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLE9BQU8sQ0FBQztBQUMvQixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2xDLE9BQU8sTUFBTSxNQUFNLDhCQUE4QixDQUFDO0FBRWxELE1BQU0sT0FBTyxHQUFHLElBQUksT0FBTyxFQUFFLENBQUM7QUFFOUI7O0dBRUc7QUFDSCxNQUFNLENBQU4sSUFBWSxLQVFYO0FBUkQsV0FBWSxLQUFLO0lBQ2Isc0JBQWEsQ0FBQTtJQUNiLHdCQUFlLENBQUE7SUFDZixzQkFBYSxDQUFBO0lBQ2IsbUNBQTBCLENBQUE7SUFDMUIsZ0RBQXVDLENBQUE7SUFDdkMsNkNBQW9DLENBQUE7SUFDcEMsMkNBQWtDLENBQUE7QUFDdEMsQ0FBQyxFQVJXLEtBQUssS0FBTCxLQUFLLFFBUWhCO0FBRUQ7Ozs7Ozs7O0VBUUU7QUFDRixNQUFNLFVBQVUsR0FBRztJQUNmLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUNwRCxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUM7SUFDdEQsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQ25ELFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FB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