@mintlify/cli
Version:
The Mintlify CLI
167 lines (158 loc) • 5.52 kB
text/typescript
import { validate, getOpenApiDocumentFromUrl } from '@mintlify/common';
import { getBrokenInternalLinks, renameFilesAndUpdateLinksInContent } from '@mintlify/link-rot';
import { dev } from '@mintlify/previewing';
import Chalk from 'chalk';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import {
checkPort,
checkForMintJson,
checkNodeVersion,
upgradeConfig,
checkForDocsJson,
} from './helpers.js';
export const cli = () =>
yargs(hideBin(process.argv))
.middleware(checkNodeVersion)
.command(
'dev',
'Runs Mintlify project locally.',
(yargs) =>
yargs
.option('open', {
type: 'boolean',
default: true,
description: 'Open 🌿 Mintlify in the browser',
})
.usage('Usage: mintlify dev [options]')
.example('mintlify dev', 'Run with default settings (opens in browser)')
.example('mintlify dev --no-open', 'Run without opening in browser'),
async (argv) => {
const port = await checkPort(argv);
if (port != undefined) {
await dev({
...argv,
port,
});
} else {
console.error(`No available port found.`);
}
}
)
.command(
'openapi-check <openapiFilenameOrUrl>',
'Validate an OpenAPI spec',
(yargs) =>
yargs.positional('openapiFilenameOrUrl', {
describe:
'The filename of the OpenAPI spec (e.g. ./openapi.yaml) or the URL to the OpenAPI spec (e.g. https://petstore3.swagger.io/api/v3/openapi.json)',
type: 'string',
demandOption: true,
}),
async ({ openapiFilenameOrUrl }) => {
try {
if (openapiFilenameOrUrl.startsWith('https://')) {
await getOpenApiDocumentFromUrl(openapiFilenameOrUrl);
console.log('✅ Your OpenAPI definition is valid.');
process.exit(0);
}
const pathname = path.resolve(process.cwd(), openapiFilenameOrUrl);
const file = await fs.readFile(pathname, 'utf-8');
const document = yaml.load(file) as Record<string, unknown> | undefined;
if (!document) {
throw new Error(
'Failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.'
);
}
await validate(document);
console.log('✅ Your OpenAPI definition is valid.');
} catch (err) {
console.error(Chalk.red(err));
process.exit(1);
}
}
)
.command(
'broken-links',
'Check for broken links in your Mintlify project.',
() => undefined,
async () => {
const hasMintJson = await checkForMintJson();
if (!hasMintJson) {
await checkForDocsJson();
}
console.log(Chalk.bold('Checking for broken links...\n'));
try {
const brokenLinks = await getBrokenInternalLinks();
if (brokenLinks.length === 0) {
console.log(Chalk.green('No broken links found.'));
return;
}
const brokenLinksByFile: Record<string, string[]> = {};
brokenLinks.forEach((mdxPath) => {
const filename = path.join(mdxPath.relativeDir, mdxPath.filename);
const brokenLinksForFile = brokenLinksByFile[filename];
if (brokenLinksForFile) {
brokenLinksForFile.push(mdxPath.originalPath);
} else {
brokenLinksByFile[filename] = [mdxPath.originalPath];
}
});
Object.entries(brokenLinksByFile).forEach(([fileName, brokenLinks]) => {
console.group(`${Chalk.underline(fileName)}`);
console.log(brokenLinks.join('\n'), '\n');
console.groupEnd();
});
console.error(Chalk.yellow(`${brokenLinks.length} broken links found.`));
process.exit(1);
} catch (err) {
console.error(Chalk.red(err));
process.exit(1);
}
}
)
.command(
'rename <from> <to>',
'Rename file in a Mintlify project and update the internal link references.',
(yargs) =>
yargs
.positional('from', {
describe: 'The file to rename',
type: 'string',
})
.positional('to', {
describe: 'The new name for the file',
type: 'string',
})
.demandOption(['from', 'to'])
.epilog('Example: `mintlify rename introduction.mdx overview.mdx`'),
async ({ from, to }) => {
const hasMintJson = await checkForMintJson();
if (!hasMintJson) {
await checkForDocsJson();
}
await renameFilesAndUpdateLinksInContent(from, to);
}
)
.command(
'upgrade',
'Upgrade the mint.json file to v2 (docs.json)',
() => undefined,
async () => {
const hasMintJson = await checkForMintJson();
if (!hasMintJson) {
await checkForDocsJson();
}
await upgradeConfig();
}
)
// Print the help menu when the user enters an invalid command.
.strictCommands()
.demandCommand(1, 'Unknown command. See above for the list of supported commands.')
// Alias option flags --help = -h, --version = -v
.alias('h', 'help')
.alias('v', 'version')
.parse();