@mintlify/common
Version:
Commonly shared code within Mintlify
111 lines (110 loc) • 5.42 kB
JavaScript
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());
});
};
import { fromMarkdown } from 'mdast-util-from-markdown';
import { gfmFromMarkdown } from 'mdast-util-gfm';
import { mdxFromMarkdown } from 'mdast-util-mdx';
import { gfm } from 'micromark-extension-gfm';
import { mdxjs } from 'micromark-extension-mdxjs';
import { coreRemark, remarkMdxInjectSnippets } from '../../../index.js';
export function createSnippetTreeMap(snippets) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (snippets.length === 0) {
return {};
}
const orderedSnippets = orderSnippetsByNumberOfSnippetsInContent(snippets);
let treeMap = {};
// If the lowest number of references is greater than 0, then there will be cyclical references that will never resolve.
// In these cases we do not try to replace snippets in snippets
// TODO: Better error handling for cyclical references
if (((_a = orderedSnippets[0]) === null || _a === void 0 ? void 0 : _a.numSnippetsInContent) === 0) {
orderedSnippets.forEach((snippet) => {
treeMap = parseSnippetContentToMap(snippet, treeMap);
});
return treeMap;
}
let failedParseArr = [];
orderedSnippets.forEach((snippet) => __awaiter(this, void 0, void 0, function* () {
if (snippet.numSnippetsInContent === 0) {
// No need to remove references if none exist
treeMap = parseSnippetContentToMap(snippet, treeMap);
}
if (failedParseArr.length > 0) {
let prevArrLength = failedParseArr.length + 1;
// We try to resolve the failed snippets which likely failed because it was referencing
// another snippet that has not been resolved yet.
// We loop while snippets continue to be resolved.
while (prevArrLength > failedParseArr.length && prevArrLength !== 0) {
const newlyParsedSnippets = [];
for (const failedParse of failedParseArr) {
try {
treeMap = yield addParsedSnippetTreeToMap(failedParse, treeMap);
newlyParsedSnippets.push(failedParse);
}
catch (_a) { }
}
prevArrLength = failedParseArr.length;
failedParseArr = failedParseArr.filter((elem) => !newlyParsedSnippets.includes(elem));
}
}
try {
treeMap = yield addParsedSnippetTreeToMap(snippet, treeMap);
}
catch (_b) {
// TODO - find exact error message for when inner snippet doesn't exist
failedParseArr.push(snippet);
}
}));
return treeMap;
});
}
function parseSnippetContentToMap(snippet, map) {
try {
const tree = fromMarkdown(snippet.content, {
extensions: [gfm(), mdxjs()],
mdastExtensions: [gfmFromMarkdown(), mdxFromMarkdown()],
});
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
map = Object.assign(Object.assign({}, map), { [snippet.snippetFileLocation]: tree });
}
catch (_a) {
// TODO: Better error handling
}
return map;
}
function addParsedSnippetTreeToMap(snippet, treeMap) {
return __awaiter(this, void 0, void 0, function* () {
const parsedContent = yield preparseSnippet(snippet, treeMap);
return parseSnippetContentToMap(Object.assign(Object.assign({}, snippet), { content: parsedContent }), treeMap);
});
}
/*
* We order the snippets by increasing number of references to other snippets in an attempt to avoid
* trying to parse a snippet that references another snippet that has not been resolved yet.
*/
function orderSnippetsByNumberOfSnippetsInContent(snippets) {
const snippetsWithNumSnippets = snippets.map((snippet) => {
return Object.assign(Object.assign({}, snippet), {
// Note: It's possible the regex might not be enough
numSnippetsInContent: (snippet.content.match(/<Snippet/g) || []).length });
});
snippetsWithNumSnippets.sort(function (first, second) {
return first.numSnippetsInContent - second.numSnippetsInContent;
});
return snippetsWithNumSnippets;
}
function preparseSnippet(snippet, snippetTreeMap) {
return __awaiter(this, void 0, void 0, function* () {
const file = yield coreRemark()
.use([remarkMdxInjectSnippets, snippetTreeMap])
.process(snippet.content);
return String(file);
});
}