UNPKG

@mintlify/scraping

Version:

Scrape documentation frameworks to Mintlify docs

253 lines 11.7 kB
import { getOpenApiTitleAndDescription, optionallyAddLeadingSlash, optionallyRemoveLeadingSlash, slugToTitle, isAllowedLocalSchemaUrl, buildOpenApiMetaTag, registerXMintContent, prepareStringToBeValidFilename, generateUniqueFilenameWithoutExtension, getTagDisplayName, } from '@mintlify/common'; import { outputFile } from 'fs-extra'; import fse from 'fs-extra'; import fs from 'fs/promises'; import yaml from 'js-yaml'; import { OpenAPIV3 } from 'openapi-types'; import path, { join, parse, posix } from 'path'; import { fetchOpenApi } from '../utils/network.js'; export const getOpenApiDefinition = async (pathOrDocumentOrUrl, localSchema) => { if (typeof pathOrDocumentOrUrl === 'string') { if (pathOrDocumentOrUrl.startsWith('http:') && !localSchema) { // This is an invalid location either for a file or a URL throw new Error('Only HTTPS URLs are supported. HTTP URLs are only supported with the cli option --local-schema.'); } else { try { const url = new URL(pathOrDocumentOrUrl); pathOrDocumentOrUrl = url; } catch { const pathname = path.join(process.cwd(), pathOrDocumentOrUrl.toString()); const file = await fs.readFile(pathname, 'utf-8'); pathOrDocumentOrUrl = yaml.load(file); } } } const isUrl = pathOrDocumentOrUrl instanceof URL; if (pathOrDocumentOrUrl instanceof URL) { if (!isAllowedLocalSchemaUrl(pathOrDocumentOrUrl.toString(), localSchema)) { throw new Error('Only HTTPS URLs are supported. HTTP URLs are only supported with the cli option --local-schema.'); } pathOrDocumentOrUrl = await fetchOpenApi(pathOrDocumentOrUrl); } return { document: pathOrDocumentOrUrl, isUrl }; }; export const createOpenApiFrontmatter = async ({ filename, openApiMetaTag, version, deprecated, metadata, extraContent, }) => { let frontmatter = `---\nopenapi: ${openApiMetaTag}`; if (metadata && 'version' in metadata) { frontmatter += `\nversion: ${metadata.version}`; } else if (version) { frontmatter += `\nversion: ${version}`; } if (metadata && 'deprecated' in metadata) { frontmatter += `\ndeprecated: ${metadata.deprecated}`; } else if (deprecated) { frontmatter += `\ndeprecated: ${deprecated}`; } if (metadata) { const reserved = new Set(['openapi', 'version', 'deprecated']); Object.entries(metadata) .filter(([k]) => !reserved.has(k)) .forEach(([key, value]) => { frontmatter += `\n${key}: ${value}`; }); } frontmatter += `\n---`; const data = extraContent ? `${frontmatter}\n\n${extraContent}` : frontmatter; await outputFile(filename, data); }; const isHiddenOperation = (operation) => { return operation['x-hidden']; }; const isExcludedOperation = (operation) => { return operation['x-excluded']; }; export function processOpenApiPath(path, pathItemObject, schema, nav, decoratedNav, writePromises, pagesAcc, options, // eslint-disable-next-line @typescript-eslint/no-explicit-any findNavGroup) { const openApiFilePathFromRoot = options.openApiFilePath ? optionallyAddLeadingSlash(options.openApiFilePath) : undefined; Object.values(OpenAPIV3.HttpMethods).forEach((method) => { if (method in pathItemObject) { const operation = pathItemObject[method]; if (isExcludedOperation(operation)) { return; } const xMint = operation?.['x-mint']; const xMintGroups = getXMintGroups({ pathObject: pathItemObject, operationObject: operation, }); if (xMint?.href) { xMint.href = optionallyAddLeadingSlash(xMint.href); } const tagName = operation?.tags?.[0]; const groupName = getTagDisplayName(tagName, schema); let title = prepareStringToBeValidFilename(operation?.summary) ?? `${method}-${prepareStringToBeValidFilename(path)}`; let folder = prepareStringToBeValidFilename(tagName) ?? ''; let base = posix.join(options.outDir ?? '', folder, title); if (xMint?.href) { const slug = optionallyRemoveLeadingSlash(xMint.href); title = posix.parse(slug).name; folder = posix.parse(slug).dir; base = posix.join(folder, title); } const navGroup = findNavGroup(nav, groupName); const decoratedNavGroup = findNavGroup(decoratedNav, groupName); const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(navGroup, base); const openapiMetaTag = buildOpenApiMetaTag({ filePath: openApiFilePathFromRoot, method, path, }); const { title: titleTag, description } = getOpenApiTitleAndDescription([ { filename: options.openApiFilePath ? parse(options.openApiFilePath).name : 'filler-filename', spec: schema, originalFileLocation: openApiFilePathFromRoot, }, ], openapiMetaTag); let xMintMetadata = xMint?.metadata; if (xMintGroups.length > 0) { xMintMetadata = { ...xMintMetadata, groups: [ ...(Array.isArray(xMintMetadata?.groups) ? xMintMetadata.groups : []), ...xMintGroups, ], }; } const slugTitle = slugToTitle(filenameWithoutExtension); const page = { title: titleTag ?? slugTitle, description, deprecated: operation?.deprecated, version: options.version, // When a file-path spec has x-mint.href overriding the path, use the slug-derived // title for the sidebar so the nav shows the user-chosen name rather than the // OpenAPI summary. Only for openApiFilePath specs to avoid changing URL-based behavior. ...(xMint?.href && titleTag && options.openApiFilePath ? { sidebarTitle: slugTitle } : {}), ...xMintMetadata, openapi: openapiMetaTag, href: posix.resolve('/', filenameWithoutExtension), }; if (!isHiddenOperation(operation)) { navGroup.push(filenameWithoutExtension); decoratedNavGroup.push(page); } pagesAcc[filenameWithoutExtension] = page; if (!options.writeFiles) { registerXMintContent(filenameWithoutExtension, xMint?.content); } const targetPath = options.outDirBasePath ? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`) : `${filenameWithoutExtension}.mdx`; if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) { writePromises.push(createOpenApiFrontmatter({ filename: targetPath, openApiMetaTag: openapiMetaTag, version: options.version, deprecated: operation?.deprecated, metadata: xMintMetadata, extraContent: xMint?.content, })); } } }); } export function processOpenApiWebhook(webhook, webhookObject, schema, nav, decoratedNav, writePromises, pagesAcc, options, // eslint-disable-next-line @typescript-eslint/no-explicit-any findNavGroup) { const openApiFilePathFromRoot = options.openApiFilePath ? optionallyAddLeadingSlash(options.openApiFilePath) : undefined; Object.values(OpenAPIV3.HttpMethods).forEach((method) => { if (method in webhookObject) { const operation = webhookObject[method]; if (isExcludedOperation(operation)) { return; } const xMint = operation?.['x-mint']; if (xMint?.href) { xMint.href = optionallyAddLeadingSlash(xMint.href); } const tagName = operation?.tags?.[0]; const groupName = getTagDisplayName(tagName, schema); let title = prepareStringToBeValidFilename(operation?.summary) ?? `${prepareStringToBeValidFilename(webhook)}`; let folder = prepareStringToBeValidFilename(tagName) ?? ''; let base = posix.join(options.outDir ?? '', folder, title); if (xMint?.href) { const slug = optionallyRemoveLeadingSlash(xMint.href); title = posix.parse(slug).name; folder = posix.parse(slug).dir; base = posix.join(folder, title); } const navGroup = findNavGroup(nav, groupName); const decoratedNavGroup = findNavGroup(decoratedNav, groupName); const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(navGroup, base); const openapiMetaTag = buildOpenApiMetaTag({ filePath: openApiFilePathFromRoot, method: 'webhook', path: webhook, }); const page = { title: slugToTitle(filenameWithoutExtension), description: operation?.description, version: options.version, deprecated: operation?.deprecated, ...xMint?.metadata, openapi: openapiMetaTag, href: posix.resolve('/', filenameWithoutExtension), }; if (!isHiddenOperation(operation)) { navGroup.push(filenameWithoutExtension); decoratedNavGroup.push(page); } pagesAcc[filenameWithoutExtension] = page; if (!options.writeFiles) { registerXMintContent(filenameWithoutExtension, xMint?.content); } const targetPath = options.outDirBasePath ? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`) : `${filenameWithoutExtension}.mdx`; if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) { writePromises.push(createOpenApiFrontmatter({ filename: targetPath, openApiMetaTag: openapiMetaTag, version: options.version, deprecated: operation?.deprecated, metadata: xMint?.metadata, extraContent: xMint?.content, })); } } }); } export const getXMintGroups = ({ pathObject, operationObject, }) => { const allowedGroups = []; if ('x-mint' in pathObject && pathObject['x-mint'] && typeof pathObject['x-mint'] === 'object' && 'groups' in pathObject['x-mint'] && Array.isArray(pathObject['x-mint'].groups)) { allowedGroups.push(...pathObject['x-mint'].groups); } if (operationObject && 'x-mint' in operationObject && operationObject['x-mint'] && typeof operationObject['x-mint'] === 'object' && 'groups' in operationObject['x-mint'] && Array.isArray(operationObject['x-mint'].groups)) { allowedGroups.push(...operationObject['x-mint'].groups); } return allowedGroups; }; //# sourceMappingURL=common.js.map