create-genesis-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 +47 -0
- package/bin/index.js +106 -0
- package/package.json +27 -0
- package/template/.env.local +0 -0
- package/template/AGENTS.md +5 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +9 -0
- package/template/_gitignore +41 -0
- package/template/eslint.config.mjs +18 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +7 -0
- package/template/package.json +30 -0
- package/template/pnpm-workspace.yaml +7 -0
- package/template/postcss.config.mjs +7 -0
- package/template/public/.gitkeep +0 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +10 -0
- package/template/src/app/layout.tsx +21 -0
- package/template/src/app/page.tsx +11 -0
- package/template/src/components/.gitkeep +0 -0
- package/template/src/lib/fonts.ts +17 -0
- package/template/src/lib/tailwind-utils.ts +6 -0
- package/template/tsconfig.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# create-genesis-app
|
|
2
|
+
|
|
3
|
+
Scaffold an opinionated Next.js app into the current folder.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm create genesis-app . # into the current directory
|
|
7
|
+
pnpm create genesis-app my-app # into ./my-app
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Also works with `npm create genesis-app@latest .` and `yarn create genesis-app .`.
|
|
11
|
+
|
|
12
|
+
## What you get
|
|
13
|
+
|
|
14
|
+
The default `create-next-app` stack, kept current:
|
|
15
|
+
|
|
16
|
+
- **Next.js** (App Router) + **React** latest
|
|
17
|
+
- **TypeScript** (strict)
|
|
18
|
+
- **Tailwind CSS v4**
|
|
19
|
+
- **ESLint** (flat config, `next/core-web-vitals` + `next/typescript`)
|
|
20
|
+
|
|
21
|
+
All template dependencies are set to `latest`, so every scaffold pulls the newest
|
|
22
|
+
published versions at install time.
|
|
23
|
+
|
|
24
|
+
## Flags
|
|
25
|
+
|
|
26
|
+
| Flag | Effect |
|
|
27
|
+
| --------------------------------------- | -------------------------------------- |
|
|
28
|
+
| `--no-install` | Copy files but skip dependency install |
|
|
29
|
+
| `--no-git` | Skip `git init` |
|
|
30
|
+
| `--pnpm` / `--npm` / `--yarn` / `--bun` | Force a specific package manager |
|
|
31
|
+
|
|
32
|
+
By default the CLI uses the package manager that invoked it (`pnpm create` → pnpm,
|
|
33
|
+
`npm create` → npm, etc.), and falls back to **pnpm** when it can't tell. Pass one of
|
|
34
|
+
the flags above to override.
|
|
35
|
+
|
|
36
|
+
## Repo layout
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
create-genesis-app/
|
|
40
|
+
├── package.json # the published package (bin + files)
|
|
41
|
+
├── bin/index.js # the scaffolder (Node built-ins only, no deps)
|
|
42
|
+
└── template/ # the project that gets copied
|
|
43
|
+
├── _gitignore # de-dotted: npm strips real dotfiles on publish
|
|
44
|
+
├── src/
|
|
45
|
+
├── public/
|
|
46
|
+
└── package.json # deps set to "latest"
|
|
47
|
+
```
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cp, rename, readFile, writeFile, readdir, rm } from "node:fs/promises";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, join, resolve, basename } from "node:path";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const templateDir = join(__dirname, "..", "template");
|
|
10
|
+
|
|
11
|
+
// ---- parse args -------------------------------------------------------------
|
|
12
|
+
const argv = process.argv.slice(2);
|
|
13
|
+
const flags = new Set(argv.filter((a) => a.startsWith("--")));
|
|
14
|
+
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
15
|
+
|
|
16
|
+
const target = resolve(process.cwd(), positional[0] ?? ".");
|
|
17
|
+
const projectName = basename(target);
|
|
18
|
+
const skipInstall = flags.has("--no-install");
|
|
19
|
+
const skipGit = flags.has("--no-git");
|
|
20
|
+
|
|
21
|
+
// Files that npm strips on publish, stored de-dotted in template/.
|
|
22
|
+
const dotfiles = { _gitignore: ".gitignore", _npmrc: ".npmrc", _env: ".env", "_env.local": ".env.local" };
|
|
23
|
+
|
|
24
|
+
// Pick the package manager: explicit flag > the PM that invoked us > pnpm.
|
|
25
|
+
const KNOWN_PMS = ["pnpm", "npm", "yarn", "bun"];
|
|
26
|
+
const pmFlag = KNOWN_PMS.find((p) => flags.has(`--${p}`));
|
|
27
|
+
const invokedWith = (process.env.npm_config_user_agent ?? "").split("/")[0];
|
|
28
|
+
const pm = pmFlag ?? (KNOWN_PMS.includes(invokedWith) ? invokedWith : "pnpm");
|
|
29
|
+
|
|
30
|
+
// ---- scaffold ---------------------------------------------------------------
|
|
31
|
+
console.log(`\nCreating a Next.js app in ${target}\n`);
|
|
32
|
+
|
|
33
|
+
await cp(templateDir, target, {
|
|
34
|
+
recursive: true,
|
|
35
|
+
filter: (src) =>
|
|
36
|
+
!/[\\/](node_modules|\.git|pnpm-lock\.yaml|package-lock\.json)$/.test(src),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Restore the de-dotted files to their real names.
|
|
40
|
+
for (const [from, to] of Object.entries(dotfiles)) {
|
|
41
|
+
const fromPath = join(target, from);
|
|
42
|
+
if (existsSync(fromPath)) await rename(fromPath, join(target, to));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Drop keep-files: they only exist so npm ships otherwise-empty folders.
|
|
46
|
+
// Removing them leaves the folder present but genuinely empty in the scaffold.
|
|
47
|
+
const allEntries = await readdir(target, { recursive: true });
|
|
48
|
+
for (const entry of allEntries) {
|
|
49
|
+
if ([".gitkeep", ".keep"].includes(basename(entry))) {
|
|
50
|
+
await rm(join(target, entry));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Use the target folder name as the project name.
|
|
55
|
+
const pkgPath = join(target, "package.json");
|
|
56
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
57
|
+
pkg.name = projectName;
|
|
58
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
59
|
+
|
|
60
|
+
// Substitute {{name}} placeholders (e.g. in the README and metadata).
|
|
61
|
+
for (const file of ["README.md", "src/app/layout.tsx"]) {
|
|
62
|
+
const filePath = join(target, file);
|
|
63
|
+
if (!existsSync(filePath)) continue;
|
|
64
|
+
const contents = await readFile(filePath, "utf8");
|
|
65
|
+
await writeFile(filePath, contents.replaceAll("{{name}}", projectName));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---- install ----------------------------------------------------------------
|
|
69
|
+
// Done before git init on purpose: with no repo yet, editors can't transiently
|
|
70
|
+
// flag the thousands of node_modules files written during install.
|
|
71
|
+
if (!skipInstall) {
|
|
72
|
+
console.log(`Installing dependencies with ${pm}...\n`);
|
|
73
|
+
spawnSync(pm, ["install"], { cwd: target, stdio: "inherit" });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---- git --------------------------------------------------------------------
|
|
77
|
+
// Init + initial commit last, so the working tree lands clean (.gitignore is
|
|
78
|
+
// already in place, so node_modules is excluded from the commit).
|
|
79
|
+
if (!skipGit && !existsSync(join(target, ".git"))) {
|
|
80
|
+
const git = (...args) =>
|
|
81
|
+
spawnSync("git", args, { cwd: target, stdio: "ignore" });
|
|
82
|
+
git("init", "-q");
|
|
83
|
+
git("add", "-A");
|
|
84
|
+
const msg = "init";
|
|
85
|
+
const commit = git("commit", "-m", msg, "--quiet");
|
|
86
|
+
// Fall back to a neutral identity if the user has none configured globally.
|
|
87
|
+
if (commit.status !== 0) {
|
|
88
|
+
git(
|
|
89
|
+
"-c",
|
|
90
|
+
"user.name=create-genesis",
|
|
91
|
+
"-c",
|
|
92
|
+
"user.email=create-genesis@users.noreply.github.com",
|
|
93
|
+
"commit",
|
|
94
|
+
"-m",
|
|
95
|
+
msg,
|
|
96
|
+
"--quiet",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---- done -------------------------------------------------------------------
|
|
102
|
+
const rel = target === process.cwd() ? "." : projectName;
|
|
103
|
+
console.log(`\n✓ Done.\n`);
|
|
104
|
+
if (rel !== ".") console.log(` cd ${rel}`);
|
|
105
|
+
if (skipInstall) console.log(` ${pm} install`);
|
|
106
|
+
console.log(` ${pm} run dev\n`);
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-genesis-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a Next.js app (App Router, TypeScript, Tailwind v4) into the current folder.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-genesis-app": "bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"next",
|
|
18
|
+
"nextjs",
|
|
19
|
+
"react",
|
|
20
|
+
"tailwind",
|
|
21
|
+
"typescript",
|
|
22
|
+
"create",
|
|
23
|
+
"template",
|
|
24
|
+
"starter"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- BEGIN:nextjs-agent-rules -->
|
|
2
|
+
# This is NOT the Next.js you know
|
|
3
|
+
|
|
4
|
+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
|
5
|
+
<!-- END:nextjs-agent-rules -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.*
|
|
7
|
+
.yarn/*
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
10
|
+
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
12
|
+
|
|
13
|
+
# testing
|
|
14
|
+
/coverage
|
|
15
|
+
|
|
16
|
+
# next.js
|
|
17
|
+
/.next/
|
|
18
|
+
/out/
|
|
19
|
+
|
|
20
|
+
# production
|
|
21
|
+
/build
|
|
22
|
+
|
|
23
|
+
# misc
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.pem
|
|
26
|
+
|
|
27
|
+
# debug
|
|
28
|
+
npm-debug.log*
|
|
29
|
+
yarn-debug.log*
|
|
30
|
+
yarn-error.log*
|
|
31
|
+
.pnpm-debug.log*
|
|
32
|
+
|
|
33
|
+
# env files (can opt-in for committing if needed)
|
|
34
|
+
.env*
|
|
35
|
+
|
|
36
|
+
# vercel
|
|
37
|
+
.vercel
|
|
38
|
+
|
|
39
|
+
# typescript
|
|
40
|
+
*.tsbuildinfo
|
|
41
|
+
next-env.d.ts
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
// Override default ignores of eslint-config-next.
|
|
9
|
+
globalIgnores([
|
|
10
|
+
// Default ignores of eslint-config-next:
|
|
11
|
+
".next/**",
|
|
12
|
+
"out/**",
|
|
13
|
+
"build/**",
|
|
14
|
+
"next-env.d.ts",
|
|
15
|
+
]),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "genesis-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"next": "latest",
|
|
13
|
+
"react": "latest",
|
|
14
|
+
"react-dom": "latest",
|
|
15
|
+
"clsx": "latest",
|
|
16
|
+
"tailwind-merge": "latest",
|
|
17
|
+
"radix-ui": "latest",
|
|
18
|
+
"class-variance-authority": "latest"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@tailwindcss/postcss": "latest",
|
|
22
|
+
"@types/node": "latest",
|
|
23
|
+
"@types/react": "latest",
|
|
24
|
+
"@types/react-dom": "latest",
|
|
25
|
+
"eslint": "latest",
|
|
26
|
+
"eslint-config-next": "latest",
|
|
27
|
+
"tailwindcss": "latest",
|
|
28
|
+
"typescript": "latest"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import "./globals.css";
|
|
3
|
+
import { cn } from "@/lib/tailwind-utils";
|
|
4
|
+
import fonts from "@/lib/fonts";
|
|
5
|
+
|
|
6
|
+
export const metadata: Metadata = {
|
|
7
|
+
title: "{{name}}",
|
|
8
|
+
description: "{{name}} - created with create-genesis",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function RootLayout({
|
|
12
|
+
children,
|
|
13
|
+
}: Readonly<{
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}>) {
|
|
16
|
+
return (
|
|
17
|
+
<html lang="en" className={cn(fonts, "h-full antialiased")}>
|
|
18
|
+
<body className="min-h-full flex flex-col">{children}</body>
|
|
19
|
+
</html>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Inter, Geist_Mono } from "next/font/google";
|
|
2
|
+
import { cn } from "@/lib/tailwind-utils";
|
|
3
|
+
|
|
4
|
+
const regularSansFont = Inter({
|
|
5
|
+
variable: "--font-regular-sans",
|
|
6
|
+
subsets: ["latin"],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const regularMonoFont = Geist_Mono({
|
|
10
|
+
variable: "--font-regular-mono",
|
|
11
|
+
subsets: ["latin"],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default cn(
|
|
15
|
+
regularSansFont.variable,
|
|
16
|
+
regularMonoFont.variable,
|
|
17
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [
|
|
17
|
+
{
|
|
18
|
+
"name": "next"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"paths": {
|
|
22
|
+
"@/*": ["./src/*"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"include": [
|
|
26
|
+
"next-env.d.ts",
|
|
27
|
+
"**/*.ts",
|
|
28
|
+
"**/*.tsx",
|
|
29
|
+
".next/types/**/*.ts",
|
|
30
|
+
".next/dev/types/**/*.ts",
|
|
31
|
+
"**/*.mts"
|
|
32
|
+
],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|