easy-starter 1.1.0 → 1.2.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/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.1.0",
3
+ "version": "1.2.1",
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,108 @@
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
+ ```
93
+
94
+ ---
95
+
96
+ ## 4. Styling & Image Manipulation Guidelines
97
+
98
+ ### CSS / SCSS Implementation
99
+ * **Centralized & Generic**: Implement as many styles as possible in `src/styles.scss` using generic, reusable selectors.
100
+ * **Minimize Inline Styles**: Avoid inline styles (e.g., `style={{ ... }}`) in React components. Keep style declarations in the central SCSS file to maintain design consistency and code quality.
101
+
102
+ ### Image Asset Handling
103
+ * **STRONG PREFERENCE**: It is highly preferred to insert images as `background-image` declarations inside `src/styles.scss` rather than using `<img>` tags in React components wherever possible.
104
+ * **CRITICAL FOR SCSS**: When referencing images inside `src/styles.scss` (e.g., as background-image), always append the `?as=webp` query parameter to convert them to modern WebP format during build, unless the source file is already an `.svg`. Do specify width or height query parameters.
105
+ * **CORRECT**: `background-image: url("./images/icon.png?as=webp&height=128");`
106
+ * **CRITICAL FOR CODE (React / HTML / JavaScript)**: If you must reference an image path directly within the code (e.g., in `<img src="..." />` or JS variables), you **MUST** only refer to images located inside the `public` directory (and nowhere else).
107
+ * **CORRECT**: `<img src="/logo.png" />` (when referencing `/public/logo.png`)
108
+ * **INCORRECT**: `<img src="../src/images/logo.png" />` or importing images directly into React files.
@@ -26,4 +26,7 @@ jobs:
26
26
  SSH_HOST: ${{ secrets.SSH_HOST }}
27
27
  SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
28
28
  SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }}
29
+ SSH_PATH: ${{ secrets.SSH_PATH }}
30
+ PM2_APP_NAME: ${{ secrets.PM2_APP_NAME }}
31
+ SERVER_URL: ${{ secrets.SERVER_URL }}
29
32
  run: npm run deploy
@@ -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.d.ts` - Shared TypeScript definitions for your API and Database.
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,12 @@
1
- NODE_ENV=development
1
+ NODE_ENV=development # development/production
2
2
  SERVER_PORT=1111
3
3
  ADMIN_PASSWORD=admin
4
4
 
5
+ SERVER_URL=https://your-production-url.com/
6
+
5
7
  # deploy
6
- SSH_HOST=***
7
- SSH_USERNAME=***
8
- SSH_USERNAME=***
8
+ SSH_HOST=[IP_ADDRESS]
9
+ SSH_USERNAME=root
10
+ SSH_PASSWORD=[PASSWORD]
11
+ SSH_PATH=/root/easy-starter-app
12
+ PM2_APP_NAME=easy-starter-app
@@ -9,22 +9,28 @@ 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 = "https://your-production-url.com/";
13
- const pm2AppName = "easy-starter-app"; // The name of the process in PM2
14
- const sshPath = resolve("/", "root", "your-production-url.com"); // Path on the remote VPS
12
+ const server = process.env.SERVER_URL;
13
+ const pm2AppName = process.env.PM2_APP_NAME;
14
+ const sshPath = process.env.SSH_PATH; // 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
- ".github"
23
+ "easy-db",
24
+ "easy-db-files",
25
+ "easy-db-backup",
26
+ ".DS_Store",
22
27
  ];
23
28
 
24
- // Ensure credentials are provided
29
+ // Ensure credentials and paths are provided
25
30
  if (host === undefined) throw new Error("SSH_HOST environment variable is not defined.");
26
31
  if (username === undefined) throw new Error("SSH_USERNAME environment variable is not defined.");
27
32
  if (password === undefined) throw new Error("SSH_PASSWORD environment variable is not defined.");
33
+ if (sshPath === undefined) throw new Error("SSH_PATH environment variable is not defined.");
28
34
 
29
35
  deploy();
30
36
 
