UNPKG

@vivliostyle/vfm

Version:

Custom Markdown syntax specialized in book authoring.

236 lines (235 loc) 8.1 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.readMetadata = void 0; var js_yaml_1 = require("js-yaml"); var mdast_util_to_string_1 = __importDefault(require("mdast-util-to-string")); var rehype_stringify_1 = __importDefault(require("rehype-stringify")); var remark_frontmatter_1 = __importDefault(require("remark-frontmatter")); var remark_parse_1 = __importDefault(require("remark-parse")); var remark_rehype_1 = __importDefault(require("remark-rehype")); var unified_1 = __importDefault(require("unified")); var unist_util_select_1 = require("unist-util-select"); var unist_util_visit_1 = __importDefault(require("unist-util-visit")); var attr_1 = require("./attr"); var footnotes_1 = require("./footnotes"); /** * Read the title from heading without footnotes. * @param tree Tree of Markdown AST. * @returns Title text or `undefined`. */ var readTitleFromHeading = function (tree) { var heading = (0, unist_util_select_1.select)('heading', tree); if (!heading) { return; } // Create title string with footnotes removed var children = __spreadArray([], heading.children, true); heading.children = heading.children.filter(function (child) { return child.type !== 'footnote'; }); // Remove ruby text and HTML tags var text = (0, mdast_util_to_string_1.default)(heading) .replace(/{(.+?)(?<=[^\\|])\|(.+?)}/g, '$1') .replace(/<[^<>]*>/g, ''); heading.children = children; return text; }; /** * Parse Markdown's Frontmatter to metadate (`VFile.data`). * @returns Handler. * @see https://github.com/Symbitic/remark-plugins/blob/master/packages/remark-meta/src/index.js */ var mdast = function () { return function (tree, file) { (0, unist_util_visit_1.default)(tree, ['yaml'], function (node) { var value = (0, js_yaml_1.load)(node.value, { schema: js_yaml_1.JSON_SCHEMA }); if (typeof value === 'object') { file.data = __assign(__assign({}, file.data), value); } }); // If title is undefined in frontmatter, read from heading if (!file.data.title) { var title = readTitleFromHeading(tree); if (title) { file.data.title = title; } } (0, unist_util_visit_1.default)(tree, ['shortcode'], function (node) { if (node.identifier !== 'toc') { return; } if (file.data.vfm) { file.data.vfm.toc = true; } else { file.data.vfm = { math: true, toc: true }; } }); }; }; /** * Parse Markdown frontmatter. * @param md Markdown. * @returns Key/Value pair. */ var parseMarkdown = function (md) { var processor = (0, unified_1.default)() .use([ [remark_parse_1.default, { gfm: true, commonmark: true }], // Remove footnotes when reading title from heading footnotes_1.mdast, attr_1.mdast, remark_frontmatter_1.default, mdast, ]) .data('settings', { position: false }) .use(remark_rehype_1.default) .use(rehype_stringify_1.default); return processor.processSync(md).data; }; /** * Read the string or null value in the YAML parse result. * If the value is null, it will be an empty string * @param value Value of YAML parse result. * @returns Text. */ var readStringOrNullValue = function (value) { return value === null ? '' : "".concat(value); }; /** * Read an attributes from data object. * @param data Data object. * @returns Attributes of HTML tag. */ var readAttributes = function (data) { if (data === null || typeof data !== 'object') { return; } var result = []; for (var _i = 0, _a = Object.keys(data); _i < _a.length; _i++) { var key = _a[_i]; result.push({ name: key, value: readStringOrNullValue(data[key]) }); } return result; }; /** * Read an attributes collection from data object. * @param data Data object. * @returns Attributes collection of HTML tag. */ var readAttributesCollection = function (data) { if (!Array.isArray(data)) { return; } var result = []; data.forEach(function (value) { var attributes = readAttributes(value); if (attributes) { result.push(attributes); } }); return result; }; /** * Read VFM settings from data object. * @param data Data object. * @returns Settings. */ var readSettings = function (data) { if (data === null || typeof data !== 'object') { return { toc: false }; } return { math: typeof data.math === 'boolean' ? data.math : undefined, partial: typeof data.partial === 'boolean' ? data.partial : undefined, hardLineBreaks: typeof data.hardLineBreaks === 'boolean' ? data.hardLineBreaks : undefined, disableFormatHtml: typeof data.disableFormatHtml === 'boolean' ? data.disableFormatHtml : undefined, theme: typeof data.theme === 'string' ? data.theme : undefined, toc: typeof data.toc === 'boolean' ? data.toc : false, }; }; /** * Read metadata from Markdown frontmatter. * * Keys that are not defined as VFM are treated as `meta`. If you specify a key name in `customKeys`, the key and its data type will be preserved and stored in `custom` instead of `meta`. * @param md Markdown. * @param customKeys A collection of key names to be ignored by meta processing. * @returns Metadata. */ var readMetadata = function (md, customKeys) { if (customKeys === void 0) { customKeys = []; } var metadata = {}; var data = parseMarkdown(md); var others = []; for (var _i = 0, _a = Object.keys(data); _i < _a.length; _i++) { var key = _a[_i]; if (customKeys.includes(key)) { if (!metadata.custom) { metadata.custom = {}; } metadata.custom[key] = data[key]; continue; } switch (key) { case 'id': case 'lang': case 'dir': case 'class': case 'title': metadata[key] = readStringOrNullValue(data[key]); break; case 'html': case 'body': case 'base': metadata[key] = readAttributes(data[key]); break; case 'meta': case 'link': case 'script': metadata[key] = readAttributesCollection(data[key]); break; case 'vfm': metadata[key] = readSettings(data[key]); break; case 'style': case 'head': // Reserved for future use. break; default: others.push([ { name: 'name', value: key }, { name: 'content', value: readStringOrNullValue(data[key]) }, ]); break; } } // Other properties should be `<meta>` if (0 < others.length) { metadata.meta = metadata.meta ? metadata.meta.concat(others) : others; } return metadata; }; exports.readMetadata = readMetadata;