create-gardener 1.0.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/.envrc ADDED
@@ -0,0 +1 @@
1
+ use flake
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ritishDas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/Readme.md ADDED
@@ -0,0 +1,349 @@
1
+ # Gardener 🌱
2
+
3
+ **Gardener** is a small development toolkit and micro-framework for building websites with **declarative DOM JSON**, server-rendered templates, and a **custom static site generation pipeline**.
4
+
5
+ It is designed for developers who want:
6
+
7
+ * full control over HTML structure
8
+ * minimal abstractions
9
+ * a fast local dev experience
10
+ * a deterministic static output for production
11
+
12
+ Gardener sits somewhere between a tiny framework and a build system.
13
+
14
+ ---
15
+
16
+ ## What Gardener Includes
17
+
18
+ ### Core
19
+
20
+ * 🌿 **gardener.js** — declarative DOM builder using JSON objects
21
+ * 🔁 **parser** — convert real DOM elements back into gardener-compatible JSON
22
+ * 📄 **EJS** for simple server-rendered views
23
+ * 🎨 **Tailwind CSS** for fast styling
24
+
25
+ ### Dev Server
26
+
27
+ * Express-based development server
28
+ * Hot reload toggle (Ctrl + H)
29
+ * Endpoints to create pages and components at runtime (dev convenience)
30
+
31
+ ### Images
32
+
33
+ * Deterministic image optimization endpoint
34
+ * Sharp-powered resize + WebP conversion
35
+ * Filesystem cache reused during static builds
36
+
37
+ ### Static Site Generation (SSG)
38
+
39
+ * Render EJS views into HTML
40
+ * Convert route-encoded filenames into nested directories
41
+ * Merge frontend assets and image cache
42
+ * Clean temporary build artifacts
43
+ * Produce a deployable static directory
44
+
45
+ ---
46
+
47
+ ## Project Structure
48
+
49
+ ```
50
+ src/
51
+ ├── backend/
52
+ │ ├── routes/
53
+ │ ├── controllers/
54
+ │ ├── libs/
55
+ │ ├── cache/ # generated image cache (build artifact)
56
+ │ └── server.ts
57
+
58
+ ├── frontend/
59
+ │ ├── views/ # EJS templates (source)
60
+ │ ├── assets/ # original images
61
+ │ ├── components/
62
+ │ ├── gardener.js
63
+ │ └── styles/
64
+
65
+ ├── frontendStatic/ # final static output (generated)
66
+ └── tempfrontend/ # temporary build output (deleted after build)
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Requirements
72
+
73
+ * Node.js v16+ (v18+ recommended)
74
+ * pnpm (recommended) or npm
75
+ * Optional: PostgreSQL
76
+
77
+ ---
78
+
79
+ ## Quickstart (Development)
80
+
81
+ ### 1. Install
82
+
83
+ ```bash
84
+ git clone https://github.com/ritishDas/Gardener.git
85
+ cd Gardener
86
+ pnpm install
87
+ ```
88
+
89
+ ### 2. Run dev server
90
+
91
+ ```bash
92
+ pnpm run dev
93
+ ```
94
+
95
+ * Server runs at **[http://localhost:3000](http://localhost:3000)**
96
+ * Tailwind watcher and TypeScript server run together
97
+
98
+ ---
99
+
100
+ ## Image Optimization & Caching
101
+
102
+ Gardener provides a **deterministic image optimization endpoint**.
103
+
104
+ ### Route
105
+
106
+ ```
107
+ GET /cache/:name
108
+ ```
109
+
110
+ ### Filename format
111
+
112
+ ```
113
+ <basename>_<width>x<height>.webp
114
+ ```
115
+
116
+ ### Example
117
+
118
+ ```http
119
+ GET /cache/hero_500x300.webp
120
+ ```
121
+
122
+ HTML usage:
123
+
124
+ ```html
125
+ <img src="/cache/hero_500x300.webp" alt="hero" />
126
+ ```
127
+
128
+ ---
129
+
130
+ ### How it works
131
+
132
+ 1. Parses filename to extract:
133
+
134
+ * base name
135
+ * width
136
+ * height
137
+ 2. Checks cache:
138
+
139
+ ```
140
+ src/backend/cache/
141
+ ```
142
+ 3. If cached → return immediately
143
+ 4. If not cached:
144
+
145
+ * Finds source image in:
146
+
147
+ ```
148
+ src/frontend/assets/
149
+ ```
150
+ * Resizes and converts to WebP (Sharp)
151
+ * Stores result in cache
152
+ 5. Serves the optimized image
153
+
154
+ ---
155
+
156
+ ### Static Build Integration
157
+
158
+ During static generation:
159
+
160
+ * All cached images under:
161
+
162
+ ```
163
+ src/backend/cache/
164
+ ```
165
+
166
+ are copied into:
167
+
168
+ ```
169
+ src/frontendStatic/
170
+ ```
171
+
172
+ Static HTML can safely reference:
173
+
174
+ ```html
175
+ <img src="/cache/hero_500x300.webp" />
176
+ ```
177
+
178
+ No runtime image processing is required in production.
179
+
180
+ ---
181
+
182
+ ## Static Site Generation
183
+
184
+ Gardener includes a custom static build pipeline.
185
+
186
+ ### What it does
187
+
188
+ 1. Renders EJS views into HTML
189
+ 2. Writes temporary files using route-encoded filenames
190
+ (example: `_blog_posts_hello.html`)
191
+ 3. Converts them into directory-based routes:
192
+
193
+ ```
194
+ blog/posts/hello/index.html
195
+ ```
196
+ 4. Copies frontend assets
197
+ 5. Copies image cache
198
+ 6. Deletes temporary build directory
199
+
200
+ ### Output
201
+
202
+ ```
203
+ src/frontendStatic/
204
+ ├── index.html
205
+ ├── blog/
206
+ │ └── posts/
207
+ │ └── hello/
208
+ │ └── index.html
209
+ ├── assets/
210
+ └── cache/
211
+ ```
212
+
213
+ This directory is ready for:
214
+
215
+ * static hosting
216
+ * CDN deployment
217
+ * Nginx / Caddy / Netlify / Vercel
218
+
219
+ ---
220
+
221
+ ## Frontend API — `gardener.js`
222
+
223
+ File: `src/frontend/gardener.js`
224
+
225
+ ### `gardener(obj)`
226
+
227
+ Create DOM elements from JSON.
228
+
229
+ ```js
230
+ const el = gardener({
231
+ t: 'div',
232
+ cn: ['card', 'p-4'],
233
+ children: [
234
+ { t: 'h2', txt: 'Title' },
235
+ { t: 'p', txt: 'Content' }
236
+ ]
237
+ });
238
+
239
+ document.body.appendChild(el);
240
+ ```
241
+
242
+ ---
243
+
244
+ ### `parser(elementOrHtml, isParent = true)`
245
+
246
+ Convert DOM into gardener JSON.
247
+
248
+ ```js
249
+ const json = parser(document.querySelector('.hero'));
250
+ console.log(json);
251
+ ```
252
+
253
+ ---
254
+
255
+ ### `parserWindow(text)`
256
+
257
+ Dev-only UI:
258
+
259
+ * Preview parsed JSON
260
+ * Press **Y** to create a component file
261
+
262
+ ---
263
+
264
+ ### Utilities
265
+
266
+ * `imagePreloader(images)`
267
+ * `fetchElement(selector)`
268
+ * `appendElement(parent, child)`
269
+ * `replaceElement(original, newElem)`
270
+ * `createElement(type, classname)`
271
+
272
+ ---
273
+
274
+ ## Dev-Only Endpoints ⚠️
275
+
276
+ ### `POST /addcomponent`
277
+
278
+ Creates a frontend component file.
279
+
280
+ ```json
281
+ {
282
+ "path": "components/MyComp.js",
283
+ "component": "{ t: 'div', txt: 'Hello' }"
284
+ }
285
+ ```
286
+
287
+ Writes directly to the filesystem.
288
+
289
+ ---
290
+
291
+ ### `POST /addpage`
292
+
293
+ Creates an EJS page and registers a route.
294
+
295
+ ```json
296
+ { "page": "/my-page" }
297
+ ```
298
+
299
+ * Generates a new EJS file
300
+ * Appends a route to the backend router
301
+
302
+ ---
303
+
304
+ ## Security Notes
305
+
306
+ ⚠️ **Important**
307
+
308
+ * `/addcomponent` and `/addpage` mutate files and routes
309
+ * Intended for **local development only**
310
+ * Do NOT expose publicly without authentication and sanitization
311
+
312
+ ---
313
+
314
+ ## Troubleshooting
315
+
316
+ * **Server not starting**
317
+
318
+ * Check `.env`
319
+ * Ensure PostgreSQL is running (if enabled)
320
+ * Inspect logs from `pnpm run dev`
321
+
322
+ * **Images not loading**
323
+
324
+ * Ensure source image exists in `src/frontend/assets`
325
+ * Filename must match `<name>_<width>x<height>.webp`
326
+
327
+ * **Tailwind not updating**
328
+
329
+ * Ensure `pnpm install` completed successfully
330
+
331
+ ---
332
+
333
+ ## Contributing
334
+
335
+ This is a small personal/dev-focused toolkit.
336
+
337
+ Contributions are welcome:
338
+
339
+ * bug fixes
340
+ * documentation improvements
341
+ * build pipeline enhancements
342
+ * security hardening
343
+
344
+ ---
345
+
346
+ ## License
347
+
348
+ MIT
349
+
package/flake.lock ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "nodes": {
3
+ "flake-utils": {
4
+ "inputs": {
5
+ "systems": "systems"
6
+ },
7
+ "locked": {
8
+ "lastModified": 1731533236,
9
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
10
+ "owner": "numtide",
11
+ "repo": "flake-utils",
12
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
13
+ "type": "github"
14
+ },
15
+ "original": {
16
+ "owner": "numtide",
17
+ "repo": "flake-utils",
18
+ "type": "github"
19
+ }
20
+ },
21
+ "nixpkgs": {
22
+ "locked": {
23
+ "lastModified": 1769018530,
24
+ "narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=",
25
+ "owner": "NixOS",
26
+ "repo": "nixpkgs",
27
+ "rev": "88d3861acdd3d2f0e361767018218e51810df8a1",
28
+ "type": "github"
29
+ },
30
+ "original": {
31
+ "owner": "NixOS",
32
+ "ref": "nixos-unstable",
33
+ "repo": "nixpkgs",
34
+ "type": "github"
35
+ }
36
+ },
37
+ "root": {
38
+ "inputs": {
39
+ "flake-utils": "flake-utils",
40
+ "nixpkgs": "nixpkgs"
41
+ }
42
+ },
43
+ "systems": {
44
+ "locked": {
45
+ "lastModified": 1681028828,
46
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47
+ "owner": "nix-systems",
48
+ "repo": "default",
49
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50
+ "type": "github"
51
+ },
52
+ "original": {
53
+ "owner": "nix-systems",
54
+ "repo": "default",
55
+ "type": "github"
56
+ }
57
+ }
58
+ },
59
+ "root": "root",
60
+ "version": 7
61
+ }
package/flake.nix ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ description="A flake template";
3
+ inputs={
4
+ nixpkgs.url="github:NixOS/nixpkgs/nixos-unstable";
5
+ flake-utils.url="github:numtide/flake-utils";
6
+ };
7
+ outputs={self,nixpkgs,flake-utils}:
8
+ flake-utils.lib.eachDefaultSystem(system:
9
+ let
10
+ pkgs=import nixpkgs{
11
+ inherit system;
12
+ };
13
+ in {
14
+ devShells.default=pkgs.mkShell{
15
+ name="Flake template";
16
+ shellHook=''
17
+ export PNPM_HOME="$HOME/.local/share/pnpm"
18
+ export PATH="$PNPM_HOME:$PATH"
19
+ '';
20
+ };
21
+ }
22
+ );
23
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "create-gardener",
3
+ "version": "1.0.0",
4
+ "description": "A dom gardener converting dom elements into json and vice versa",
5
+ "main": "index.js",
6
+ "bin":{
7
+ "create-gardener":"./starter.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1",
12
+ "dev": "concurrently \"tsx watch src/backend/server.ts\" \"tailwindcss -w -i src/frontend/tailwind.css -o src/frontend/style.css\""
13
+ },
14
+ "keywords": [],
15
+ "author": "ritishDas",
16
+ "license": "MIT",
17
+ "packageManager": "pnpm@10.20.0",
18
+ "dependencies": {
19
+ "@types/ejs": "^3.1.5",
20
+ "dotenv": "^17.2.3",
21
+ "ejs": "^3.1.10",
22
+ "express": "^5.2.1",
23
+ "sharp": "^0.34.5",
24
+ "tailwindcss": "^4.1.18",
25
+ "types": "^0.1.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^5.0.6",
29
+ "@types/node": "^25.0.2",
30
+ "concurrently": "^9.2.1",
31
+ "tsx": "^4.21.0",
32
+ "typescript": "^5.9.3"
33
+ }
34
+ }
package/starter.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ // --- ESM __dirname fix ---
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ // --- CLI args ---
12
+ const projectName = process.argv[2];
13
+
14
+ if (!projectName) {
15
+ console.log(`
16
+ 🌱 create-gardener
17
+
18
+ Usage:
19
+ create-gardener <project-name>
20
+
21
+ Example:
22
+ create-gardener my-app
23
+ `);
24
+ process.exit(1);
25
+ }
26
+
27
+ // --- Target dir ---
28
+ const targetDir = path.resolve(process.cwd(), projectName);
29
+
30
+ if (fs.existsSync(targetDir)) {
31
+ console.error(`❌ Directory "${projectName}" already exists`);
32
+ process.exit(1);
33
+ }
34
+
35
+ fs.mkdirSync(targetDir, { recursive: true });
36
+ console.log(`✅ Created ${projectName}`);
37
+
38
+ // --- Copy template ---
39
+ const templateDir = path.join(__dirname, "template");
40
+
41
+ if (!fs.existsSync(templateDir)) {
42
+ console.error("❌ Template folder not found");
43
+ process.exit(1);
44
+ }
45
+
46
+ copyDir(templateDir, targetDir);
47
+
48
+ console.log(`
49
+ 🌿 Project ready!
50
+
51
+ Next steps:
52
+ cd ${projectName}
53
+ pnpm install
54
+ pnpm dev
55
+ `);
56
+
57
+
58
+ // ---------- helpers ----------
59
+ function copyDir(src, dest) {
60
+ fs.mkdirSync(dest, { recursive: true });
61
+
62
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
63
+ const srcPath = path.join(src, entry.name);
64
+ const destPath = path.join(dest, entry.name);
65
+
66
+ if (entry.isDirectory()) {
67
+ copyDir(srcPath, destPath);
68
+ } else {
69
+ fs.copyFileSync(srcPath, destPath);
70
+ }
71
+ }
72
+ }
73
+