Merge pull request #5 from emdash-cms/feat/create-emdash-ux
feat(create-emdash): improve CLI branding and UX
This commit is contained in:
5
.changeset/create-emdash-ux.md
Normal file
5
.changeset/create-emdash-ux.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"create-emdash": patch
|
||||
---
|
||||
|
||||
Improve create-emdash CLI experience: add the EmDash branded banner, let users pick their package manager (auto-detects the one that invoked it), and ask whether to install dependencies with a spinner showing progress.
|
||||
@@ -18,6 +18,17 @@ const PROJECT_NAME_PATTERN = /^[a-z0-9-]+$/;
|
||||
|
||||
const GITHUB_REPO = "emdash-cms/templates";
|
||||
|
||||
type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
|
||||
|
||||
/** Detect which package manager invoked us, or fall back to npm */
|
||||
function detectPackageManager(): PackageManager {
|
||||
const agent = process.env.npm_config_user_agent ?? "";
|
||||
if (agent.startsWith("pnpm")) return "pnpm";
|
||||
if (agent.startsWith("yarn")) return "yarn";
|
||||
if (agent.startsWith("bun")) return "bun";
|
||||
return "npm";
|
||||
}
|
||||
|
||||
type Platform = "node" | "cloudflare";
|
||||
|
||||
interface TemplateConfig {
|
||||
@@ -95,7 +106,8 @@ function selectOptions<K extends string>(
|
||||
async function main() {
|
||||
console.clear();
|
||||
|
||||
p.intro(`${pc.bgCyan(pc.black(" create-emdash "))}`);
|
||||
console.log(`\n ${pc.bold(pc.cyan("— E M D A S H —"))}\n`);
|
||||
p.intro("Create a new EmDash project");
|
||||
|
||||
const projectName = await p.text({
|
||||
message: "Project name?",
|
||||
@@ -132,16 +144,16 @@ async function main() {
|
||||
const platform = await p.select<Platform>({
|
||||
message: "Where will you deploy?",
|
||||
options: [
|
||||
{
|
||||
value: "node",
|
||||
label: "Node.js",
|
||||
hint: "SQLite + local file storage",
|
||||
},
|
||||
{
|
||||
value: "cloudflare",
|
||||
label: "Cloudflare Workers",
|
||||
hint: "D1 + R2",
|
||||
},
|
||||
{
|
||||
value: "node",
|
||||
label: "Node.js",
|
||||
hint: "SQLite + local file storage",
|
||||
},
|
||||
],
|
||||
initialValue: "node",
|
||||
});
|
||||
@@ -175,6 +187,38 @@ async function main() {
|
||||
? NODE_TEMPLATES[templateKey as NodeTemplate]
|
||||
: CLOUDFLARE_TEMPLATES[templateKey as CloudflareTemplate];
|
||||
|
||||
// Step 3: pick package manager
|
||||
const detectedPm = detectPackageManager();
|
||||
const pm = await p.select<PackageManager>({
|
||||
message: "Which package manager?",
|
||||
options: [
|
||||
{ value: "pnpm", label: "pnpm" },
|
||||
{ value: "npm", label: "npm" },
|
||||
{ value: "yarn", label: "yarn" },
|
||||
{ value: "bun", label: "bun" },
|
||||
],
|
||||
initialValue: detectedPm,
|
||||
});
|
||||
|
||||
if (p.isCancel(pm)) {
|
||||
p.cancel("Operation cancelled.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Step 4: install dependencies?
|
||||
const shouldInstall = await p.confirm({
|
||||
message: "Install dependencies?",
|
||||
initialValue: true,
|
||||
});
|
||||
|
||||
if (p.isCancel(shouldInstall)) {
|
||||
p.cancel("Operation cancelled.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const installCmd = `${pm} install`;
|
||||
const runCmd = (script: string) => (pm === "npm" ? `npm run ${script}` : `${pm} ${script}`);
|
||||
|
||||
const s = p.spinner();
|
||||
s.start("Creating project...");
|
||||
|
||||
@@ -204,19 +248,25 @@ async function main() {
|
||||
|
||||
s.stop("Project created!");
|
||||
|
||||
s.start("Installing dependencies...");
|
||||
try {
|
||||
execSync("pnpm install", {
|
||||
cwd: projectDir,
|
||||
stdio: "ignore",
|
||||
});
|
||||
s.stop("Dependencies installed!");
|
||||
} catch {
|
||||
s.stop("Failed to install dependencies");
|
||||
p.log.warn(`Run ${pc.cyan(`cd ${projectName} && pnpm install`)} manually`);
|
||||
if (shouldInstall) {
|
||||
s.start(`Installing dependencies with ${pc.cyan(pm)}...`);
|
||||
try {
|
||||
execSync(installCmd, {
|
||||
cwd: projectDir,
|
||||
stdio: "ignore",
|
||||
});
|
||||
s.stop("Dependencies installed!");
|
||||
} catch {
|
||||
s.stop("Failed to install dependencies");
|
||||
p.log.warn(`Run ${pc.cyan(`cd ${projectName} && ${installCmd}`)} manually`);
|
||||
}
|
||||
}
|
||||
|
||||
p.note(`cd ${projectName}\npnpm run bootstrap\npnpm run dev`, "Next steps");
|
||||
const steps = [`cd ${projectName}`];
|
||||
if (!shouldInstall) steps.push(installCmd);
|
||||
steps.push(runCmd("bootstrap"), runCmd("dev"));
|
||||
|
||||
p.note(steps.join("\n"), "Next steps");
|
||||
|
||||
p.outro(`${pc.green("Done!")} Your EmDash project is ready at ${pc.cyan(projectName)}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,43 +1,48 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-ai-moderation",
|
||||
"version": "0.0.2",
|
||||
"description": "AI-powered comment moderation plugin for EmDash CMS using Cloudflare Workers AI (Llama Guard)",
|
||||
"type": "module",
|
||||
"main": "src/descriptor.ts",
|
||||
"exports": {
|
||||
".": "./src/descriptor.ts",
|
||||
"./plugin": "./src/index.ts",
|
||||
"./admin": "./src/admin.tsx"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"ai",
|
||||
"moderation",
|
||||
"comments",
|
||||
"llama-guard"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@cloudflare/kumo": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20250224.0",
|
||||
"@types/react": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"optionalDependencies": {}
|
||||
"name": "@emdash-cms/plugin-ai-moderation",
|
||||
"version": "0.0.2",
|
||||
"description": "AI-powered comment moderation plugin for EmDash CMS using Cloudflare Workers AI (Llama Guard)",
|
||||
"type": "module",
|
||||
"main": "src/descriptor.ts",
|
||||
"exports": {
|
||||
".": "./src/descriptor.ts",
|
||||
"./plugin": "./src/index.ts",
|
||||
"./admin": "./src/admin.tsx"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"ai",
|
||||
"moderation",
|
||||
"comments",
|
||||
"llama-guard"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@cloudflare/kumo": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20250224.0",
|
||||
"@types/react": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"optionalDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/ai-moderation"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,37 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-api-test",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"description": "Test plugin that exercises all EmDash plugin APIs",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./admin": "./src/admin.tsx"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"test",
|
||||
"api"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
"name": "@emdash-cms/plugin-api-test",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"description": "Test plugin that exercises all EmDash plugin APIs",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./admin": "./src/admin.tsx"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"test",
|
||||
"api"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"optionalDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/api-test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,10 @@
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/atproto"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-audit-log",
|
||||
"version": "0.0.2",
|
||||
"description": "Audit logging plugin for EmDash CMS - tracks content changes",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./sandbox": "./src/sandbox-entry.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"audit",
|
||||
"logging",
|
||||
"history"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
"name": "@emdash-cms/plugin-audit-log",
|
||||
"version": "0.0.2",
|
||||
"description": "Audit logging plugin for EmDash CMS - tracks content changes",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./sandbox": "./src/sandbox-entry.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"audit",
|
||||
"logging",
|
||||
"history"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"optionalDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/audit-log"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,5 +30,10 @@
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/color"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,42 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-embeds",
|
||||
"version": "0.0.2",
|
||||
"description": "Embed blocks for EmDash CMS - YouTube, Vimeo, Twitter, Bluesky, Mastodon, and more",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./astro": "./src/astro/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"embed",
|
||||
"youtube",
|
||||
"vimeo",
|
||||
"twitter",
|
||||
"bluesky"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"astro": ">=6.0.0-beta.0",
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emdash-cms/blocks": "workspace:*",
|
||||
"astro-embed": "^0.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
}
|
||||
}
|
||||
"name": "@emdash-cms/plugin-embeds",
|
||||
"version": "0.0.2",
|
||||
"description": "Embed blocks for EmDash CMS - YouTube, Vimeo, Twitter, Bluesky, Mastodon, and more",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./astro": "./src/astro/index.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"embed",
|
||||
"youtube",
|
||||
"vimeo",
|
||||
"twitter",
|
||||
"bluesky"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"astro": ">=6.0.0-beta.0",
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emdash-cms/blocks": "workspace:*",
|
||||
"astro-embed": "^0.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/embeds"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,5 +36,10 @@
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/forms"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,5 +35,10 @@
|
||||
"devDependencies": {
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/marketplace-test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,46 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-sandboxed-test",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"description": "Test plugin for sandboxed plugin system",
|
||||
"type": "module",
|
||||
"main": "dist/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts"
|
||||
},
|
||||
"./sandbox": "./dist/sandbox-entry.mjs"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean",
|
||||
"dev": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --watch",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"test",
|
||||
"sandbox"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
"name": "@emdash-cms/plugin-sandboxed-test",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"description": "Test plugin for sandboxed plugin system",
|
||||
"type": "module",
|
||||
"main": "dist/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts"
|
||||
},
|
||||
"./sandbox": "./dist/sandbox-entry.mjs"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean",
|
||||
"dev": "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --watch",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"test",
|
||||
"sandbox"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"optionalDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/sandboxed-test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
{
|
||||
"name": "@emdash-cms/plugin-webhook-notifier",
|
||||
"version": "0.0.2",
|
||||
"description": "Webhook notification plugin for EmDash CMS - posts to external URLs on content changes",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./sandbox": "./src/sandbox-entry.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"webhook",
|
||||
"notifications",
|
||||
"integration"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"optionalDependencies": {}
|
||||
}
|
||||
"name": "@emdash-cms/plugin-webhook-notifier",
|
||||
"version": "0.0.2",
|
||||
"description": "Webhook notification plugin for EmDash CMS - posts to external URLs on content changes",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./sandbox": "./src/sandbox-entry.ts"
|
||||
},
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"keywords": [
|
||||
"emdash",
|
||||
"cms",
|
||||
"plugin",
|
||||
"webhook",
|
||||
"notifications",
|
||||
"integration"
|
||||
],
|
||||
"author": "Matt Kane",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"emdash": "workspace:*"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"optionalDependencies": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/emdash-cms/emdash.git",
|
||||
"directory": "packages/plugins/webhook-notifier"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user