@mintlify/common
Version:
Commonly shared code within Mintlify
124 lines (123 loc) • 6.17 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 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;
};