@mintlify/prebuild
Version:
Helpful functions for Mintlify's prebuild step
127 lines (126 loc) • 6.33 kB
JavaScript
import { stringifyTree, containsUpdates } from '@mintlify/common';
import { upgradeToDocsConfig } from '@mintlify/validation';
import { outputFile } from 'fs-extra';
import { join } from 'path';
import { updateDocsConfigFile } from './docsConfig/index.js';
import { updateMintConfigFile } from './mintConfig/index.js';
import { readPageContents, readSnippetsV2Contents } from './read/readContent.js';
import { resolveImportsAndWriteFiles } from './resolveImportsAndWriteFiles.js';
import { updateFavicons } from './updateFavicons.js';
import { updateGeneratedDocsNav, updateGeneratedNav } from './updateGeneratedNav.js';
import { writeAsyncApiFiles } from './write/writeAsyncApiFiles.js';
import { writeFiles, writeFile } from './write/writeFiles.js';
import { writeOpenApiData } from './write/writeOpenApiData.js';
import { writeRssFiles } from './write/writeRssFiles.js';
export const update = async ({ contentDirectoryPath, staticFilenames, openApiFiles, asyncApiFiles, contentFilenames, snippets, snippetV2Filenames, docsConfigPath, localSchema, groups, mintIgnore, disableOpenApi, strict, }) => {
const mintConfigResult = await updateMintConfigFile(contentDirectoryPath, openApiFiles, localSchema, strict);
// we used the original mint config without openapi pages injected
// because we will do it in `updateDocsConfigFile`, this will avoid duplicated openapi pages
const docsConfig = mintConfigResult != null ? upgradeToDocsConfig(mintConfigResult.originalMintConfig) : undefined;
const { docsConfig: newDocsConfig, pagesAcc, newOpenApiFiles, newAsyncApiFiles, customLanguages, } = await updateDocsConfigFile({
contentDirectoryPath,
openApiFiles,
asyncApiFiles,
docsConfig: docsConfigPath ? undefined : docsConfig,
localSchema,
disableOpenApi,
strict,
});
const pagePromises = readPageContents({
contentDirectoryPath,
openApiFiles: newOpenApiFiles,
asyncApiFiles: newAsyncApiFiles,
contentFilenames,
pagesAcc,
});
const snippetV2Promises = readSnippetsV2Contents(contentDirectoryPath, snippetV2Filenames);
const [snippetV2Contents, { mdxFilesWithNoImports, filesWithImports }] = await Promise.all([
snippetV2Promises,
pagePromises,
]);
// Filter pages with rss: true in frontmatter OR pages that contain Update components
const rssPages = mdxFilesWithNoImports.filter((page) => page.metadata.rss === true || containsUpdates(page.tree));
// Also include RSS pages that have imports (e.g., changelog entries as separate snippet files)
// For these, we need to extract the snippets using the importMap and include the component names
// so they can be properly resolved on the server side
const rssPagesWithImports = filesWithImports
.filter((file) => {
// Check if the file has rss: true metadata
if (file.metadata?.rss === true) {
return true;
}
// Check if the file itself contains Update components
if (containsUpdates(file.tree)) {
return true;
}
// Check if any of the imported snippets contain Update components
const importedSnippets = Object.keys(file.importMap).map((importPath) => snippetV2Contents.find((s) => s.filename === importPath));
return importedSnippets.some((snippet) => snippet && containsUpdates(snippet.tree));
})
.map((file) => {
// Extract snippets referenced by this page using its importMap
// Map component names (import specifiers) to snippets for proper injection
const referencedSnippets = [];
for (const [importPath, importSpecifiers] of Object.entries(file.importMap)) {
const snippet = snippetV2Contents.find((s) => s.filename === importPath);
if (snippet) {
// Get all the import names (component names) for this snippet
const importNames = importSpecifiers.map((spec) => spec.name);
referencedSnippets.push({
...snippet,
importNames,
});
}
}
return {
targetPath: join('src', '_props', file.filename),
sourcePath: join(contentDirectoryPath, file.filename),
tree: file.tree,
metadata: file.metadata,
snippets: referencedSnippets,
};
});
const allRssPages = [...rssPages, ...rssPagesWithImports];
const writeDevGroupsPromise = groups && groups.length > 0
? outputFile('src/_props/dev-groups.json', JSON.stringify({ groups }, null, 2), { flag: 'w' })
: Promise.resolve();
const writeMintIgnorePromise = mintIgnore && mintIgnore.length > 0
? outputFile('src/_props/mint-ignore.json', JSON.stringify(mintIgnore, null, 2), {
flag: 'w',
})
: Promise.resolve();
await Promise.all([
writeDevGroupsPromise,
writeMintIgnorePromise,
resolveImportsAndWriteFiles({
openApiFiles: newOpenApiFiles,
asyncApiFiles: newAsyncApiFiles,
pagesAcc,
snippetsV2: snippetV2Contents,
filesWithImports,
}),
writeFile(customLanguages, 'src/_props/customLanguages.json'),
writeOpenApiData(newOpenApiFiles),
writeAsyncApiFiles(newAsyncApiFiles),
writeRssFiles(newDocsConfig, allRssPages, contentDirectoryPath, snippetV2Contents),
updateFavicons(newDocsConfig, contentDirectoryPath),
...writeMdxFilesWithNoImports(mdxFilesWithNoImports),
...writeFiles(contentDirectoryPath, 'public', [...staticFilenames, ...snippets]),
]);
await updateGeneratedDocsNav(pagesAcc, newDocsConfig.navigation);
if (mintConfigResult?.mintConfig) {
await updateGeneratedNav(pagesAcc, mintConfigResult.mintConfig.navigation);
}
return newDocsConfig;
};
export const writeMdxFilesWithNoImports = (mdxFilesWithNoImports) => {
return mdxFilesWithNoImports.map(async (response) => {
const { targetPath, tree } = response;
await outputFile(targetPath, stringifyTree(tree), {
flag: 'w',
});
});
};
export * from './mintConfig/index.js';
export * from './docsConfig/index.js';
export * from './ConfigUpdater.js';