create-emdash 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +67 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
5
6
|
import * as p from "@clack/prompts";
|
|
6
7
|
import { downloadTemplate } from "giget";
|
|
7
8
|
import pc from "picocolors";
|
|
@@ -14,8 +15,17 @@ import pc from "picocolors";
|
|
|
14
15
|
*
|
|
15
16
|
* Usage: npm create emdash@latest
|
|
16
17
|
*/
|
|
18
|
+
const execAsync = promisify(exec);
|
|
17
19
|
const PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
18
20
|
const GITHUB_REPO = "emdash-cms/templates";
|
|
21
|
+
/** Detect which package manager invoked us, or fall back to npm */
|
|
22
|
+
function detectPackageManager() {
|
|
23
|
+
const agent = process.env.npm_config_user_agent ?? "";
|
|
24
|
+
if (agent.startsWith("pnpm")) return "pnpm";
|
|
25
|
+
if (agent.startsWith("yarn")) return "yarn";
|
|
26
|
+
if (agent.startsWith("bun")) return "bun";
|
|
27
|
+
return "npm";
|
|
28
|
+
}
|
|
19
29
|
const NODE_TEMPLATES = {
|
|
20
30
|
blog: {
|
|
21
31
|
name: "Blog",
|
|
@@ -75,7 +85,8 @@ function selectOptions(obj) {
|
|
|
75
85
|
}
|
|
76
86
|
async function main() {
|
|
77
87
|
console.clear();
|
|
78
|
-
|
|
88
|
+
console.log(`\n ${pc.bold(pc.cyan("— E M D A S H —"))}\n`);
|
|
89
|
+
p.intro("Create a new EmDash project");
|
|
79
90
|
const projectName = await p.text({
|
|
80
91
|
message: "Project name?",
|
|
81
92
|
placeholder: "my-site",
|
|
@@ -103,13 +114,13 @@ async function main() {
|
|
|
103
114
|
const platform = await p.select({
|
|
104
115
|
message: "Where will you deploy?",
|
|
105
116
|
options: [{
|
|
106
|
-
value: "node",
|
|
107
|
-
label: "Node.js",
|
|
108
|
-
hint: "SQLite + local file storage"
|
|
109
|
-
}, {
|
|
110
117
|
value: "cloudflare",
|
|
111
118
|
label: "Cloudflare Workers",
|
|
112
119
|
hint: "D1 + R2"
|
|
120
|
+
}, {
|
|
121
|
+
value: "node",
|
|
122
|
+
label: "Node.js",
|
|
123
|
+
hint: "SQLite + local file storage"
|
|
113
124
|
}],
|
|
114
125
|
initialValue: "node"
|
|
115
126
|
});
|
|
@@ -131,6 +142,43 @@ async function main() {
|
|
|
131
142
|
process.exit(0);
|
|
132
143
|
}
|
|
133
144
|
const templateConfig = platform === "node" ? NODE_TEMPLATES[templateKey] : CLOUDFLARE_TEMPLATES[templateKey];
|
|
145
|
+
const detectedPm = detectPackageManager();
|
|
146
|
+
const pm = await p.select({
|
|
147
|
+
message: "Which package manager?",
|
|
148
|
+
options: [
|
|
149
|
+
{
|
|
150
|
+
value: "pnpm",
|
|
151
|
+
label: "pnpm"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
value: "npm",
|
|
155
|
+
label: "npm"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
value: "yarn",
|
|
159
|
+
label: "yarn"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
value: "bun",
|
|
163
|
+
label: "bun"
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
initialValue: detectedPm
|
|
167
|
+
});
|
|
168
|
+
if (p.isCancel(pm)) {
|
|
169
|
+
p.cancel("Operation cancelled.");
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
const shouldInstall = await p.confirm({
|
|
173
|
+
message: "Install dependencies?",
|
|
174
|
+
initialValue: true
|
|
175
|
+
});
|
|
176
|
+
if (p.isCancel(shouldInstall)) {
|
|
177
|
+
p.cancel("Operation cancelled.");
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
const installCmd = `${pm} install`;
|
|
181
|
+
const runCmd = (script) => pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
|
|
134
182
|
const s = p.spinner();
|
|
135
183
|
s.start("Creating project...");
|
|
136
184
|
try {
|
|
@@ -149,18 +197,20 @@ async function main() {
|
|
|
149
197
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
|
|
150
198
|
}
|
|
151
199
|
s.stop("Project created!");
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
cwd: projectDir
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
p.log.warn(`Run ${pc.cyan(`cd ${projectName} && pnpm install`)} manually`);
|
|
200
|
+
if (shouldInstall) {
|
|
201
|
+
s.start(`Installing dependencies with ${pc.cyan(pm)}...`);
|
|
202
|
+
try {
|
|
203
|
+
await execAsync(installCmd, { cwd: projectDir });
|
|
204
|
+
s.stop("Dependencies installed!");
|
|
205
|
+
} catch {
|
|
206
|
+
s.stop("Failed to install dependencies");
|
|
207
|
+
p.log.warn(`Run ${pc.cyan(`cd ${projectName} && ${installCmd}`)} manually`);
|
|
208
|
+
}
|
|
162
209
|
}
|
|
163
|
-
|
|
210
|
+
const steps = [`cd ${projectName}`];
|
|
211
|
+
if (!shouldInstall) steps.push(installCmd);
|
|
212
|
+
steps.push(runCmd("bootstrap"), runCmd("dev"));
|
|
213
|
+
p.note(steps.join("\n"), "Next steps");
|
|
164
214
|
p.outro(`${pc.green("Done!")} Your EmDash project is ready at ${pc.cyan(projectName)}`);
|
|
165
215
|
} catch (error) {
|
|
166
216
|
s.stop("Failed to create project");
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * create-emdash\n *\n * Interactive CLI for creating new EmDash projects\n *\n * Usage: npm create emdash@latest\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nimport * as p from \"@clack/prompts\";\nimport { downloadTemplate } from \"giget\";\nimport pc from \"picocolors\";\n\nconst PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/;\n\nconst GITHUB_REPO = \"emdash-cms/templates\";\n\ntype Platform = \"node\" | \"cloudflare\";\n\ninterface TemplateConfig {\n\tname: string;\n\tdescription: string;\n\t/** Directory name in the templates repo */\n\tdir: string;\n}\n\nconst NODE_TEMPLATES = {\n\tblog: {\n\t\tname: \"Blog\",\n\t\tdescription: \"A blog with posts, pages, and authors\",\n\t\tdir: \"blog\",\n\t},\n\tstarter: {\n\t\tname: \"Starter\",\n\t\tdescription: \"A general-purpose starter with posts and pages\",\n\t\tdir: \"starter\",\n\t},\n\tmarketing: {\n\t\tname: \"Marketing\",\n\t\tdescription: \"A marketing site with landing pages and CTAs\",\n\t\tdir: \"marketing\",\n\t},\n\tportfolio: {\n\t\tname: \"Portfolio\",\n\t\tdescription: \"A portfolio site with projects and case studies\",\n\t\tdir: \"portfolio\",\n\t},\n\tblank: {\n\t\tname: \"Blank\",\n\t\tdescription: \"A minimal starter with no content or styling\",\n\t\tdir: \"blank\",\n\t},\n} as const satisfies Record<string, TemplateConfig>;\n\nconst CLOUDFLARE_TEMPLATES = {\n\tblog: {\n\t\tname: \"Blog\",\n\t\tdescription: \"A blog with posts, pages, and authors\",\n\t\tdir: \"blog-cloudflare\",\n\t},\n\tstarter: {\n\t\tname: \"Starter\",\n\t\tdescription: \"A general-purpose starter with posts and pages\",\n\t\tdir: \"starter-cloudflare\",\n\t},\n\tmarketing: {\n\t\tname: \"Marketing\",\n\t\tdescription: \"A marketing site with landing pages and CTAs\",\n\t\tdir: \"marketing-cloudflare\",\n\t},\n\tportfolio: {\n\t\tname: \"Portfolio\",\n\t\tdescription: \"A portfolio site with projects and case studies\",\n\t\tdir: \"portfolio-cloudflare\",\n\t},\n} as const satisfies Record<string, TemplateConfig>;\n\ntype NodeTemplate = keyof typeof NODE_TEMPLATES;\ntype CloudflareTemplate = keyof typeof CLOUDFLARE_TEMPLATES;\n\n/** Build select options from a config object, preserving literal key types */\nfunction selectOptions<K extends string>(\n\tobj: Readonly<Record<K, Readonly<{ name: string; description: string }>>>,\n): { value: K; label: string; hint: string }[] {\n\treturn (Object.keys(obj) as K[]).map((key) => ({\n\t\tvalue: key,\n\t\tlabel: obj[key].name,\n\t\thint: obj[key].description,\n\t}));\n}\n\nasync function main() {\n\tconsole.clear();\n\n\tp.intro(`${pc.bgCyan(pc.black(\" create-emdash \"))}`);\n\n\tconst projectName = await p.text({\n\t\tmessage: \"Project name?\",\n\t\tplaceholder: \"my-site\",\n\t\tdefaultValue: \"my-site\",\n\t\tvalidate: (value) => {\n\t\t\tif (!value) return \"Project name is required\";\n\t\t\tif (!PROJECT_NAME_PATTERN.test(value))\n\t\t\t\treturn \"Project name can only contain lowercase letters, numbers, and hyphens\";\n\t\t\treturn undefined;\n\t\t},\n\t});\n\n\tif (p.isCancel(projectName)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tconst projectDir = resolve(process.cwd(), projectName);\n\n\tif (existsSync(projectDir)) {\n\t\tconst overwrite = await p.confirm({\n\t\t\tmessage: `Directory ${projectName} already exists. Overwrite?`,\n\t\t\tinitialValue: false,\n\t\t});\n\n\t\tif (p.isCancel(overwrite) || !overwrite) {\n\t\t\tp.cancel(\"Operation cancelled.\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\t// Step 1: pick platform\n\tconst platform = await p.select<Platform>({\n\t\tmessage: \"Where will you deploy?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"node\",\n\t\t\t\tlabel: \"Node.js\",\n\t\t\t\thint: \"SQLite + local file storage\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"cloudflare\",\n\t\t\t\tlabel: \"Cloudflare Workers\",\n\t\t\t\thint: \"D1 + R2\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"node\",\n\t});\n\n\tif (p.isCancel(platform)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\t// Step 2: pick template\n\tconst templateKey =\n\t\tplatform === \"node\"\n\t\t\t? await p.select<NodeTemplate>({\n\t\t\t\t\tmessage: \"Which template?\",\n\t\t\t\t\toptions: selectOptions(NODE_TEMPLATES),\n\t\t\t\t\tinitialValue: \"blog\",\n\t\t\t\t})\n\t\t\t: await p.select<CloudflareTemplate>({\n\t\t\t\t\tmessage: \"Which template?\",\n\t\t\t\t\toptions: selectOptions(CLOUDFLARE_TEMPLATES),\n\t\t\t\t\tinitialValue: \"blog\",\n\t\t\t\t});\n\n\tif (p.isCancel(templateKey)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tconst templateConfig =\n\t\tplatform === \"node\"\n\t\t\t? NODE_TEMPLATES[templateKey as NodeTemplate]\n\t\t\t: CLOUDFLARE_TEMPLATES[templateKey as CloudflareTemplate];\n\n\tconst s = p.spinner();\n\ts.start(\"Creating project...\");\n\n\ttry {\n\t\tawait downloadTemplate(`github:${GITHUB_REPO}/${templateConfig.dir}`, {\n\t\t\tdir: projectDir,\n\t\t\tforce: true,\n\t\t});\n\n\t\t// Set project name in package.json\n\t\tconst pkgPath = resolve(projectDir, \"package.json\");\n\t\tif (existsSync(pkgPath)) {\n\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t\t\tpkg.name = projectName;\n\n\t\t\t// Add emdash config if template has seed data\n\t\t\tconst seedPath = resolve(projectDir, \"seed\", \"seed.json\");\n\t\t\tif (existsSync(seedPath)) {\n\t\t\t\tpkg.emdash = {\n\t\t\t\t\tlabel: templateConfig.name,\n\t\t\t\t\tseed: \"seed/seed.json\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\twriteFileSync(pkgPath, JSON.stringify(pkg, null, 2));\n\t\t}\n\n\t\ts.stop(\"Project created!\");\n\n\t\ts.start(\"Installing dependencies...\");\n\t\ttry {\n\t\t\texecSync(\"pnpm install\", {\n\t\t\t\tcwd: projectDir,\n\t\t\t\tstdio: \"ignore\",\n\t\t\t});\n\t\t\ts.stop(\"Dependencies installed!\");\n\t\t} catch {\n\t\t\ts.stop(\"Failed to install dependencies\");\n\t\t\tp.log.warn(`Run ${pc.cyan(`cd ${projectName} && pnpm install`)} manually`);\n\t\t}\n\n\t\tp.note(`cd ${projectName}\\npnpm run bootstrap\\npnpm run dev`, \"Next steps\");\n\n\t\tp.outro(`${pc.green(\"Done!\")} Your EmDash project is ready at ${pc.cyan(projectName)}`);\n\t} catch (error) {\n\t\ts.stop(\"Failed to create project\");\n\t\tp.log.error(error instanceof Error ? error.message : String(error));\n\t\tprocess.exit(1);\n\t}\n}\n\nmain().catch((error) => {\n\tconsole.error(error);\n\tprocess.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAM,uBAAuB;AAE7B,MAAM,cAAc;AAWpB,MAAM,iBAAiB;CACtB,MAAM;EACL,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,SAAS;EACR,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,WAAW;EACV,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,WAAW;EACV,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,OAAO;EACN,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD;AAED,MAAM,uBAAuB;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,SAAS;EACR,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,WAAW;EACV,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD,WAAW;EACV,MAAM;EACN,aAAa;EACb,KAAK;EACL;CACD;;AAMD,SAAS,cACR,KAC8C;AAC9C,QAAQ,OAAO,KAAK,IAAI,CAAS,KAAK,SAAS;EAC9C,OAAO;EACP,OAAO,IAAI,KAAK;EAChB,MAAM,IAAI,KAAK;EACf,EAAE;;AAGJ,eAAe,OAAO;AACrB,SAAQ,OAAO;AAEf,GAAE,MAAM,GAAG,GAAG,OAAO,GAAG,MAAM,kBAAkB,CAAC,GAAG;CAEpD,MAAM,cAAc,MAAM,EAAE,KAAK;EAChC,SAAS;EACT,aAAa;EACb,cAAc;EACd,WAAW,UAAU;AACpB,OAAI,CAAC,MAAO,QAAO;AACnB,OAAI,CAAC,qBAAqB,KAAK,MAAM,CACpC,QAAO;;EAGT,CAAC;AAEF,KAAI,EAAE,SAAS,YAAY,EAAE;AAC5B,IAAE,OAAO,uBAAuB;AAChC,UAAQ,KAAK,EAAE;;CAGhB,MAAM,aAAa,QAAQ,QAAQ,KAAK,EAAE,YAAY;AAEtD,KAAI,WAAW,WAAW,EAAE;EAC3B,MAAM,YAAY,MAAM,EAAE,QAAQ;GACjC,SAAS,aAAa,YAAY;GAClC,cAAc;GACd,CAAC;AAEF,MAAI,EAAE,SAAS,UAAU,IAAI,CAAC,WAAW;AACxC,KAAE,OAAO,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;;CAKjB,MAAM,WAAW,MAAM,EAAE,OAAiB;EACzC,SAAS;EACT,SAAS,CACR;GACC,OAAO;GACP,OAAO;GACP,MAAM;GACN,EACD;GACC,OAAO;GACP,OAAO;GACP,MAAM;GACN,CACD;EACD,cAAc;EACd,CAAC;AAEF,KAAI,EAAE,SAAS,SAAS,EAAE;AACzB,IAAE,OAAO,uBAAuB;AAChC,UAAQ,KAAK,EAAE;;CAIhB,MAAM,cACL,aAAa,SACV,MAAM,EAAE,OAAqB;EAC7B,SAAS;EACT,SAAS,cAAc,eAAe;EACtC,cAAc;EACd,CAAC,GACD,MAAM,EAAE,OAA2B;EACnC,SAAS;EACT,SAAS,cAAc,qBAAqB;EAC5C,cAAc;EACd,CAAC;AAEL,KAAI,EAAE,SAAS,YAAY,EAAE;AAC5B,IAAE,OAAO,uBAAuB;AAChC,UAAQ,KAAK,EAAE;;CAGhB,MAAM,iBACL,aAAa,SACV,eAAe,eACf,qBAAqB;CAEzB,MAAM,IAAI,EAAE,SAAS;AACrB,GAAE,MAAM,sBAAsB;AAE9B,KAAI;AACH,QAAM,iBAAiB,UAAU,YAAY,GAAG,eAAe,OAAO;GACrE,KAAK;GACL,OAAO;GACP,CAAC;EAGF,MAAM,UAAU,QAAQ,YAAY,eAAe;AACnD,MAAI,WAAW,QAAQ,EAAE;GACxB,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AACtD,OAAI,OAAO;AAIX,OAAI,WADa,QAAQ,YAAY,QAAQ,YAAY,CACjC,CACvB,KAAI,SAAS;IACZ,OAAO,eAAe;IACtB,MAAM;IACN;AAGF,iBAAc,SAAS,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC;;AAGrD,IAAE,KAAK,mBAAmB;AAE1B,IAAE,MAAM,6BAA6B;AACrC,MAAI;AACH,YAAS,gBAAgB;IACxB,KAAK;IACL,OAAO;IACP,CAAC;AACF,KAAE,KAAK,0BAA0B;UAC1B;AACP,KAAE,KAAK,iCAAiC;AACxC,KAAE,IAAI,KAAK,OAAO,GAAG,KAAK,MAAM,YAAY,kBAAkB,CAAC,WAAW;;AAG3E,IAAE,KAAK,MAAM,YAAY,qCAAqC,aAAa;AAE3E,IAAE,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,mCAAmC,GAAG,KAAK,YAAY,GAAG;UAC/E,OAAO;AACf,IAAE,KAAK,2BAA2B;AAClC,IAAE,IAAI,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;AACnE,UAAQ,KAAK,EAAE;;;AAIjB,MAAM,CAAC,OAAO,UAAU;AACvB,SAAQ,MAAM,MAAM;AACpB,SAAQ,KAAK,EAAE;EACd"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * create-emdash\n *\n * Interactive CLI for creating new EmDash projects\n *\n * Usage: npm create emdash@latest\n */\n\nimport { exec } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execAsync = promisify(exec);\n\nimport * as p from \"@clack/prompts\";\nimport { downloadTemplate } from \"giget\";\nimport pc from \"picocolors\";\n\nconst PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/;\n\nconst GITHUB_REPO = \"emdash-cms/templates\";\n\ntype PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\n/** Detect which package manager invoked us, or fall back to npm */\nfunction detectPackageManager(): PackageManager {\n\tconst agent = process.env.npm_config_user_agent ?? \"\";\n\tif (agent.startsWith(\"pnpm\")) return \"pnpm\";\n\tif (agent.startsWith(\"yarn\")) return \"yarn\";\n\tif (agent.startsWith(\"bun\")) return \"bun\";\n\treturn \"npm\";\n}\n\ntype Platform = \"node\" | \"cloudflare\";\n\ninterface TemplateConfig {\n\tname: string;\n\tdescription: string;\n\t/** Directory name in the templates repo */\n\tdir: string;\n}\n\nconst NODE_TEMPLATES = {\n\tblog: {\n\t\tname: \"Blog\",\n\t\tdescription: \"A blog with posts, pages, and authors\",\n\t\tdir: \"blog\",\n\t},\n\tstarter: {\n\t\tname: \"Starter\",\n\t\tdescription: \"A general-purpose starter with posts and pages\",\n\t\tdir: \"starter\",\n\t},\n\tmarketing: {\n\t\tname: \"Marketing\",\n\t\tdescription: \"A marketing site with landing pages and CTAs\",\n\t\tdir: \"marketing\",\n\t},\n\tportfolio: {\n\t\tname: \"Portfolio\",\n\t\tdescription: \"A portfolio site with projects and case studies\",\n\t\tdir: \"portfolio\",\n\t},\n\tblank: {\n\t\tname: \"Blank\",\n\t\tdescription: \"A minimal starter with no content or styling\",\n\t\tdir: \"blank\",\n\t},\n} as const satisfies Record<string, TemplateConfig>;\n\nconst CLOUDFLARE_TEMPLATES = {\n\tblog: {\n\t\tname: \"Blog\",\n\t\tdescription: \"A blog with posts, pages, and authors\",\n\t\tdir: \"blog-cloudflare\",\n\t},\n\tstarter: {\n\t\tname: \"Starter\",\n\t\tdescription: \"A general-purpose starter with posts and pages\",\n\t\tdir: \"starter-cloudflare\",\n\t},\n\tmarketing: {\n\t\tname: \"Marketing\",\n\t\tdescription: \"A marketing site with landing pages and CTAs\",\n\t\tdir: \"marketing-cloudflare\",\n\t},\n\tportfolio: {\n\t\tname: \"Portfolio\",\n\t\tdescription: \"A portfolio site with projects and case studies\",\n\t\tdir: \"portfolio-cloudflare\",\n\t},\n} as const satisfies Record<string, TemplateConfig>;\n\ntype NodeTemplate = keyof typeof NODE_TEMPLATES;\ntype CloudflareTemplate = keyof typeof CLOUDFLARE_TEMPLATES;\n\n/** Build select options from a config object, preserving literal key types */\nfunction selectOptions<K extends string>(\n\tobj: Readonly<Record<K, Readonly<{ name: string; description: string }>>>,\n): { value: K; label: string; hint: string }[] {\n\treturn (Object.keys(obj) as K[]).map((key) => ({\n\t\tvalue: key,\n\t\tlabel: obj[key].name,\n\t\thint: obj[key].description,\n\t}));\n}\n\nasync function main() {\n\tconsole.clear();\n\n\tconsole.log(`\\n ${pc.bold(pc.cyan(\"— E M D A S H —\"))}\\n`);\n\tp.intro(\"Create a new EmDash project\");\n\n\tconst projectName = await p.text({\n\t\tmessage: \"Project name?\",\n\t\tplaceholder: \"my-site\",\n\t\tdefaultValue: \"my-site\",\n\t\tvalidate: (value) => {\n\t\t\tif (!value) return \"Project name is required\";\n\t\t\tif (!PROJECT_NAME_PATTERN.test(value))\n\t\t\t\treturn \"Project name can only contain lowercase letters, numbers, and hyphens\";\n\t\t\treturn undefined;\n\t\t},\n\t});\n\n\tif (p.isCancel(projectName)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tconst projectDir = resolve(process.cwd(), projectName);\n\n\tif (existsSync(projectDir)) {\n\t\tconst overwrite = await p.confirm({\n\t\t\tmessage: `Directory ${projectName} already exists. Overwrite?`,\n\t\t\tinitialValue: false,\n\t\t});\n\n\t\tif (p.isCancel(overwrite) || !overwrite) {\n\t\t\tp.cancel(\"Operation cancelled.\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\t// Step 1: pick platform\n\tconst platform = await p.select<Platform>({\n\t\tmessage: \"Where will you deploy?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"cloudflare\",\n\t\t\t\tlabel: \"Cloudflare Workers\",\n\t\t\t\thint: \"D1 + R2\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"node\",\n\t\t\t\tlabel: \"Node.js\",\n\t\t\t\thint: \"SQLite + local file storage\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"node\",\n\t});\n\n\tif (p.isCancel(platform)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\t// Step 2: pick template\n\tconst templateKey =\n\t\tplatform === \"node\"\n\t\t\t? await p.select<NodeTemplate>({\n\t\t\t\t\tmessage: \"Which template?\",\n\t\t\t\t\toptions: selectOptions(NODE_TEMPLATES),\n\t\t\t\t\tinitialValue: \"blog\",\n\t\t\t\t})\n\t\t\t: await p.select<CloudflareTemplate>({\n\t\t\t\t\tmessage: \"Which template?\",\n\t\t\t\t\toptions: selectOptions(CLOUDFLARE_TEMPLATES),\n\t\t\t\t\tinitialValue: \"blog\",\n\t\t\t\t});\n\n\tif (p.isCancel(templateKey)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tconst templateConfig =\n\t\tplatform === \"node\"\n\t\t\t? NODE_TEMPLATES[templateKey as NodeTemplate]\n\t\t\t: CLOUDFLARE_TEMPLATES[templateKey as CloudflareTemplate];\n\n\t// Step 3: pick package manager\n\tconst detectedPm = detectPackageManager();\n\tconst pm = await p.select<PackageManager>({\n\t\tmessage: \"Which package manager?\",\n\t\toptions: [\n\t\t\t{ value: \"pnpm\", label: \"pnpm\" },\n\t\t\t{ value: \"npm\", label: \"npm\" },\n\t\t\t{ value: \"yarn\", label: \"yarn\" },\n\t\t\t{ value: \"bun\", label: \"bun\" },\n\t\t],\n\t\tinitialValue: detectedPm,\n\t});\n\n\tif (p.isCancel(pm)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\t// Step 4: install dependencies?\n\tconst shouldInstall = await p.confirm({\n\t\tmessage: \"Install dependencies?\",\n\t\tinitialValue: true,\n\t});\n\n\tif (p.isCancel(shouldInstall)) {\n\t\tp.cancel(\"Operation cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tconst installCmd = `${pm} install`;\n\tconst runCmd = (script: string) => (pm === \"npm\" ? `npm run ${script}` : `${pm} ${script}`);\n\n\tconst s = p.spinner();\n\ts.start(\"Creating project...\");\n\n\ttry {\n\t\tawait downloadTemplate(`github:${GITHUB_REPO}/${templateConfig.dir}`, {\n\t\t\tdir: projectDir,\n\t\t\tforce: true,\n\t\t});\n\n\t\t// Set project name in package.json\n\t\tconst pkgPath = resolve(projectDir, \"package.json\");\n\t\tif (existsSync(pkgPath)) {\n\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n\t\t\tpkg.name = projectName;\n\n\t\t\t// Add emdash config if template has seed data\n\t\t\tconst seedPath = resolve(projectDir, \"seed\", \"seed.json\");\n\t\t\tif (existsSync(seedPath)) {\n\t\t\t\tpkg.emdash = {\n\t\t\t\t\tlabel: templateConfig.name,\n\t\t\t\t\tseed: \"seed/seed.json\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\twriteFileSync(pkgPath, JSON.stringify(pkg, null, 2));\n\t\t}\n\n\t\ts.stop(\"Project created!\");\n\n\t\tif (shouldInstall) {\n\t\t\ts.start(`Installing dependencies with ${pc.cyan(pm)}...`);\n\t\t\ttry {\n\t\t\t\tawait execAsync(installCmd, { cwd: projectDir });\n\t\t\t\ts.stop(\"Dependencies installed!\");\n\t\t\t} catch {\n\t\t\t\ts.stop(\"Failed to install dependencies\");\n\t\t\t\tp.log.warn(`Run ${pc.cyan(`cd ${projectName} && ${installCmd}`)} manually`);\n\t\t\t}\n\t\t}\n\n\t\tconst steps = [`cd ${projectName}`];\n\t\tif (!shouldInstall) steps.push(installCmd);\n\t\tsteps.push(runCmd(\"bootstrap\"), runCmd(\"dev\"));\n\n\t\tp.note(steps.join(\"\\n\"), \"Next steps\");\n\n\t\tp.outro(`${pc.green(\"Done!\")} Your EmDash project is ready at ${pc.cyan(projectName)}`);\n\t} catch (error) {\n\t\ts.stop(\"Failed to create project\");\n\t\tp.log.error(error instanceof Error ? error.message : String(error));\n\t\tprocess.exit(1);\n\t}\n}\n\nmain().catch((error) => {\n\tconsole.error(error);\n\tprocess.exit(1);\n});\n"],"mappings":"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"}
|