diff --git a/.changeset/fix-create-emdash.md b/.changeset/fix-create-emdash.md new file mode 100644 index 0000000..e002553 --- /dev/null +++ b/.changeset/fix-create-emdash.md @@ -0,0 +1,5 @@ +--- +"create-emdash": patch +--- + +Fix create-emdash to use all available templates from the new standalone templates repo. Templates are now selected in two steps: platform (Node.js or Cloudflare Workers) then template type (blog, starter, marketing, portfolio, blank). Downloads from `emdash-cms/templates` instead of the old monorepo path. diff --git a/packages/create-emdash/package.json b/packages/create-emdash/package.json index 4f29e48..12cc794 100644 --- a/packages/create-emdash/package.json +++ b/packages/create-emdash/package.json @@ -14,10 +14,11 @@ }, "dependencies": { "@clack/prompts": "^0.10.0", - "picocolors": "^1.1.1", - "giget": "^1.2.3" + "giget": "^1.2.3", + "picocolors": "^1.1.1" }, "devDependencies": { + "@types/node": "catalog:", "tsdown": "catalog:", "typescript": "catalog:" }, @@ -33,7 +34,5 @@ "type": "git", "url": "git+https://github.com/emdash-cms/emdash.git", "directory": "packages/create-emdash" - }, - "peerDependencies": {}, - "optionalDependencies": {} + } } \ No newline at end of file diff --git a/packages/create-emdash/src/index.ts b/packages/create-emdash/src/index.ts index 810fd2f..cb479ef 100644 --- a/packages/create-emdash/src/index.ts +++ b/packages/create-emdash/src/index.ts @@ -7,43 +7,84 @@ */ import { execSync } from "node:child_process"; -import { cpSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import * as p from "@clack/prompts"; import { downloadTemplate } from "giget"; import pc from "picocolors"; -type Template = "blog" | "cloudflare" | "blank"; - const PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/; -const TEMPLATES = { +const GITHUB_REPO = "emdash-cms/templates"; + +type Platform = "node" | "cloudflare"; + +interface TemplateConfig { + name: string; + description: string; + /** Directory name in the templates repo */ + dir: string; +} + +const NODE_TEMPLATES = { blog: { name: "Blog", - description: "A blog with posts and pages (Node.js + SQLite)", + description: "A blog with posts, pages, and authors", dir: "blog", - repo: "github:emdash-cms/emdash/templates/blog", }, - cloudflare: { - name: "Cloudflare", - description: "A blog on Cloudflare Workers (D1 + R2)", - dir: "cloudflare", - repo: "github:emdash-cms/emdash/templates/cloudflare", + starter: { + name: "Starter", + description: "A general-purpose starter with posts and pages", + dir: "starter", + }, + marketing: { + name: "Marketing", + description: "A marketing site with landing pages and CTAs", + dir: "marketing", + }, + portfolio: { + name: "Portfolio", + description: "A portfolio site with projects and case studies", + dir: "portfolio", }, blank: { name: "Blank", - description: "A minimal starter project", + description: "A minimal starter with no content or styling", dir: "blank", - repo: "github:emdash-cms/emdash/templates/blank", }, -} as const; +} as const satisfies Record; + +const CLOUDFLARE_TEMPLATES = { + blog: { + name: "Blog", + description: "A blog with posts, pages, and authors", + dir: "blog-cloudflare", + }, + starter: { + name: "Starter", + description: "A general-purpose starter with posts and pages", + dir: "starter-cloudflare", + }, + marketing: { + name: "Marketing", + description: "A marketing site with landing pages and CTAs", + dir: "marketing-cloudflare", + }, + portfolio: { + name: "Portfolio", + description: "A portfolio site with projects and case studies", + dir: "portfolio-cloudflare", + }, +} as const satisfies Record; + +type NodeTemplate = keyof typeof NODE_TEMPLATES; +type CloudflareTemplate = keyof typeof CLOUDFLARE_TEMPLATES; /** Build select options from a config object, preserving literal key types */ function selectOptions( obj: Readonly>>, ): { value: K; label: string; hint: string }[] { - // eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Object.keys returns string[]; narrowed to K which is the known key union return (Object.keys(obj) as K[]).map((key) => ({ value: key, label: obj[key].name, @@ -51,26 +92,10 @@ function selectOptions( })); } -function findMonorepoRoot(): string | null { - let dir = process.cwd(); - while (true) { - if (existsSync(resolve(dir, "pnpm-workspace.yaml")) && existsSync(resolve(dir, "templates"))) { - return dir; - } - const parent = resolve(dir, ".."); - if (parent === dir) return null; - dir = parent; - } -} - -const useRemote = process.argv.includes("--remote"); - async function main() { console.clear(); - const monorepoRoot = useRemote ? null : findMonorepoRoot(); - - p.intro(`— ${pc.bgCyan(pc.black(" create-emdash "))}`); + p.intro(`${pc.bgCyan(pc.black(" create-emdash "))}`); const projectName = await p.text({ message: "Project name?", @@ -89,13 +114,11 @@ async function main() { process.exit(0); } - const projectDir = monorepoRoot - ? resolve(monorepoRoot, "demos", projectName) - : resolve(process.cwd(), projectName); + const projectDir = resolve(process.cwd(), projectName); if (existsSync(projectDir)) { const overwrite = await p.confirm({ - message: `Directory ${monorepoRoot ? `demos/${projectName}` : projectName} already exists. Overwrite?`, + message: `Directory ${projectName} already exists. Overwrite?`, initialValue: false, }); @@ -103,36 +126,63 @@ async function main() { p.cancel("Operation cancelled."); process.exit(0); } - - rmSync(projectDir, { recursive: true, force: true }); } - const template = await p.select