UNPKG

@curvenote/cli

Version:
98 lines (97 loc) 4.04 kB
import chalk from 'chalk'; import fs from 'node:fs'; import inquirer from 'inquirer'; import { loadConfig, selectors, writeConfigs } from 'myst-cli'; import { LogLevel } from 'myst-cli-utils'; import { pullProject } from './pull/project.js'; import questions from './questions.js'; import { projectIdFromLink, getDefaultProjectConfig, getDefaultSiteConfigFromRemote, processOption, validateLinkIsAProject, cleanProjectConfigForWrite, } from './utils.js'; export async function interactiveCloneQuestions(session, opts) { // This is an interactive clone let project; if (opts?.remote) { project = await validateLinkIsAProject(session, opts.remote); if (!project) throw new Error(`Invalid remote address: "${opts.remote}"`); } else { while (!project) { const { projectLink } = await inquirer.prompt([questions.projectLink()]); project = await validateLinkIsAProject(session, projectLink); } } let path; const defaultPath = '.'; // Skip path prompt if: --output provided, --yes flag set, or CLI-driven with --remote if (opts?.path || opts?.yes || opts?.remote) { path = opts?.path ?? defaultPath; if (path !== '.' && fs.existsSync(path)) { throw new Error(`Invalid path for clone: "${path}", it must not exist.`); } } else { const promptConfirmPath = await inquirer.prompt([questions.projectPath(defaultPath)]); if (!promptConfirmPath.projectPath) throw new Error('Aborted'); path = defaultPath; } try { // Throw if project doesn't exist - that's what we want! await loadConfig(session, path); if (!selectors.selectLocalProjectConfig(session.store.getState(), path)) throw Error(); } catch { // Project config does not exist; good! const nonNullProjectData = Object.fromEntries(Object.entries(project.data).map(([key, item]) => [key, item === null ? undefined : item])); const projectConfig = { ...(await getDefaultProjectConfig(project.data.title)), ...nonNullProjectData, remote: nonNullProjectData.id, id: nonNullProjectData.id, }; return { siteProject: { path, slug: project.data.name }, projectConfig, }; } throw new Error(`Project already exists: "${path}" - did you mean to ${chalk.bold('curvenote pull')}`); } /** * Interactively clone a project from curvenote.com to a given path * * If a site config is present, the project is also added to the site. */ export async function clone(session, remote, path, opts) { const processedOpts = processOption(opts); // Site config is loaded on session init const siteConfig = selectors.selectCurrentSiteConfig(session.store.getState()); if (!siteConfig) { session.log.debug('Site config not found'); } const { siteProject, projectConfig } = await interactiveCloneQuestions(session, { ...processedOpts, remote, path, }); await writeConfigs(session, siteProject.path, { projectConfig: cleanProjectConfigForWrite(projectConfig), }); if (!siteConfig && remote) { // If there is no site config, but a remote is provided, create a config const newSiteConfig = await getDefaultSiteConfigFromRemote(session, projectIdFromLink(session, remote), siteProject); await writeConfigs(session, '.', { siteConfig: newSiteConfig }); } else if (siteConfig) { const newSiteConfig = { ...siteConfig, nav: [ ...(siteConfig?.nav || []), { title: projectConfig.title || '', url: `/${siteProject.slug}` }, ], projects: [...(siteConfig?.projects || []), siteProject], }; await writeConfigs(session, '.', { siteConfig: newSiteConfig }); } await pullProject(session, siteProject.path, { level: LogLevel.info, ci: opts?.ci }); }