tss-stack 1.2.0 → 1.2.1
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 +0 -38
- package/bin/cli.js +33 -10
- package/package.json +1 -1
- package/src/generators/frontend.js +145 -145
package/README.md
CHANGED
|
@@ -283,44 +283,6 @@ tss-stack/
|
|
|
283
283
|
│ └── utils.js ← shared helpers (toPascal, toRoute, toCamel)
|
|
284
284
|
└── package.json
|
|
285
285
|
```
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## Local Development
|
|
290
|
-
|
|
291
|
-
Clone the repo and link it globally to test before publishing:
|
|
292
|
-
|
|
293
|
-
```bash
|
|
294
|
-
git clone https://github.com/your-username/tss-stack.git
|
|
295
|
-
cd tss-stack
|
|
296
|
-
npm install
|
|
297
|
-
npm link
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Now test it like a real user:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
tss-stack test-project
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
When done testing:
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
npm unlink -g tss-stack
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## Publishing a New Version
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
npm version patch # bug fix: 1.0.0 → 1.0.1
|
|
318
|
-
npm version minor # new feature: 1.0.0 → 1.1.0
|
|
319
|
-
npm version major # breaking: 1.0.0 → 2.0.0
|
|
320
|
-
|
|
321
|
-
npm publish
|
|
322
|
-
```
|
|
323
|
-
|
|
324
286
|
---
|
|
325
287
|
|
|
326
288
|
## Roadmap
|
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
const ora = require("ora");
|
|
@@ -310,14 +310,26 @@ process.stdout.write("\x1Bc");
|
|
|
310
310
|
|
|
311
311
|
async function showProjectTree(config) {
|
|
312
312
|
const backendRouteFiles = [
|
|
313
|
-
config.needsAuth ? "│ ├── auth.js" : null,
|
|
314
|
-
...config.tables.map((t) =>
|
|
313
|
+
config.needsAuth ? "│ │ ├── auth.js" : null,
|
|
314
|
+
...config.tables.map((t, i) => {
|
|
315
|
+
const isLast = i === config.tables.length - 1;
|
|
316
|
+
return `│ │ ${isLast && !config.tables.slice(i + 1).length ? "└──" : "├──"} ${t.name}.js`;
|
|
317
|
+
}),
|
|
315
318
|
].filter(Boolean);
|
|
316
319
|
|
|
320
|
+
const frontendContextFiles = config.needsAuth ? [
|
|
321
|
+
"│ │ ├── AuthContext.jsx",
|
|
322
|
+
] : [];
|
|
323
|
+
|
|
324
|
+
const frontendComponentFiles = config.needsAuth ? [
|
|
325
|
+
"│ │ └── PrivateRoute.jsx",
|
|
326
|
+
] : [];
|
|
327
|
+
|
|
317
328
|
const frontendPageFiles = [
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
config.
|
|
329
|
+
"│ │ ├── Home.jsx",
|
|
330
|
+
config.needsAuth ? "│ │ ├── Login.jsx" : null,
|
|
331
|
+
...config.tables.map((t) => `│ │ ├── ${toPascal(t.name)}.jsx`),
|
|
332
|
+
config.needsReports ? "│ │ └── Reports.jsx" : null,
|
|
321
333
|
].filter(Boolean);
|
|
322
334
|
|
|
323
335
|
const tree = [
|
|
@@ -328,9 +340,9 @@ async function showProjectTree(config) {
|
|
|
328
340
|
"│ │ ├── db.js",
|
|
329
341
|
"│ │ └── database.sql",
|
|
330
342
|
"│ ├── middleware/",
|
|
331
|
-
config.needsAuth ? "│ │ └── auth.js" :
|
|
343
|
+
config.needsAuth ? "│ │ └── auth.js" : "│ │ (none)",
|
|
332
344
|
"│ ├── routes/",
|
|
333
|
-
...
|
|
345
|
+
...backendRouteFiles,
|
|
334
346
|
"│ ├── server.js",
|
|
335
347
|
"│ ├── .env.example",
|
|
336
348
|
"│ └── package.json",
|
|
@@ -339,10 +351,21 @@ async function showProjectTree(config) {
|
|
|
339
351
|
" ├── src/",
|
|
340
352
|
" │ ├── api/",
|
|
341
353
|
" │ │ └── axios.js",
|
|
354
|
+
config.needsAuth ? " │ ├── context/" : null,
|
|
355
|
+
...frontendContextFiles,
|
|
356
|
+
config.needsAuth ? " │ ├── components/" : null,
|
|
357
|
+
...frontendComponentFiles,
|
|
342
358
|
" │ ├── pages/",
|
|
343
|
-
...
|
|
359
|
+
...frontendPageFiles,
|
|
344
360
|
" │ ├── App.jsx",
|
|
345
|
-
" │
|
|
361
|
+
" │ ├── main.jsx",
|
|
362
|
+
" │ └── index.css",
|
|
363
|
+
" ├── vite.config.js",
|
|
364
|
+
" ├── tailwind.config.js",
|
|
365
|
+
" ├── postcss.config.js",
|
|
366
|
+
" ├── index.html",
|
|
367
|
+
" ├── .env.local.example",
|
|
368
|
+
" ├── .gitignore",
|
|
346
369
|
" └── package.json",
|
|
347
370
|
].filter(Boolean);
|
|
348
371
|
|
package/package.json
CHANGED
|
@@ -3,38 +3,38 @@ const path = require("path");
|
|
|
3
3
|
const { toPascal, toRoute } = require("./utils");
|
|
4
4
|
|
|
5
5
|
function generateFrontend(config) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
6
|
+
const { projectName, tables, needsAuth, needsReports, targetDir } = config;
|
|
7
|
+
const root = path.join(targetDir, "frontend-project");
|
|
8
|
+
|
|
9
|
+
fs.outputFileSync(
|
|
10
|
+
path.join(root, "package.json"),
|
|
11
|
+
JSON.stringify(
|
|
12
|
+
{
|
|
13
|
+
name: "frontend-project",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
scripts: { dev: "vite", build: "vite build" },
|
|
16
|
+
dependencies: {
|
|
17
|
+
react: "^18.2.0",
|
|
18
|
+
"react-dom": "^18.2.0",
|
|
19
|
+
"react-router-dom": "^6.18.0",
|
|
20
|
+
axios: "^1.6.0",
|
|
21
|
+
},
|
|
22
|
+
devDependencies: {
|
|
23
|
+
vite: "^5.0.0",
|
|
24
|
+
"@vitejs/plugin-react": "^4.2.0",
|
|
25
|
+
tailwindcss: "^3.3.0",
|
|
26
|
+
autoprefixer: "^10.4.16",
|
|
27
|
+
postcss: "^8.4.31",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
null,
|
|
31
|
+
2
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
fs.outputFileSync(
|
|
36
|
+
path.join(root, "vite.config.js"),
|
|
37
|
+
`import { defineConfig } from 'vite';
|
|
38
38
|
import react from '@vitejs/plugin-react';
|
|
39
39
|
|
|
40
40
|
export default defineConfig({
|
|
@@ -45,11 +45,11 @@ export default defineConfig({
|
|
|
45
45
|
},
|
|
46
46
|
});
|
|
47
47
|
`
|
|
48
|
-
|
|
48
|
+
);
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
fs.outputFileSync(
|
|
51
|
+
path.join(root, "tailwind.config.js"),
|
|
52
|
+
`export default {
|
|
53
53
|
content: [
|
|
54
54
|
"./index.html",
|
|
55
55
|
"./src/**/*.{js,jsx}",
|
|
@@ -60,22 +60,22 @@ export default defineConfig({
|
|
|
60
60
|
plugins: [],
|
|
61
61
|
};
|
|
62
62
|
`
|
|
63
|
-
|
|
63
|
+
);
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
fs.outputFileSync(
|
|
66
|
+
path.join(root, "postcss.config.js"),
|
|
67
|
+
`export default {
|
|
68
68
|
plugins: {
|
|
69
69
|
tailwindcss: {},
|
|
70
70
|
autoprefixer: {},
|
|
71
71
|
},
|
|
72
72
|
};
|
|
73
73
|
`
|
|
74
|
-
|
|
74
|
+
);
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
fs.outputFileSync(
|
|
77
|
+
path.join(root, "index.html"),
|
|
78
|
+
`<!DOCTYPE html>
|
|
79
79
|
<html lang="en">
|
|
80
80
|
<head>
|
|
81
81
|
<meta charset="UTF-8" />
|
|
@@ -88,17 +88,17 @@ export default defineConfig({
|
|
|
88
88
|
</body>
|
|
89
89
|
</html>
|
|
90
90
|
`
|
|
91
|
-
|
|
91
|
+
);
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
fs.outputFileSync(
|
|
94
|
+
path.join(root, ".env.local.example"),
|
|
95
|
+
`VITE_API_URL=http://localhost:5000
|
|
96
96
|
`
|
|
97
|
-
|
|
97
|
+
);
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
fs.outputFileSync(
|
|
100
|
+
path.join(root, ".gitignore"),
|
|
101
|
+
`node_modules/
|
|
102
102
|
dist/
|
|
103
103
|
.env
|
|
104
104
|
.env.local
|
|
@@ -107,11 +107,11 @@ dist/
|
|
|
107
107
|
.idea/
|
|
108
108
|
.vscode/
|
|
109
109
|
`
|
|
110
|
-
|
|
110
|
+
);
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
fs.outputFileSync(
|
|
113
|
+
path.join(root, "src", "api", "axios.js"),
|
|
114
|
+
`import axios from "axios";
|
|
115
115
|
|
|
116
116
|
const API = axios.create({
|
|
117
117
|
baseURL: import.meta.env.VITE_API_URL || "http://localhost:5000",
|
|
@@ -120,11 +120,11 @@ const API = axios.create({
|
|
|
120
120
|
|
|
121
121
|
export default API;
|
|
122
122
|
`
|
|
123
|
-
|
|
123
|
+
);
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
fs.outputFileSync(
|
|
126
|
+
path.join(root, "src", "main.jsx"),
|
|
127
|
+
`import React from "react";
|
|
128
128
|
import ReactDOM from "react-dom/client";
|
|
129
129
|
import App from "./App";
|
|
130
130
|
import "./index.css";
|
|
@@ -135,11 +135,11 @@ ReactDOM.createRoot(document.getElementById("root")).render(
|
|
|
135
135
|
</React.StrictMode>
|
|
136
136
|
);
|
|
137
137
|
`
|
|
138
|
-
|
|
138
|
+
);
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
fs.outputFileSync(
|
|
141
|
+
path.join(root, "src", "index.css"),
|
|
142
|
+
`@tailwind base;
|
|
143
143
|
@tailwind components;
|
|
144
144
|
@tailwind utilities;
|
|
145
145
|
|
|
@@ -147,12 +147,12 @@ body {
|
|
|
147
147
|
font-family: system-ui, -apple-system, sans-serif;
|
|
148
148
|
}
|
|
149
149
|
`
|
|
150
|
-
|
|
150
|
+
);
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
if (needsAuth) {
|
|
153
|
+
fs.outputFileSync(
|
|
154
|
+
path.join(root, "src", "context", "AuthContext.jsx"),
|
|
155
|
+
`import React, { createContext, useState, useEffect } from "react";
|
|
156
156
|
import API from "../api/axios";
|
|
157
157
|
|
|
158
158
|
export const AuthContext = createContext();
|
|
@@ -182,11 +182,11 @@ export function AuthProvider({ children }) {
|
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
`
|
|
185
|
-
|
|
185
|
+
);
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
fs.outputFileSync(
|
|
188
|
+
path.join(root, "src", "components", "PrivateRoute.jsx"),
|
|
189
|
+
`import { Navigate } from "react-router-dom";
|
|
190
190
|
import { useContext } from "react";
|
|
191
191
|
import { AuthContext } from "../context/AuthContext";
|
|
192
192
|
|
|
@@ -197,12 +197,12 @@ export default function PrivateRoute({ children }) {
|
|
|
197
197
|
return user ? children : <Navigate to="/login" />;
|
|
198
198
|
}
|
|
199
199
|
`
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
fs.outputFileSync(
|
|
204
|
+
path.join(root, "src", "pages", "Home.jsx"),
|
|
205
|
+
`export default function Home() {
|
|
206
206
|
return (
|
|
207
207
|
<div className="p-6 max-w-2xl">
|
|
208
208
|
<h1 className="text-3xl font-bold mb-4">Welcome</h1>
|
|
@@ -211,20 +211,20 @@ export default function PrivateRoute({ children }) {
|
|
|
211
211
|
);
|
|
212
212
|
}
|
|
213
213
|
`
|
|
214
|
-
|
|
214
|
+
);
|
|
215
215
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
216
|
+
for (const table of tables) {
|
|
217
|
+
const name = toPascal(table.name);
|
|
218
|
+
const route = toRoute(table.name);
|
|
219
|
+
const fields = table.fields;
|
|
220
|
+
const ops = table.operations;
|
|
221
|
+
|
|
222
|
+
const stateFields = fields.map((f) => ` ${f}: ""`).join(",\n");
|
|
223
|
+
const formReset = fields.map((f) => `${f}: ""`).join(", ");
|
|
224
|
+
const editSet = fields.map((f) => `${f}: item.${f}`).join(", ");
|
|
225
|
+
const inputs = fields
|
|
226
|
+
.map(
|
|
227
|
+
(f) => ` <input
|
|
228
228
|
type="text"
|
|
229
229
|
placeholder="${f}"
|
|
230
230
|
value={form.${f}}
|
|
@@ -232,18 +232,18 @@ export default function PrivateRoute({ children }) {
|
|
|
232
232
|
className="border p-2 rounded w-full"
|
|
233
233
|
required
|
|
234
234
|
/>`
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
)
|
|
236
|
+
.join("\n");
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
const tableHeaders = ["id", ...fields, "created_at"]
|
|
239
|
+
.map((f) => ` <th className="border px-4 py-2">${f}</th>`)
|
|
240
|
+
.join("\n");
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
const tableRow = ["id", ...fields, "created_at"]
|
|
243
|
+
.map((f) => ` <td className="border px-4 py-2">{item.${f}}</td>`)
|
|
244
|
+
.join("\n");
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
let page = `import { useState, useEffect } from "react";
|
|
247
247
|
import API from "../api/axios";
|
|
248
248
|
|
|
249
249
|
export default function ${name}() {
|
|
@@ -272,8 +272,8 @@ ${ops.includes("update") ? " const [editId, setEditId] = useState(null);" : ""}
|
|
|
272
272
|
|
|
273
273
|
`;
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
if (ops.includes("insert")) {
|
|
276
|
+
page += ` const handleSubmit = async (e) => {
|
|
277
277
|
e.preventDefault();
|
|
278
278
|
setLoading(true);
|
|
279
279
|
setError("");
|
|
@@ -298,10 +298,10 @@ ${ops.includes("update") ? ` if (editId) {
|
|
|
298
298
|
};
|
|
299
299
|
|
|
300
300
|
`;
|
|
301
|
-
|
|
301
|
+
}
|
|
302
302
|
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
if (ops.includes("delete")) {
|
|
304
|
+
page += ` const handleDelete = async (id) => {
|
|
305
305
|
if (!window.confirm("Are you sure?")) return;
|
|
306
306
|
setLoading(true);
|
|
307
307
|
setError("");
|
|
@@ -317,10 +317,10 @@ ${ops.includes("update") ? ` if (editId) {
|
|
|
317
317
|
};
|
|
318
318
|
|
|
319
319
|
`;
|
|
320
|
-
|
|
320
|
+
}
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
if (ops.includes("update")) {
|
|
323
|
+
page += ` const handleEdit = (item) => {
|
|
324
324
|
setEditId(item.id);
|
|
325
325
|
setForm({ ${editSet} });
|
|
326
326
|
};
|
|
@@ -331,9 +331,9 @@ ${ops.includes("update") ? ` if (editId) {
|
|
|
331
331
|
};
|
|
332
332
|
|
|
333
333
|
`;
|
|
334
|
-
|
|
334
|
+
}
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
page += ` return (
|
|
337
337
|
<div className="p-6">
|
|
338
338
|
<h1 className="text-2xl font-bold mb-4">${name}</h1>
|
|
339
339
|
|
|
@@ -374,14 +374,14 @@ ${ops.includes("delete") ? ' <button onClick={() => handleDelete(item
|
|
|
374
374
|
}
|
|
375
375
|
`;
|
|
376
376
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
377
|
+
fs.outputFileSync(path.join(root, "src", "pages", `${name}.jsx`), page);
|
|
378
|
+
console.log(` [✓] pages/${name}.jsx`);
|
|
379
|
+
}
|
|
380
380
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
if (needsAuth) {
|
|
382
|
+
fs.outputFileSync(
|
|
383
|
+
path.join(root, "src", "pages", "Login.jsx"),
|
|
384
|
+
`import { useState, useContext } from "react";
|
|
385
385
|
import { useNavigate } from "react-router-dom";
|
|
386
386
|
import { AuthContext } from "../context/AuthContext";
|
|
387
387
|
import API from "../api/axios";
|
|
@@ -447,13 +447,13 @@ export default function Login() {
|
|
|
447
447
|
);
|
|
448
448
|
}
|
|
449
449
|
`
|
|
450
|
-
|
|
451
|
-
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
452
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
453
|
+
if (needsReports) {
|
|
454
|
+
fs.outputFileSync(
|
|
455
|
+
path.join(root, "src", "pages", "Reports.jsx"),
|
|
456
|
+
`export default function Reports() {
|
|
457
457
|
return (
|
|
458
458
|
<div className="p-6">
|
|
459
459
|
<h1 className="text-2xl font-bold mb-4">Reports</h1>
|
|
@@ -462,27 +462,27 @@ export default function Login() {
|
|
|
462
462
|
);
|
|
463
463
|
}
|
|
464
464
|
`
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const imports = tables
|
|
469
|
+
.map((t) => `import ${toPascal(t.name)} from "./pages/${toPascal(t.name)}";`)
|
|
470
|
+
.join("\n");
|
|
471
|
+
|
|
472
|
+
const routes = tables
|
|
473
|
+
.map((t) => {
|
|
474
|
+
const route = `<Route path="/${toRoute(t.name)}" element={<${toPascal(t.name)} />} />`;
|
|
475
|
+
return needsAuth
|
|
476
|
+
? ` <Route path="/${toRoute(t.name)}" element={<PrivateRoute><${toPascal(t.name)} /></PrivateRoute>} />`
|
|
477
|
+
: ` ${route}`;
|
|
478
|
+
})
|
|
479
|
+
.join("\n");
|
|
480
|
+
|
|
481
|
+
const navLinks = tables
|
|
482
|
+
.map((t) => ` <Link to="/${toRoute(t.name)}" className="hover:underline">${toPascal(t.name)}</Link>`)
|
|
483
|
+
.join("\n");
|
|
484
|
+
|
|
485
|
+
let app = `import { BrowserRouter, Routes, Route, Link, useNavigate } from "react-router-dom";
|
|
486
486
|
${imports}
|
|
487
487
|
${needsAuth ? 'import Login from "./pages/Login";\nimport PrivateRoute from "./components/PrivateRoute";\nimport { AuthContext, AuthProvider } from "./context/AuthContext";\nimport { useContext } from "react";' : ""}
|
|
488
488
|
${needsReports ? 'import Reports from "./pages/Reports";' : ""}
|
|
@@ -534,8 +534,8 @@ ${needsAuth ? ' <AuthProvider>\n <BrowserRouter>\n <AppRoutes />\
|
|
|
534
534
|
}
|
|
535
535
|
`;
|
|
536
536
|
|
|
537
|
-
|
|
538
|
-
|
|
537
|
+
fs.outputFileSync(path.join(root, "src", "App.jsx"), app);
|
|
538
|
+
console.log(" [✓] App.jsx");
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
module.exports = { generateFrontend };
|