create-oxom-app 0.1.0

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/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # create-oxom-app
2
+
3
+ Scaffold a new [plattform](https://github.com/oxom-de/plattform) workspace in seconds.
4
+
5
+ ```bash
6
+ npx create-oxom-app my-workspace
7
+ ```
8
+
9
+ Or interactively:
10
+
11
+ ```bash
12
+ npx create-oxom-app
13
+ ```
14
+
15
+ ## What it does
16
+
17
+ 1. Clones the latest `plattform` template from GitHub
18
+ 2. Asks for a project name and brand name
19
+ 3. Writes a `.env.local` with your brand name pre-filled
20
+ 4. Runs `pnpm install` (falls back to `bun` or `npm`)
21
+ 5. Prints the next steps
22
+
23
+ ## Requirements
24
+
25
+ - Node.js 20+
26
+ - git
27
+ - pnpm (recommended), bun, or npm
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from "node:child_process";
4
+ import { createInterface } from "node:readline";
5
+ import { existsSync, rmSync, readFileSync, writeFileSync, copyFileSync } from "node:fs";
6
+ import { resolve, join } from "node:path";
7
+
8
+ // ── Config ─────────────────────────────────────────────────────────────────
9
+
10
+ const REPO_URL = "https://github.com/oxom-de/plattform.git";
11
+ const REPO_DISPLAY = "github.com/oxom-de/plattform";
12
+
13
+ // ── Helpers ────────────────────────────────────────────────────────────────
14
+
15
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
16
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
17
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
18
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
19
+
20
+ function prompt(rl, question) {
21
+ return new Promise((resolve) => rl.question(question, resolve));
22
+ }
23
+
24
+ function run(cmd, cwd) {
25
+ execSync(cmd, { cwd, stdio: "inherit" });
26
+ }
27
+
28
+ function hasBin(bin) {
29
+ try {
30
+ execSync(`which ${bin}`, { stdio: "ignore" });
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ // ── Main ───────────────────────────────────────────────────────────────────
38
+
39
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
40
+
41
+ console.log(`\n${bold("create-oxom-app")} ${dim(`→ ${REPO_DISPLAY}`)}\n`);
42
+
43
+ // 1. Project name
44
+ let projectName = process.argv[2]?.trim();
45
+ if (!projectName) {
46
+ projectName = (await prompt(rl, `Project name ${dim("(my-workspace)")} `)).trim() || "my-workspace";
47
+ }
48
+
49
+ const projectDir = resolve(process.cwd(), projectName);
50
+ if (existsSync(projectDir)) {
51
+ console.error(`\nDirectory "${projectName}" already exists. Choose a different name.\n`);
52
+ rl.close();
53
+ process.exit(1);
54
+ }
55
+
56
+ // 2. Brand name
57
+ const brandName = (await prompt(rl, `Brand name ${dim(`(${projectName})`)} `)).trim() || projectName;
58
+
59
+ rl.close();
60
+
61
+ // 3. Clone
62
+ console.log(`\n${cyan("→")} Cloning template from ${REPO_DISPLAY}…`);
63
+ try {
64
+ run(`git clone --depth=1 ${REPO_URL} "${projectName}"`, process.cwd());
65
+ } catch {
66
+ console.error("\nFailed to clone repository. Make sure git is installed and you have internet access.\n");
67
+ process.exit(1);
68
+ }
69
+
70
+ // Remove git history so the user starts fresh
71
+ rmSync(join(projectDir, ".git"), { recursive: true, force: true });
72
+
73
+ // 4. Patch package.json name
74
+ const pkgPath = join(projectDir, "package.json");
75
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
76
+ pkg.name = projectName;
77
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t") + "\n");
78
+
79
+ // 5. Write .env.local from example
80
+ const envExamplePath = join(projectDir, ".env.local.example");
81
+ const envLocalPath = join(projectDir, ".env.local");
82
+
83
+ if (existsSync(envExamplePath)) {
84
+ let env = readFileSync(envExamplePath, "utf8");
85
+ // Patch brand name
86
+ env = env.replace(
87
+ /^NEXT_PUBLIC_BRAND_NAME=.*/m,
88
+ `NEXT_PUBLIC_BRAND_NAME=${brandName}`,
89
+ );
90
+ writeFileSync(envLocalPath, env);
91
+ } else {
92
+ writeFileSync(
93
+ envLocalPath,
94
+ `NEXT_PUBLIC_BRAND_NAME=${brandName}\nNEXT_PUBLIC_APP_URL=http://localhost:3000\n`,
95
+ );
96
+ }
97
+
98
+ // 6. Install dependencies
99
+ const pm = hasBin("pnpm") ? "pnpm" : hasBin("bun") ? "bun" : "npm";
100
+ console.log(`\n${cyan("→")} Installing dependencies with ${pm}…\n`);
101
+ try {
102
+ run(`${pm} install`, projectDir);
103
+ } catch {
104
+ console.warn("\nDependency install failed — run it manually inside the project directory.\n");
105
+ }
106
+
107
+ // 7. Done
108
+ console.log(`
109
+ ${green("✓")} ${bold(projectName)} is ready.
110
+
111
+ ${bold("Next steps:")}
112
+
113
+ ${cyan(`cd ${projectName}`)}
114
+ ${dim("# Fill in your credentials:")}
115
+ ${cyan("open .env.local")}
116
+ ${dim("# Apply database migrations to your Supabase project:")}
117
+ ${dim("# → supabase/migrations/")}
118
+ ${cyan(`${pm} dev`)}
119
+
120
+ ${bold("Services you need:")}
121
+
122
+ Clerk https://clerk.com
123
+ Supabase https://supabase.com
124
+ Liveblocks https://liveblocks.io
125
+ Dub https://dub.co ${dim("(optional — link shortening)")}
126
+ R2 / S3 ${dim("(optional — configure per workspace in Settings → Drive)")}
127
+
128
+ ${dim(`Docs: ${REPO_DISPLAY}#readme`)}
129
+ `);
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "create-oxom-app",
3
+ "version": "0.1.0",
4
+ "description": "Create a new plattform workspace app",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-oxom-app": "./bin/create-oxom-app.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "keywords": [
14
+ "create-app",
15
+ "plattform",
16
+ "workspace",
17
+ "nextjs",
18
+ "clerk",
19
+ "supabase"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/oxom-de/create-oxom-app"
24
+ }
25
+ }