UNPKG

@vivliostyle/vfm

Version:

Custom Markdown syntax specialized in book authoring.

174 lines (173 loc) 6.85 kB
"use strict"; /** * derived from `remark-sectionize`. * original: 2019 Jake Low * modified: 2020 Yasuaki Uechi, 2021 and later is Akabeko * @license MIT * @see https://github.com/jake-low/remark-sectionize */ 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mdast = void 0; var unist_util_find_after_1 = __importDefault(require("unist-util-find-after")); var unist_util_visit_parents_1 = __importDefault(require("unist-util-visit-parents")); /** Maximum depth of hierarchy to process headings. */ var MAX_HEADING_DEPTH = 6; /** * Create the attribute properties of a section. * @param depth - Depth of heading elements that are sections. * @param node - Node of Markdown AST. * @returns Properties. */ var createProperties = function (depth, node) { var _a, _b; var properties = { class: ["level".concat(depth)], }; if ((_b = (_a = node === null || node === void 0 ? void 0 : node.data) === null || _a === void 0 ? void 0 : _a.hProperties) === null || _b === void 0 ? void 0 : _b.id) { properties['aria-labelledby'] = node === null || node === void 0 ? void 0 : node.data.hProperties.id; } return properties; }; var getHeadingLine = function (node, file) { var _a, _b, _c, _d; if ((node === null || node === void 0 ? void 0 : node.type) !== 'heading') { return ''; } var startOffset = (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.start.offset) !== null && _b !== void 0 ? _b : 0; var endOffset = (_d = (_c = node.position) === null || _c === void 0 ? void 0 : _c.end.offset) !== null && _d !== void 0 ? _d : 0; var text = file.toString().slice(startOffset, endOffset); return text.trim(); }; /** * Check if the heading has a non-section mark (sufficient number of closing hashes). * @param node Node of Markdown AST. * @param file Virtual file. * @returns `true` if the node has a non-section mark. */ var hasNonSectionMark = function (node, file) { var _a, _b, _c; var line = getHeadingLine(node, file); return (!!line && ((_c = (_b = (_a = /^#.*[ \t](#+)$/.exec(line)) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) >= node.depth); }; /** * Check if the node is a section-end mark (line with only hashes). * @param node Node of Markdown AST. * @returns `true` if the node is a section-end mark. */ var isSectionEndMark = function (node, file) { var _a, _b; var line = getHeadingLine(node, file); return !!line && ((_b = (_a = /^(#+)$/.exec(line)) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.length) === node.depth; }; /** * Wrap the header in sections. * - Do not sectionize if parent is `blockquote`. * - Set the `levelN` class in the section to match the heading depth. * @param node Node of Markdown AST. * @param ancestors Parents. * @todo handle `@subtitle` properly. */ var sectionizeIfRequired = function (node, ancestors, file) { var _a; if (hasNonSectionMark(node, file)) { return; } var parent = ancestors[ancestors.length - 1]; if (parent.type === 'blockquote') { return; } var start = node; var depth = start.depth; // check if it's HTML end tag without corresponding start tag in sibling nodes. var isHtmlEnd = function (node) { var _a; if (node.type !== 'html') { return false; } var tag = (_a = /<\/([^>\s]+)\s*>[^<]*$/.exec(node.value)) === null || _a === void 0 ? void 0 : _a[1]; if (!tag) { return false; } // it's HTML end tag, check if it has corresponding start tag var isHtmlStart = function (node) { return node.type === 'html' && new RegExp("<".concat(tag, "\\b[^>]*>")).test(node.value); }; var htmlStart = (0, unist_util_find_after_1.default)(parent, start, isHtmlStart); if (!htmlStart || parent.children.indexOf(htmlStart) > parent.children.indexOf(node)) { // corresponding start tag is not found in this section level, // check if it is found earlier. var htmlStart1 = (0, unist_util_find_after_1.default)(parent, 0, isHtmlStart); if (htmlStart1 && parent.children.indexOf(htmlStart1) < parent.children.indexOf(start)) { return true; } } return false; }; var isEnd = function (node) { return (node.type === 'heading' && node.depth <= depth) || node.type === 'export' || isHtmlEnd(node); }; var end = (0, unist_util_find_after_1.default)(parent, start, isEnd); var startIndex = parent.children.indexOf(start); var endIndex = parent.children.indexOf(end); var between = parent.children.slice(startIndex, endIndex > 0 ? endIndex : undefined); var hProperties = createProperties(depth, node); // {hidden} specifier if (Object.keys(node.data.hProperties).includes('hidden')) { node.data.hProperties.hidden = 'hidden'; } var isDuplicated = parent.type === 'section'; if (isDuplicated) { if ((_a = parent.data) === null || _a === void 0 ? void 0 : _a.hProperties) { parent.data.hProperties = __assign(__assign({}, parent.data.hProperties), hProperties); } return; } var type = 'section'; var section = { type: type, data: { hName: type, hProperties: hProperties, }, depth: depth, children: between, }; parent.children.splice(startIndex, section.children.length + (isSectionEndMark(end, file) && end.depth === depth ? 1 : 0), section); }; /** * Process Markdown AST. * @returns Transformer. */ var mdast = function () { return function (tree, file) { var sectionize = function (node, ancestors) { sectionizeIfRequired(node, ancestors, file); }; var _loop_1 = function (depth) { (0, unist_util_visit_parents_1.default)(tree, function (node) { return node.type === 'heading' && node.depth === depth; }, sectionize); }; for (var depth = MAX_HEADING_DEPTH; depth > 0; depth--) { _loop_1(depth); } }; }; exports.mdast = mdast;