@mintlify/common
Version:
Commonly shared code within Mintlify
150 lines (149 loc) • 6.99 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 { 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);
}
};