@mintlify/cli
Version:
The Mintlify CLI
230 lines (229 loc) • 11.3 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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { validate, getOpenApiDocumentFromUrl, isAllowedLocalSchemaUrl } from '@mintlify/common';
import { getBrokenInternalLinks, renameFilesAndUpdateLinksInContent } from '@mintlify/link-rot';
import { addLog, dev, ErrorLog, SpinnerLog, SuccessLog, Logs, clearLogs, BrokenLinksLog, WarningLog, } from '@mintlify/previewing';
import { render, Text } from 'ink';
import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { accessibilityCheck } from './accessibilityCheck.js';
import { checkPort, checkForMintJson, checkNodeVersion, upgradeConfig, checkForDocsJson, getVersions, suppressConsoleWarnings, terminate, readLocalOpenApiFile, } from './helpers.js';
import { init } from './init.js';
import { mdxLinter } from './mdxLinter.js';
import { migrateMdx } from './migrateMdx.js';
import { update } from './update.js';
export const cli = ({ packageName = 'mint' }) => {
render(_jsx(Logs, {}));
return (yargs(hideBin(process.argv))
.scriptName(packageName)
.middleware(checkNodeVersion)
.middleware(suppressConsoleWarnings)
.command('dev', 'initialize a local preview environment', (yargs) => yargs
.option('open', {
type: 'boolean',
default: true,
description: 'open a local preview in the browser',
})
.option('local-schema', {
type: 'boolean',
default: false,
hidden: true,
description: 'use a locally hosted schema file (note: only https protocol is supported in production)',
})
.option('client-version', {
type: 'string',
hidden: true,
description: 'the version of the client to use for cli testing',
})
.option('groups', {
type: 'array',
description: 'Mock user groups for local development and testing',
example: '--groups admin user',
})
.option('disable-openapi', {
type: 'boolean',
default: false,
description: 'Disable OpenAPI file generation',
})
.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'), (argv) => __awaiter(void 0, void 0, void 0, function* () {
let nodeVersionString = process.version;
if (nodeVersionString.charAt(0) === 'v') {
nodeVersionString = nodeVersionString.slice(1);
}
const versionArr = nodeVersionString.split('.');
const majorVersion = parseInt(versionArr[0], 10);
const minorVersion = parseInt(versionArr[1], 10);
if (majorVersion >= 25) {
addLog(_jsx(ErrorLog, { message: "mint dev is not supported on node versions 25+. Please downgrade to an LTS node version." }));
yield terminate(1);
}
if (majorVersion < 20 || (majorVersion === 20 && minorVersion < 17)) {
addLog(_jsx(ErrorLog, { message: "mint dev is not supported on node versions below 20.17 Please upgrade to an LTS node version." }));
yield terminate(1);
}
const port = yield checkPort(argv);
const { cli: cliVersion } = getVersions();
if (port != undefined) {
yield dev(Object.assign(Object.assign({}, argv), { port,
packageName,
cliVersion }));
}
else {
addLog(_jsx(ErrorLog, { message: "no available port found" }));
yield terminate(1);
}
}))
.command('openapi-check <filename>', 'check if an OpenAPI spec is valid', (yargs) => yargs
.positional('filename', {
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,
})
.option('local-schema', {
type: 'boolean',
default: false,
description: 'use a locally hosted schema file (note: only https protocol is supported in production)',
}), (_a) => __awaiter(void 0, [_a], void 0, function* ({ filename, 'local-schema': localSchema }) {
try {
if (isAllowedLocalSchemaUrl(filename, localSchema)) {
yield getOpenApiDocumentFromUrl(filename);
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
yield terminate(0);
}
if (filename.startsWith('http://') && !localSchema) {
addLog(_jsx(WarningLog, { message: "include the --local-schema flag to check locally hosted OpenAPI files" }));
addLog(_jsx(WarningLog, { message: "only https protocol is supported in production" }));
yield terminate(0);
}
const document = yield readLocalOpenApiFile(filename);
if (!document) {
throw new Error('failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.');
}
yield validate(document);
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
}
catch (err) {
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
addLog(_jsx(ErrorLog, { message: `file not found, please check the path provided: ${filename}` }));
}
else {
addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
}
yield terminate(1);
}
yield terminate(0);
}))
.command('broken-links', 'check for invalid internal links', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
const hasMintJson = yield checkForMintJson();
if (!hasMintJson) {
yield checkForDocsJson();
}
addLog(_jsx(SpinnerLog, { message: "checking for broken links..." }));
try {
const brokenLinks = yield getBrokenInternalLinks();
if (brokenLinks.length === 0) {
clearLogs();
addLog(_jsx(SuccessLog, { message: "no broken links found" }));
yield terminate(0);
}
const brokenLinksByFile = {};
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];
}
});
clearLogs();
addLog(_jsx(BrokenLinksLog, { brokenLinksByFile: brokenLinksByFile }));
}
catch (err) {
addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
yield terminate(1);
}
yield terminate(1);
}))
.command('rename <from> <to>', 'rename a file and update all 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'])
.option('force', {
type: 'boolean',
default: false,
description: 'rename files and skip errors',
})
.epilog('example: `mintlify rename introduction.mdx overview.mdx`'), (_a) => __awaiter(void 0, [_a], void 0, function* ({ from, to, force }) {
const hasMintJson = yield checkForMintJson();
if (!hasMintJson) {
yield checkForDocsJson();
}
yield renameFilesAndUpdateLinksInContent(from, to, force);
yield terminate(0);
}))
.command('update', 'update the CLI to the latest version', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
yield update({ packageName });
yield terminate(0);
}))
.command('upgrade', 'upgrade mint.json file to docs.json (current format)', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
const hasMintJson = yield checkForMintJson();
if (!hasMintJson) {
yield checkForDocsJson();
}
yield upgradeConfig();
}))
.command('migrate-mdx', 'migrate MDX OpenAPI endpoint pages to x-mint extensions and docs.json', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
yield migrateMdx();
yield terminate(0);
}))
.command(['a11y', 'accessibility-check', 'a11y-check', 'accessibility'], 'check for accessibility issues in documentation', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
const accessibilityCheckTerminateCode = yield accessibilityCheck();
const mdxLinterTerminateCode = yield mdxLinter();
yield terminate(accessibilityCheckTerminateCode || mdxLinterTerminateCode);
}))
.command(['version', 'v'], 'display the current version of the CLI and client', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
const { cli, client } = getVersions();
addLog(_jsxs(Text, { children: [_jsx(Text, { bold: true, color: "green", children: "cli version" }), ' ', cli] }));
addLog(_jsxs(Text, { children: [_jsx(Text, { bold: true, color: "green", children: "client version" }), ' ', client] }));
}))
.command('new [directory]', 'Create a new Mintlify documentation site', (yargs) => yargs.positional('directory', {
describe: 'The directory to initialize your documentation',
type: 'string',
default: '.',
}), (_a) => __awaiter(void 0, [_a], void 0, function* ({ directory }) {
try {
yield init(directory);
yield terminate(0);
}
catch (error) {
addLog(_jsx(ErrorLog, { message: error instanceof Error ? error.message : 'error occurred' }));
yield terminate(1);
}
}))
// 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, default --version = -v
.alias('h', 'help')
.alias('v', 'version')
.parse());
};