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

232 lines (231 loc) 7.44 kB
import path from "@reliverse/pathkit"; import { re } from "@reliverse/relico"; import { ensuredir } from "@reliverse/relifso"; import fs from "@reliverse/relifso"; import { setHiddenAttribute } from "@reliverse/relifso"; import { relinka } from "@reliverse/relinka"; import { Value } from "@sinclair/typebox/value"; import { parseJSONC } from "confbox"; import { ofetch } from "ofetch"; import { cliHomeRepos } from "../constants.js"; import { experimental, recommended } from "./badgeNotifiers.js"; import { DEFAULT_REPOS_CONFIG, reposSchema, generateReposJsonSchema, shouldRegenerateSchema } from "./schemaTemplate.js"; export const REPO_TEMPLATES = [ { id: "blefnk/relivator-nextjs-template", author: "blefnk", name: "relivator", description: "Full-featured e-commerce template with auth, payments, etc.", category: "website" }, { id: "blefnk/next-react-ts-src-minimal", author: "blefnk", name: "next-react-ts-src-minimal", description: "Essentials only: minimal Next.js with TypeScript template", category: "website" }, { id: "blefnk/all-in-one-nextjs-template", author: "blefnk", name: "all-in-one-nextjs-template", description: "Comprehensive Next.js eCommerce template with multiple features", category: "website" }, { id: "blefnk/create-t3-app", author: "blefnk", name: "create-t3-app", description: "Type-safe Next.js template with tRPC, Drizzle, and Tailwind", category: "website" }, { id: "blefnk/create-next-app", author: "blefnk", name: "create-next-app", description: "Basic Next.js starter template", category: "website" }, { id: "blefnk/astro-starlight-template", author: "blefnk", name: "astro-starlight-template", description: "Documentation site template using Astro and Starlight", category: "website" }, { id: "blefnk/versator-nextjs-template", author: "blefnk", name: "versator", description: "Versatile Next.js template for various use cases", category: "website" }, { id: "microsoft/vscode-extension-samples", author: "microsoft", name: "vscode-extension-samples", description: "Official VS Code extension samples", category: "vscode" }, { id: "microsoft/vscode-extension-template", author: "microsoft", name: "vscode-extension-template", description: "Basic VS Code extension template", category: "vscode" }, { id: "reliverse/template-browser-extension", author: "reliverse", name: "template-browser-extension", description: "Browser extension starter template", category: "browser" }, { id: "blefnk/relivator-docker-template", author: "blefnk", name: "relivator-docker-template", description: "Relivator template with Docker", category: "website" } ]; async function getReposConfigPath() { await ensuredir(cliHomeRepos); if (await shouldRegenerateSchema()) { await generateReposJsonSchema(); } return path.join(cliHomeRepos, "repos.json"); } async function readReposConfig() { const configPath = await getReposConfigPath(); if (!await fs.pathExists(configPath)) { return DEFAULT_REPOS_CONFIG; } try { const content = await fs.readFile(configPath, "utf-8"); const parsed = parseJSONC(content); if (Value.Check(reposSchema, parsed)) { return parsed; } } catch (error) { relinka("warn", "Failed to parse repos.json:", String(error)); } return DEFAULT_REPOS_CONFIG; } async function writeReposConfig(config) { const configPath = await getReposConfigPath(); await fs.writeFile(configPath, JSON.stringify(config, null, 2)); } export async function getRepoInfo(repoId) { const config = await readReposConfig(); return config.repos.find((t) => t.id === repoId) ?? null; } async function fetchRepoData(owner, name) { const url = `https://ungh.cc/repos/${owner}/${name}`; try { const data = await ofetch(url); return data.repo; } catch (error) { relinka("warn", "Failed to fetch repo info from ungh:", String(error)); return null; } } export async function saveRepoToDevice(repo, projectPath) { try { const repoSavePath = path.join(cliHomeRepos, repo.author, repo.name); await ensuredir(path.dirname(repoSavePath)); await fs.copy(projectPath, repoSavePath); const gitFolderPath = path.join(repoSavePath, ".git"); await setHiddenAttribute(gitFolderPath); const [owner, repoName] = repo.id.split("/"); if (!owner || !repoName) { throw new Error(`Invalid repo ID format: ${repo.id}`); } const repoData = await fetchRepoData(owner, repoName); const repoInfo = { id: repo.id, author: repo.author, name: repo.name, description: repo.description, category: repo.category, lastUpdated: (/* @__PURE__ */ new Date()).toISOString(), localPath: repoSavePath, github: repoData ? { stars: repoData.stars, forks: repoData.forks, watchers: repoData.watchers, createdAt: repoData.createdAt, updatedAt: repoData.updatedAt, pushedAt: repoData.pushedAt, defaultBranch: repoData.defaultBranch } : { stars: 0, forks: 0, watchers: 0, createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString(), pushedAt: (/* @__PURE__ */ new Date()).toISOString(), defaultBranch: "main" } }; const config = await readReposConfig(); const existingIndex = config.repos.findIndex((t) => t.id === repo.id); if (existingIndex >= 0) { config.repos[existingIndex] = repoInfo; } else { config.repos.push(repoInfo); } await writeReposConfig(config); } catch (error) { relinka("error", "Failed to save repo:", String(error)); throw error; } } export const TEMP_FULLSTACK_WEBSITE_TEMPLATE_OPTIONS = { "blefnk/relivator-nextjs-template": { label: `Relivator ${recommended}`, value: "blefnk/relivator-nextjs-template", hint: re.dim("Full-featured template with BetterAuth, Polar, and more") }, "blefnk/versator-nextjs-template": { label: `Versator ${experimental}`, value: "blefnk/versator-nextjs-template", hint: re.dim("Versatile template with Clerk, Stripe, and more") }, "blefnk/next-react-ts-src-minimal": { label: `Next.js Only ${experimental}`, value: "blefnk/next-react-ts-src-minimal", hint: re.dim("Essentials only: minimal Next.js with TypeScript") } }; export const TEMP_SEPARATED_WEBSITE_TEMPLATE_OPTIONS = { "blefnk/relivator-docker-repo": { label: `${experimental} Relivator Docker Repo`, value: "blefnk/relivator-docker-repo", hint: re.dim("Separated frontend and backend") } }; export const TEMP_VSCODE_TEMPLATE_OPTIONS = { "microsoft/vscode-extension-samples": { label: "VS Code Extension Sample", value: "microsoft/vscode-extension-samples", hint: re.dim("Official VS Code extension samples") }, "microsoft/vscode-extension-template": { label: "VS Code Extension Template", value: "microsoft/vscode-extension-template", hint: re.dim("Basic VS Code extension template") } }; export const TEMP_BROWSER_TEMPLATE_OPTIONS = { "rsebrowser-extension": { label: "Browser Extension Repo", value: "rsebrowser-extension", hint: re.dim("Browser extension starter repo") } };