UNPKG

@reliverse/rse

Version:

@reliverse/rse is your all-in-one companion for bootstrapping and improving any kind of projects (especially web apps built with frameworks like Next.js) — whether you're kicking off something new or upgrading an existing app. It is also a little AI-power

223 lines (222 loc) 6.76 kB
import { relinka } from "@reliverse/relinka"; import { selectPrompt, inputPrompt, confirmPrompt, multiselectPrompt } from "@reliverse/rempts"; import { getUserPkgManager } from "../../../../../utils/dependencies/getUserPkgManager.js"; import { handleDownload } from "../../../../../utils/downloading/handleDownload.js"; import { ensureGithubToken } from "../../../../../utils/instanceGithub.js"; import { askProjectName } from "../../../../../utils/prompts/askProjectName.js"; import { cd } from "../../../../../utils/terminalHelpers.js"; function normalizeGitHubUrl(url) { return url.trim().replace( /^https?:\/\/(www\.)?(github|gitlab|bitbucket|sourcehut)\.com\//i, "" ).replace(/^(github|gitlab|bitbucket|sourcehut)\.com\//i, "").replace(/\.git$/i, ""); } const REPO_OWNERS = { blefnk: [ "all-in-one-nextjs-template", "astro-starlight-template", "create-next-app", "create-t3-app", "relivator-nextjs-template", "versator-nextjs-template" ], rse: [ "template-browser-extension", "acme", "cli", "pm", "prompts", "relico", "relinka" ], onwidget: ["astrowind"], "shadcn-ui": ["taxonomy"], "47ng": ["nuqs"], biomejs: ["biome"], pmndrs: ["zustand"], unjs: ["template"], "webpro-nl": ["knip"] }; function createMenuOptions(repos, owner, config) { const customRepos = (config.customUserFocusedRepos ?? []).concat(config.customDevsFocusedRepos ?? []).map(normalizeGitHubUrl).filter((repo) => repo.startsWith(`${owner}/`)).map((repo) => repo.split("/")[1]).filter((repo) => repo !== void 0); const allRepos = config.hideRepoSuggestions && customRepos.length > 0 ? customRepos : [...repos, ...customRepos]; if (!config.multipleRepoCloneMode) { return [ { label: "\u{1F4DD} I want to provide a custom repository name", value: "custom" }, ...allRepos.map( (repo) => ({ label: repo, value: repo, hint: customRepos.includes(repo) ? "custom" : void 0 }) ) ]; } return allRepos.map( (repo) => ({ label: repo, value: repo, hint: customRepos.includes(repo) ? "custom" : void 0 }) ); } async function promptForRepo({ title, owner, options, config }) { if (config.multipleRepoCloneMode) { const selections = await multiselectPrompt({ title, options }); if (selections.length === 0) { relinka("error", "Please select at least one repository."); return promptForRepo({ title, owner, options, config }); } const isCustomMap = /* @__PURE__ */ new Map(); const repos = selections.map((repo) => { const fullRepo = `${owner}/${repo}`; isCustomMap.set(fullRepo, false); return fullRepo; }); return { repos, isCustomMap }; } else { const selection = await selectPrompt({ title, options }); if (selection === "custom") { const customRepo = await inputPrompt({ title: `Enter a repository name for ${owner}`, content: `This will be combined as ${owner}/<your-input>` }); return { repo: `${owner}/${customRepo}`, isCustom: true }; } return { repo: `${owner}/${selection}`, isCustom: false }; } } async function downloadAndSetupRepo(owner, repoFullName, config, memory, isDev, cwd, isCustom) { let privacy = "public"; if (!REPO_OWNERS[owner]?.includes(repoFullName.split("/")[1])) { privacy = await selectPrompt({ title: `Is repo ${repoFullName} public or private?`, options: [ { label: "Public", value: "public" }, { label: "Private", value: "private" } ] }); } const { packageManager } = await getUserPkgManager(); let shouldInstallDeps = false; if (!isDev) { shouldInstallDeps = await confirmPrompt({ title: "Do you want me to install dependencies?", content: `I can run "${packageManager} install" in the directory of the cloned repo.` }); } const projectName = await askProjectName({ repoName: repoFullName }); const gitPreference = await selectPrompt({ title: "How would you like to handle Git history?", content: `(project: ${projectName} | repo: ${repoFullName})`, options: [ { label: "Preserve original Git history", hint: "keeps the original .git folder", value: "preserve" }, { label: "Start fresh Git history", hint: "initializes a new .git folder", value: "fresh" } ] }); let githubToken = ""; if (privacy === "private") { githubToken = await ensureGithubToken(memory, "prompt"); } const { source, dir } = await handleDownload({ cwd, isDev, skipPrompts: false, projectPath: "", projectName, selectedRepo: repoFullName, githubToken, preserveGit: gitPreference === "preserve", config: gitPreference === "fresh" ? config : void 0, install: shouldInstallDeps, isCustom, isTemplateDownload: false, cache: false }); relinka("success", `\u{1F389} ${source} was downloaded to ${dir}`); } export async function showCloneProjectMenu({ isDev, cwd, config, memory }) { if (isDev) { await cd("tests-runtime"); } relinka( "success", "Please note: This menu only allows cloning repositories.", "If you want a fully personalized project bootstrapped with a desired template, re-run the CLI and choose the `\u2728 Create a brand new project` option instead." ); const ownerOptions = [ ...Object.keys(REPO_OWNERS).map((owner2) => ({ label: owner2, value: owner2 })), { label: "\u{1F4DD} Enter a custom owner", value: "custom" }, { label: "\u{1F448} Exit", value: "exit" } ]; const selectedOwner = await selectPrompt({ title: "Select or enter a repository owner", options: ownerOptions }); if (selectedOwner === "exit") { relinka("info", "Exiting without cloning any repository."); return; } const owner = selectedOwner === "custom" ? await inputPrompt({ title: "Enter the GitHub username or organization" }) : selectedOwner; const ownerRepos = REPO_OWNERS[owner] || []; const repoPromptResult = await promptForRepo({ title: `Select repositories from ${owner}`, owner, options: createMenuOptions(ownerRepos, owner, config), config }); if ("repos" in repoPromptResult) { for (const repo of repoPromptResult.repos) { await downloadAndSetupRepo( owner, repo, config, memory, isDev, cwd, repoPromptResult.isCustomMap.get(repo) || false ); } } else { await downloadAndSetupRepo( owner, repoPromptResult.repo, config, memory, isDev, cwd, repoPromptResult.isCustom ); } }