Skip to content

Commit 1b3baa2

Browse files
committed
feat: add project initialization command and update README for template usage
1 parent 42b3b19 commit 1b3baa2

4 files changed

Lines changed: 177 additions & 34 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
check:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- uses: actions/setup-node@v4
16+
with:
17+
node-version: 20
18+
cache: npm
19+
20+
- name: Install dependencies
21+
run: npm ci
22+
23+
- name: Type check
24+
run: npm run type-check
25+
26+
- name: Lint
27+
run: npm run lint

README.md

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,44 @@ A production-ready, opinionated Next.js frontend template by Hybrid Interactive.
2929

3030
## Quick Start
3131

32-
### 1. Clone or create from template
32+
### 1. Create your repo from the template
3333

34-
```bash
35-
# Option A: clone directly
36-
git clone <repo-url> my-project
37-
cd my-project
34+
Click **"Use this template"** on GitHub → **"Create a new repository"** → name your repo → click **"Create repository"**.
35+
36+
> First time? Go to the [template repo](https://github.com/hybridinteractive/nextjs-template) and click the green **"Use this template"** button.
37+
38+
### 2. Clone and enter the repo
3839

39-
# Option B: bootstrap from template CLI (from the nextjs-template directory)
40-
node ncube.js create my-project --variant rbac
41-
cd ../my-project
40+
```bash
41+
git clone https://github.com/<your-org>/<your-repo>.git
42+
cd <your-repo>
4243
```
4344

44-
### 2. Install dependencies
45+
### 3. Install dependencies
4546

4647
```bash
4748
npm install
4849
```
4950

50-
### 3. Set up environment
51+
### 4. Run the init command
5152

5253
```bash
53-
cp .env.example .env
54-
# Edit .env — at minimum set NEXT_PUBLIC_API_URL
54+
node ncube.js init # or: node ncube.js init my-app-name
5555
```
5656

57-
### 4. Install shadcn/ui components
57+
This does three things in one step:
58+
- Sets the project name in `package.json`
59+
- Creates `.env` from `.env.example` (with your app name pre-filled)
60+
- Installs all shadcn/ui components into `src/components/ui/`
61+
62+
### 5. Configure your backend URL
5863

5964
```bash
60-
node ncube.js setup
61-
# or manually:
62-
npx shadcn@latest add button input label card badge dialog dropdown-menu sheet table tabs skeleton avatar separator scroll-area form select checkbox switch textarea popover command
65+
# Edit .env — at minimum set:
66+
NEXT_PUBLIC_API_URL="http://localhost:8000"
6367
```
6468

65-
### 5. Start development server
69+
### 6. Start the development server
6670

6771
```bash
6872
npm run dev
@@ -72,6 +76,22 @@ Open [http://localhost:3000](http://localhost:3000).
7276

7377
---
7478

79+
### Legacy: bootstrap from a local clone
80+
81+
If you prefer to work from a local copy of the template instead of GitHub's "Use this template":
82+
83+
```bash
84+
# From inside the nextjs-template directory:
85+
node ncube.js create my-app [--variant base|rbac|full]
86+
cd ../my-app
87+
npm install
88+
npm run dev
89+
```
90+
91+
> `create` is deprecated in favour of the template flow above. It still works but will show a notice.
92+
93+
---
94+
7595
## Project Structure
7696

7797
```
@@ -122,19 +142,15 @@ src/
122142

123143
## Variants
124144

125-
Three variants are available via the CLI:
145+
When you use **"Use this template"** on GitHub, you always get the **full** variant — everything included. No selection needed.
126146

127-
| Variant | Includes | Use when |
128-
|---------|----------|----------|
129-
| `base` | Auth, layout shell, no RBAC | Simple internal tools, single-role apps |
130-
| `rbac` *(default)* | Base + full permissions/RBAC system | Most SaaS apps, multi-role dashboards |
131-
| `full` | RBAC + access-control admin panel | Platforms with custom role management |
147+
| Variant | Includes | How to get it |
148+
|---------|----------|---------------|
149+
| `full` *(default via template)* | Auth + full RBAC + access-control admin panel | Use GitHub "Use this template" |
150+
| `rbac` | Auth + full RBAC, no admin panel | Use template → delete `src/app/(dashboard)/access-control/` |
151+
| `base` | Auth + layout shell, no RBAC | Use template → delete `src/lib/permissions/` and `src/app/(dashboard)/access-control/` |
132152

133-
```bash
134-
node ncube.js create my-app --variant base
135-
node ncube.js create my-app --variant rbac # default
136-
node ncube.js create my-app --variant full
137-
```
153+
> For local bootstrapping (deprecated), the `create` command still supports `--variant base|rbac|full`.
138154
139155
---
140156

@@ -143,6 +159,9 @@ node ncube.js create my-app --variant full
143159
The `ncube.js` CLI mirrors the FastAPI `fcube.py` module generator. It scaffolds complete feature domains following the architecture conventions.
144160

145161
```bash
162+
# Post-clone setup (name, .env, shadcn) — run once after cloning
163+
node ncube.js init [my-app-name]
164+
146165
# Scaffold a new domain
147166
node ncube.js startdomain Product
148167
node ncube.js startdomain LeadManagement
@@ -151,10 +170,10 @@ node ncube.js startdomain InvoiceItem
151170
# List existing domains
152171
node ncube.js listdomains
153172

154-
# Install shadcn/ui components
173+
# Install shadcn/ui components (included in init, but can run standalone)
155174
node ncube.js setup
156175

157-
# Bootstrap a new project
176+
# (Deprecated) Bootstrap locally from the template directory
158177
node ncube.js create my-app [--variant base|rbac|full]
159178
```
160179

ncube.js

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
* Mirrors the FastAPI fcube.py module generator for frontend domains.
66
*
77
* Commands:
8+
* node ncube.js init [name] — post-clone setup (name, .env, shadcn)
89
* node ncube.js startdomain <DomainName> — scaffold a new feature domain
910
* node ncube.js listdomains — list existing domains in lib/
1011
* node ncube.js setup — install shadcn/ui components
1112
* node ncube.js create <name> [--variant base|rbac|full]
12-
* — bootstrap a new project from this template
13+
* — (deprecated) bootstrap from local template
1314
*/
1415

1516
const fs = require("fs");
@@ -692,7 +693,98 @@ function cmdSetup() {
692693
}
693694
}
694695

696+
function cmdInit(rawArgs) {
697+
// Determine app name
698+
// Priority: positional arg → --name flag → current directory name
699+
const nameFlag = rawArgs.indexOf("--name");
700+
let appName;
701+
if (nameFlag !== -1 && rawArgs[nameFlag + 1]) {
702+
appName = toKebabCase(rawArgs[nameFlag + 1]);
703+
} else {
704+
const positional = rawArgs.find((a) => !a.startsWith("--"));
705+
appName = positional
706+
? toKebabCase(positional)
707+
: path.basename(process.cwd());
708+
}
709+
710+
header(`Initializing project: ${appName}`);
711+
712+
// 1. Update package.json name
713+
const pkgPath = path.join(process.cwd(), "package.json");
714+
if (fs.existsSync(pkgPath)) {
715+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
716+
const oldName = pkg.name;
717+
if (oldName !== appName) {
718+
pkg.name = appName;
719+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
720+
ok(`package.json name: ${c.dim}${oldName}${c.reset}${c.green}${appName}${c.reset}`);
721+
} else {
722+
ok(`package.json name: ${appName} (already set)`);
723+
}
724+
}
725+
726+
// 2. Create .env from .env.example if not already present
727+
const envExamplePath = path.join(process.cwd(), ".env.example");
728+
const envPath = path.join(process.cwd(), ".env");
729+
if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
730+
let envContent = fs.readFileSync(envExamplePath, "utf8");
731+
envContent = envContent.replace(
732+
/^NEXT_PUBLIC_APP_NAME=.*$/m,
733+
`NEXT_PUBLIC_APP_NAME="${appName}"`,
734+
);
735+
fs.writeFileSync(envPath, envContent);
736+
ok("Created .env from .env.example");
737+
} else if (fs.existsSync(envPath)) {
738+
info(".env already exists — skipping");
739+
} else {
740+
warn(".env.example not found — skipping .env creation");
741+
}
742+
743+
// 3. Install shadcn components if src/components/ui/ is missing or empty
744+
const hasSrc = fs.existsSync(path.join(process.cwd(), "src"));
745+
const uiDir = hasSrc
746+
? path.join(process.cwd(), "src", "components", "ui")
747+
: path.join(process.cwd(), "components", "ui");
748+
const needsSetup =
749+
!fs.existsSync(uiDir) || fs.readdirSync(uiDir).length === 0;
750+
751+
if (needsSetup) {
752+
cmdSetup();
753+
} else {
754+
ok("shadcn/ui components already installed — skipping setup");
755+
}
756+
757+
console.log("");
758+
header("You're ready!");
759+
dim("Next steps:");
760+
dim(" 1. Edit .env — set NEXT_PUBLIC_API_URL to your backend URL");
761+
dim(" 2. npm run dev");
762+
console.log("");
763+
info("Add feature domains with: node ncube.js startdomain <Name>");
764+
console.log("");
765+
}
766+
695767
function cmdCreate(projectName, variant) {
768+
// ── Deprecation notice ────────────────────────────────────────────────────────
769+
console.log("");
770+
console.log(`${c.yellow}${c.bold}⚠ Deprecation notice${c.reset}`);
771+
console.log(
772+
`${c.dim} 'create' is no longer the recommended way to start a project.${c.reset}`,
773+
);
774+
console.log(
775+
`${c.dim} Use GitHub's "Use this template" button instead:${c.reset}`,
776+
);
777+
console.log(
778+
`${c.dim} 1. Click "Use this template" on GitHub → name your repo${c.reset}`,
779+
);
780+
console.log(
781+
`${c.dim} 2. git clone <your-repo> && cd <your-repo>${c.reset}`,
782+
);
783+
console.log(`${c.dim} 3. npm install && node ncube.js init${c.reset}`);
784+
console.log(`${c.dim} Continuing with local 'create' anyway…${c.reset}`);
785+
console.log("");
786+
// ─────────────────────────────────────────────────────────────────────────────
787+
696788
if (!projectName) {
697789
err("Usage: node ncube.js create <project-name> [--variant base|rbac|full]");
698790
process.exit(1);
@@ -886,23 +978,27 @@ function main() {
886978
${c.bold}${c.blue}NCube CLI${c.reset} — Next.js scaffolding tool
887979
888980
${c.bold}Commands:${c.reset}
981+
${c.cyan}init${c.reset} [name] Post-clone setup: name, .env, shadcn
889982
${c.cyan}startdomain${c.reset} <DomainName> Scaffold a new feature domain
890983
${c.cyan}listdomains${c.reset} List existing domains
891984
${c.cyan}setup${c.reset} Install shadcn/ui components
892-
${c.cyan}create${c.reset} <name> [--variant base|rbac|full] Bootstrap a new project
985+
${c.cyan}create${c.reset} <name> [--variant base|rbac|full] ${c.dim}(deprecated)${c.reset} Bootstrap locally
893986
${c.cyan}bump${c.reset} <patch|minor|major> Bump version + add changelog entry
894987
895988
${c.bold}Examples:${c.reset}
989+
node ncube.js init my-saas
896990
node ncube.js startdomain Product
897991
node ncube.js listdomains
898992
node ncube.js setup
899-
node ncube.js create my-saas --variant rbac
900993
node ncube.js bump minor
901994
`);
902995
return;
903996
}
904997

905998
switch (command) {
999+
case "init":
1000+
cmdInit(rest);
1001+
break;
9061002
case "startdomain":
9071003
cmdStartDomain(rest[0]);
9081004
break;

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"start": "next start",
99
"lint": "next lint",
1010
"type-check": "tsc --noEmit",
11-
"setup": "node ncube.js setup"
11+
"setup": "node ncube.js setup",
12+
"init": "node ncube.js init"
1213
},
1314
"dependencies": {
1415
"@hookform/resolvers": "^3.10.0",

0 commit comments

Comments
 (0)