UNPKG

@mintlify/common

Version:

Commonly shared code within Mintlify

124 lines (123 loc) 6.17 kB
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 { walk } from 'estree-walker'; import matter from 'gray-matter'; import { remark } from 'remark'; import { visit } from 'unist-util-visit'; import { coreRemarkMdxPlugins, getAST } from '../../remark.js'; import { createUniqueVariableName, isMdxJsxFlowElement } from '../../utils.js'; import { findAndRemoveExports } from '../findAndRemoveExports.js'; import { injectToTopOfFile } from './injectToTopOfFile.js'; /** * * @param content The content to inject into * @param componentName The component name we are injecting into * @param contentToInject The content to inject in place of the component */ export const resolveComponentWithContent = (content, componentName, snippet, exportMap) => __awaiter(void 0, void 0, void 0, function* () { const { content: snippetWithoutFrontmatter } = matter(snippet); const { exportMap: snippetExportMap, content: snippetWithoutExports } = yield findAndRemoveExports(snippetWithoutFrontmatter); const snippetAST = getAST(snippetWithoutExports); const convertedContent = yield remark() .use(coreRemarkMdxPlugins) .use(remarkMdxResolveComponentWithContent(snippetAST, componentName, exportMap, snippetExportMap)) .process(content); const convertedContentStr = String(convertedContent); return reinsertExports(convertedContentStr, exportMap, snippetExportMap); }); /** * Everywhere where there is a component with the name `componentName`, replace it with the `treeToInject`. * New variables are also exported to be used for the variables. Additional mutations are made to support duplicated snippets * * This function is used to resolve default imports * @param treeToInject * @param componentName * @returns */ const remarkMdxResolveComponentWithContent = (treeToInject, componentName, exportMap, snippetExportMap) => () => (tree) => { visit(tree, isMdxJsxFlowElement, (node, i, parent) => { var _a; if (node.name === componentName && parent && i != null) { // Creating clone to restore treeToInject with default values at the end of operations const treeToInjectClone = structuredClone(treeToInject); node.name = null; const { variableDeclarationNodes, snippetNodesToInject } = replaceVariablesWithProps(node, treeToInject, exportMap, snippetExportMap); parent.children.splice(i, 1, ...snippetNodesToInject); tree.children.splice(((_a = tree.children[0]) === null || _a === void 0 ? void 0 : _a.type) === 'yaml' ? 1 : 0, 0, ...variableDeclarationNodes); treeToInject = treeToInjectClone; } }); }; /** * Replace the variables with value provided from the props by defining the variable before the snippet * Also prevents duplicates by attaching a unique identifier to every variable (var to var_1) * * @param node * @param treeToInject * @returns */ const replaceVariablesWithProps = (node, treeToInject, exportMap, snippetExportMap) => { const variableNameMap = {}; const variablesMap = {}; visit(treeToInject, (node) => { var _a; if ((node.type === 'mdxTextExpression' || node.type === 'mdxFlowExpression') && ((_a = node.data) === null || _a === void 0 ? void 0 : _a.estree)) { node.data.estree = walk(node.data.estree, { enter(jsNode) { if (jsNode.type === 'Identifier' && snippetExportMap[jsNode.name] == undefined) { let id = 0; let uniqueName = createUniqueVariableName(jsNode.name, id); while (exportMap[uniqueName]) { id += 1; uniqueName = createUniqueVariableName(jsNode.name, id); } // Replace node value with unique name node.value = node.value.replace(jsNode.name, uniqueName); variablesMap[uniqueName] = { type: 'unknown', value: 'undefined' }; variableNameMap[jsNode.name] = uniqueName; jsNode.name = uniqueName; } }, }); } }); node.attributes.forEach((attribute) => { if (attribute.type === 'mdxJsxAttribute' && attribute.value) { const uniqueName = variableNameMap[attribute.name]; if (uniqueName != undefined) { variablesMap[uniqueName] = typeof attribute.value === 'string' ? { type: 'string', value: attribute.value } : { type: 'unknown', value: attribute.value.value }; } } }); const variableDeclarationNodes = []; Object.entries(variablesMap).map(([variable, value]) => { const statement = `export const ${variable} = ${value.type === 'string' ? `"${value.value}"` : value.value}`; exportMap[variable] = statement; variableDeclarationNodes.push(...getAST(statement).children); }); return { variableDeclarationNodes, snippetNodesToInject: treeToInject.children, }; }; const reinsertExports = (content, exportMap, snippetExportMap) => { for (const [key, value] of Object.entries(snippetExportMap)) { // TODO: handle duplicate exports if (exportMap[key] == undefined) { content = injectToTopOfFile(content, value); exportMap[key] = value; } } return content; };