@mintlify/previewing
Version:
Preview Mintlify docs locally
167 lines (166 loc) • 6.64 kB
JavaScript
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 };
};