UNPKG

@mintlify/common

Version:

Commonly shared code within Mintlify

150 lines (149 loc) 6.99 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 { visit } from 'unist-util-visit'; import { removeFrontmatterFromAST } from '../../astUtils.js'; import { getAST } from '../../remark.js'; import { createUniqueVariableName, isMdxJsxFlowElement } from '../../utils.js'; import { findAndRemoveExports } from '../findAndRemoveExports.js'; import { injectToTopOfFileOfTree } from './injectToTopOfFile.js'; /** * * @param tree The tree to inject into * @param componentName The component name we are injecting into * @param snippet The snippet to inject in place of the component * @param exportMap The export map of the snippet */ export const resolveComponentWithContent = (tree, componentName, snippet, exportMap) => __awaiter(void 0, void 0, void 0, function* () { const clonedSnippet = structuredClone(snippet); removeFrontmatterFromAST(clonedSnippet); const snippetExportMap = findAndRemoveExports(clonedSnippet); let treeToInject = clonedSnippet; 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); const isDefaultExport = snippetExportMap['default'] && (snippetExportMap[componentName] === undefined || Object.keys(snippetExportMap).length === 1); let snippetNodesToInject; let variableDeclarationNodes = []; // for named exports, replace the variables with the props if (!isDefaultExport) { node.name = null; const result = replaceVariablesWithProps(node, treeToInject, exportMap, snippetExportMap); variableDeclarationNodes = result.variableDeclarationNodes; snippetNodesToInject = result.snippetNodesToInject; 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; } }); reinsertExports(tree, exportMap, snippetExportMap); return tree; }); /** * 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 = {}; const propValues = getSnippetPropValues(node); replaceCodePlaceholdersWithProps(treeToInject, propValues); 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 getSnippetPropValues = (node) => { const propValues = {}; node.attributes.forEach((attribute) => { if (attribute.type !== 'mdxJsxAttribute' || !attribute.value || typeof attribute.value !== 'string') return; propValues[attribute.name] = attribute.value; }); return propValues; }; const replaceCodePlaceholdersWithProps = (tree, propValues) => { visit(tree, (node) => { if (node.type !== 'code' || typeof node.value !== 'string') { return; } node.value = node.value.replace(/\{\s*([A-Za-z_$][\w$]*)\s*\}/g, (match, prop) => { var _a; if (!(prop in propValues)) return match; return (_a = propValues[prop]) !== null && _a !== void 0 ? _a : match; }); }); }; const reinsertExports = (content, exportMap, snippetExportMap) => { const nodesToInject = []; for (const [key, value] of Object.entries(snippetExportMap)) { // TODO: handle duplicate exports if (exportMap[key] == undefined) { const exportAST = getAST(value); nodesToInject.push(...exportAST.children); exportMap[key] = value; } } if (nodesToInject.length > 0) { injectToTopOfFileOfTree(content, nodesToInject); } };