@diplodoc/transform
Version:
A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML
105 lines • 4.26 kB
JavaScript
const chalk_1 = require("chalk");
const utilsFS_1 = require("../../utilsFS");
const utils_1 = require("../../utils");
const INCLUDE_REGEXP = /^{%\s*include\s*(notitle)?\s*\[(.+?)]\((.+?)\)\s*%}$/;
function stripTitleTokens(tokens) {
const [open, _, close] = tokens;
if ((open === null || open === void 0 ? void 0 : open.type) === 'heading_open' && (close === null || close === void 0 ? void 0 : close.type) === 'heading_close') {
tokens.splice(0, 3);
}
}
function unfoldIncludes(md, state, path, options) {
var _a, _b;
const { root, notFoundCb, log, noReplaceInclude = false } = options;
const { tokens } = state;
let i = 0;
while (i < tokens.length) {
const openToken = tokens[i];
const contentToken = tokens[i + 1];
const closeToken = tokens[i + 2];
let match;
if (openToken.type === 'paragraph_open' &&
contentToken.type === 'inline' &&
(match = contentToken.content.match(INCLUDE_REGEXP)) &&
closeToken.type === 'paragraph_close') {
try {
const [, keyword /* description */, , includePath] = match;
const fullIncludePath = (0, utilsFS_1.getFullIncludePath)(includePath, root, path);
// Check the real path of the file in case of a symlink
let pathname = (0, utilsFS_1.getRealPath)(fullIncludePath);
if (!pathname.startsWith(root)) {
i++;
continue;
}
let hash = '';
const hashIndex = fullIncludePath.lastIndexOf('#');
if (hashIndex > -1 && !(0, utilsFS_1.isFileExists)(pathname)) {
pathname = fullIncludePath.slice(0, hashIndex);
hash = fullIncludePath.slice(hashIndex + 1);
}
// Check the existed included store and extract it
const included = (_a = md.included) === null || _a === void 0 ? void 0 : _a[pathname];
const fileTokens = (0, utilsFS_1.getFileTokens)(pathname, state, options, included);
let includedTokens;
if (hash) {
// TODO: add warning about missed block
// TODO: findBlockTokens requires markdown-it-attrs plugin for find block with id=hash
includedTokens = (0, utils_1.findBlockTokens)(fileTokens, hash);
}
else {
includedTokens = fileTokens;
}
if (keyword === 'notitle') {
stripTitleTokens(includedTokens);
}
if (noReplaceInclude) {
i++;
}
else {
tokens.splice(i, 3, ...includedTokens);
i += includedTokens.length;
}
}
catch (e) {
// @ts-ignore for some reason typescript fails here
const errPath = (_b = e.path) === null || _b === void 0 ? void 0 : _b.replace(root, '');
if (notFoundCb) {
notFoundCb(errPath);
}
log.error(`Skip error: ${e} in ${errPath}`);
i++;
}
}
else {
i++;
}
}
}
const index = (md, options) => {
const { path: optPath, log } = options;
const plugin = (state) => {
const { env } = state;
const path = env.path || optPath;
env.includes = env.includes || [];
const isCircularInclude = env.includes.includes(path);
if (isCircularInclude && state.env.disableCircularError) {
return;
}
if (isCircularInclude) {
log.error(`Circular includes: ${(0, chalk_1.bold)(env.includes.concat(path).join(' ▶ '))}`);
process.exit(1);
}
env.includes.push(path);
unfoldIncludes(md, state, path, options);
env.includes.pop();
};
try {
md.core.ruler.before('curly_attributes', 'includes', plugin);
}
catch (e) {
md.core.ruler.push('includes', plugin);
}
};
module.exports = index;
//# sourceMappingURL=index.js.map
;