UNPKG

litvis-integration-mume

Version:

Enables litvis functionality in mume and markdown-preview-enhanced

253 lines 13.7 kB
"use strict"; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.enhanceWithLitvisLiterateElm = void 0; const cheerio_1 = __importDefault(require("cheerio")); const html_entities_1 = require("html-entities"); const litvis_1 = require("litvis"); const lodash_1 = __importDefault(require("lodash")); const object_hash_1 = __importDefault(require("object-hash")); const flattenJsonToRawMarkdown = (data) => { if (data instanceof Array) { return data.map(flattenJsonToRawMarkdown).join(" "); } if (typeof data === "object" && data !== null) { return flattenJsonToRawMarkdown(Object.entries(data)); } return `${data}`; }; const generate$output = (arrayOf$outputItems) => { return (0, cheerio_1.default)('<div data-role="litvisOutput" style="display: inline;" />').append("", ...arrayOf$outputItems); }; const generateArrayOf$outputItems = (outputExpressions, outputFormat, derivatives) => { return outputExpressions.map((outputExpression) => { const $outputItem = (0, cheerio_1.default)(`<span data-role="litvisOutputItem" data-context-name="${(0, html_entities_1.encode)(derivatives.contextName)}" data-expression="${(0, html_entities_1.encode)(outputExpression)}" data-interactive="${derivatives.interactive}" data-output-format="${(0, html_entities_1.encode)(outputFormat)}"><code>${(0, html_entities_1.encode)(outputFormat)}=${(0, html_entities_1.encode)(outputExpression)}</code></span>`); return $outputItem; }); }; const mapAutogeneratedContextNames = (litvisContextNames, mumeContextNames, autogeneratedPrefix) => { const autogeneratedLitvisContextNames = lodash_1.default.sortBy(lodash_1.default.filter(litvisContextNames, (name) => lodash_1.default.startsWith(name, autogeneratedPrefix))); const autogeneratedMumeContextNames = lodash_1.default.sortBy(lodash_1.default.filter(mumeContextNames, (name) => lodash_1.default.startsWith(name, autogeneratedPrefix))); const minLength = Math.min(autogeneratedLitvisContextNames.length, autogeneratedMumeContextNames.length); const pickedAutogeneratedLitvisContextNames = lodash_1.default.slice(autogeneratedLitvisContextNames, -minLength); const pickedAutogeneratedMumeContextNames = lodash_1.default.slice(autogeneratedMumeContextNames, -minLength); const result = { // key - litvis context name // value - mume context name }; lodash_1.default.forEach(litvisContextNames, (name) => { const index = lodash_1.default.indexOf(autogeneratedLitvisContextNames, name); if (index !== -1) { result[pickedAutogeneratedLitvisContextNames[index]] = pickedAutogeneratedMumeContextNames[index]; } else { result[name] = name; } }); return result; }; const enhanceWithLitvisLiterateElm = ($, processedNarrative, cache, parseMd) => __awaiter(void 0, void 0, void 0, function* () { // search for all elm code blocks and surround them // with output items if they reference expressions to output const mumeContextNames = []; $('[data-role="codeBlock"]').each((i, container) => { const $container = $(container); if ($container.data("executor")) { return; } const info = $container.data("parsedInfo"); if (`${info.language}`.toLowerCase() !== "elm") { return; } const derivatives = (0, litvis_1.extractAttributeDerivatives)(info.attributes); if (!derivatives) { return; } $container.data("executor", "litvis"); if (derivatives.outputFormats.indexOf("l") === -1) { $container.data("hiddenByEnhancer", true); } mumeContextNames.push(derivatives.contextName); const derivativesWithResolvedExpressions = (0, litvis_1.resolveExpressions)(derivatives, $container.text()); const arrayOf$outputItemsBeforeCodeBlock = []; const arrayOf$outputItemsAfterCodeBlock = []; let currentArrayOf$outputItems = arrayOf$outputItemsBeforeCodeBlock; derivativesWithResolvedExpressions.outputFormats.forEach((outputFormat) => { switch (outputFormat) { case "l": currentArrayOf$outputItems = arrayOf$outputItemsAfterCodeBlock; break; default: { const expressions = derivativesWithResolvedExpressions.outputExpressionsByFormat[outputFormat]; if (expressions) { currentArrayOf$outputItems.push(...generateArrayOf$outputItems(expressions, outputFormat, derivativesWithResolvedExpressions)); } } } }); if (arrayOf$outputItemsBeforeCodeBlock.length) { $container.before(generate$output(arrayOf$outputItemsBeforeCodeBlock)); } if (arrayOf$outputItemsAfterCodeBlock.length) { $container.after(generate$output(arrayOf$outputItemsAfterCodeBlock)); } }); // search for all triple hat references and turn them into output items $('[data-role="litvis:triple-hat-reference"]').each((i, el) => { const $el = $(el); const info = JSON.parse($el.attr("data-parsedinfo") || ""); if (`${info.language}`.toLowerCase() !== "elm") { return; } const derivatives = (0, litvis_1.extractAttributeDerivatives)(info.attributes); if (!derivatives) { return; } const arrayOf$outputItems = []; derivatives.outputFormats.forEach((outputFormat) => { switch (outputFormat) { case "l": break; default: { const expressions = derivatives.outputExpressionsByFormat[outputFormat]; if (expressions) { arrayOf$outputItems.push(...generateArrayOf$outputItems(expressions, outputFormat, derivatives)); } } } }); $el.replaceWith(generate$output(arrayOf$outputItems)); }); // Some context names in sidings are autogenerated by litvis, // but their ids may not match ids autogenerated by mume. // Because it is known that the number of contexts starting with '_autogenerated__X' match // And X is incremental, is possible to generate a mapping between lit names. const litvisContextNameToMumeContextName = mapAutogeneratedContextNames(lodash_1.default.map(processedNarrative.contexts, "name"), lodash_1.default.uniq(mumeContextNames), "_autogenerated__"); const contextsByMumeContextName = lodash_1.default.keyBy(processedNarrative.contexts, ({ name }) => litvisContextNameToMumeContextName[name]); // walk through all litvis output items and render them const $outputItems = $('[data-role="litvisOutputItem"]'); const mappedOutputItems = $outputItems.map((i, el) => __awaiter(void 0, void 0, void 0, function* () { const $el = $(el); const contextName = $el.data("contextName"); const outputFormat = $el.data("outputFormat"); const expressionText = $el.data("expression"); const interactive = $el.data("interactive"); const renderKey = (0, object_hash_1.default)({ contextName, outputFormat, expressionText, path: processedNarrative.documents[processedNarrative.documents.length - 1].path, }); const context = contextsByMumeContextName[contextName]; // const evaluatedOutputExpressionsByText = keyBy(context.evaluatedOutputExpressions, oe => oe.data.text); try { if (!context) { throw new Error(`Non-existing context ${contextName}`); } if (context.status !== "succeeded") { throw new Error(`Code execution in context ${contextName} was not successful`); } // TODO: find() is expensive, consider optimizing by indexing const evaluatedOutputExpression = lodash_1.default.find(context.evaluatedOutputExpressions, (oe) => oe.data.text === expressionText); if (!evaluatedOutputExpression) { throw new Error(`Could not find expression ${expressionText}`); } if (typeof evaluatedOutputExpression.data.valueStringRepresentation !== "string") { throw new Error(`Could not evaluate expression ${expressionText}`); } if (outputFormat !== "r" && evaluatedOutputExpression.data.value instanceof Error) { throw new Error(`Could not parse value of ${expressionText}`); } let $result; let resultNormalizedInfo = null; switch (outputFormat) { case "r": $result = $("<span/>").text(evaluatedOutputExpression.data.valueStringRepresentation); break; case "j": $result = $(`<pre data-role="codeBlock" />`); resultNormalizedInfo = { language: "json", attributes: { style: "display: inline-block" }, }; $result.text(JSON.stringify(evaluatedOutputExpression.data.value, null, 2)); break; case "m": { const rawMarkdown = flattenJsonToRawMarkdown(evaluatedOutputExpression.data.value); const { html } = yield parseMd(rawMarkdown.length ? rawMarkdown : " ", // parseMD accepts non-empty strings only { useRelativeFilePath: true, isForPreview: false, hideFrontMatter: true, }); $result = $(html); $result.filter("h1,h2,h3,h4,h5,h6").removeClass("mume-header"); break; } case "v": { const vegaOrVegaLiteJson = evaluatedOutputExpression.data.value; const language = lodash_1.default.get(vegaOrVegaLiteJson, "$schema", "") .toLowerCase() .indexOf("lite") !== -1 ? "vega-lite" : "vega"; $result = $(`<pre data-role="codeBlock" />`); resultNormalizedInfo = { language, attributes: { interactive: interactive === true, style: "display: inline-block", }, }; $result.text(JSON.stringify(vegaOrVegaLiteJson, null, 2)); break; } default: return; } // because serializing/deserializing data attributes works inconsistently // in Cheerio, setting normalized info as attribute first... $result.attr("data-normalized-info", JSON.stringify(resultNormalizedInfo)); cache.successfulRenders.set(renderKey, $("<div/>").append($result).html() || ""); // ...and then as data $result.removeAttr("data-normalized-info"); $result.data("normalizedInfo", resultNormalizedInfo); $el.replaceWith($result); } catch (e) { const $error = $("<span/>").attr("style", "background: rgba(255,200,200,0.1); min-height: 1em; min-width: 1em; display: inline-block;"); const successfulRender = cache.successfulRenders.get(renderKey); if (successfulRender) { const $successfulRender = $(successfulRender); const $fadedSuccessfulRender = $("<span />"); $fadedSuccessfulRender.attr("style", "opacity: 0.8; display: inline-block; filter: sepia(0.7) hue-rotate(-50deg);"); $fadedSuccessfulRender.append($successfulRender); $error.append($fadedSuccessfulRender); } else { const $errorText = $("<span/>").attr("style", "color: red; padding: 0 0.3em; font-weight: bold; white-space: nowrap"); $errorText.text(`${outputFormat}=${expressionText}${contextName !== "default" ? ` [${contextName}]` : ``}`); $error.append($errorText); } $error.attr("title", `${outputFormat}=${expressionText}${contextName !== "default" ? ` [context ${contextName}]` : ``}: ${e.message}`); $el.empty().append($error); } })); yield Promise.all(mappedOutputItems.get()); }); exports.enhanceWithLitvisLiterateElm = enhanceWithLitvisLiterateElm; //# sourceMappingURL=enhanceWithLitvisLiterateElm.js.map