peezy-cli
Version:
Production-ready CLI for scaffolding modern applications with curated full-stack templates, intelligent migrations, and enterprise security.
188 lines • 6.16 kB
JavaScript
import path from "node:path";
import { fileURLToPath } from "node:url";
import { RemoteRegistryService } from "./services/remote-registry.js";
import { log } from "./utils/logger.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/**
* Helper function to resolve template paths
*/
const templatePath = (name) => path.resolve(__dirname, "../templates", name);
/**
* Local template registry with embedded templates
* Hero templates are marked as popular for UI prioritization
*/
export const localRegistry = {
// Hero Experiences - Curated Full-Stack Templates
"nextjs-fullstack": {
title: "🚀 Next.js Full-Stack + Auth + Database",
path: templatePath("nextjs-fullstack"),
popular: true,
hero: true,
description: "Complete Next.js 14 app with authentication, database, and modern UI",
tags: [
"nextjs",
"react",
"typescript",
"tailwind",
"auth",
"database",
"drizzle",
],
},
"express-fullstack": {
title: "🚀 Express + React Full-Stack",
path: templatePath("express-fullstack"),
popular: true,
hero: true,
description: "Express.js backend with React frontend, authentication, and database",
tags: ["express", "react", "typescript", "api", "auth", "database"],
},
"react-spa-advanced": {
title: "🚀 React SPA + Advanced Features",
path: templatePath("react-spa-advanced"),
popular: true,
hero: true,
description: "Modern React SPA with routing, state management, and testing",
tags: ["react", "spa", "typescript", "tailwind", "testing", "routing"],
},
// Existing Templates
"nextjs-app-router": {
title: "Next.js 14 + App Router + TypeScript + Tailwind",
path: templatePath("nextjs-app-router"),
popular: true,
},
"express-typescript": {
title: "Express.js + TypeScript + REST API",
path: templatePath("express-typescript"),
popular: true,
},
"bun-react-tailwind": {
title: "Bun + React + Vite + Tailwind",
path: templatePath("bun-react-tailwind"),
popular: true,
},
"vite-vue-tailwind": {
title: "Vite + Vue + Tailwind",
path: templatePath("vite-vue-tailwind"),
popular: true,
},
flask: {
title: "Python Flask API/App",
path: templatePath("flask"),
popular: true,
},
fastapi: {
title: "Python FastAPI",
path: templatePath("fastapi"),
},
"flask-bun-hybrid": {
title: "Flask backend + Bun frontend",
path: templatePath("flask-bun-hybrid"),
},
};
// Backward compatibility
export const registry = localRegistry;
/**
* Remote registry service instance
*/
const remoteRegistry = new RemoteRegistryService();
/**
* Parse template name with optional version and scope
* Examples: "template", "@org/template", "template@1.0.0", "@org/template@1.0.0"
*/
export function parseTemplateName(input) {
// Handle scoped packages
const scopeMatch = input.match(/^(@[^/]+\/)/);
const scope = scopeMatch?.[1];
const withoutScope = scope ? input.slice(scope.length) : input;
// Handle version
const versionMatch = withoutScope.match(/^([^@]+)@(.+)$/);
const name = versionMatch ? versionMatch[1] : withoutScope;
const version = versionMatch ? versionMatch[2] : undefined;
const fullName = scope ? `${scope}${name}` : name;
return { scope, name, version, fullName };
}
/**
* Get all available templates (local + remote)
*/
export async function getAllTemplates() {
try {
const remoteTemplates = await remoteRegistry.listTemplates();
return {
local: Object.keys(localRegistry),
remote: remoteTemplates,
};
}
catch (error) {
log.warn(`Failed to fetch remote templates: ${error instanceof Error ? error.message : String(error)}`);
return {
local: Object.keys(localRegistry),
remote: [],
};
}
}
/**
* Get all template keys as an array (local only for backward compatibility)
*/
export const templateKeys = Object.keys(localRegistry);
/**
* Get popular templates first, then others (local only for backward compatibility)
*/
export const getOrderedTemplates = () => {
const popular = templateKeys.filter((key) => localRegistry[key].popular);
const others = templateKeys.filter((key) => !localRegistry[key].popular);
return [...popular, ...others];
};
/**
* Validate if a template key exists (local only)
*/
export const isValidTemplate = (key) => {
return key in localRegistry;
};
/**
* Check if a template name is a remote template
*/
export function isRemoteTemplate(templateName) {
const parsed = parseTemplateName(templateName);
return (parsed.scope !== undefined ||
parsed.version !== undefined ||
!isValidTemplate(parsed.fullName));
}
/**
* Get template definition by key (local only)
*/
export const getTemplate = (key) => {
return localRegistry[key];
};
/**
* Resolve template path (local or remote)
*/
export async function resolveTemplate(templateName) {
const parsed = parseTemplateName(templateName);
// Check if it's a local template first
if (!parsed.scope && !parsed.version && isValidTemplate(parsed.name)) {
return {
path: localRegistry[parsed.name].path,
isRemote: false,
};
}
// Try to resolve as remote template
try {
const templatePath = await remoteRegistry.downloadTemplate(parsed.fullName, parsed.version);
return {
path: templatePath,
isRemote: true,
version: parsed.version,
};
}
catch (error) {
throw new Error(`Template "${templateName}" not found in local or remote registry: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Get remote registry service instance
*/
export function getRemoteRegistry() {
return remoteRegistry;
}
//# sourceMappingURL=registry.js.map