create-caspian-app 0.0.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 +418 -0
- package/dist/.prettierrc +10 -0
- package/dist/app-gitignore +28 -0
- package/dist/caspian.js +2 -0
- package/dist/index.js +2 -0
- package/dist/main.py +525 -0
- package/dist/postcss.config.js +6 -0
- package/dist/public/css/styles.css +1 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/js/main.js +1 -0
- package/dist/public/js/pp-reactive-v1.js +1 -0
- package/dist/pyproject.toml +9 -0
- package/dist/settings/bs-config.json +7 -0
- package/dist/settings/bs-config.ts +291 -0
- package/dist/settings/build.ts +19 -0
- package/dist/settings/component-map.json +1361 -0
- package/dist/settings/component-map.ts +381 -0
- package/dist/settings/files-list.json +36 -0
- package/dist/settings/files-list.ts +49 -0
- package/dist/settings/project-name.ts +119 -0
- package/dist/settings/python-server.ts +173 -0
- package/dist/settings/utils.ts +239 -0
- package/dist/settings/vite-plugins/generate-global-types.ts +246 -0
- package/dist/src/app/error.html +130 -0
- package/dist/src/app/globals.css +24 -0
- package/dist/src/app/index.html +159 -0
- package/dist/src/app/layout.html +22 -0
- package/dist/src/app/not-found.html +18 -0
- package/dist/ts/global-functions.ts +35 -0
- package/dist/ts/main.ts +5 -0
- package/dist/tsconfig.json +111 -0
- package/dist/vite.config.ts +61 -0
- package/package.json +42 -0
- package/tsconfig.json +49 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, mkdirSync, readFileSync } from "fs";
|
|
2
|
+
import browserSync, { BrowserSyncInstance } from "browser-sync";
|
|
3
|
+
import { generateFileListJson } from "./files-list.js";
|
|
4
|
+
import { join, dirname, relative } from "path";
|
|
5
|
+
import { getFileMeta, PUBLIC_DIR, SRC_DIR } from "./utils.js";
|
|
6
|
+
import { DebouncedWorker, createSrcWatcher, DEFAULT_AWF } from "./utils.js";
|
|
7
|
+
import {
|
|
8
|
+
startPythonServer,
|
|
9
|
+
restartPythonServer,
|
|
10
|
+
waitForPort,
|
|
11
|
+
} from "./python-server.js";
|
|
12
|
+
import { componentMap } from "./component-map.js";
|
|
13
|
+
import { createServer } from "net";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
|
|
16
|
+
const { __dirname } = getFileMeta();
|
|
17
|
+
const bs: BrowserSyncInstance = browserSync.create();
|
|
18
|
+
|
|
19
|
+
const PUBLIC_IGNORE_DIRS = [""];
|
|
20
|
+
let previousRouteFiles: string[] = [];
|
|
21
|
+
let lastChangedFile: string | null = null;
|
|
22
|
+
|
|
23
|
+
let pythonPort = 0;
|
|
24
|
+
let bsPort = 0;
|
|
25
|
+
|
|
26
|
+
function getAvailablePort(startPort: number): Promise<number> {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const server = createServer();
|
|
29
|
+
server.listen(startPort, () => {
|
|
30
|
+
server.close(() => resolve(startPort));
|
|
31
|
+
});
|
|
32
|
+
server.on("error", () => {
|
|
33
|
+
resolve(getAvailablePort(startPort + 1));
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const pipeline = new DebouncedWorker(
|
|
39
|
+
async () => {
|
|
40
|
+
const changedFile = lastChangedFile;
|
|
41
|
+
lastChangedFile = null;
|
|
42
|
+
|
|
43
|
+
await generateFileListJson();
|
|
44
|
+
await componentMap();
|
|
45
|
+
|
|
46
|
+
const needsPythonRestart =
|
|
47
|
+
changedFile &&
|
|
48
|
+
changedFile.endsWith(".py") &&
|
|
49
|
+
!changedFile.includes("__pycache__");
|
|
50
|
+
|
|
51
|
+
if (needsPythonRestart) {
|
|
52
|
+
await restartPythonServer(pythonPort);
|
|
53
|
+
updateRouteFilesCache();
|
|
54
|
+
|
|
55
|
+
const isReady = await waitForPort(pythonPort);
|
|
56
|
+
if (isReady && bs.active) {
|
|
57
|
+
bs.reload();
|
|
58
|
+
} else {
|
|
59
|
+
console.error(chalk.red("⚠ Server failed to start or timed out."));
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
changedFile &&
|
|
66
|
+
(changedFile.endsWith(".pyc") || changedFile.includes("__pycache__"))
|
|
67
|
+
)
|
|
68
|
+
return;
|
|
69
|
+
|
|
70
|
+
const filesListPath = join(__dirname, "files-list.json");
|
|
71
|
+
if (existsSync(filesListPath)) {
|
|
72
|
+
const filesList = JSON.parse(readFileSync(filesListPath, "utf-8"));
|
|
73
|
+
const routeFiles = filesList.filter(
|
|
74
|
+
(f: string) =>
|
|
75
|
+
f.startsWith("./src/") && (f.endsWith(".py") || f.endsWith(".html"))
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const routesChanged =
|
|
79
|
+
previousRouteFiles.length !== routeFiles.length ||
|
|
80
|
+
!routeFiles.every((f: string) => previousRouteFiles.includes(f));
|
|
81
|
+
|
|
82
|
+
if (previousRouteFiles.length > 0 && routesChanged) {
|
|
83
|
+
console.log(
|
|
84
|
+
chalk.yellow(
|
|
85
|
+
"→ Structure changed (New/Deleted file), restarting Python server..."
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
await restartPythonServer(pythonPort);
|
|
89
|
+
const isReady = await waitForPort(pythonPort);
|
|
90
|
+
if (isReady && bs.active) bs.reload();
|
|
91
|
+
} else if (bs.active) {
|
|
92
|
+
bs.reload();
|
|
93
|
+
}
|
|
94
|
+
previousRouteFiles = routeFiles;
|
|
95
|
+
} else if (bs.active) {
|
|
96
|
+
bs.reload();
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
350,
|
|
100
|
+
"bs-pipeline"
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
function updateRouteFilesCache() {
|
|
104
|
+
const filesListPath = join(__dirname, "files-list.json");
|
|
105
|
+
if (existsSync(filesListPath)) {
|
|
106
|
+
const filesList = JSON.parse(readFileSync(filesListPath, "utf-8"));
|
|
107
|
+
previousRouteFiles = filesList.filter(
|
|
108
|
+
(f: string) =>
|
|
109
|
+
f.startsWith("./src/") && (f.endsWith(".py") || f.endsWith(".html"))
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const publicPipeline = new DebouncedWorker(
|
|
115
|
+
async () => {
|
|
116
|
+
console.log(chalk.cyan("→ Public directory changed, reloading browser..."));
|
|
117
|
+
if (bs.active) bs.reload();
|
|
118
|
+
},
|
|
119
|
+
350,
|
|
120
|
+
"bs-public-pipeline"
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
(async () => {
|
|
124
|
+
bsPort = await getAvailablePort(5090);
|
|
125
|
+
|
|
126
|
+
pythonPort = await getAvailablePort(bsPort + 10);
|
|
127
|
+
|
|
128
|
+
console.log("");
|
|
129
|
+
console.log(chalk.green.bold("✔ Ports Configured:"));
|
|
130
|
+
console.log(
|
|
131
|
+
` ${chalk.blue.bold("Frontend (BrowserSync):")} ${chalk.magenta(
|
|
132
|
+
`http://localhost:${bsPort}`
|
|
133
|
+
)}`
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
` ${chalk.yellow.bold("Backend (Python):")} ${chalk.magenta(
|
|
137
|
+
`http://localhost:${pythonPort}`
|
|
138
|
+
)}`
|
|
139
|
+
);
|
|
140
|
+
console.log("");
|
|
141
|
+
|
|
142
|
+
updateRouteFilesCache();
|
|
143
|
+
|
|
144
|
+
createSrcWatcher(join(SRC_DIR, "**", "*"), {
|
|
145
|
+
onEvent: (_ev, _abs, rel) => {
|
|
146
|
+
if (rel.includes("__pycache__") || rel.endsWith(".pyc")) return;
|
|
147
|
+
lastChangedFile = rel;
|
|
148
|
+
pipeline.schedule(rel);
|
|
149
|
+
},
|
|
150
|
+
awaitWriteFinish: DEFAULT_AWF,
|
|
151
|
+
logPrefix: "watch-src",
|
|
152
|
+
usePolling: true,
|
|
153
|
+
interval: 1000,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
createSrcWatcher(join(PUBLIC_DIR, "**", "*"), {
|
|
157
|
+
onEvent: (_ev, abs, _) => {
|
|
158
|
+
const relFromPublic = relative(PUBLIC_DIR, abs);
|
|
159
|
+
const normalized = relFromPublic.replace(/\\/g, "/");
|
|
160
|
+
if (PUBLIC_IGNORE_DIRS.includes(normalized.split("/")[0])) return;
|
|
161
|
+
publicPipeline.schedule(relFromPublic);
|
|
162
|
+
},
|
|
163
|
+
awaitWriteFinish: DEFAULT_AWF,
|
|
164
|
+
logPrefix: "watch-public",
|
|
165
|
+
usePolling: true,
|
|
166
|
+
interval: 1000,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const viteFlagFile = join(__dirname, "..", ".casp", ".vite-build-complete");
|
|
170
|
+
mkdirSync(dirname(viteFlagFile), { recursive: true });
|
|
171
|
+
if (!existsSync(viteFlagFile)) writeFileSync(viteFlagFile, "0");
|
|
172
|
+
|
|
173
|
+
createSrcWatcher(viteFlagFile, {
|
|
174
|
+
onEvent: (ev) => {
|
|
175
|
+
if (ev === "change" && bs.active) {
|
|
176
|
+
bs.reload();
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
|
|
180
|
+
logPrefix: "watch-vite",
|
|
181
|
+
usePolling: true,
|
|
182
|
+
interval: 500,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
createSrcWatcher(join(__dirname, "..", "utils", "**", "*.py"), {
|
|
186
|
+
onEvent: async (_ev, _abs, _) => {
|
|
187
|
+
if (_abs.includes("__pycache__")) return;
|
|
188
|
+
await restartPythonServer(pythonPort);
|
|
189
|
+
const isReady = await waitForPort(pythonPort);
|
|
190
|
+
if (isReady && bs.active) bs.reload();
|
|
191
|
+
},
|
|
192
|
+
awaitWriteFinish: DEFAULT_AWF,
|
|
193
|
+
logPrefix: "watch-utils",
|
|
194
|
+
usePolling: true,
|
|
195
|
+
interval: 1000,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
createSrcWatcher(join(__dirname, "..", "main.py"), {
|
|
199
|
+
onEvent: async (_ev, _abs, _) => {
|
|
200
|
+
if (_abs.includes("__pycache__")) return;
|
|
201
|
+
await restartPythonServer(pythonPort);
|
|
202
|
+
const isReady = await waitForPort(pythonPort);
|
|
203
|
+
if (isReady && bs.active) bs.reload();
|
|
204
|
+
},
|
|
205
|
+
awaitWriteFinish: DEFAULT_AWF,
|
|
206
|
+
logPrefix: "watch-main",
|
|
207
|
+
usePolling: true,
|
|
208
|
+
interval: 1000,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
startPythonServer(pythonPort);
|
|
212
|
+
|
|
213
|
+
bs.init(
|
|
214
|
+
{
|
|
215
|
+
proxy: `http://localhost:${pythonPort}`,
|
|
216
|
+
port: bsPort,
|
|
217
|
+
middleware: [
|
|
218
|
+
(_req, res, next) => {
|
|
219
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
220
|
+
res.setHeader("Pragma", "no-cache");
|
|
221
|
+
res.setHeader("Expires", "0");
|
|
222
|
+
next();
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
notify: false,
|
|
226
|
+
open: false,
|
|
227
|
+
ghostMode: false,
|
|
228
|
+
codeSync: true,
|
|
229
|
+
logLevel: "silent",
|
|
230
|
+
},
|
|
231
|
+
(err, bsInstance) => {
|
|
232
|
+
if (err) {
|
|
233
|
+
console.error(chalk.red("BrowserSync failed to start:"), err);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const urls = bsInstance.getOption("urls");
|
|
238
|
+
const localUrl = urls.get("local");
|
|
239
|
+
const externalUrl = urls.get("external");
|
|
240
|
+
const uiUrl = urls.get("ui");
|
|
241
|
+
const uiExtUrl = urls.get("ui-external");
|
|
242
|
+
|
|
243
|
+
console.log(
|
|
244
|
+
`${chalk.magenta("[Browsersync]")} ${chalk.cyan(
|
|
245
|
+
"Proxying:"
|
|
246
|
+
)} ${chalk.magenta(`http://localhost:${pythonPort}`)}`
|
|
247
|
+
);
|
|
248
|
+
console.log(
|
|
249
|
+
`${chalk.magenta("[Browsersync]")} ${chalk.bold("Access URLs:")}`
|
|
250
|
+
);
|
|
251
|
+
console.log(chalk.gray(" ------------------------------------"));
|
|
252
|
+
|
|
253
|
+
console.log(` ${chalk.bold("Local:")} ${chalk.magenta(localUrl)}`);
|
|
254
|
+
|
|
255
|
+
if (externalUrl) {
|
|
256
|
+
console.log(
|
|
257
|
+
` ${chalk.bold("External:")} ${chalk.magenta(externalUrl)}`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.log(chalk.gray(" ------------------------------------"));
|
|
262
|
+
|
|
263
|
+
if (uiUrl) {
|
|
264
|
+
console.log(` ${chalk.bold("UI:")} ${chalk.magenta(uiUrl)}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (uiExtUrl) {
|
|
268
|
+
console.log(
|
|
269
|
+
` ${chalk.bold("UI External:")} ${chalk.magenta(uiExtUrl)}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(chalk.gray(" ------------------------------------"));
|
|
274
|
+
|
|
275
|
+
const out = {
|
|
276
|
+
local: localUrl,
|
|
277
|
+
external: externalUrl,
|
|
278
|
+
ui: uiUrl,
|
|
279
|
+
uiExternal: uiExtUrl,
|
|
280
|
+
backend: `http://localhost:${pythonPort}`,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
writeFileSync(
|
|
284
|
+
join(__dirname, "bs-config.json"),
|
|
285
|
+
JSON.stringify(out, null, 2)
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
console.log(`\n${chalk.gray("Press Ctrl+C to stop.")}\n`);
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
})();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { generateFileListJson } from "./files-list.js";
|
|
2
|
+
import {
|
|
3
|
+
deleteFilesIfExist,
|
|
4
|
+
filesToDelete,
|
|
5
|
+
deleteDirectoriesIfExist,
|
|
6
|
+
dirsToDelete,
|
|
7
|
+
} from "./project-name.js";
|
|
8
|
+
import { componentMap } from "./component-map.js";
|
|
9
|
+
|
|
10
|
+
(async () => {
|
|
11
|
+
console.log("📦 Generating files for production...");
|
|
12
|
+
|
|
13
|
+
await deleteFilesIfExist(filesToDelete);
|
|
14
|
+
await deleteDirectoriesIfExist(dirsToDelete);
|
|
15
|
+
await generateFileListJson();
|
|
16
|
+
await componentMap();
|
|
17
|
+
|
|
18
|
+
console.log("✅ Generating files for production completed.");
|
|
19
|
+
})();
|