create-gardener 1.1.7 → 1.1.8
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 +2 -313
- package/package.json +1 -1
- package/template/package.json +11 -12
- package/template/pnpm-lock.yaml +1545 -0
- package/template/src/backend/controllers/gardener.controller.ts +1 -13
- package/template/src/frontend/gardenerST.js +185 -95
- package/template/src/frontend/static/gardener.js +62 -3
- package/template/src/frontend/style.css +1045 -0
- package/template/src/frontend/views/_.ejs +11 -2
- package/template/tsconfig.json +1 -1
- package/template/Readme.md +0 -349
|
@@ -19,20 +19,8 @@ interface AddComponentBody {
|
|
|
19
19
|
export function addComponent(req: Request<{}, {}, AddComponentBody>, res: Response) {
|
|
20
20
|
try {
|
|
21
21
|
const { path: filePath, component } = req.body;
|
|
22
|
-
const parsed = JSON.parse(component);
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
const formatted = JSON.stringify(parsed, null, 2);
|
|
26
|
-
|
|
27
|
-
const filecontent = `
|
|
28
|
-
import { gardener, fetchElement, replaceElement } from '../gardener.js'
|
|
29
|
-
|
|
30
|
-
export default function thisfun() {
|
|
31
|
-
return gardener(${formatted})
|
|
32
|
-
}
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
fs.writeFileSync(`./src/frontend/${filePath}`, filecontent, "utf8");
|
|
23
|
+
fs.writeFileSync(`./src/frontend/${filePath}`, component, "utf8");
|
|
36
24
|
|
|
37
25
|
res.json({ success: true });
|
|
38
26
|
} catch (err) {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
//Standalone gardener version for no backend environment
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
3
|
const config = {
|
|
6
4
|
mode: 'dev',
|
|
7
5
|
hotreload: false
|
|
@@ -9,57 +7,6 @@ const config = {
|
|
|
9
7
|
|
|
10
8
|
let hotReloadtimeout;
|
|
11
9
|
const body = fetchElement('body');
|
|
12
|
-
let hotReload = localStorage.getItem('hotreload');
|
|
13
|
-
|
|
14
|
-
if (hotReload === null) hotReload = config.hotreload;
|
|
15
|
-
else if (hotReload === 'true') hotReload = true
|
|
16
|
-
else if (hotReload === 'false') hotReload = false
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
appendElement(body, gardener({
|
|
22
|
-
t: 'p',
|
|
23
|
-
cn: ['bg-gray-200', 'fixed', 'bottom-0', 'z-100', 'right-0', 'border-b-1', 'p-2', 'rounded-md'],
|
|
24
|
-
children: [
|
|
25
|
-
{
|
|
26
|
-
t: 'span',
|
|
27
|
-
txt: 'Press '
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
t: 'span',
|
|
31
|
-
cn: ['text-green-500', 'font-bold'],
|
|
32
|
-
txt: 'Ctrl+h'
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
t: 'span',
|
|
36
|
-
txt: ' to toggle Hot Reload'
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
t: 'form',
|
|
40
|
-
attr: {
|
|
41
|
-
id: 'hrcheckbox',
|
|
42
|
-
},
|
|
43
|
-
events: {
|
|
44
|
-
click: () => togglehotreload()
|
|
45
|
-
},
|
|
46
|
-
cn: ['p-2', 'bg-red-300'],
|
|
47
|
-
children: [{
|
|
48
|
-
t: 'label',
|
|
49
|
-
txt: 'Hot Reload ',
|
|
50
|
-
}
|
|
51
|
-
, {
|
|
52
|
-
t: 'input',
|
|
53
|
-
cn: ['hrcheckbox'],
|
|
54
|
-
attr: {
|
|
55
|
-
type: 'checkbox'
|
|
56
|
-
}
|
|
57
|
-
}]
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
}))
|
|
61
|
-
|
|
62
|
-
//appendElement(body, gardener())
|
|
63
10
|
|
|
64
11
|
|
|
65
12
|
function applyHotReloadState() {
|
|
@@ -77,20 +24,79 @@ function applyHotReloadState() {
|
|
|
77
24
|
}
|
|
78
25
|
}
|
|
79
26
|
|
|
80
|
-
|
|
27
|
+
let hotReload = localStorage.getItem('hotreload');
|
|
28
|
+
if (hotReload === null) hotReload = config.hotreload;
|
|
29
|
+
else if (hotReload === 'true') hotReload = true
|
|
30
|
+
else if (hotReload === 'false') hotReload = false
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if (config.mode === 'dev') {
|
|
36
|
+
appendElement(body, gardener({
|
|
37
|
+
t: 'p',
|
|
38
|
+
attr: {
|
|
39
|
+
style: `
|
|
40
|
+
position:fixed;
|
|
41
|
+
bottom:0;
|
|
42
|
+
right:0;
|
|
43
|
+
z-index:100;
|
|
44
|
+
background:#e5e7eb;
|
|
45
|
+
padding:8px;
|
|
46
|
+
border-radius:6px;
|
|
47
|
+
font-family:sans-serif;
|
|
48
|
+
color:black;
|
|
49
|
+
font-size:14px;
|
|
50
|
+
`
|
|
51
|
+
},
|
|
52
|
+
children: [
|
|
53
|
+
{ t: 'span', txt: 'Press ' },
|
|
54
|
+
{
|
|
55
|
+
t: 'span',
|
|
56
|
+
attr: { style: 'color:#22c55e;font-weight:bold;' },
|
|
57
|
+
txt: 'Ctrl+h'
|
|
58
|
+
},
|
|
59
|
+
{ t: 'span', txt: ' to toggle Hot Reload' },
|
|
60
|
+
{
|
|
61
|
+
t: 'form',
|
|
62
|
+
attr: {
|
|
63
|
+
id: 'hrcheckbox',
|
|
64
|
+
style: 'margin-top:6px;padding:6px;background:red;border-radius:4px;cursor:pointer;'
|
|
65
|
+
},
|
|
66
|
+
events: { click: togglehotreload },
|
|
67
|
+
children: [
|
|
68
|
+
{ t: 'label', txt: 'Hot Reload ' },
|
|
69
|
+
{
|
|
70
|
+
t: 'input',
|
|
71
|
+
cn: ['hrcheckbox'],
|
|
72
|
+
attr: { type: 'checkbox' }
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
applyHotReloadState();
|
|
80
|
+
|
|
81
|
+
document.addEventListener('keydown', function(e) {
|
|
82
|
+
// Detect Ctrl + H
|
|
83
|
+
if (e.ctrlKey && e.key.toLowerCase() === 'h') {
|
|
84
|
+
e.preventDefault(); // Stop browser from opening history
|
|
85
|
+
// Your logic here...
|
|
86
|
+
togglehotreload();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//appendElement(body, gardener())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
81
95
|
|
|
82
96
|
// togglehotreload();
|
|
83
97
|
|
|
84
98
|
|
|
85
99
|
|
|
86
|
-
document.addEventListener('keydown', function(e) {
|
|
87
|
-
// Detect Ctrl + H
|
|
88
|
-
if (e.ctrlKey && e.key.toLowerCase() === 'h') {
|
|
89
|
-
e.preventDefault(); // Stop browser from opening history
|
|
90
|
-
// Your logic here...
|
|
91
|
-
togglehotreload();
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
100
|
|
|
95
101
|
//if (config.mode === 'dev') {
|
|
96
102
|
|
|
@@ -113,48 +119,83 @@ function togglehotreload() {
|
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const parsed = JSON.parse(input);
|
|
120
|
-
const text = JSON.stringify(parsed, null, 1);
|
|
122
|
+
function parserWindow(input) {
|
|
121
123
|
if (config.mode !== 'dev') return;
|
|
122
124
|
|
|
125
|
+
let text;
|
|
126
|
+
try {
|
|
127
|
+
text = JSON.stringify(JSON.parse(input), null, 2);
|
|
128
|
+
} catch {
|
|
129
|
+
text = input;
|
|
130
|
+
}
|
|
123
131
|
|
|
124
132
|
const result = gardener({
|
|
125
133
|
t: 'div',
|
|
126
|
-
|
|
134
|
+
attr: {
|
|
135
|
+
style: `
|
|
136
|
+
position:fixed;
|
|
137
|
+
top:25%;
|
|
138
|
+
left:25%;
|
|
139
|
+
width:50%;
|
|
140
|
+
height:50%;
|
|
141
|
+
background:#6b7280;
|
|
142
|
+
color:white;
|
|
143
|
+
border:2px solid black;
|
|
144
|
+
border-radius:8px;
|
|
145
|
+
z-index:90;
|
|
146
|
+
display:flex;
|
|
147
|
+
flex-direction:column;
|
|
148
|
+
justify-content:space-between;
|
|
149
|
+
`
|
|
150
|
+
},
|
|
127
151
|
children: [
|
|
128
152
|
{
|
|
129
153
|
t: 'div',
|
|
130
|
-
|
|
154
|
+
attr: {
|
|
155
|
+
style: `
|
|
156
|
+
background:#e5e7eb;
|
|
157
|
+
color:black;
|
|
158
|
+
padding:10px;
|
|
159
|
+
display:flex;
|
|
160
|
+
justify-content:space-between;
|
|
161
|
+
align-items:center;
|
|
162
|
+
border-top-left-radius:8px;
|
|
163
|
+
border-top-right-radius:8px;
|
|
164
|
+
`
|
|
165
|
+
},
|
|
131
166
|
children: [
|
|
132
|
-
{
|
|
133
|
-
t: 'h3',
|
|
134
|
-
cn: ['font-bold'],
|
|
135
|
-
txt: 'Parser Window'
|
|
136
|
-
},
|
|
167
|
+
{ t: 'h3', txt: 'Parser Window' },
|
|
137
168
|
{
|
|
138
169
|
t: 'button',
|
|
139
|
-
|
|
140
|
-
txt: 'Add Component',
|
|
170
|
+
txt: 'Copy JSON',
|
|
141
171
|
attr: {
|
|
142
|
-
|
|
172
|
+
style: `
|
|
173
|
+
padding:6px 10px;
|
|
174
|
+
background:#f87171;
|
|
175
|
+
border-radius:6px;
|
|
176
|
+
cursor:pointer;
|
|
177
|
+
`
|
|
143
178
|
},
|
|
144
179
|
events: {
|
|
145
|
-
click: (
|
|
180
|
+
click: () => copyTextToClipboard(text)
|
|
146
181
|
}
|
|
147
182
|
}
|
|
148
183
|
]
|
|
149
184
|
},
|
|
150
185
|
{
|
|
151
186
|
t: 'pre',
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
txt: text,
|
|
188
|
+
attr: {
|
|
189
|
+
style: `
|
|
190
|
+
padding:12px;
|
|
191
|
+
overflow:auto;
|
|
192
|
+
font-size:12px;
|
|
193
|
+
white-space:pre-wrap;
|
|
194
|
+
`
|
|
195
|
+
}
|
|
154
196
|
}
|
|
155
197
|
]
|
|
156
|
-
})
|
|
157
|
-
|
|
198
|
+
});
|
|
158
199
|
|
|
159
200
|
appendElement(body, result);
|
|
160
201
|
}
|
|
@@ -164,7 +205,7 @@ export function parserWindow(input) {
|
|
|
164
205
|
async function copyTextToClipboard(txt) {
|
|
165
206
|
try {
|
|
166
207
|
await navigator.clipboard.writeText(txt);
|
|
167
|
-
|
|
208
|
+
alert('Component copied to clipboard');
|
|
168
209
|
} catch (err) {
|
|
169
210
|
console.error('Clipboard copy failed', err);
|
|
170
211
|
}
|
|
@@ -178,9 +219,7 @@ export function fetchElement(param) {
|
|
|
178
219
|
}
|
|
179
220
|
|
|
180
221
|
export function appendElement(parent, child) {
|
|
181
|
-
if (typeof parent === 'string')
|
|
182
|
-
parent = fetchElement(parent);
|
|
183
|
-
}
|
|
222
|
+
if (typeof parent === 'string') parent = fetchElement(parent);
|
|
184
223
|
parent.appendChild(child);
|
|
185
224
|
}
|
|
186
225
|
|
|
@@ -196,9 +235,7 @@ export function insertText(element, text) {
|
|
|
196
235
|
}
|
|
197
236
|
|
|
198
237
|
export function replaceElement(original, New) {
|
|
199
|
-
if (typeof original === 'string')
|
|
200
|
-
original = fetchElement(original);
|
|
201
|
-
}
|
|
238
|
+
if (typeof original === 'string') original = fetchElement(original);
|
|
202
239
|
original.replaceWith(New);
|
|
203
240
|
}
|
|
204
241
|
|
|
@@ -260,12 +297,51 @@ export function gardener(Dom) {
|
|
|
260
297
|
return element;
|
|
261
298
|
}
|
|
262
299
|
|
|
300
|
+
function cleanStringAndList(input) {
|
|
301
|
+
const pattern = /\?"?(\w+)"?\?/g;
|
|
302
|
+
const vars = new Set();
|
|
303
|
+
let match;
|
|
304
|
+
|
|
305
|
+
while ((match = pattern.exec(input)) !== null) {
|
|
306
|
+
vars.add(match[1]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Replace ?var? with "+var+" and clean up resulting empty strings or double quotes
|
|
310
|
+
const cleanedString = input
|
|
311
|
+
.replace(pattern, '"+$1+"')
|
|
312
|
+
.replace(/^""\+/, '')
|
|
313
|
+
.replace(/\+""$/, '');
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
cleanedString,
|
|
317
|
+
extractedList: [...vars].join(', ')
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function generateFile(obj) {
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
const formatted = JSON.stringify(obj, null, 2);
|
|
325
|
+
const { cleanedString, extractedList } = cleanStringAndList(formatted);
|
|
326
|
+
|
|
327
|
+
return `
|
|
328
|
+
import { gardener, fetchElement, replaceElement } from '../gardener.js'
|
|
329
|
+
|
|
330
|
+
export default function thisfun({${extractedList}}) {
|
|
331
|
+
return gardener(${cleanedString})
|
|
332
|
+
}
|
|
333
|
+
`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
263
339
|
export function parser(element, isParent = true) {
|
|
264
340
|
if (typeof element === 'string') {
|
|
265
341
|
element = fetchElement(element);
|
|
266
|
-
// If user passes raw HTML string
|
|
267
342
|
}
|
|
268
343
|
|
|
344
|
+
console.log(element)
|
|
269
345
|
const obj = {
|
|
270
346
|
t: element.tagName.toLowerCase(),
|
|
271
347
|
};
|
|
@@ -286,28 +362,43 @@ export function parser(element, isParent = true) {
|
|
|
286
362
|
obj.txt = element.textContent.trim();
|
|
287
363
|
|
|
288
364
|
if (isParent) {
|
|
289
|
-
|
|
365
|
+
|
|
366
|
+
parserWindow(generateFile(obj))
|
|
290
367
|
}
|
|
291
368
|
|
|
292
369
|
return obj;
|
|
293
370
|
}
|
|
294
371
|
|
|
372
|
+
|
|
295
373
|
// add children recursively
|
|
296
374
|
const children = [];
|
|
297
|
-
for (const child of element.
|
|
375
|
+
for (const child of element.childNodes) {
|
|
376
|
+
if (child.nodeType === Node.COMMENT_NODE) continue;
|
|
377
|
+
|
|
378
|
+
if (child.nodeType === Node.TEXT_NODE && child.textContent.trim() === '') continue;
|
|
379
|
+
|
|
380
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
381
|
+
children.push({ t: 'span', txt: child.textContent.trim() });
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
298
384
|
children.push(parser(child, false));
|
|
299
385
|
}
|
|
300
386
|
if (children.length) obj.children = children;
|
|
301
387
|
|
|
302
388
|
|
|
303
389
|
if (isParent) {
|
|
304
|
-
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
parserWindow(generateFile(obj))
|
|
305
393
|
}
|
|
306
394
|
|
|
307
395
|
return obj
|
|
396
|
+
|
|
397
|
+
|
|
308
398
|
//Let Browser do the migration from html to json and then use copy paste
|
|
309
399
|
}
|
|
310
400
|
|
|
401
|
+
|
|
311
402
|
export function addEL(parent, event, fun) {
|
|
312
403
|
if (typeof parent === 'string') {
|
|
313
404
|
parent = fetchElement(parent);
|
|
@@ -315,8 +406,6 @@ export function addEL(parent, event, fun) {
|
|
|
315
406
|
parent.addEventListener(event, fun)
|
|
316
407
|
}
|
|
317
408
|
|
|
318
|
-
|
|
319
|
-
|
|
320
409
|
export function imagePreloader(images) {
|
|
321
410
|
const body = fetchElement('body')
|
|
322
411
|
images.forEach(entry => {
|
|
@@ -338,3 +427,4 @@ export function imagePreloader(images) {
|
|
|
338
427
|
}
|
|
339
428
|
|
|
340
429
|
|
|
430
|
+
|
|
@@ -337,11 +337,56 @@ export function gardener(Dom) {
|
|
|
337
337
|
return element;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
|
|
341
|
+
function cleanStringAndList(input) {
|
|
342
|
+
const pattern = /\?"?(\w+)"?\?/g;
|
|
343
|
+
const vars = new Set();
|
|
344
|
+
let match;
|
|
345
|
+
|
|
346
|
+
while ((match = pattern.exec(input)) !== null) {
|
|
347
|
+
vars.add(match[1]);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Replace ?var? with "+var+" and clean up resulting empty strings or double quotes
|
|
351
|
+
const cleanedString = input
|
|
352
|
+
.replace(pattern, '"+$1+"')
|
|
353
|
+
.replace(/^""\+/, '')
|
|
354
|
+
.replace(/\+""$/, '');
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
cleanedString,
|
|
358
|
+
extractedList: [...vars].join(', ')
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function generateFile(obj) {
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
const formatted = JSON.stringify(obj, null, 2);
|
|
366
|
+
const { cleanedString, extractedList } = cleanStringAndList(formatted);
|
|
367
|
+
|
|
368
|
+
return `
|
|
369
|
+
import { gardener, fetchElement, replaceElement } from '../gardener.js'
|
|
370
|
+
|
|
371
|
+
export default function thisfun({${extractedList}}) {
|
|
372
|
+
return gardener(${cleanedString})
|
|
373
|
+
}
|
|
374
|
+
`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
// Example:
|
|
379
|
+
// const result = cleanStringAndList('hi "{ritish}" how are you');
|
|
380
|
+
// console.log(result.cleanedString); // "hi ritish how are you"
|
|
381
|
+
// console.log(result.extractedList); // ["ritish"]
|
|
382
|
+
|
|
383
|
+
|
|
340
384
|
export function parser(element, isParent = true) {
|
|
341
385
|
if (typeof element === 'string') {
|
|
342
386
|
element = fetchElement(element);
|
|
343
387
|
}
|
|
344
388
|
|
|
389
|
+
console.log(element)
|
|
345
390
|
const obj = {
|
|
346
391
|
t: element.tagName.toLowerCase(),
|
|
347
392
|
};
|
|
@@ -362,25 +407,39 @@ export function parser(element, isParent = true) {
|
|
|
362
407
|
obj.txt = element.textContent.trim();
|
|
363
408
|
|
|
364
409
|
if (isParent) {
|
|
365
|
-
|
|
410
|
+
|
|
411
|
+
parserWindow(generateFile(obj))
|
|
366
412
|
}
|
|
367
413
|
|
|
368
414
|
return obj;
|
|
369
415
|
}
|
|
370
416
|
|
|
417
|
+
|
|
371
418
|
// add children recursively
|
|
372
419
|
const children = [];
|
|
373
|
-
for (const child of element.
|
|
420
|
+
for (const child of element.childNodes) {
|
|
421
|
+
if (child.nodeType === Node.COMMENT_NODE) continue;
|
|
422
|
+
|
|
423
|
+
if (child.nodeType === Node.TEXT_NODE && child.textContent.trim() === '') continue;
|
|
424
|
+
|
|
425
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
426
|
+
children.push({ t: 'span', txt: child.textContent.trim() });
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
374
429
|
children.push(parser(child, false));
|
|
375
430
|
}
|
|
376
431
|
if (children.length) obj.children = children;
|
|
377
432
|
|
|
378
433
|
|
|
379
434
|
if (isParent) {
|
|
380
|
-
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
parserWindow(generateFile(obj))
|
|
381
438
|
}
|
|
382
439
|
|
|
383
440
|
return obj
|
|
441
|
+
|
|
442
|
+
|
|
384
443
|
//Let Browser do the migration from html to json and then use copy paste
|
|
385
444
|
}
|
|
386
445
|
|