create-gardener 1.1.11 → 1.1.13

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.11",
8
+ "version": "1.1.13",
9
9
  "description": "A dom gardener converting dom elements into json and vice versa",
10
10
  "main": "index.js",
11
11
  "bin": {
@@ -16,10 +16,12 @@ interface AddComponentBody {
16
16
  component: string;
17
17
  }
18
18
 
19
- export function addComponent(req: Request<{}, {}, AddComponentBody>, res: Response) {
19
+ export async function addComponent(req: Request<{}, {}, AddComponentBody>, res: Response) {
20
20
  try {
21
21
  const { path: filePath, component } = req.body;
22
22
 
23
+ await fsp.mkdir('./src/frontend/static/components', { recursive: true });
24
+
23
25
  fs.writeFileSync(`./src/frontend/${filePath}`, component, "utf8");
24
26
 
25
27
  res.json({ success: true });
@@ -101,6 +103,9 @@ export async function addPage(req: Request, res: Response) {
101
103
 
102
104
  fs.appendFileSync('./src/backend/routes/gardener.route.ts', ` router.route("${pagename}").get((req: Request, res: Response) => res.render("${name}"))\n `);
103
105
 
106
+ await fsp.mkdir('src/frontend/static/pages', { recursive: true });
107
+
108
+ fs.writeFileSync(`./src/frontend/static/pages/${name}.js`, 'import { log, parser, fetchElement, replaceElement, appendElement, State, addEL } from "/static/gardener.js";', "utf8");
104
109
  res.json({ success: true });
105
110
  }
106
111
  catch (err) {
@@ -185,3 +190,4 @@ export async function createStatic(req: Request, res: Response) {
185
190
  }
186
191
  }
187
192
 
193
+
@@ -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
 
@@ -323,6 +323,14 @@ function generateFile(obj) {
323
323
  const formatted = JSON.stringify(obj, null, 2);
324
324
  const { cleanedString, extractedList } = cleanStringAndList(formatted);
325
325
 
326
+ if (extractedList.length === 0) return `
327
+ import { gardener, fetchElement, replaceElement } from '../gardener.js'
328
+
329
+ export default function thisfun() {
330
+ return gardener(${cleanedString})
331
+ }
332
+ `;
333
+
326
334
  return `
327
335
  import { gardener, fetchElement, replaceElement } from '../gardener.js'
328
336
 
@@ -394,7 +402,6 @@ export function parser(element, isParent = true) {
394
402
  return obj
395
403
 
396
404
 
397
- //Let Browser do the migration from html to json and then use copy paste
398
405
  }
399
406
 
400
407
 
@@ -405,26 +412,20 @@ export function addEL(parent, event, fun) {
405
412
  parent.addEventListener(event, fun)
406
413
  }
407
414
 
408
- export function imagePreloader(images) {
409
- const body = fetchElement('body')
410
- images.forEach(entry => {
411
- appendElement(body, gardener({
412
- t: 'img',
413
- cn: ['preloaderimage'],
414
- attr: {
415
- src: entry,
416
- alt: entry
417
- }
418
- }));
419
415
 
420
- setTimeout(() => {
421
- const images = document.querySelectorAll('.preloaderimage');
422
- images.forEach(entry => { entry.style.display = 'none' });
423
- }, 0)
424
-
425
- })
416
+ export class State {
417
+ constructor(value) {
418
+ this.value = value;
419
+ this.cb = [];
420
+ }
421
+ registerCb(cb) {
422
+ cb(this.value);
423
+ this.cb.push(cb);
424
+ }
425
+ setTo(val) {
426
+ this.value = val;
427
+ this.cb.forEach(cb => { cb(val) });
428
+ }
426
429
  }
427
430
 
428
431
 
429
-
430
-
@@ -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,25 +455,23 @@ 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
 
474
+ export function log(target) {
475
+ console.log(target)
476
+ }
482
477
 
@@ -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'));