create-rotor 0.2.2 → 0.3.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/dist/index.js CHANGED
@@ -1171,7 +1171,7 @@ var MODULES = {
1171
1171
  name: "swr",
1172
1172
  label: "SWR",
1173
1173
  hint: "Data fetching",
1174
- files: [],
1174
+ files: ["lib/fetcher.ts"],
1175
1175
  dependencies: {
1176
1176
  swr: "2.4.1"
1177
1177
  },
@@ -1210,7 +1210,7 @@ var MODULES = {
1210
1210
  // src/helpers.ts
1211
1211
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1212
1212
  import { join } from "node:path";
1213
- function trimDependencies(pkgPath, selectedModules) {
1213
+ function trimPackageJson(pkgPath, selectedModules, options) {
1214
1214
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1215
1215
  const unselected = Object.keys(MODULES).filter((m) => !selectedModules.includes(m));
1216
1216
  for (const moduleName of unselected) {
@@ -1222,11 +1222,6 @@ function trimDependencies(pkgPath, selectedModules) {
1222
1222
  delete pkg.devDependencies?.[dep];
1223
1223
  }
1224
1224
  }
1225
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
1226
- `);
1227
- }
1228
- function trimScripts(pkgPath, selectedModules) {
1229
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1230
1225
  if (!selectedModules.includes("drizzle")) {
1231
1226
  for (const key of Object.keys(pkg.scripts || {})) {
1232
1227
  if (key.startsWith("db:")) {
@@ -1234,12 +1229,17 @@ function trimScripts(pkgPath, selectedModules) {
1234
1229
  }
1235
1230
  }
1236
1231
  }
1232
+ if (options?.removeHusky) {
1233
+ delete pkg.devDependencies?.husky;
1234
+ delete pkg.devDependencies?.["lint-staged"];
1235
+ delete pkg.scripts?.prepare;
1236
+ }
1237
1237
  writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
1238
1238
  `);
1239
1239
  }
1240
1240
  function trimEnvFile(envPath, selectedModules) {
1241
1241
  if (!existsSync(envPath))
1242
- return;
1242
+ return false;
1243
1243
  const content = readFileSync(envPath, "utf-8");
1244
1244
  const lines = content.split(`
1245
1245
  `);
@@ -1271,21 +1271,28 @@ function trimEnvFile(envPath, selectedModules) {
1271
1271
  `).trim();
1272
1272
  writeFileSync(envPath, cleaned ? `${cleaned}
1273
1273
  ` : "");
1274
+ return cleaned.length > 0;
1275
+ }
1276
+ function removeHuskyFiles(projectDir) {
1277
+ rmSync(join(projectDir, ".husky"), { recursive: true, force: true });
1278
+ rmSync(join(projectDir, ".lintstagedrc"), { force: true });
1274
1279
  }
1275
1280
  function removeModuleFiles(projectDir, selectedModules) {
1276
1281
  const unselected = Object.keys(MODULES).filter((m) => !selectedModules.includes(m));
1277
1282
  for (const moduleName of unselected) {
1278
1283
  const mod = MODULES[moduleName];
1279
1284
  for (const file of mod.files) {
1280
- const filePath = join(projectDir, file);
1281
- if (existsSync(filePath)) {
1282
- rmSync(filePath, { recursive: true, force: true });
1283
- }
1285
+ rmSync(join(projectDir, file), { recursive: true, force: true });
1284
1286
  }
1285
1287
  }
1286
1288
  }
1287
1289
  function replaceProjectName(projectDir, projectName) {
1288
- const filesToReplace = ["package.json", "app/layout.tsx", "README.md"];
1290
+ const filesToReplace = [
1291
+ "package.json",
1292
+ "app/layout.tsx",
1293
+ "README.md",
1294
+ "CLAUDE.md"
1295
+ ];
1289
1296
  for (const file of filesToReplace) {
1290
1297
  const filePath = join(projectDir, file);
1291
1298
  if (!existsSync(filePath))
@@ -1422,34 +1429,53 @@ async function main() {
1422
1429
  renameSync(join2(targetDir, "gitignore"), join2(targetDir, ".gitignore"));
1423
1430
  replaceProjectName(targetDir, projectName);
1424
1431
  removeModuleFiles(targetDir, selectedModules);
1425
- trimDependencies(join2(targetDir, "package.json"), selectedModules);
1426
- trimScripts(join2(targetDir, "package.json"), selectedModules);
1427
- trimEnvFile(join2(targetDir, ".env.example"), selectedModules);
1432
+ trimPackageJson(join2(targetDir, "package.json"), selectedModules, {
1433
+ removeHusky: !initGit
1434
+ });
1435
+ const hasEnv = trimEnvFile(join2(targetDir, ".env.example"), selectedModules);
1428
1436
  if (!selectedModules.includes("shadcn")) {
1429
1437
  trimCssShadcn(join2(targetDir, "app", "globals.css"));
1430
1438
  }
1439
+ if (!initGit) {
1440
+ removeHuskyFiles(targetDir);
1441
+ }
1431
1442
  s.stop("Project created!");
1443
+ let installed = false;
1432
1444
  if (installDeps) {
1433
1445
  const installSpinner = be();
1434
1446
  installSpinner.start("Installing dependencies...");
1447
+ let hasBun = false;
1435
1448
  try {
1436
- execSync("bun install", { cwd: targetDir, stdio: "ignore" });
1437
- installSpinner.stop("Dependencies installed!");
1449
+ execSync("bun --version", { stdio: "ignore" });
1450
+ hasBun = true;
1438
1451
  } catch {
1439
- installSpinner.stop('Failed to install dependencies. Run "bun install" manually.');
1452
+ installSpinner.stop('Bun not found. Install it from https://bun.sh then run "bun install".');
1453
+ }
1454
+ if (hasBun) {
1455
+ try {
1456
+ execSync("bun install", { cwd: targetDir, stdio: "ignore" });
1457
+ installSpinner.stop("Dependencies installed!");
1458
+ installed = true;
1459
+ } catch {
1460
+ installSpinner.stop('Failed to install dependencies. Run "bun install" manually.');
1461
+ }
1440
1462
  }
1441
1463
  }
1442
1464
  if (initGit) {
1443
- execSync("git init", { cwd: targetDir, stdio: "ignore" });
1444
- execSync("git add -A", { cwd: targetDir, stdio: "ignore" });
1445
- execSync('git commit -m "init"', { cwd: targetDir, stdio: "ignore" });
1465
+ try {
1466
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
1467
+ execSync("git add -A", { cwd: targetDir, stdio: "ignore" });
1468
+ execSync('git commit -m "chore: init from create-rotor"', {
1469
+ cwd: targetDir,
1470
+ stdio: "ignore"
1471
+ });
1472
+ } catch {}
1446
1473
  }
1447
1474
  const steps = [`cd ${projectName}`];
1448
- if (!installDeps) {
1475
+ if (!installed) {
1449
1476
  steps.push("bun install");
1450
1477
  }
1451
- const envPath = join2(targetDir, ".env.example");
1452
- if (readFileSync2(envPath, "utf-8").trim().length > 0) {
1478
+ if (hasEnv) {
1453
1479
  steps.push("cp .env.example .env # configure environment variables");
1454
1480
  }
1455
1481
  steps.push("bun dev");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-rotor",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Scaffold Next.js projects with Bun, Tailwind, Biome, and more",
5
5
  "type": "module",
6
6
  "bin": "dist/index.js",
@@ -0,0 +1,24 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ ## Commands
4
+
5
+ ```bash
6
+ bun dev # Start dev server with Turbopack
7
+ bun build # Production build
8
+ bun start # Start production server
9
+ bun run check # Lint and format with Biome
10
+ ```
11
+
12
+ ## Architecture
13
+
14
+ Next.js App Router project.
15
+
16
+ - **Styling**: Tailwind CSS v4 — CSS-first config in `app/globals.css`, no `tailwind.config`
17
+ - **Code quality**: Biome (lint + format)
18
+ - **Path alias**: `@/*` maps to project root
19
+
20
+ ## Key Directories
21
+
22
+ - `app/` — Routes, layouts, pages, API routes
23
+ - `lib/` — Utilities, database client, shared logic
24
+ - `components/` — React components
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ export default function ErrorPage({
4
+ reset,
5
+ }: {
6
+ error: Error & { digest?: string };
7
+ reset: () => void;
8
+ }) {
9
+ return (
10
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
11
+ <h1 className="font-bold text-4xl">Something went wrong</h1>
12
+ <button
13
+ className="mt-4 rounded-md bg-neutral-900 px-4 py-2 text-sm text-white hover:bg-neutral-800"
14
+ onClick={reset}
15
+ type="button"
16
+ >
17
+ Try again
18
+ </button>
19
+ </main>
20
+ );
21
+ }
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="8" fill="black"/>
3
+ </svg>
@@ -0,0 +1,8 @@
1
+ export default function NotFound() {
2
+ return (
3
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
4
+ <h1 className="font-bold text-4xl">404</h1>
5
+ <p className="mt-2 text-gray-500">Page not found</p>
6
+ </main>
7
+ );
8
+ }
@@ -23,6 +23,12 @@ yarn-error.log*
23
23
  .env
24
24
  .env*.local
25
25
 
26
+ # vercel
27
+ .vercel
28
+
29
+ # bun
30
+ .bun
31
+
26
32
  # typescript
27
33
  *.tsbuildinfo
28
34
  next-env.d.ts
@@ -0,0 +1,9 @@
1
+ export async function fetcher<T>(url: string): Promise<T> {
2
+ const res = await fetch(url);
3
+
4
+ if (!res.ok) {
5
+ throw new Error(`Fetch error: ${res.status}`);
6
+ }
7
+
8
+ return res.json() as Promise<T>;
9
+ }