@@ -73,20 +79,28 @@ async function deploy() {
73
79
  console.log(`✔ Build project.\n`);
74
80
 
75
81
  // PM2
76
- try {
77
- await sshExec(ssh, `pm2 reload ${pm2AppName}`);
78
- } catch (e) {
79
- console.log(`✘ ${pm2AppName} is not running in PM2!`);
80
- await sshExec(ssh, `pm2 start npm --name "${pm2AppName}" -- start`, `${pm2AppName} could not be registered in PM2!`);
82
+ if (pm2AppName) {
83
+ try {
84
+ await sshExec(ssh, `pm2 reload ${pm2AppName}`);
85
+ } catch (e) {
86
+ console.log(`✘ ${pm2AppName} is not running in PM2!`);
87
+ await sshExec(ssh, `pm2 start npm --name "${pm2AppName}" -- start`, `${pm2AppName} could not be registered in PM2!`);
88
+ }
89
+ await sshExec(ssh, `pm2 list`);
90
+ } else {
91
+ console.log(`i PM2 step is skipped because PM2_APP_NAME is not defined.\n`);
81
92
  }
82
- await sshExec(ssh, `pm2 list`);
83
93
 
84
94
  ssh.dispose();
85
95
  console.log(`✔ SSH connection is closed.\n`);
86
96
 
87
- if (!await serverCheck(server))
88
- throw new Error(`Server ${server} is not available!`);
89
- console.log(`✔ Server ${server} is running.\n`);
97
+ if (server) {
98
+ if (!await serverCheck(server))
99
+ throw new Error(`Server ${server} is not available!`);
100
+ console.log(`✔ Server ${server} is running.\n`);
101
+ } else {
102
+ console.log(`i Server check is skipped because SERVER_URL is not defined.\n`);
103
+ }
90
104
 
91
105
  console.log(`i Server was ${(new Date().getTime() - timeServerOff) / 1000}s off!\n`);
92
106
 
@@ -4,19 +4,10 @@ import { readFile } from "fs/promises";
4
4
  import cors from "cors";
5
5
  import express from "express";
6
6
 
7
- // easy-db-node provides a simple local database storing data in JSON files.
8
- // It's ideal for small to medium projects where setting up a full SQL/NoSQL DB is overkill.
9
7
  import easyDBNode from "easy-db-node";
10
-
11
- // ssr-hook allows for simple Server-Side Rendering of React components,
12
- // ensuring SEO friendliness and faster initial page loads.
13
8
  import { renderToHTML } from "ssr-hook/server";
14
-
15
- // typed-client-server-api establishes a type-safe RPC-like API between your React frontend
16
- // and this Express backend, preventing mismatch errors and providing autocomplete.
17
9
  import { setAPIBackend } from "typed-client-server-api";
18
10
 
19
- // easy-analytics handles privacy-friendly local analytics collection.
20
11
  import { postEasyAnalytics, getEasyAnalytics } from "easy-analytics/server";
21
12
 
22
13
  import App from "../src/App";
@@ -29,7 +20,7 @@ const LOCALHOST = `http://localhost:${PORT}`;
29
20
  // The ORIGIN is used for SEO and by ssr-hook to know where the requests are coming from.
30
21
  const ORIGIN = process.env.NODE_ENV === "development"
31
22
  ? LOCALHOST
32
- : "https://your-production-url.com"; // TODO: Update to your domain
23
+ : process.env.SERVER_URL;
33
24
 
34
25
  const INDEX_HTML_PATH = resolve(__dirname, "..", "dist", "index.html");
35
26
 
@@ -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
 
@@ -18,6 +18,6 @@
18
18
  "include": [
19
19
  "src/**/*",
20
20
  "server/**/*",
21
- "types.d.ts"
21
+ "types.ts"
22
22
  ]
23
23
  }
@@ -22,5 +22,5 @@ export type Item = {
22
22
  // The Database type defines the structure of your JSON files managed by easy-db-node.
23
23
  // Each property represents a 'table' (or a separate JSON file).
24
24
  export type Database = {
25
- item: Omit<Item, "_id">;
25
+ item: Item;
26
26
  };