create-gardener 1.1.3 → 1.1.4

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.
@@ -1,404 +0,0 @@
1
- const config = {
2
- mode: 'dev',
3
- componentdir: 'components',
4
- hotreload: true
5
- }
6
-
7
- let hotReloadtimeout;
8
- const body = fetchElement('body');
9
- let hotReload = localStorage.getItem('hotreload');
10
-
11
- if (hotReload === null) hotReload = config.hotreload;
12
- else if (hotReload === 'true') hotReload = true
13
- else if (hotReload === 'false') hotReload = false
14
-
15
- function opnPagedialog(btn = true) {
16
- if (btn) {
17
- const dialog = gardener({
18
- t: 'form', cn: ['addpageform', 'fixed', 'left-2/5', 'bg-gray-200', 'rounded-lg', 'block', 'top-2/5', 'p-2', 'flex', 'flex-col', 'p-5', 'gap-2'], events: {
19
- submit: async (e) => {
20
- try {
21
- e.preventDefault()
22
- const data = new FormData(e.target);
23
- const input = Object.fromEntries(data.entries());
24
- console.log(input)
25
-
26
- const response = await fetch('/addpage', {
27
- method: 'POST',
28
- headers: {
29
- "Content-Type": 'application/json'
30
- },
31
- body: JSON.stringify(input)
32
- }).then(res => res.json())
33
- console.log(response)
34
- opnPagedialog(false)
35
- window.location.href = input.page
36
- }
37
- catch (err) {
38
- console.log(err)
39
- }
40
-
41
- }
42
- }, children: [{
43
- t: 'label',
44
- txt: 'ENTER PATH FOR NEW PAGE'
45
- }, { t: 'input', attr: { name: 'page' }, cn: ['pathinput'] }]
46
-
47
- })
48
-
49
- console.log('test')
50
- appendElement(body, dialog);
51
- fetchElement('.pathinput').focus();
52
- }
53
- else {
54
- console.log('removed')
55
- fetchElement('.addpageform').remove();
56
- }
57
- }
58
-
59
- if (config.mode === 'dev') {
60
- const addPagebtn = gardener({
61
- t: 'button',
62
- cn: ['pb-1.5', 'flex', 'items-center', 'justify-center', 'h-15', 'w-15', 'bg-green-300', 'fixed', 'bottom-22', 'right-2', 'rounded-full', 'text-5xl'],
63
- children: [{ t: 'span', txt: '+' }],
64
- events: {
65
- click: opnPagedialog
66
- }
67
- });
68
-
69
- appendElement(body, addPagebtn);
70
-
71
-
72
- appendElement(body, gardener({
73
- t: 'p',
74
- cn: ['bg-gray-200', 'fixed', 'bottom-0', 'z-100', 'right-0', 'border-b-1', 'p-2', 'rounded-md'],
75
- children: [
76
- {
77
- t: 'span',
78
- txt: 'Press '
79
- },
80
- {
81
- t: 'span',
82
- cn: ['text-green-500', 'font-bold'],
83
- txt: 'Ctrl+h'
84
- },
85
- {
86
- t: 'span',
87
- txt: ' to toggle Hot Reload'
88
- },
89
- {
90
- t: 'form',
91
- attr: {
92
- id: 'hrcheckbox',
93
- },
94
- events: {
95
- click: () => togglehotreload()
96
- },
97
- cn: ['p-2', 'bg-red-300'],
98
- children: [{
99
- t: 'label',
100
- txt: 'Hot Reload ',
101
- }
102
- , {
103
- t: 'input',
104
- cn: ['hrcheckbox'],
105
- attr: {
106
- type: 'checkbox'
107
- }
108
- }]
109
- }
110
- ]
111
- }))
112
-
113
- //appendElement(body, gardener())
114
-
115
-
116
- togglehotreload();
117
- document.addEventListener('keydown', function(e) {
118
- // Detect Ctrl + H
119
- if (e.ctrlKey && e.key.toLowerCase() === 'h') {
120
- e.preventDefault(); // Stop browser from opening history
121
- // Your logic here...
122
- togglehotreload();
123
- }
124
- });
125
- }
126
-
127
- //if (config.mode === 'dev') {
128
-
129
-
130
- function togglehotreload() {
131
- const hr = hotReload;
132
- const hrcheck = fetchElement('#hrcheckbox');
133
-
134
- localStorage.setItem('hotreload', hr);
135
-
136
- hotReload = !hotReload;
137
-
138
- if (hr) {
139
- hrcheck.style.background = '#66e666';
140
- fetchElement('.hrcheckbox').checked = true;
141
- localStorage.setItem('hotreload', 'true');
142
- hotReloadtimeout = setTimeout(() => window.location.reload(), 1000);
143
- }
144
- else {
145
- hrcheck.style.background = 'red';
146
- fetchElement('.hrcheckbox').checked = false;
147
- localStorage.setItem('hotreload', 'false');
148
- clearTimeout(hotReloadtimeout);
149
- }
150
-
151
- //localStorage.setItem('hotreload', hotReload);
152
- }
153
-
154
- export function parserWindow(text) {
155
- if (config.mode !== 'dev') return;
156
-
157
-
158
- const result = gardener({
159
- t: 'div',
160
- cn: ['fixed', 'border-2', 'border-black', 'bg-gray-500', 'text-white', 'rounded-lg', 'z-90', 'w-2/4', 'h-2/4', 'left-1/4', 'flex', 'flex-col', 'justify-between', 'top-1/4'],
161
- children: [
162
- {
163
- t: 'div',
164
- cn: ['bg-gray-200', 'h-15', 'text-black', 'rounded-t-lg', 'flex', 'items-center', 'justify-around'],
165
- children: [
166
- {
167
- t: 'h3',
168
- cn: ['font-bold'],
169
- txt: 'Parser Window'
170
- },
171
- {
172
- t: 'button',
173
- cn: ['p-2', 'bg-red-300', 'rounded-lg', 'cursor-pointer'],
174
- txt: 'Add Component',
175
- attr: {
176
- id: 'copybtn'
177
- },
178
- events: {
179
- click: copytxt
180
- }
181
- }
182
- ]
183
- },
184
- {
185
- t: 'p',
186
- cn: ['p-5', 'overflow-scroll'],
187
- txt: text
188
- },
189
- ]
190
- })
191
-
192
- function copytxt() {
193
- result.remove()
194
-
195
- const compform = gardener({
196
- t: 'form',
197
- events: {
198
- submit: (event) => {
199
- event.preventDefault()
200
- copyText(text, `${fetchElement('.componentInp').value}.js`)
201
- compform.remove();
202
- }
203
- },
204
- cn: ['fixed', 'left-2/5', 'bg-gray-500', 'rounded-lg', 'block', 'top-2/5', 'p-2'],
205
- children: [
206
- {
207
- t: 'input',
208
- cn: ['bg-white', 'componentInp'],
209
- attr: {
210
- type: 'text',
211
- placeholder: 'Component Name'
212
- }
213
- }
214
- ]
215
- });
216
- appendElement(body, compform);
217
-
218
- fetchElement('.componentInp').focus();
219
- //setTimeout(() => result.remove(), 500);
220
- }
221
-
222
- appendElement(body, result);
223
- }
224
-
225
-
226
-
227
- async function copyText(txt, path) {
228
- // await navigator.clipboard.writeText(txt);
229
- try {
230
- const res = await fetch('/addcomponent', {
231
- method: 'POST',
232
- headers: {
233
- "Content-Type": 'application/json'
234
- },
235
- body: JSON.stringify({ component: txt, path: `${config.componentdir}/${path}` })
236
- })
237
-
238
- if (!res.ok) console.error('wrong');
239
-
240
- const data = await res.json()
241
- console.log(data);
242
-
243
- }
244
- catch (err) {
245
- console.error(err);
246
- }
247
-
248
- }
249
-
250
-
251
-
252
-
253
- export function fetchElement(param) {
254
- return document.querySelector(param);
255
- }
256
-
257
- export function appendElement(parent, child) {
258
- parent.appendChild(child);
259
- }
260
-
261
- export function createElement(type, classname) {
262
- let element = document.createElement(type);
263
- if (classname)
264
- element.classList.add(...classname);
265
- return element;
266
- }
267
-
268
- export function insertText(element, text) {
269
- element.innerText = text;
270
- }
271
-
272
- export function replaceElement(original, New) {
273
- original.replaceWith(New);
274
- }
275
-
276
- export function gardener(Dom) {
277
-
278
- if (Dom.nodeType === 1) return Dom;
279
- // detect if this is an SVG element
280
- const isSVG = [
281
- 'svg', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'g', 'defs', 'clipPath', 'use'
282
- ].includes(Dom.t);
283
-
284
- // create element accordingly
285
- let element;
286
-
287
- if (isSVG) {
288
- element = document.createElementNS('http://www.w3.org/2000/svg', Dom.t);
289
- if (Dom.cn)
290
- element.classList.add(...Dom.cn);
291
- }
292
- else {
293
- element = createElement(Dom.t, Dom.cn);
294
- }
295
-
296
- // text content (skip for SVG like <path>)
297
- if (Dom.txt) {
298
- insertText(element, Dom.txt);
299
- }
300
-
301
- // apply attributes safely
302
- const propertyNames = new Set([
303
- 'value', 'selected', 'muted', 'disabled',
304
- 'selectedIndex', 'volume', // etc.
305
- ]);
306
-
307
- if (Dom.attr) {
308
- for (const [key, value] of Object.entries(Dom.attr)) {
309
- if (isSVG || key.startsWith('data-') || key.startsWith('aria-')) {
310
- element.setAttribute(key, value);
311
- } else if (key in element && !propertyNames.has(key)) {
312
- // Prefer property for known safe cases
313
- try { element[key] = value === '' ? true : value; } catch (e) { element.setAttribute(key, value); }
314
- } else {
315
- element.setAttribute(key, value);
316
- }
317
- }
318
- }
319
-
320
- if (Dom.events) {
321
- Object.entries(Dom.events).forEach(([eventName, handler]) => {
322
- element.addEventListener(eventName, handler);
323
- });
324
- }
325
-
326
- // recursively handle children
327
- if (Dom.children) {
328
- Dom.children.forEach(child => appendElement(element, gardener(child)));
329
- }
330
-
331
- return element;
332
- }
333
-
334
- export function parser(element, isParent = true) {
335
- if (typeof element === 'string') {
336
- // If user passes raw HTML string
337
- const temp = document.createElement('div');
338
- temp.innerHTML = element.trim();
339
- element = temp.firstElementChild;
340
- }
341
-
342
- const obj = {
343
- t: element.tagName.toLowerCase(),
344
- };
345
-
346
- // add classes if present
347
- if (element.classList.length) {
348
- obj.cn = Array.from(element.classList);
349
- }
350
-
351
- // add attributes if present
352
- const attrs = {};
353
- for (const attr of element.attributes) {
354
- if (attr.name !== 'class') attrs[attr.name] = attr.value;
355
- }
356
- if (Object.keys(attrs).length) obj.attr = attrs;
357
- // add text content (only if no children)
358
- if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
359
- obj.txt = element.textContent.trim();
360
-
361
- if (isParent) {
362
- parserWindow(JSON.stringify(obj))
363
- }
364
-
365
- return obj;
366
- }
367
-
368
- // add children recursively
369
- const children = [];
370
- for (const child of element.children) {
371
- children.push(parser(child, false));
372
- }
373
- if (children.length) obj.children = children;
374
-
375
-
376
- if (isParent) {
377
- parserWindow(JSON.stringify(obj))
378
- }
379
-
380
- return obj
381
- //Let Browser do the migration from html to json and then use copy paste
382
- }
383
-
384
- export function imagePreloader(images) {
385
- const body = fetchElement('body')
386
- images.forEach(entry => {
387
- appendElement(body, gardener({
388
- t: 'img',
389
- cn: ['preloaderimage'],
390
- attr: {
391
- src: entry,
392
- alt: entry
393
- }
394
- }));
395
-
396
- setTimeout(() => {
397
- const images = document.querySelectorAll('.preloaderimage');
398
- images.forEach(entry => { entry.style.display = 'none' });
399
- }, 0)
400
-
401
- })
402
- }
403
-
404
-
@@ -1,57 +0,0 @@
1
- import { parser, fetchElement, replaceElement, gardener, appendElement } from './gardener.js'
2
-
3
- const body = fetchElement('#body')
4
-
5
- nextPagehandler();
6
- pageloader();
7
-
8
-
9
- function nextPagehandler() {
10
- const anchor = document.querySelectorAll('a')
11
- anchor.forEach(link => {
12
-
13
- link.addEventListener('click', (e) => {
14
-
15
- //event delegation
16
- const link = e.target.closest('a');
17
- if (!link) return;
18
- if (link.target === '_blank') return;
19
- //event delegation
20
-
21
- e.preventDefault();
22
- appendElement(body, gardener({
23
- t: 'div',
24
- cn: ['tempnpdiv', 'top-0', 'left-[100vw]', 'absolute', 'h-screen', 'w-screen', 'shadow-[30px_0_60px_15px_rgb(0,0,0)]'],
25
- }))
26
- const width = window.innerWidth
27
- console.log(width)
28
- body.style.transition = '.2s';
29
- body.style.transform = `translateX(-${width}px)`
30
- setTimeout(() => {
31
- window.location.href = link.href
32
- }, 200)
33
- })
34
- })
35
-
36
- window.addEventListener('pagehide', () => {
37
- setTimeout(() => {
38
- body.style.transform = 'translateX(0px)';
39
- setTimeout(() => {
40
- fetchElement('.tempnpdiv').remove()
41
-
42
- }, 200)
43
- }, 200);
44
- });
45
-
46
- }
47
-
48
- function pageloader() {
49
- const loader = fetchElement('.loader');
50
- loader.style.transition = '.4s';
51
- loader.style.opacity = '0';
52
- setTimeout(() => loader.remove(), 400)
53
-
54
- }
55
- //console.log('hellooo');
56
- //parser(fetchElement('.hero'));
57
- //parser(fetchElement('nav'));
@@ -1,216 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Gardener – Declarative DOM Library</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <!-- Assume your custom styles are still here -->
9
- <link href="/style.css" rel="stylesheet"/>
10
- </head>
11
-
12
- <body class="bg-slate-50 text-slate-900 antialiased">
13
-
14
- <div class="max-w-7xl mx-auto px-6 py-12 space-y-20">
15
-
16
- <!-- Hero / Introduction -->
17
- <section class="bg-gradient-to-br from-emerald-50 to-cyan-50 rounded-3xl p-10 lg:p-16 shadow-xl">
18
- <div class="flex flex-col lg:flex-row items-center justify-between gap-12">
19
- <div class="max-w-2xl space-y-6">
20
- <h1 class="text-4xl lg:text-5xl font-bold text-slate-800">Gardener</h1>
21
- <p class="text-xl text-slate-700 leading-relaxed">
22
- Gardener is a lightweight front-end library for creating and manipulating DOM elements using a clean, declarative JavaScript object syntax.
23
- </p>
24
- <p class="text-lg text-slate-600 leading-relaxed">
25
- It comes with a development server featuring hot reload, on-the-fly component creation from existing HTML, dynamic image resizing & caching, and zero virtual DOM / JSX / build-step philosophy.
26
- </p>
27
- </div>
28
-
29
- <img
30
- src="/cache/gardener_500x500.webp"
31
- alt="Gardener logo"
32
- class="w-64 lg:w-80 rounded-2xl shadow-2xl"
33
- />
34
- </div>
35
- </section>
36
-
37
- <!-- Design Philosophy -->
38
- <section class="bg-linear-to-br from-slate-900 to-slate-800 rounded-3xl p-10 lg:p-12 shadow-2xl space-y-6">
39
- <h2 class="text-3xl font-bold">Design Philosophy</h2>
40
- <ul class="list-disc list-inside space-y-3 text-lg">
41
- <li>No virtual DOM</li>
42
- <li>No JSX</li>
43
- <li>No compilation / build step</li>
44
- <li>DOM-first and fully deterministic</li>
45
- <li>Everything is explicit and inspectable in the browser</li>
46
- </ul>
47
- </section>
48
-
49
- <!-- Core API -->
50
- <section class="space-y-10">
51
- <h2 class="text-3xl font-bold text-center lg:text-left">Core Utilities</h2>
52
-
53
- <div class="grid md:grid-cols-2 gap-8">
54
- <div class="bg-white p-8 rounded-2xl shadow space-y-4">
55
- <h3 class="text-2xl font-semibold">DOM Helpers</h3>
56
- <ul class="list-disc list-inside space-y-2 text-slate-700">
57
- <li><code class="font-mono">fetchElement(selector)</code> — safe <code>querySelector</code> wrapper</li>
58
- <li><code>appendElement(parent, child)</code> — append child node</li>
59
- <li><code>replaceElement(oldElement, newElement)</code> — replace DOM node</li>
60
- <li><code>createElement(type, classes)</code> — low-level element factory</li>
61
- <li><code>imagePreloader(images)</code> — preload images efficiently</li>
62
- </ul>
63
- </div>
64
-
65
- <div class="bg-white p-8 rounded-2xl shadow space-y-4">
66
- <h3 class="text-2xl font-semibold">Gardener – The Declarative Builder</h3>
67
- <pre class="bg-slate-900 text-slate-100 text-sm p-5 rounded-xl overflow-x-auto font-mono">
68
- <code>gardener({
69
- t: 'div',
70
- cn: ['p-6', 'flex', 'gap-4'],
71
- attr: { id: 'hero', role: 'banner' },
72
- txt: 'Welcome',
73
- events: {
74
- click: () => console.log('clicked!')
75
- },
76
- children: [ /* nested objects */ ]
77
- })</code>
78
- </pre>
79
- </div>
80
- </div>
81
-
82
- <div class="bg-white p-8 rounded-2xl shadow overflow-x-auto">
83
- <table class="w-full text-left border border-slate-200">
84
- <thead class="bg-slate-100">
85
- <tr>
86
- <th class="p-4 font-semibold">Key</th>
87
- <th class="p-4 font-semibold">Description</th>
88
- </tr>
89
- </thead>
90
- <tbody class="text-slate-700">
91
- <tr><td class="p-4 border-t"><code>t</code></td> <td class="p-4 border-t">HTML/SVG tag name</td></tr>
92
- <tr><td class="p-4 border-t"><code>cn</code></td> <td class="p-4 border-t">Array of class names</td></tr>
93
- <tr><td class="p-4 border-t"><code>attr</code></td> <td class="p-4 border-t">Attributes / properties object</td></tr>
94
- <tr><td class="p-4 border-t"><code>txt</code></td> <td class="p-4 border-t">Text content (string)</td></tr>
95
- <tr><td class="p-4 border-t"><code>events</code></td><td class="p-4 border-t">Event listeners <code>{ event: handler }</code></td></tr>
96
- <tr><td class="p-4 border-t"><code>children</code></td><td class="p-4 border-t">Array of nested gardener objects</td></tr>
97
- </tbody>
98
- </table>
99
- </div>
100
- </section>
101
-
102
- <!-- Runtime Configuration -->
103
- <section class="bg-white p-10 rounded-2xl shadow space-y-6">
104
- <h2 class="text-3xl font-bold">Runtime Configuration</h2>
105
- <pre class="bg-slate-900 text-slate-100 p-6 rounded-xl overflow-x-auto font-mono text-sm">
106
- <code>const config = {
107
- mode: 'dev', // 'dev' | 'prod'
108
- componentdir: 'components',
109
- hotreload: true
110
- }</code>
111
- </pre>
112
- <ul class="list-disc list-inside space-y-3 text-slate-700">
113
- <li><strong>mode</strong>: <code>dev</code> → enables parser, hot reload, dev UI; <code>prod</code> → disables everything</li>
114
- <li><strong>componentdir</strong>: folder for auto-generated component files</li>
115
- <li><strong>hotreload</strong>: persisted in localStorage</li>
116
- </ul>
117
- </section>
118
-
119
- <!-- Hot Reload -->
120
- <section class="bg-white p-10 rounded-2xl shadow space-y-6">
121
- <h2 class="text-3xl font-bold">Hot Reload (Dev Mode)</h2>
122
- <ul class="list-disc list-inside space-y-3 text-slate-700">
123
- <li>State saved in <code>localStorage</code></li>
124
- <li>Auto-reload after ~1 second when enabled</li>
125
- <li>Disabling cancels pending timers</li>
126
- </ul>
127
- <div class="inline-block bg-slate-100 px-5 py-3 rounded-xl mt-4">
128
- <p class="font-semibold">Toggle shortcut: <kbd class="bg-white px-2 py-1 rounded border border-slate-300">Ctrl + H</kbd></p>
129
- </div>
130
- </section>
131
-
132
- <!-- SVG Support -->
133
- <section class="bg-white p-10 rounded-2xl shadow space-y-6">
134
- <h2 class="text-3xl font-bold">SVG Support</h2>
135
- <p class="text-slate-700">
136
- Gardener automatically uses the SVG namespace for these tags:
137
- </p>
138
- <pre class="bg-slate-900 text-emerald-300 font-mono p-5 rounded-xl inline-block">
139
- <code>svg, path, circle, rect, line, polygon, polyline, g, defs, clipPath, use</code>
140
- </pre>
141
- <p class="text-slate-700">All attributes are applied via <code>setAttribute</code> safely.</p>
142
- </section>
143
-
144
- <!-- Creating & Using Components -->
145
- <section class="grid lg:grid-cols-2 gap-10">
146
- <div class="bg-white p-10 rounded-2xl shadow space-y-6">
147
- <h2 class="text-3xl font-bold">Creating Components</h2>
148
- <ol class="list-decimal list-inside space-y-4 text-slate-700">
149
- <li>Write normal HTML + classes (give parent an <code>id</code> or unique selector)</li>
150
- <li>Run: <code>parser(fetchElement('#your-id'))</code></li>
151
- <li>Popup appears → choose component name</li>
152
- <li>File saved to <code>/src/frontend/components/YourName.js</code></li>
153
- </ol>
154
- </div>
155
-
156
- <div class="bg-white p-10 rounded-2xl shadow space-y-6">
157
- <h2 class="text-3xl font-bold">Using Components</h2>
158
- <pre class="bg-slate-900 text-slate-100 p-6 rounded-xl font-mono text-sm overflow-x-auto">
159
- <code>import { myHeader } from '/components/myHeader.js'
160
-
161
- replaceElement(
162
- fetchElement('#header-placeholder'),
163
- myHeader()
164
- )</code>
165
- </pre>
166
- </div>
167
- </section>
168
-
169
- <!-- Image Optimization -->
170
- <section class="bg-white p-10 rounded-2xl shadow space-y-6">
171
- <h2 class="text-3xl font-bold">Image Optimization & Caching</h2>
172
- <ul class="list-disc list-inside space-y-3 text-slate-700">
173
- <li>Place originals in <code>/src/frontend/assets/</code></li>
174
- <li>Use: <code>&lt;img src="/cache/photo_800x600.webp"&gt;</code></li>
175
- </ul>
176
- <p class="text-slate-600 mt-4">
177
- Server auto-converts to WebP, resizes, and caches on demand.
178
- </p>
179
- </section>
180
-
181
- <!-- Development Tools -->
182
- <section class="space-y-10">
183
- <h2 class="text-3xl font-bold text-center">Development Tools</h2>
184
-
185
- <div class="grid md:grid-cols-2 gap-8">
186
- <div class="bg-white p-8 rounded-2xl shadow space-y-4">
187
- <h3 class="text-2xl font-semibold">Create New Page</h3>
188
- <ol class="list-decimal list-inside space-y-2 text-slate-700">
189
- <li>Click floating <strong>+</strong> button (dev mode only)</li>
190
- <li>Enter route (<code>/about</code>, etc.)</li>
191
- <li>New page created & browser navigates</li>
192
- </ol>
193
- <p class="text-sm text-slate-500 mt-4">
194
- Template: <code>/src/backend/frontendtemplate.ejs</code>
195
- </p>
196
- </div>
197
-
198
- <div class="bg-white p-8 rounded-2xl shadow space-y-4">
199
- <h3 class="text-2xl font-semibold">Generate Static Site</h3>
200
- <p class="text-slate-700">
201
- Visit <code>/createstatic</code> to build a fully static version in
202
- <code>/src/frontendStatic</code>
203
- </p>
204
- </div>
205
- </div>
206
- </section>
207
-
208
- </div>
209
-
210
- <script src="/global.js" type="module"></script>
211
- <script type="module">
212
- import { parser, fetchElement } from "/gardener.js";
213
- // parser(fetchElement("#some-selector")); // uncomment when needed
214
- </script>
215
- </body>
216
- </html>