Skip to content

Commit

Permalink
Update templates, tweak prompts, add some magical actions ✨ (#20)
Browse files Browse the repository at this point in the history
* Update templates: Include fiberplane studio, use db:seed command

* Port over CLI changes

* Update cli package.json

* Revert template links back to create-honc-app instead of brettimus

* Remove verbose D1 setup instructions

* Ignore package-lock files

* Tweak setup instructions for d1 and supa

* Use inferSelect and seed each database with more clever names

* Format

* Tweak cancellation message

* Tweak intro

* Start parsing a hatch flag

* Remove commented out code

* Add small dev instructions for cli and clean up database preamble

* Add cleaning scripts

* Add dev script to test hatching

* Update template deps and wrangler toml name fields

* Organize postinstall instructions

* Add better hello messages

* Format and then give a better example for POSTing a user

* Remind to run npm run fiberplane after seeding

* Update the template github links for giget to point to main

* Add back sample api

* Update fiberplane studio dep to latest not canary
  • Loading branch information
brettimus authored Oct 29, 2024
1 parent d8a2d40 commit 3325d4e
Show file tree
Hide file tree
Showing 48 changed files with 1,038 additions and 292 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
node_modules

# Ignore package-lock.json files in templates
templates/**/package-lock.json

# Ignore test apps
cli/test-apps/**
!cli/test-apps/.gitkeep

# Ignore macOS DS_Store files
.DS_Store
7 changes: 7 additions & 0 deletions DEVELOPING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Development Guide

To test the CLI, run `pnpm dev` in the `cli` directory.

It's recommendeded to add test apps to the `test-apps` directory, since they will be ignored by git.

To test the hatching feature, run `pnpm dev:hatch`.
13 changes: 9 additions & 4 deletions cli/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{
"name": "create-honc-app",
"type": "module",
"version": "1.2.1",
"description": "An interactive CLI to create modular data APIs using TypeScript",
"version": "1.3.0",
"description": "An interactive CLI to create modular typesafe data APIs using TypeScript",
"scripts": {
"build": "tsup",
"build": "pnpm clean && tsup",
"dev": "tsup --watch && node dist/index.js",
"format": "biome format . --write"
"dev:hatch": "tsup --watch --onSuccess 'node dist/index.js --hatch'",
"format": "biome format . --write",
"typecheck": "tsc --noEmit",
"clean": "rimraf dist",
"clean:test-apps": "rimraf test-apps/* && touch test-apps/.gitkeep"
},
"exports": "./dist/index.js",
"files": ["dist", "README.md", "LICENSE", ".gitignore"],
Expand All @@ -30,6 +34,7 @@
},
"devDependencies": {
"@types/node": "^22.2.0",
"rimraf": "^6.0.1",
"tsup": "^8.2.3"
},
"engines": {
Expand Down
62 changes: 62 additions & 0 deletions cli/src/actions/code-gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { writeFileSync } from "node:fs";
import path from "node:path";
import type { Context } from "@/context";
import { getScaffoldedFiles, shouldSkipCodeGen } from "@/integrations/code-gen";
import { CodeGenError } from "@/types";
import { spinner } from "@clack/prompts";

/**
* Start the code generation request in the background.
* We save it as a promise so we can await the result later, in `actionCodeGenFinish`.
*
* @param ctx - The context object.
*/
export async function actionCodeGenStart(ctx: Context) {
if (shouldSkipCodeGen(ctx)) {
return;
}

ctx.codeGenPromise = getScaffoldedFiles(ctx);
}

export async function actionCodeGenFinish(ctx: Context) {
if (shouldSkipCodeGen(ctx)) {
return;
}

const s = spinner();
s.start("Generating code from project description...");

try {
const scaffoldedFiles = await ctx.codeGenPromise;

if (scaffoldedFiles) {
const currentPath = ctx.path ?? ".";
if (scaffoldedFiles.indexFile) {
writeFileSync(
path.join(currentPath, "src", "index.ts"),
scaffoldedFiles.indexFile,
);
}
if (scaffoldedFiles.schemaFile) {
writeFileSync(
path.join(currentPath, "src", "db", "schema.ts"),
scaffoldedFiles.schemaFile,
);
}
if (scaffoldedFiles.seedFile) {
writeFileSync(
path.join(currentPath, "seed.ts"),
scaffoldedFiles.seedFile,
);
}
}
s.stop();
return;
} catch (error) {
s.stop();
return new CodeGenError(
error instanceof Error ? error.message : "Unknown error",
);
}
}
14 changes: 1 addition & 13 deletions cli/src/actions/database/d1.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
import { log } from "@clack/prompts";

export function showD1SetupInstructions() {
log.step("Setting up :");
log.step("local development: Wrangler can spin up a D1 database locally");

log.step(`production: Create a Cloudflare account and D1 instance, retrieve the database key and your account id from the dashboard and addionally create an API token with D1 edit rights, and add it to your .prod.vars file.
CLOUDFLARE_D1_TOKEN=""
CLOUDFLARE_ACCOUNT_ID=""
CLOUDFLARE_DATABASE_ID=""
`);
log.step("visit https://developers.cloudflare.com/d1/ for more information");


log.step("Setting up D1: Look in the file D1-explained.md");
}
26 changes: 21 additions & 5 deletions cli/src/actions/database/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { Context } from "@/context";
import { confirm } from "@clack/prompts";
import { showNeonSetupInstructions, runNeonSetup } from "./neon";
import { showSupabaseSetupInstructions } from "./supabase";
import { showD1SetupInstructions } from "./d1";
import { runNeonSetup, showNeonSetupInstructions } from "./neon";
import { showSupabaseSetupInstructions } from "./supabase";

export async function promptDatabase(ctx: Context) {
switch (ctx.template) {
case "base-supa": {
ctx.database = "supabase";
break;
}
case "sample-d1":{
ctx.database ="d1";
case "sample-d1": {
ctx.database = "d1";
break;
}
case "base": {
Expand Down Expand Up @@ -39,7 +39,7 @@ export async function actionDatabase(ctx: Context) {
showSupabaseSetupInstructions();
return;
}
if(ctx.database === "d1"){
if (ctx.database === "d1") {
showD1SetupInstructions();
return;
}
Expand All @@ -57,3 +57,19 @@ export async function actionDatabase(ctx: Context) {

return;
}

export function getDatabasePreamble(ctx: Context) {
switch (ctx.database) {
case "supabase":
return "Once you've added your connection string, set up the database using the following command:";
case "d1":
return "You can now navigate to the project folder and run the following to set up the database:";
case "neon": {
return ctx.flags.includes("setup-neon")
? "You can now navigate to the project folder and run the following to set up the database:"
: "Once you've added your connection string, set up the database using the following command:";
}
default:
return "";
}
}
6 changes: 4 additions & 2 deletions cli/src/actions/database/neon.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import fs from "node:fs";
import type { Context } from "@/context";
import { getNeonAuthToken } from "@/integrations/neon";
import fs from "node:fs";
import { isCancel, log, select, text } from "@clack/prompts";
import { type Api, createApiClient, type Role } from "@neondatabase/api-client";
import { type Api, type Role, createApiClient } from "@neondatabase/api-client";

/*
* Used if the user rejects the Neon setup flow
Expand Down Expand Up @@ -120,6 +120,7 @@ async function createNewProject(neon: Api<unknown>): Promise<string | symbol> {
if (value.length > 64) {
return "Project name must be less than 64 characters.";
}
return undefined;
},
});

Expand Down Expand Up @@ -252,6 +253,7 @@ async function createNewDatabase(
if (value.length > 64) {
return "Database name must be less than 64 characters.";
}
return undefined;
},
});

Expand Down
8 changes: 3 additions & 5 deletions cli/src/actions/database/supabase.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { log } from "@clack/prompts";

export function showSupabaseSetupInstructions() {
log.step("Setting up Supabase:");
log.step(`Create a Supabase account and project, retrieve the connection key from the dashboard, and add it to your .dev.vars file.
DATABASE_URL=postgres://....
`);
log.step(
"Setting up Supabase: Add your project's connection key to the .dev.vars file.",
);
log.step(
"Visit https://supabase.com/docs/guides/database/connecting-to-supabase to create an account and project.",
);
Expand Down
2 changes: 1 addition & 1 deletion cli/src/actions/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path";
import type { Context } from "@/context";
import { runShell } from "@/utils";
import { confirm, log, spinner } from "@clack/prompts";
import path from "node:path";

export async function promptDependencies(ctx: Context) {
try {
Expand Down
24 changes: 24 additions & 0 deletions cli/src/actions/description.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Context } from "@/context";
import { text } from "@clack/prompts";

export async function promptDescription(ctx: Context) {
try {
const placeholder = 'E.g., "A social network for geese"';
const result = await text({
message: "Briefly describe what you want to build.",
placeholder,
defaultValue: "",
});

// NOTE - Do not give a default description
if (typeof result === "string") {
if (result !== "") {
ctx.description = result;
}
}

return result;
} catch (error) {
return error;
}
}
2 changes: 1 addition & 1 deletion cli/src/actions/git.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path";
import type { Context } from "@/context";
import { runShell } from "@/utils";
import { confirm, log, spinner } from "@clack/prompts";
import path from "node:path";

export async function promptGit(ctx: Context) {
try {
Expand Down
8 changes: 6 additions & 2 deletions cli/src/actions/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ export async function promptPath(ctx: Context) {
if (value !== "" && value[0] !== ".") {
return "Please enter a relative path.";
}
return undefined;
},
});

if (typeof result === "string") {
if (result === "") ctx.path = placeholder;
else ctx.path = result;
if (result === "") {
ctx.path = placeholder;
} else {
ctx.path = result;
}
}

return result;
Expand Down
26 changes: 19 additions & 7 deletions cli/src/actions/template.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { join } from "node:path";
import type { Context } from "@/context";
import type { Template } from "@/types";
import { safeReadFile } from "@/utils";
import { log, select, spinner } from "@clack/prompts";
import { downloadTemplate } from "giget";

Expand All @@ -10,24 +12,24 @@ export async function promptTemplate(ctx: Context) {
options: [
{
value: "base",
label: "Base template",
label: "Neon template",
hint: "A barebones HONC project with a Neon database",
},
{
value: "base-supa",
label: "(Supa)base template",
hint: "A barebones HONC project with a Supabase database",
},
{
value: "sample-d1",
label: "D1 base template",
hint: "A barebones HONC project with a D1 Database",
},
{
value: "sample-api",
label: "Sample API template",
hint: "A configured sample API using the HONC stack",
},
{
value: "sample-d1",
label: "D1 base template",
hint: "A barebones HONC project with a D1 Database"
}
],
initialValue: "base",
});
Expand Down Expand Up @@ -65,7 +67,7 @@ export async function actionTemplate(ctx: Context) {
break;
case "sample-d1":
templateUrl = "github:fiberplane/create-honc-app/templates/d1";
break
break;
default:
return new Error(`Invalid template selected: ${ctx.template}`);
}
Expand All @@ -77,6 +79,16 @@ export async function actionTemplate(ctx: Context) {
force: true,
provider: "github",
});

const projectDir = join(ctx.cwd, ctx.path);

const indexFile = safeReadFile(join(projectDir, "src", "index.ts"));
const schemaFile = safeReadFile(join(projectDir, "src", "db", "schema.ts"));
const seedFile = safeReadFile(join(projectDir, "seed.ts"));

ctx.indexFile = indexFile?.toString();
ctx.schemaFile = schemaFile?.toString();
ctx.seedFile = seedFile?.toString();
} catch (error) {
return error;
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ export const HONC_TITLE = `
`;

export const CANCEL_MESSAGE = "create-honc-app cancelled 🪿";
export const CANCEL_MESSAGE = "create-honc-app cancelled! goosebye 🪿";
Loading

0 comments on commit 3325d4e

Please sign in to comment.