create-gardener 2.0.2 → 2.0.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.
- package/package.json +2 -2
- package/template/jsconfig.json +6 -12
- package/template/package.json +3 -2
- package/template/pnpm-lock.yaml +537 -12
- package/template/src/backend/controllers/gardener/addPage.ts +40 -3
- package/template/src/backend/controllers/gardener/index.ts +1 -0
- package/template/src/backend/controllers/gardener/saveTemplate.ts +60 -0
- package/template/src/backend/routes/gardener.route.ts +2 -10
- package/template/src/frontend/static/gardenerDev.js +98 -5
- package/template/src/frontend/static/pages/{_get-started.js → pages._al.js} +1 -4
- package/template/src/frontend/static/pages/pages._new.js +2 -0
- package/template/src/frontend/static/style.css +1070 -2
- package/template/src/frontend/static/style2.css +4 -0
- package/template/src/frontend/template/template._.ejs +120 -0
- package/template/src/frontend/views/_.ejs +9 -3
- package/template/src/frontend/frontendtemplate.ejs +0 -24
- package/template/src/frontend/views/_get-started.ejs +0 -25
- /package/template/src/frontend/static/pages/{_.js → pages._.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Request, Response } from "express";
|
|
2
2
|
import fsp from "fs/promises";
|
|
3
|
+
import { access } from "fs/promises";
|
|
3
4
|
import path from 'path';
|
|
4
5
|
|
|
5
6
|
import { fileURLToPath } from "url";
|
|
@@ -7,22 +8,58 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
8
|
const __dirname = path.dirname(__filename);
|
|
8
9
|
|
|
9
10
|
const frontendDir = path.resolve(__dirname, '..', '..', '..', 'frontend');
|
|
11
|
+
const templateDir = path.join(frontendDir, 'template');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async function findTemplate(fileName: string) {
|
|
16
|
+
while (fileName.length !== 0) {
|
|
17
|
+
|
|
18
|
+
console.log(fileName)
|
|
19
|
+
const searchFile = `template.${fileName}.ejs`;
|
|
20
|
+
console.log(searchFile)
|
|
21
|
+
const searchPath = path.join(templateDir, searchFile);
|
|
22
|
+
|
|
23
|
+
console.log(searchPath)
|
|
24
|
+
try {
|
|
25
|
+
await access(searchPath); // ✅ checks if file exists
|
|
26
|
+
return searchPath; // return full path immediately
|
|
27
|
+
} catch {
|
|
28
|
+
// file does not exist → continue
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const lastUnderscore = fileName.lastIndexOf('_');
|
|
32
|
+
if (lastUnderscore === -1) break;
|
|
33
|
+
|
|
34
|
+
fileName = fileName.substring(0, lastUnderscore + 1);
|
|
35
|
+
console.log(fileName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ❗ explicit failure instead of silent bug
|
|
39
|
+
throw new Error("Template not found");
|
|
40
|
+
}
|
|
10
41
|
|
|
11
42
|
export async function addPage(req: Request, res: Response) {
|
|
12
43
|
try {
|
|
13
44
|
const pagename: string = req.body.page;
|
|
14
45
|
const name = pagename.replaceAll('/', '_');
|
|
15
46
|
|
|
16
|
-
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const templatePath = await findTemplate(name);//path.join(frontendDir, findTemplate(name)); //path.join(frontendDir, 'frontendtemplate.ejs');
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
17
54
|
const viewPath = path.join(frontendDir, `views`, `${name}.ejs`);
|
|
18
55
|
const routePath = path.resolve(__dirname, '..', '..', 'routes', 'gardener.route.ts');
|
|
19
56
|
const jsDir = path.join(frontendDir, 'static/pages');
|
|
20
|
-
const jsFilePath = path.join(jsDir,
|
|
57
|
+
const jsFilePath = path.join(jsDir, `pages.${name}.js`);
|
|
21
58
|
|
|
22
59
|
const templateContent = await fsp.readFile(templatePath, 'utf8');
|
|
23
60
|
await fsp.writeFile(viewPath, templateContent, "utf8");
|
|
24
61
|
|
|
25
|
-
await replaceLastOccurrence(viewPath, '<script', `<script src="/static/pages
|
|
62
|
+
await replaceLastOccurrence(viewPath, '<script', `<script src="/static/pages/pages.${name}.js" type='module'></script>`);
|
|
26
63
|
|
|
27
64
|
const routeEntry = `router.route("${pagename}").get((req: Request, res: Response) => res.render("${name}"));\n`;
|
|
28
65
|
await fsp.appendFile(routePath, routeEntry, "utf8");
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
import fsp from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const frontendDir = path.resolve(__dirname, "..", "..", "..", "frontend");
|
|
10
|
+
const templateDir = path.join(frontendDir, "template");
|
|
11
|
+
|
|
12
|
+
export async function saveTemplate(req: Request, res: Response) {
|
|
13
|
+
try {
|
|
14
|
+
const { path: reqPath } = req.body; // ✅ renamed
|
|
15
|
+
|
|
16
|
+
const name = reqPath.replaceAll("/", "_");
|
|
17
|
+
|
|
18
|
+
const sourceFile = path.join(frontendDir, 'views', `${name}.ejs`);
|
|
19
|
+
|
|
20
|
+
const sourceFileContent = await fsp.readFile(sourceFile, "utf-8");
|
|
21
|
+
|
|
22
|
+
const targetFileContent = replaceLastOccurrence(
|
|
23
|
+
sourceFileContent,
|
|
24
|
+
`/static/pages/pages.${name}.js`,
|
|
25
|
+
""
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// ✅ ensure directory exists
|
|
29
|
+
await fsp.mkdir(templateDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
await fsp.writeFile(
|
|
32
|
+
path.join(templateDir, `template.${name}.ejs`),
|
|
33
|
+
targetFileContent,
|
|
34
|
+
"utf-8"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return res.json({ message: "Template Saved Successfully" });
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(err);
|
|
40
|
+
return res.status(500).json({ message: "Something went wrong" });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function replaceLastOccurrence(
|
|
45
|
+
content: string,
|
|
46
|
+
searchPattern: string,
|
|
47
|
+
replacementLine: string
|
|
48
|
+
) {
|
|
49
|
+
const lines = content.split("\n");
|
|
50
|
+
|
|
51
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
52
|
+
if (lines[i]!.includes(searchPattern)) {
|
|
53
|
+
lines[i] = replacementLine; // ✅ replace cleanly
|
|
54
|
+
return lines.join("\n"); // ✅ return result
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.warn(`Pattern not found: ${searchPattern}`);
|
|
59
|
+
return content; // fallback
|
|
60
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Request, Response } from 'express';
|
|
2
2
|
import { Router } from "express";
|
|
3
|
-
import { addComponent, addPage, createStatic, imageOptimiser } from "../controllers/gardener/index.js";
|
|
3
|
+
import { addComponent, addPage, createStatic, imageOptimiser, saveTemplate } from "../controllers/gardener/index.js";
|
|
4
4
|
|
|
5
5
|
const router: Router = Router();
|
|
6
6
|
export default router;
|
|
@@ -14,6 +14,7 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
14
14
|
router.route("/createstatic").get(createStatic);
|
|
15
15
|
router.route('/addcomponent').post(addComponent);
|
|
16
16
|
router.route('/addpage').post(addPage);
|
|
17
|
+
router.route('/savetemplate').post(saveTemplate);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
|
|
@@ -21,12 +22,3 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
router.route('/').get((req: Request, res: Response) => res.render('_'));
|
|
24
|
-
router.route('/login').get((req: Request, res: Response) => res.render('_login'));
|
|
25
|
-
router.route("/test").get((req: Request, res: Response) => res.render("_test"))
|
|
26
|
-
router.route("/rd").get((req: Request, res: Response) => res.render("_rd"));
|
|
27
|
-
router.route("/car").get((req: Request, res: Response) => res.render("_car"));
|
|
28
|
-
router.route("/onemore").get((req: Request, res: Response) => res.render("_onemore"));
|
|
29
|
-
|
|
30
|
-
router.route("/re").get((req: Request, res: Response) => res.render("_re"));
|
|
31
|
-
router.route("/playground").get((req: Request, res: Response) => res.render("_playground"));
|
|
32
|
-
router.route("/get-started").get((req: Request, res: Response) => res.render("_get-started"));
|
|
@@ -20,15 +20,108 @@ else if (localStore === 'false') hotReload = false
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
// const addPagebtn = gardener({
|
|
24
|
+
// t: 'button',
|
|
25
|
+
// 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'],
|
|
26
|
+
// children: [{ t: 'span', txt: '+' }],
|
|
27
|
+
// events: {
|
|
28
|
+
// click: opnPagedialog
|
|
29
|
+
// }
|
|
30
|
+
// });
|
|
31
|
+
|
|
32
|
+
const pagebtns = gardener({
|
|
33
|
+
t: 'div',
|
|
34
|
+
cn: [
|
|
35
|
+
'fixed',
|
|
36
|
+
'bottom-20',
|
|
37
|
+
'right-4',
|
|
38
|
+
'flex',
|
|
39
|
+
'flex-col',
|
|
40
|
+
'gap-2',
|
|
41
|
+
'bg-white',
|
|
42
|
+
'shadow-lg',
|
|
43
|
+
'rounded-xl',
|
|
44
|
+
'p-3',
|
|
45
|
+
'z-50'
|
|
46
|
+
],
|
|
47
|
+
children: [
|
|
48
|
+
{
|
|
49
|
+
t: 'button',
|
|
50
|
+
cn: [
|
|
51
|
+
'px-4',
|
|
52
|
+
'py-2',
|
|
53
|
+
'bg-blue-500',
|
|
54
|
+
'text-white',
|
|
55
|
+
'rounded-lg',
|
|
56
|
+
'hover:bg-blue-600',
|
|
57
|
+
'transition'
|
|
58
|
+
],
|
|
59
|
+
children: [{ t: 'span', txt: 'New Page' }],
|
|
60
|
+
events: {
|
|
61
|
+
click: opnPagedialog
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
t: 'button',
|
|
66
|
+
cn: [
|
|
67
|
+
'px-4',
|
|
68
|
+
'py-2',
|
|
69
|
+
'bg-gray-800',
|
|
70
|
+
'text-white',
|
|
71
|
+
'rounded-lg',
|
|
72
|
+
'hover:bg-gray-900',
|
|
73
|
+
'transition'
|
|
74
|
+
],
|
|
75
|
+
children: [{ t: 'span', txt: 'Save Template' }],
|
|
76
|
+
events: {
|
|
77
|
+
click: async () => {
|
|
78
|
+
const result = await fetch('/savetemplate', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { "Content-Type": 'application/json' },
|
|
81
|
+
body: JSON.stringify({ path: window.location.pathname })
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const data = await result.json(); // ✅ fix
|
|
85
|
+
alert(data.message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
});
|
|
23
91
|
const addPagebtn = gardener({
|
|
24
|
-
t: '
|
|
25
|
-
cn: ['
|
|
26
|
-
children: [{ t: 'span', txt: '+' }],
|
|
92
|
+
t: 'div',
|
|
93
|
+
cn: ['fixed', 'bottom-4', 'right-4', 'z-50'],
|
|
27
94
|
events: {
|
|
28
|
-
|
|
29
|
-
|
|
95
|
+
mouseenter: () => appendElement(addPagebtn, pagebtns),
|
|
96
|
+
mouseleave: () => pagebtns.remove()
|
|
97
|
+
},
|
|
98
|
+
children: [
|
|
99
|
+
{
|
|
100
|
+
t: 'span',
|
|
101
|
+
// cn: ['pb-1.5', 'flex', 'items-center', 'justify-center', 'h-15', 'w-15', 'bg-black', 'text-white', 'fixed', 'bottom-22', 'right-2'],
|
|
102
|
+
|
|
103
|
+
cn: [
|
|
104
|
+
'flex',
|
|
105
|
+
'items-center',
|
|
106
|
+
'justify-center',
|
|
107
|
+
'h-14',
|
|
108
|
+
'w-14',
|
|
109
|
+
'bg-black',
|
|
110
|
+
'text-white',
|
|
111
|
+
'fixed',
|
|
112
|
+
'bottom-22',
|
|
113
|
+
'right-2',
|
|
114
|
+
'rounded-full',
|
|
115
|
+
'shadow-lg',
|
|
116
|
+
'cursor-pointer',
|
|
117
|
+
],
|
|
118
|
+
txt: 'GR'
|
|
119
|
+
}
|
|
120
|
+
]
|
|
30
121
|
});
|
|
31
122
|
|
|
123
|
+
|
|
124
|
+
|
|
32
125
|
if (mode === 'dev') {
|
|
33
126
|
appendElement(body, addPagebtn);
|
|
34
127
|
|