UNPKG

@diplodoc/transform

Version:

A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML

243 lines 8.65 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const chalk_1 = require("chalk"); const log_1 = require("../log"); const evaluation_1 = require("./evaluation"); const lexical_1 = require("./lexical"); const sourceMap_1 = require("./sourceMap"); const legacyConditions_1 = __importDefault(require("./legacyConditions")); function resourcemap(source, ifTag, ifCon, api) { const [sourseStartLine, sourceEndLine] = [ (0, sourceMap_1.getLineNumber)(source, ifTag.start + 1), (0, sourceMap_1.getLineNumber)(source, ifTag.end - 1), ]; if (sourseStartLine === sourceEndLine || ifTag === ifCon) { return; } const linesTotal = source.split('\n').length; const { getSourceMapValue, moveLines, removeLines } = api; let offsetRestLines; if (ifCon) { const [resultStartLine, resultEndLine] = [ (0, sourceMap_1.getLineNumber)(source, ifCon.start), (0, sourceMap_1.getLineNumber)(source, ifCon.end), ]; // Move condition's content to the top const offsetContentLines = sourseStartLine - resultStartLine; moveLines({ start: resultStartLine, end: resultEndLine, offset: offsetContentLines, withReplace: true, }); // Remove the rest lines of the condition block removeLines({ start: sourseStartLine, end: resultStartLine - 1 }); removeLines({ start: resultEndLine + 1, end: sourceEndLine }); // Calculate an offset of the rest lines offsetRestLines = getSourceMapValue(resultEndLine) - sourceEndLine; } else { // Remove the whole condition block removeLines({ start: sourseStartLine, end: sourceEndLine }); // Calculate offset of the rest lines offsetRestLines = sourseStartLine - sourceEndLine - 1; } // Offset the rest lines moveLines({ start: sourceEndLine + 1, end: linesTotal, offset: offsetRestLines }); } function headLinebreak(raw) { const match = raw.match(/^([^{]+){.*/); return match ? match[1] : ''; } function tailLinebreak(raw) { const match = raw.match(/.*}(\s*\n)$/); return match ? match[1] : ''; } function trimResult(content, ifTag, ifCon) { if (!ifCon) { const head = headLinebreak(ifTag.rawStart); const tail = tailLinebreak(ifTag.rawEnd); let rest = head + tail; if (rest !== head && rest !== tail) { // We have extra line break, if condition was placed on individual line rest = rest.replace('\n', ''); } return ifTag.isBlock ? '\n' : rest; } content = content.substring(ifCon.start, ifCon.end); if (ifTag.isBlock) { return trimBlockResult(content, ifCon); } else { return trimInlineResult(content, ifTag); } } function trimBlockResult(content, ifCon) { const head = headLinebreak(ifCon.rawStart); if (head) { content = '\n' + content; } const tail = tailLinebreak(ifCon.rawEnd); if (tail) { content = content + '\n'; } return content; } function trimInlineResult(content, ifTag) { const head = headLinebreak(ifTag.rawStart); if (head) { content = head + content; } const tail = tailLinebreak(ifTag.rawEnd); if (tail) { content = content + tail; } return content; } class IfTag { constructor() { this.conditions = []; } get start() { if (!this.conditions.length) { return -1; } const first = this.conditions[0]; return first.start - first.rawStart.length; } get end() { if (!this.conditions.length) { return -1; } const last = this.conditions[this.conditions.length - 1]; return last.end + last.rawEnd.length; } get rawStart() { if (!this.conditions.length) { return ''; } const first = this.conditions[0]; return first.rawStart; } get rawEnd() { if (!this.conditions.length) { return ''; } const last = this.conditions[this.conditions.length - 1]; return last.rawEnd; } get isBlock() { const first = this.conditions[0]; const last = this.conditions[this.conditions.length - 1]; return tailLinebreak(first.rawStart) && headLinebreak(last.rawEnd); } *[Symbol.iterator]() { for (const condition of this.conditions) { yield condition; } } openCondition(raw, expr, start) { this.closeCondition(raw, start); this.conditions.push({ rawStart: raw, start: start + raw.length, expr, }); return start + raw.length - tailLinebreak(raw).length; } closeCondition(raw, end) { const condition = this.conditions[this.conditions.length - 1]; if (condition) { condition.rawEnd = raw; condition.end = end; } } } function inlineConditions(content, ifTag, vars, strict) { let ifCon = null; for (const condition of ifTag) { const value = (0, evaluation_1.evalExp)(condition.expr, vars, strict); if (condition.expr && value === evaluation_1.NoValue) { return { result: content, // Fix offset for next matches. // There can be some significant linebreak and spaces. lastIndex: ifTag.end - tailLinebreak(ifTag.rawEnd).length, ifCon: ifTag, }; } if (!condition.expr || value) { ifCon = condition; break; } } const start = content.slice(0, ifTag.start); const end = content.slice(ifTag.end); const result = trimResult(content, ifTag, ifCon); return { result: start + result + end, lastIndex: start.length + result.length - tailLinebreak(ifTag.rawEnd).length, ifCon, }; } module.exports = function conditions(input, vars, path, settings) { if (settings === null || settings === void 0 ? void 0 : settings.useLegacyConditions) { return (0, legacyConditions_1.default)(input, vars, path, settings); } const sourceMap = (settings === null || settings === void 0 ? void 0 : settings.sourceMap) || {}; const strict = (settings === null || settings === void 0 ? void 0 : settings.strict) || false; const tagStack = []; // Consumes all between curly braces // and all closest upon to first linebreak before and after braces. const R_LIQUID = /((?:\n[\t ]*)?{%-?([\s\S]*?)-?%}(?:[\t ]*\n)?)/g; let match; while ((match = R_LIQUID.exec(input)) !== null) { if (!match[1]) { continue; } const tagMatch = match[2].trim().match(lexical_1.tagLine); if (!tagMatch) { continue; } const [type, args] = tagMatch.slice(1); switch (type) { case 'if': { const tag = new IfTag(); R_LIQUID.lastIndex = tag.openCondition(match[1], args, match.index); tagStack.push(tag); break; } case 'elsif': case 'else': { const tag = tagStack[tagStack.length - 1]; R_LIQUID.lastIndex = tag.openCondition(match[1], args, match.index); break; } case 'endif': { const ifTag = tagStack.pop(); if (!ifTag) { // TODO(3y3): make lint rule log_1.log.error(`If block must be opened before close${path ? ` in ${(0, chalk_1.bold)(path)}` : ''}`); break; } ifTag.closeCondition(match[1], match.index); const { result, lastIndex, ifCon } = inlineConditions(input, ifTag, vars, strict); resourcemap(input, ifTag, ifCon, (0, sourceMap_1.createSourceMapApi)(sourceMap)); R_LIQUID.lastIndex = lastIndex; input = result; break; } default: // This is not condition. // Step back last linebreaks to match them on next condition R_LIQUID.lastIndex -= tailLinebreak(match[1]).length; } } if (tagStack.length !== 0) { log_1.log.error(`Condition block must be closed${path ? ` in ${(0, chalk_1.bold)(path)}` : ''}`); } return input; }; //# sourceMappingURL=conditions.js.map