@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
JavaScript
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")
}
};