easy-starter 1.1.0 → 1.2.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 +5 -0
- package/package.json +5 -2
- package/template/.cursorrules +92 -0
- package/template/README.md +6 -1
- package/template/env +6 -4
- package/template/scripts/deploy.ts +26 -13
- package/template/src/pages/admin/Admin.tsx +1 -0
- package/template/tsconfig.json +1 -1
- package/template/{types.d.ts → types.ts} +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ The generated boilerplate comes pre-configured with a stack designed for develop
|
|
|
33
33
|
* **Database:** [`easy-db-node`](https://www.npmjs.com/package/easy-db-node) - A simple, local JSON-based database perfect for small to medium projects where setting up Postgres/MongoDB is overkill.
|
|
34
34
|
* **Routing:** [`easy-page-router`](https://www.npmjs.com/package/easy-page-router) - A lightweight, programmatic routing solution.
|
|
35
35
|
* **Analytics:** [`easy-analytics`](https://www.npmjs.com/package/easy-analytics) - Privacy-friendly, local analytics tracking.
|
|
36
|
+
* **AI-Friendly (.cursorrules):** Pre-configured, LLM-optimized guidelines in the project root so AI coding assistants (Cursor, Windsurf, Copilot, etc.) immediately understand the codebase conventions, API restrictions, routing patterns, and database operations.
|
|
36
37
|
* **Deployment:** Integrated Github Actions workflow and PM2 deployment script for seamless, automated deployment straight to your own VPS.
|
|
37
38
|
|
|
38
39
|
## Core Concepts & Examples
|
|
@@ -96,6 +97,10 @@ export default function Index() {
|
|
|
96
97
|
}
|
|
97
98
|
```
|
|
98
99
|
|
|
100
|
+
### 3. AI Integration (`.cursorrules`)
|
|
101
|
+
|
|
102
|
+
This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`.
|
|
103
|
+
|
|
99
104
|
## Getting Started
|
|
100
105
|
|
|
101
106
|
Bootstrapping a new project is as simple as running one command:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easy-starter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A lightweight CLI tool to bootstrap modern fullstack React web projects.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"easy-starter": "./bin/index.js"
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"typescript",
|
|
16
16
|
"fullstack",
|
|
17
17
|
"easy-db",
|
|
18
|
-
"parcel"
|
|
18
|
+
"parcel",
|
|
19
|
+
"ai",
|
|
20
|
+
"cursorrules",
|
|
21
|
+
"ai-friendly"
|
|
19
22
|
],
|
|
20
23
|
"author": "Filip Paulů <ing.fenix@seznam.cz>",
|
|
21
24
|
"license": "MIT",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# AI Coding Assistant Guidelines & Project Rules
|
|
2
|
+
|
|
3
|
+
You are an expert AI software engineer pair-programming in a fullstack React application bootstrapped with `easy-starter`.
|
|
4
|
+
Follow these strict rules, project structures, and conventions at all times.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. API Architecture & Type Safety (`typed-client-server-api`)
|
|
9
|
+
|
|
10
|
+
All React-to-Express communication must use the RPC contract defined in `types.ts` via the `API` type (`APIDefinition`).
|
|
11
|
+
|
|
12
|
+
### Naming Conventions for Endpoints
|
|
13
|
+
EVERY endpoint name in `API` **MUST** start with exactly one of the following prefixes:
|
|
14
|
+
* `add...` (Create/Insert operations. Example: `addItem`, `addRecord`)
|
|
15
|
+
* `get...` (Read/Query operations. Example: `getItems`, `getItem`, `getAnalytics`)
|
|
16
|
+
* `update...` (Update/Edit operations. Example: `updateItem`, `updateUser`)
|
|
17
|
+
* `remove...` (Delete/Remove operations. Example: `removeItem`, `removePost`)
|
|
18
|
+
|
|
19
|
+
**CRITICAL**: Do NOT use other prefixes (e.g., `create`, `delete`, `post`, `fetch`, `save`).
|
|
20
|
+
|
|
21
|
+
### Frontend API Invocation Methods
|
|
22
|
+
You have exactly three (3) methods to trigger API endpoints on the frontend. Use them strictly according to these boundaries:
|
|
23
|
+
|
|
24
|
+
| Method | Import | Use Case | Pattern / Example |
|
|
25
|
+
| :--- | :--- | :--- | :--- |
|
|
26
|
+
| **`useSSRApi`** | `import { useSSRApi } from "../services/api";` | **ONLY** inside React components during initial SSR render. Enables server-side rendering and hydration without flash-of-loading or spinners. | `const [items, error, isLoading, reload] = useSSRApi.getItems({});` |
|
|
27
|
+
| **`useApi`** | `import { useApi } from "../services/api";` | **ONLY** inside React components for client-side-only reactive data fetching (e.g., late loaded components). | `const [items, error, isLoading, reload] = useApi.getItems({});` |
|
|
28
|
+
| **`api`** | `import { api } from "../services/api";` | **ONLY** inside event handlers (e.g., `onClick`, `onSubmit`), utility/non-React functions, or async triggers. Returns a Promise resolving to `[data, error]`. | `const [newId, err] = await api.addItem({ name: "Foo" });` |
|
|
29
|
+
|
|
30
|
+
**NEVER** use standard `fetch`, `axios`, or any other fetcher libraries for internal API calls.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Routing & Navigation (`easy-page-router`)
|
|
35
|
+
|
|
36
|
+
This project uses `easy-page-router`, a lightweight, universal routing library.
|
|
37
|
+
|
|
38
|
+
### Route Configurations
|
|
39
|
+
* Router paths are defined in `src/Router.tsx` within the `renderPage({ path })` callback of the `Router` component.
|
|
40
|
+
* The `path` parameter is a string array (`string[]`) of URL segments:
|
|
41
|
+
* `/` -> `path.length === 0`
|
|
42
|
+
* `/admin` -> `path[0] === "admin"`
|
|
43
|
+
* `/users/123` -> `path[0] === "users" && path[1]` (where `path[1]` is the user ID segment).
|
|
44
|
+
|
|
45
|
+
### Navigation
|
|
46
|
+
1. **Declarative Navigation**: Use `<Link href="/path">Text</Link>` imported from `"easy-page-router/react"`.
|
|
47
|
+
2. **Programmatic Navigation**: Destructure `push` from the `useRouter()` hook imported from `"easy-page-router/react"`.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { Link, useRouter } from "easy-page-router/react";
|
|
51
|
+
|
|
52
|
+
// Declarative:
|
|
53
|
+
<Link href="/admin">Go to Admin</Link>
|
|
54
|
+
|
|
55
|
+
// Programmatic:
|
|
56
|
+
const { push } = useRouter();
|
|
57
|
+
const onSubmit = async () => {
|
|
58
|
+
await saveSomething();
|
|
59
|
+
push("/dashboard");
|
|
60
|
+
};
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Database Operations (`easy-db-node`)
|
|
66
|
+
|
|
67
|
+
The local database stores records as JSON files.
|
|
68
|
+
|
|
69
|
+
### Schema Definition
|
|
70
|
+
* The database collections/tables must be structured and typed in `types.ts` inside the exported `Database` type.
|
|
71
|
+
```typescript
|
|
72
|
+
export type Database = {
|
|
73
|
+
item: Item;
|
|
74
|
+
user: User;
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Writing to Database
|
|
79
|
+
* **Auto-generated ID (Standard Insertion)**:
|
|
80
|
+
* Use `insert(table, data)`.
|
|
81
|
+
* **Rule**: Do **NOT** pass `_id` in the input data. `easy-db-node` ignores any `_id` property passed to `insert()` and automatically generates and returns the unique string ID.
|
|
82
|
+
```typescript
|
|
83
|
+
// CORRECT:
|
|
84
|
+
const newId = await insert("item", { name: "Hello", value: 10 });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
* **Custom / Specific ID (Explicit Key)**:
|
|
88
|
+
* To insert or overwrite a record with a **specific/predefined ID**, you **MUST** use `update(table, id, data)` instead of `insert`.
|
|
89
|
+
```typescript
|
|
90
|
+
// CORRECT (custom ID):
|
|
91
|
+
await update("item", "explicit-custom-id", { name: "Custom ID Item", value: 99 });
|
|
92
|
+
```
|
package/template/README.md
CHANGED
|
@@ -83,6 +83,10 @@ export default function Index() {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### 3. AI Integration (`.cursorrules`)
|
|
87
|
+
|
|
88
|
+
This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`:
|
|
89
|
+
|
|
86
90
|
## Scripts
|
|
87
91
|
|
|
88
92
|
* `npm run dev` - Starts the development server.
|
|
@@ -94,7 +98,8 @@ export default function Index() {
|
|
|
94
98
|
|
|
95
99
|
* `/src` - Your React frontend code.
|
|
96
100
|
* `/server` - Your Express backend code.
|
|
97
|
-
* `/types.
|
|
101
|
+
* `/types.ts` - Shared TypeScript definitions for your API and Database.
|
|
102
|
+
* `/.cursorrules` - Rules instructing AI coding assistants how to interact with the project stack.
|
|
98
103
|
* `/public` - Static assets (images, fonts, etc.).
|
|
99
104
|
* `/easy-db` - Where your local database JSON files are stored.
|
|
100
105
|
|
package/template/env
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
NODE_ENV=development
|
|
1
|
+
NODE_ENV=development # development/production
|
|
2
2
|
SERVER_PORT=1111
|
|
3
3
|
ADMIN_PASSWORD=admin
|
|
4
4
|
|
|
5
5
|
# deploy
|
|
6
|
-
SSH_HOST
|
|
7
|
-
SSH_USERNAME
|
|
8
|
-
|
|
6
|
+
SSH_HOST=[IP_ADDRESS]
|
|
7
|
+
SSH_USERNAME=root
|
|
8
|
+
SSH_PASSWORD=[PASSWORD]
|
|
9
|
+
SERVER_URL=https://your-production-url.com/
|
|
10
|
+
PM2_APP_NAME=easy-starter-app
|
|
@@ -9,16 +9,21 @@ const username = process.env.SSH_USERNAME;
|
|
|
9
9
|
const password = process.env.SSH_PASSWORD;
|
|
10
10
|
|
|
11
11
|
// Configuration for your target server
|
|
12
|
-
const server =
|
|
13
|
-
const pm2AppName =
|
|
14
|
-
const sshPath = resolve("/", "root", "
|
|
12
|
+
const server = process.env.SERVER_URL;
|
|
13
|
+
const pm2AppName = process.env.PM2_APP_NAME;
|
|
14
|
+
const sshPath = resolve("/", "root", pm2AppName || ""); // Path on the remote VPS
|
|
15
15
|
|
|
16
16
|
const path = resolve(process.cwd()); // Local project path
|
|
17
17
|
const ignoreFiles = [
|
|
18
18
|
".git",
|
|
19
|
+
".github",
|
|
20
|
+
".parcel-cache",
|
|
19
21
|
"node_modules",
|
|
20
22
|
"scripts",
|
|
21
|
-
"
|
|
23
|
+
"easy-db",
|
|
24
|
+
"easy-db-files",
|
|
25
|
+
"easy-db-backup",
|
|
26
|
+
".DS_Store",
|
|
22
27
|
];
|
|
23
28
|
|
|
24
29
|
// Ensure credentials are provided
|
|
@@ -73,20 +78,28 @@ async function deploy() {
|
|
|
73
78
|
console.log(`✔ Build project.\n`);
|
|
74
79
|
|
|
75
80
|
// PM2
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
if (pm2AppName) {
|
|
82
|
+
try {
|
|
83
|
+
await sshExec(ssh, `pm2 reload ${pm2AppName}`);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.log(`✘ ${pm2AppName} is not running in PM2!`);
|
|
86
|
+
await sshExec(ssh, `pm2 start npm --name "${pm2AppName}" -- start`, `${pm2AppName} could not be registered in PM2!`);
|
|
87
|
+
}
|
|
88
|
+
await sshExec(ssh, `pm2 list`);
|
|
89
|
+
} else {
|
|
90
|
+
console.log(`i PM2 step is skipped because PM2_APP_NAME is not defined.\n`);
|
|
81
91
|
}
|
|
82
|
-
await sshExec(ssh, `pm2 list`);
|
|
83
92
|
|
|
84
93
|
ssh.dispose();
|
|
85
94
|
console.log(`✔ SSH connection is closed.\n`);
|
|
86
95
|
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
if (server) {
|
|
97
|
+
if (!await serverCheck(server))
|
|
98
|
+
throw new Error(`Server ${server} is not available!`);
|
|
99
|
+
console.log(`✔ Server ${server} is running.\n`);
|
|
100
|
+
} else {
|
|
101
|
+
console.log(`i Server check is skipped because SERVER_URL is not defined.\n`);
|
|
102
|
+
}
|
|
90
103
|
|
|
91
104
|
console.log(`i Server was ${(new Date().getTime() - timeServerOff) / 1000}s off!\n`);
|
|
92
105
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { useGroup, GroupTable, getGroupedCount } from "easy-analytics/react";
|
|
3
3
|
import { Link } from "easy-page-router/react";
|
|
4
|
+
|
|
4
5
|
import { useHead } from "../../services/common";
|
|
5
6
|
import { api, handleError } from "../../services/api";
|
|
6
7
|
|
package/template/tsconfig.json
CHANGED