UNPKG

@mintlify/previewing

Version:

Preview Mintlify docs locally

167 lines (166 loc) • 6.64 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { spawn } from 'child_process'; import fse from 'fs-extra'; import path from 'path'; import { LOCAL_LINKED_CLI_VERSION, MINT_PATH, DOT_MINTLIFY, CLIENT_PATH } from '../constants.js'; import { clearLogs, addLog } from '../logging-state.js'; import { SpinnerLog } from '../logs.js'; import { getLatestClientVersion, tryDownloadTargetMint, getCompatibleClientVersion, } from './client.js'; const MINT_BACKUP_PATH = path.join(DOT_MINTLIFY, 'mint-backup'); const isCurrentlySymlinked = async () => { if (!(await fse.pathExists(MINT_PATH))) return false; const stats = await fse.lstat(MINT_PATH); return stats.isSymbolicLink(); }; const buildClient = async () => { return new Promise((resolve) => { clearLogs(); addLog(_jsx(SpinnerLog, { message: "building local client (this may take a few minutes)..." })); const buildProcess = spawn('yarn', ['build'], { cwd: CLIENT_PATH, env: { ...process.env, STANDALONE_BUILD: 'true', NEXT_PUBLIC_ENV: 'cli', NEXT_PUBLIC_IS_LOCAL_CLIENT: 'true', }, stdio: 'pipe', shell: true, }); let stderr = ''; buildProcess.stderr.on('data', (data) => { stderr += data.toString(); }); buildProcess.on('close', (code) => { if (code === 0) { resolve(undefined); } else { resolve(`Client build failed (exit code ${code}): ${stderr.slice(-500)}`); } }); buildProcess.on('error', (err) => { resolve(`Client build error: ${err.message}`); }); }); }; const restoreFromBackup = async () => { try { if (await isCurrentlySymlinked()) { await fse.remove(MINT_PATH); } if (await fse.pathExists(MINT_BACKUP_PATH)) { await fse.move(MINT_BACKUP_PATH, MINT_PATH); } return undefined; } catch (err) { return err instanceof Error ? err.message : 'unknown error'; } }; const symlinkLocalClient = async (targetPath) => { try { const resolvedPath = path.resolve(targetPath); if (!(await fse.pathExists(resolvedPath))) { return `Path does not exist: ${resolvedPath}`; } const stats = await fse.stat(resolvedPath); if (!stats.isDirectory()) { return `Path is not a directory: ${resolvedPath}`; } if (await fse.pathExists(MINT_PATH)) { const existingStats = await fse.lstat(MINT_PATH); if (existingStats.isSymbolicLink()) { await fse.remove(MINT_PATH); } else { if (await fse.pathExists(MINT_BACKUP_PATH)) { await fse.remove(MINT_BACKUP_PATH); } await fse.move(MINT_PATH, MINT_BACKUP_PATH); } } await fse.ensureDir(path.dirname(MINT_PATH)); await fse.symlink(resolvedPath, MINT_PATH); const buildError = await buildClient(); if (buildError) { return buildError; } return undefined; } catch (err) { return err instanceof Error ? err.message : 'unknown error'; } }; export const silentUpdateClient = async ({ versionString, clientVersion, localClientVersion, cliVersion, }) => { // Handle local client symlink if (localClientVersion) { const error = await symlinkLocalClient(localClientVersion); return { needsUpdate: false, error }; } // If currently symlinked but not using local client, restore from backup if (await isCurrentlySymlinked()) { const restoreError = await restoreFromBackup(); if (restoreError) { return { needsUpdate: false, error: restoreError }; } // Re-read version string after restore const newVersionString = (await fse.pathExists(path.join(MINT_PATH, 'mint-version.txt'))) ? fse.readFileSync(path.join(MINT_PATH, 'mint-version.txt'), 'utf8') : null; // Continue with normal update logic using restored version return silentUpdateClientInternal({ versionString: newVersionString, clientVersion, cliVersion, }); } return silentUpdateClientInternal({ versionString, clientVersion, cliVersion }); }; const silentUpdateClientInternal = async ({ versionString, clientVersion, cliVersion, }) => { const latestClientVersion = await getLatestClientVersion(); const hasLatestClientVersion = latestClientVersion && versionString && versionString.trim() === latestClientVersion.trim(); if (clientVersion) { // always download specific client version if provided const error = await tryDownloadTargetMint({ targetVersion: clientVersion, existingVersion: versionString, }); return { needsUpdate: false, error }; } else if ((!versionString || cliVersion === LOCAL_LINKED_CLI_VERSION) && latestClientVersion) { // if no version exists, download latest const error = await tryDownloadTargetMint({ targetVersion: latestClientVersion, existingVersion: null, }); return { needsUpdate: false, error }; } else if (cliVersion && !hasLatestClientVersion) { // if not on latest, check whether to download latest or max const maxClientVersion = await getCompatibleClientVersion({ cliVersion }); const hasMaxClientVersion = maxClientVersion && versionString && versionString.trim() === maxClientVersion.trim(); if (maxClientVersion === 'latest' && latestClientVersion) { // if latest, download latest const error = await tryDownloadTargetMint({ targetVersion: latestClientVersion, existingVersion: versionString, }); return { needsUpdate: false, error }; } else if (maxClientVersion && !hasMaxClientVersion) { // if maxxed out, needs update const error = await tryDownloadTargetMint({ targetVersion: maxClientVersion, existingVersion: versionString, }); return { needsUpdate: true, error }; } else { // if compatible client version not found, needs update return { needsUpdate: true, error: undefined }; } } return { needsUpdate: false, error: undefined }; };