create-rotor 0.2.2 → 0.3.1

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))
@@ -1294,7 +1301,7 @@ function replaceProjectName(projectDir, projectName) {
1294
1301
  writeFileSync(filePath, content.replaceAll("{{PROJECT_NAME}}", projectName));
1295
1302
  }
1296
1303
  }
1297
- function trimCssShadcn(cssPath) {
1304
+ function trimCssShadcn(cssPath, removeContent = true) {
1298
1305
  if (!existsSync(cssPath))
1299
1306
  return;
1300
1307
  const content = readFileSync(cssPath, "utf-8");
@@ -1311,7 +1318,7 @@ function trimCssShadcn(cssPath) {
1311
1318
  skipping = false;
1312
1319
  continue;
1313
1320
  }
1314
- if (!skipping) {
1321
+ if (!skipping || !removeContent) {
1315
1322
  result.push(line);
1316
1323
  }
1317
1324
  }
@@ -1422,34 +1429,51 @@ 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);
1428
- if (!selectedModules.includes("shadcn")) {
1429
- trimCssShadcn(join2(targetDir, "app", "globals.css"));
1432
+ trimPackageJson(join2(targetDir, "package.json"), selectedModules, {
1433
+ removeHusky: !initGit
1434
+ });
1435
+ const hasEnv = trimEnvFile(join2(targetDir, ".env.example"), selectedModules);
1436
+ trimCssShadcn(join2(targetDir, "app", "globals.css"), !selectedModules.includes("shadcn"));
1437
+ if (!initGit) {
1438
+ removeHuskyFiles(targetDir);
1430
1439
  }
1431
1440
  s.stop("Project created!");
1441
+ let installed = false;
1432
1442
  if (installDeps) {
1433
1443
  const installSpinner = be();
1434
1444
  installSpinner.start("Installing dependencies...");
1445
+ let hasBun = false;
1435
1446
  try {
1436
- execSync("bun install", { cwd: targetDir, stdio: "ignore" });
1437
- installSpinner.stop("Dependencies installed!");
1447
+ execSync("bun --version", { stdio: "ignore" });
1448
+ hasBun = true;
1438
1449
  } catch {
1439
- installSpinner.stop('Failed to install dependencies. Run "bun install" manually.');
1450
+ installSpinner.stop('Bun not found. Install it from https://bun.sh then run "bun install".');
1451
+ }
1452
+ if (hasBun) {
1453
+ try {
1454
+ execSync("bun install", { cwd: targetDir, stdio: "ignore" });
1455
+ installSpinner.stop("Dependencies installed!");
1456
+ installed = true;
1457
+ } catch {
1458
+ installSpinner.stop('Failed to install dependencies. Run "bun install" manually.');
1459
+ }
1440
1460
  }
1441
1461
  }
1442
1462
  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" });
1463
+ try {
1464
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
1465
+ execSync("git add -A", { cwd: targetDir, stdio: "ignore" });
1466
+ execSync('git commit -m "chore: init from create-rotor"', {
1467
+ cwd: targetDir,
1468
+ stdio: "ignore"
1469
+ });
1470
+ } catch {}
1446
1471
  }
1447
1472
  const steps = [`cd ${projectName}`];
1448
- if (!installDeps) {
1473
+ if (!installed) {
1449
1474
  steps.push("bun install");
1450
1475
  }
1451
- const envPath = join2(targetDir, ".env.example");
1452
- if (readFileSync2(envPath, "utf-8").trim().length > 0) {
1476
+ if (hasEnv) {
1453
1477
  steps.push("cp .env.example .env # configure environment variables");
1454
1478
  }
1455
1479
  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.1",
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,28 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+
5
+ export default function ErrorPage({
6
+ error,
7
+ reset,
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ useEffect(() => {
13
+ console.error(error);
14
+ }, [error]);
15
+
16
+ return (
17
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
18
+ <h1 className="font-bold text-4xl">Something went wrong</h1>
19
+ <button
20
+ className="mt-4 rounded-md bg-neutral-900 px-4 py-2 text-sm text-white hover:bg-neutral-800"
21
+ onClick={reset}
22
+ type="button"
23
+ >
24
+ Try again
25
+ </button>
26
+ </main>
27
+ );
28
+ }
@@ -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>
@@ -19,9 +19,9 @@ export const metadata: Metadata = {
19
19
 
20
20
  export default function RootLayout({
21
21
  children,
22
- }: Readonly<{
22
+ }: {
23
23
  children: React.ReactNode;
24
- }>) {
24
+ }) {
25
25
  return (
26
26
  <html lang="en">
27
27
  <body
@@ -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
+ }
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev --turbopack",
6
+ "dev": "next dev",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "check": "bunx @biomejs/biome check --write .",
File without changes
File without changes