create-gardener 1.1.10 → 1.1.12

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
@@ -1,43 +1,375 @@
1
- # Gardener 🌱
1
+ ---
2
+
3
+ # 🌱 Gardener
4
+
5
+ **Gardener** is a lightweight, DOM-first front-end library for building and manipulating HTML/SVG elements using a clean, declarative JavaScript object syntax.
6
+
7
+ No virtual DOM.
8
+ No JSX.
9
+ No compilation step.
10
+ No magic.
11
+
12
+ Everything is explicit and inspectable directly in the browser.
13
+
14
+ ---
15
+
16
+ ## ✨ Philosophy
17
+
18
+ Gardener follows a **DOM-first, deterministic approach**:
19
+
20
+ * No Virtual DOM
21
+ * No JSX
22
+ * No compilation / bundler required
23
+ * No runtime abstraction layers
24
+ * Everything renders directly to the real DOM
25
+ * Works natively in the browser (ES modules)
26
+
27
+ If you can inspect it in DevTools, you can understand it.
28
+
29
+ ---
30
+
31
+ ## 🚀 Features
32
+
33
+ * Declarative object-based DOM creation
34
+ * Automatic SVG namespace handling
35
+ * Development server support
36
+ * Hot Reload (dev mode)
37
+ * HTML → Component parser
38
+ * Dynamic image resizing & caching
39
+ * Static site generation
40
+ * Lightweight reactive `State` system
41
+ * Zero build step required
42
+
43
+ ---
44
+
45
+ # 📦 Installation
46
+
47
+ Simply include the module:
48
+
49
+ ```html
50
+ <script type="module" src="/gardener.js"></script>
51
+ ```
52
+
53
+ No bundler required.
54
+
55
+ ---
56
+
57
+ # 🌿 Core API
58
+
59
+ ## 1️⃣ `gardener()` — Declarative Element Builder
60
+
61
+ ```js
62
+ gardener({
63
+ t: 'div',
64
+ cn: ['p-6', 'flex', 'gap-4'],
65
+ attr: { id: 'hero', role: 'banner' },
66
+ txt: 'Welcome',
67
+ events: {
68
+ click: () => console.log('clicked!')
69
+ },
70
+ children: [
71
+ { t: 'span', txt: 'Nested child' }
72
+ ]
73
+ })
74
+ ```
75
+
76
+ ### Supported Keys
77
+
78
+ | Key | Description |
79
+ | ---------- | -------------------------------- |
80
+ | `t` | Tag name (HTML or SVG) |
81
+ | `cn` | Array of class names |
82
+ | `attr` | Attributes / properties object |
83
+ | `txt` | Text content |
84
+ | `events` | `{ eventName: handler }` |
85
+ | `children` | Array of nested gardener objects |
86
+
87
+ ---
88
+
89
+ ## 2️⃣ DOM Helpers
90
+
91
+ ```js
92
+ fetchElement(selector)
93
+ appendElement(parent, child)
94
+ replaceElement(oldElement, newElement)
95
+ createElement(type, classes)
96
+ insertText(element, text)
97
+ addEL(parent, event, handler)
98
+ ```
99
+
100
+ Example:
101
+
102
+ ```js
103
+ const el = gardener({ t: 'h1', txt: 'Hello' });
104
+ appendElement('body', el);
105
+ ```
106
+
107
+ ---
108
+
109
+ # 🔄 SVG Support
110
+
111
+ Gardener automatically uses the correct SVG namespace for:
112
+
113
+ ```
114
+ svg, path, circle, rect, line, polygon, polyline, g, defs, clipPath, use
115
+ ```
116
+
117
+ Example:
118
+
119
+ ```js
120
+ gardener({
121
+ t: 'svg',
122
+ attr: { width: 100, height: 100 },
123
+ children: [
124
+ { t: 'circle', attr: { cx: 50, cy: 50, r: 40, fill: 'red' } }
125
+ ]
126
+ })
127
+ ```
128
+
129
+ ---
130
+
131
+ # 🧠 State Management
132
+
133
+ A minimal reactive system.
134
+
135
+ ```js
136
+ const count = new State(0);
137
+
138
+ count.registerCb(value => {
139
+ console.log("New value:", value);
140
+ });
141
+
142
+ count.setTo(1);
143
+ ```
144
+
145
+ ### API
146
+
147
+ * `new State(initialValue)`
148
+ * `.registerCb(callback)`
149
+ * `.setTo(newValue)`
150
+
151
+ No proxies. No diffing. Just callbacks.
152
+
153
+ ---
154
+
155
+ # 🔥 Development Mode
156
+
157
+ Configure runtime behavior:
158
+
159
+ ```js
160
+ const config = {
161
+ mode: 'dev', // 'dev' | 'prod'
162
+ componentdir: 'static/components',
163
+ hotreload: false
164
+ }
165
+ ```
166
+
167
+ ## Dev Mode Includes:
168
+
169
+ * Floating "+" page creator
170
+ * Component parser
171
+ * Hot reload toggle
172
+ * Static site builder
2
173
 
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**.
174
+ ---
175
+
176
+ # ♻️ Hot Reload
177
+
178
+ * State stored in `localStorage`
179
+ * Reloads page ~1 second after change
180
+ * Toggle with:
181
+
182
+ ```
183
+ Alt + H
184
+ ```
185
+
186
+ Or use the built-in checkbox (dev mode only).
187
+
188
+ ---
189
+
190
+ # 🧩 Component Parser
191
+
192
+ Turn existing HTML into reusable JS components.
193
+
194
+ ### Step 1 — Write HTML
195
+
196
+ ```html
197
+ <div id="user-card">
198
+ Hello, ?name?
199
+ </div>
200
+ ```
201
+
202
+ ### Step 2 — Run in console
203
+
204
+ ```js
205
+ parser('#user-card')
206
+ ```
207
+
208
+ ### Step 3 — Generated File
209
+
210
+ ```js
211
+ export default function thisfun({ name }) {
212
+ return gardener({
213
+ t: "div",
214
+ txt: "Hello, " + name
215
+ })
216
+ }
217
+ ```
218
+
219
+ ---
220
+
221
+ ## 🧾 Parameter Syntax
222
+
223
+ Use:
224
+
225
+ ```
226
+ ?variable?
227
+ ```
228
+
229
+ Gardener extracts variables and generates a function with those parameters.
230
+
231
+ ---
232
+
233
+ # 🖼 Image Optimization
234
+
235
+ Place originals in:
236
+
237
+ ```
238
+ /src/frontend/assets/
239
+ ```
240
+
241
+ Use:
242
+
243
+ ```html
244
+ <img src="/static/cache/photo_800x600.webp">
245
+ ```
246
+
247
+ Server will:
4
248
 
5
- It is designed for developers who want:
249
+ * Resize
250
+ * Convert to WebP
251
+ * Cache automatically
6
252
 
7
- * full control over HTML structure
8
- * minimal abstractions
9
- * a fast local dev experience
10
- * a deterministic static output for production
253
+ ---
254
+
255
+ # 📄 Create New Pages (Dev Mode)
11
256
 
12
- Gardener sits somewhere between a tiny framework and a build system.
257
+ 1. Click floating `+`
258
+ 2. Enter route (e.g., `/about`)
259
+ 3. Page auto-generated
260
+ 4. Browser redirects
13
261
 
14
- Use it with
262
+ Template used:
263
+
264
+ ```
265
+ /src/backend/frontendtemplate.ejs
266
+ ```
15
267
 
16
- `npm create-gardener filename`
17
268
  ---
18
269
 
19
- * [See Docs](https://ritish.site/Gardener)
270
+ # 🏗 Static Site Generation
20
271
 
272
+ Visit:
21
273
 
22
- * [Github](https://github.com/ritishDas/Gardener)
274
+ ```
275
+ /createstatic
276
+ ```
23
277
 
278
+ Build output:
24
279
 
280
+ ```
281
+ /src/frontendStatic
282
+ ```
25
283
 
284
+ ---
26
285
 
27
- ## Contributing
286
+ # 🧩 How It Works
28
287
 
29
- This is a small personal/dev-focused toolkit.
288
+ Gardener:
30
289
 
31
- Contributions are welcome:
290
+ 1. Converts object → real DOM
291
+ 2. Applies attributes safely
292
+ 3. Handles properties vs attributes
293
+ 4. Recursively renders children
294
+ 5. Avoids virtual DOM entirely
32
295
 
33
- * bug fixes
34
- * documentation improvements
35
- * build pipeline enhancements
36
- * security hardening
296
+ The browser is the rendering engine.
37
297
 
38
298
  ---
39
299
 
40
- ## License
300
+ # 🎯 When To Use Gardener
301
+
302
+ Good for:
303
+
304
+ * Lightweight SPAs
305
+ * Internal tools
306
+ * Static + hybrid sites
307
+ * Dev tooling
308
+ * Projects where you want full DOM control
309
+ * No-build-step environments
310
+
311
+ Not designed to replace React/Vue — designed to avoid them when unnecessary.
312
+
313
+ ---
314
+
315
+ # 🛠 Example App
316
+
317
+ ```js
318
+ import { gardener, appendElement } from './gardener.js';
319
+
320
+ const app = gardener({
321
+ t: 'div',
322
+ cn: ['app'],
323
+ children: [
324
+ { t: 'h1', txt: 'Hello World' },
325
+ {
326
+ t: 'button',
327
+ txt: 'Click Me',
328
+ events: {
329
+ click: () => alert('Hi!')
330
+ }
331
+ }
332
+ ]
333
+ });
334
+
335
+ appendElement('body', app);
336
+ ```
337
+
338
+ ---
339
+
340
+ # 📁 Project Structure (Suggested)
341
+
342
+ ```
343
+ /src
344
+ /frontend
345
+ /backend
346
+ /frontendStatic
347
+ /static
348
+ /components
349
+ gardener.js
350
+ ```
351
+
352
+ ---
353
+
354
+ # 🧭 Roadmap Ideas
355
+
356
+ * Fine-grained reactive bindings
357
+ * Dev inspector panel
358
+ * Server-side rendering mode
359
+ * Plugin API
360
+ * Optional diff mode
361
+
362
+ ---
363
+
364
+ # 📜 License
365
+
366
+ MIT (recommended)
367
+
368
+ ---
369
+
370
+ # 🌿 Why Gardener?
371
+
372
+ Because sometimes you don't need a forest.
41
373
 
42
- MIT
374
+ Just a garden.
43
375
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "https://github.com/ritishDas/gardener"
7
7
  },
8
- "version": "1.1.10",
8
+ "version": "1.1.12",
9
9
  "description": "A dom gardener converting dom elements into json and vice versa",
10
10
  "main": "index.js",
11
11
  "bin": {
@@ -0,0 +1,11 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ async function buildHelper() {
5
+ const src = path.resolve('src', 'frontend');
6
+ const dest = path.resolve('build', 'frontend');
7
+
8
+ await fs.cp(src, dest, { recursive: true });
9
+ }
10
+
11
+ buildHelper();// const path = require('path');
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "create-gardener",
3
- "version": "1.1.8",
3
+ "version": "1.1.11",
4
4
  "description": "A dom gardener converting dom elements into json and vice versa",
5
5
  "main": "src/build/server.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "start": "node src/build/server.js",
9
9
  "test": "echo \"Error: no test specified\" && exit 1",
10
- "dev": "concurrently \"tsx watch src/backend/server.ts\" \"tailwindcss -w -i src/frontend/tailwind.css -o src/frontend/style.css\"",
11
- "build": "tsc"
10
+ "dev": "concurrently \"tsx watch src/backend/server.ts\" \"tailwindcss -w -i src/frontend/tailwind.css -o src/frontend/static/style.css\"",
11
+ "build": "tailwindcss -i src/frontend/tailwind.css -o src/frontend/static/style.css --minify && tsc && node buildHelper.js"
12
12
  },
13
13
  "keywords": [],
14
14
  "author": "ritishDas",
@@ -1,4 +1,5 @@
1
- import { Router } from "express"
1
+ import type { Request, Response } from 'express';
2
+ import { Router } from "express";
2
3
  import { addComponent, addPage, createStatic, imageOptimiser } from "../controllers/gardener.controller.js";
3
4
 
4
5
  const router: Router = Router();
@@ -16,6 +17,6 @@ router.route('/addpage').post(addPage);
16
17
 
17
18
 
18
19
 
19
- router.route('/').get((req, res) => res.render('_'));
20
- router.route('/login').get((req, res) => res.render('_login'));
20
+ router.route('/').get((req: Request, res: Response) => res.render('_'));
21
+ router.route('/login').get((req: Request, res: Response) => res.render('_login'));
21
22
 
@@ -54,7 +54,7 @@ font-size:14px;
54
54
  {
55
55
  t: 'span',
56
56
  attr: { style: 'color:#22c55e;font-weight:bold;' },
57
- txt: 'Ctrl+h'
57
+ txt: 'Alt+h'
58
58
  },
59
59
  { t: 'span', txt: ' to toggle Hot Reload' },
60
60
  {
@@ -79,8 +79,7 @@ font-size:14px;
79
79
  applyHotReloadState();
80
80
 
81
81
  document.addEventListener('keydown', function(e) {
82
- // Detect Ctrl + H
83
- if (e.ctrlKey && e.key.toLowerCase() === 'h') {
82
+ if (e.altKey && e.key.toLowerCase() === 'h') {
84
83
  e.preventDefault(); // Stop browser from opening history
85
84
  // Your logic here...
86
85
  togglehotreload();
@@ -395,7 +394,6 @@ export function parser(element, isParent = true) {
395
394
  return obj
396
395
 
397
396
 
398
- //Let Browser do the migration from html to json and then use copy paste
399
397
  }
400
398
 
401
399
 
@@ -406,25 +404,20 @@ export function addEL(parent, event, fun) {
406
404
  parent.addEventListener(event, fun)
407
405
  }
408
406
 
409
- export function imagePreloader(images) {
410
- const body = fetchElement('body')
411
- images.forEach(entry => {
412
- appendElement(body, gardener({
413
- t: 'img',
414
- cn: ['preloaderimage'],
415
- attr: {
416
- src: entry,
417
- alt: entry
418
- }
419
- }));
420
-
421
- setTimeout(() => {
422
- const images = document.querySelectorAll('.preloaderimage');
423
- images.forEach(entry => { entry.style.display = 'none' });
424
- }, 0)
425
407
 
426
- })
408
+ export class State {
409
+ constructor(value) {
410
+ this.value = value;
411
+ this.cb = [];
412
+ }
413
+ registerCb(cb) {
414
+ cb(this.value);
415
+ this.cb.push(cb);
416
+ }
417
+ setTo(val) {
418
+ this.value = val;
419
+ this.cb.forEach(cb => { cb(val) });
420
+ }
427
421
  }
428
422
 
429
423
 
430
-
@@ -80,7 +80,7 @@ if (config.mode === 'dev') {
80
80
  {
81
81
  t: 'span',
82
82
  cn: ['text-green-500', 'font-bold'],
83
- txt: 'Ctrl+h'
83
+ txt: 'Alt+h'
84
84
  },
85
85
  {
86
86
  t: 'span',
@@ -116,7 +116,7 @@ if (config.mode === 'dev') {
116
116
  togglehotreload();
117
117
  document.addEventListener('keydown', function(e) {
118
118
  // Detect Ctrl + H
119
- if (e.ctrlKey && e.key.toLowerCase() === 'h') {
119
+ if (e.altKey && e.key.toLowerCase() === 'h') {
120
120
  e.preventDefault(); // Stop browser from opening history
121
121
  // Your logic here...
122
122
  togglehotreload();
@@ -383,9 +383,6 @@ export default function thisfun({${extractedList}}) {
383
383
  }
384
384
 
385
385
 
386
- // Example:
387
- // const result = cleanStringAndList('hi "{ritish}" how are you');
388
- // console.log(result.cleanedString); // "hi ritish how are you"
389
386
  // console.log(result.extractedList); // ["ritish"]
390
387
 
391
388
 
@@ -458,24 +455,19 @@ export function addEL(parent, event, fun) {
458
455
  parent.addEventListener(event, fun)
459
456
  }
460
457
 
461
- export function imagePreloader(images) {
462
- const body = fetchElement('body')
463
- images.forEach(entry => {
464
- appendElement(body, gardener({
465
- t: 'img',
466
- cn: ['preloaderimage'],
467
- attr: {
468
- src: entry,
469
- alt: entry
470
- }
471
- }));
472
-
473
- setTimeout(() => {
474
- const images = document.querySelectorAll('.preloaderimage');
475
- images.forEach(entry => { entry.style.display = 'none' });
476
- }, 0)
477
-
478
- })
458
+ export class State {
459
+ constructor(value) {
460
+ this.value = value;
461
+ this.cb = [];
462
+ }
463
+ registerCb(cb) {
464
+ cb(this.value);
465
+ this.cb.push(cb);
466
+ }
467
+ setTo(val) {
468
+ this.value = val;
469
+ this.cb.forEach(cb => { cb(val) });
470
+ }
479
471
  }
480
472
 
481
473
 
@@ -52,6 +52,7 @@ function pageloader() {
52
52
  setTimeout(() => loader.remove(), 400)
53
53
 
54
54
  }
55
+
55
56
  //console.log('hellooo');
56
57
  //parser(fetchElement('.hero'));
57
58
  //parser(fetchElement('nav'));