wildpig 1.3.19 → 1.4.0

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.
@@ -0,0 +1,132 @@
1
+ import { getApiRouteModules } from "./apiRoutes";
2
+ import { createServer as createViteServer } from "vite";
3
+ import { createStaticHandler, createStaticRouter, matchRoutes } from "react-router";
4
+ import fs from "node:fs";
5
+
6
+ const __dirname = import.meta.dirname;
7
+
8
+ // 用户代码
9
+ import pageRoutes from "@/router/routes";
10
+ import path from "node:path";
11
+ import chalk from "chalk";
12
+
13
+ const env = process.env;
14
+ const port = env.PORT || 3000;
15
+ const hostname = env.HOST || env.HOSTNAME || "localhost";
16
+
17
+
18
+ // 启动vite server
19
+ const viteServer = await createViteServer({
20
+ configFile: path.resolve(__dirname, "../../../vite.config.ts"),
21
+ });
22
+ await viteServer.listen(viteServer.config.server.port);
23
+
24
+
25
+ const viteHandler = (apiModules: any) => async (request: Request) => {
26
+ // 判断pathname是否匹配pageRoutes
27
+ const url = new URL(request.url);
28
+
29
+ // 判断是否是vite请求
30
+ if(url.pathname.includes(".") || url.pathname.startsWith("/@")){
31
+ // 没有匹配的route,说明是一些资源什么的
32
+ const viteURL = new URL(request.url);
33
+ viteURL.port = viteServer.config.server.port.toString();
34
+ console.log("转发请求:" + viteURL.toString());
35
+ const response = await fetch(viteURL.toString(), {
36
+ headers: request.headers,
37
+ method: request.method,
38
+ });
39
+ return response;
40
+ }
41
+
42
+ const matches = matchRoutes(pageRoutes, url.pathname);
43
+ if(!matches)return new Response("404 Not Found", { status: 404 });
44
+
45
+ // 请求服务端数据
46
+ const matchRoute = matches.at(-1)!;
47
+ let serverDataApi = matchRoute.route.serverDataApi;
48
+ const getServerData = async () => {
49
+ if(!serverDataApi)return undefined;
50
+ const prefixUrl = request.url.split("/")[0] + "//" + request.url.split("/")[2];
51
+ // 需要请求服务端数据, 替换动态参数
52
+ for(const [key, value] of Object.entries(matchRoute.params)){
53
+ if(value)serverDataApi = serverDataApi.replace(":" + key, value);
54
+ }
55
+ // 加上当前request的query参数
56
+ for(const [key, value] of new URLSearchParams(request.url.split("?")[1]).entries()){
57
+ if(serverDataApi.includes(key + "="))continue; // 已经有这个参数了
58
+ serverDataApi += (serverDataApi.includes("?") ? "&" : "?") + key + "=" + value;
59
+ }
60
+ const serverRequest = new Request({
61
+ ...request.clone(),
62
+ url: prefixUrl + serverDataApi, // 替换url
63
+ });
64
+ serverRequest.headers.set("wildpig-server-data-api", serverDataApi);
65
+ const pathname = serverDataApi.split("?")[0]; // 获取路径
66
+ const serverData = await apiModules[pathname].GET(serverRequest).then((r: Response) => r.json());
67
+ return serverData;
68
+ };
69
+ let serverData = await getServerData();
70
+
71
+ // 1. 读取 index.html
72
+ const template = await viteServer.transformIndexHtml(request.url, fs.readFileSync('./index.html', 'utf-8'));
73
+ // 2. 获取渲染函数
74
+ const { render } = await viteServer.ssrLoadModule('/node_modules/wildpig/entry/server.tsx')
75
+ // 3. 获取应用程序 HTML
76
+ const appHtml = await render(request)
77
+
78
+ // 4. 注入渲染后的应用程序 HTML 到模板中。
79
+ const html = template
80
+ .replace(`<!--ssr-outlet-->`, () => appHtml)
81
+ .replace(`<!--title-->`, () => serverData?.title || "title")
82
+ .replace(`<!--server-data-->`, () => `<script>window.__SERVER_DATA__ = ${JSON.stringify(serverData)};</script>`);
83
+
84
+ return new Response(html, {
85
+ headers: {
86
+ "content-type": "text/html; charset=utf-8",
87
+ "Access-Control-Allow-Origin": "*",
88
+ }
89
+ });
90
+ }
91
+
92
+
93
+ const getPackageInfo = async () => {
94
+ const packageJson = await Bun.file(path.resolve(__dirname, "../package.json")).json();
95
+ return packageJson;
96
+ }
97
+ const packageInfo = await getPackageInfo();
98
+
99
+ /** 启动后的描述性文字 */
100
+ const afterStart = () => {
101
+ // 启动后的文字
102
+ console.log(` __ __ _ _ _ ____ _
103
+ \\ \\ / /(_)| | __| | | _ \\ (_) __ _
104
+ \\ \\ /\\ / / | || | / _\` | | |_) || | / _\` |
105
+ \\ V V / | || || (_| | | __/ | || (_| |
106
+ \\_/\\_/ |_||_| \\__,_| |_| |_| \\__, |
107
+ |___/ `)
108
+ console.log(chalk.blue.bgGreen(` 🐗 WildPig version ${packageInfo?.version} by ${packageInfo?.author} `));
109
+ console.log(chalk.green(" Strong & Fast Fullstack Framework\n"));
110
+ console.log(chalk.green("✨ WildPig is running on port " + env.PORT || 3000));
111
+ console.log(chalk.yellow("💻 Wildpig is Running in development mode."));
112
+ console.log(chalk.green("⚡ Vite server is running on port " + viteServer.config.server?.port));
113
+ console.log(chalk.green(`🔗 Click to debug in Browser: http://${hostname}:${port}`));
114
+ }
115
+
116
+ export const startServer = async () => {
117
+ // 确保重启后可以重新拿到路由
118
+ const apiModules = await getApiRouteModules("dev") as any;
119
+ console.log(apiModules)
120
+ const server = Bun.serve({
121
+ port,
122
+ hostname,
123
+ routes:{
124
+ ...apiModules,
125
+ "/*": viteHandler(apiModules),
126
+ },
127
+ development: true,
128
+ })
129
+ afterStart();
130
+ return server;
131
+ }
132
+ startServer();
@@ -0,0 +1,114 @@
1
+ import { getApiRouteModules } from "./apiRoutes";
2
+ import fs from "node:fs";
3
+ import { matchRoutes } from "react-router";
4
+ import path from "node:path";
5
+
6
+ // 用户代码
7
+ import pageRoutes from "#/src/router/routes";
8
+ import chalk from "chalk";
9
+
10
+ const env = process.env;
11
+ const port = env.PORT || 3000;
12
+ const hostname = env.HOST || env.HOSTNAME || "localhost";
13
+
14
+ const getPackageInfo = async () => {
15
+ const packageJson = await Bun.file(path.resolve(__dirname, "../package.json")).json();
16
+ return packageJson;
17
+ }
18
+ const packageInfo = await getPackageInfo();
19
+
20
+ /** 启动后的描述性文字 */
21
+ const afterStart = () => {
22
+ // 启动后的文字
23
+ console.log(` __ __ _ _ _ ____ _
24
+ \\ \\ / /(_)| | __| | | _ \\ (_) __ _
25
+ \\ \\ /\\ / / | || | / _\` | | |_) || | / _\` |
26
+ \\ V V / | || || (_| | | __/ | || (_| |
27
+ \\_/\\_/ |_||_| \\__,_| |_| |_| \\__, |
28
+ |___/ `)
29
+ console.log(chalk.blue.bgGreen(` 🐗 WildPig version ${packageInfo?.version} by ${packageInfo?.author} `));
30
+ console.log(chalk.green(" Strong & Fast Fullstack Framework\n"));
31
+ console.log(chalk.green("✨ WildPig is running on port " + env.PORT || 3000));
32
+ console.log(chalk.green("💻 Wildpig is Running in production mode."));
33
+ console.log(chalk.green(`🔗 Click to debug in Browser: http://${hostname}:${port}`));
34
+ }
35
+
36
+
37
+
38
+ export const startServer = async () => {
39
+ // 确保重启后可以重新拿到路由
40
+ const apiModules = await getApiRouteModules("prod") as any;
41
+ return Bun.serve({
42
+ port,
43
+ hostname,
44
+ routes:{
45
+ ...apiModules,
46
+ "/*": async (request: Request) => {
47
+ // 判断pathname是否匹配pageRoutes
48
+ const url = new URL(request.url);
49
+ const matches = matchRoutes(pageRoutes, url.pathname);
50
+ if(!matches){
51
+ const filepath = "./client" + url.pathname;
52
+ // 检查文件是否存在
53
+ if(fs.existsSync(filepath) && fs.statSync(filepath).isFile()){
54
+ return new Response(Bun.file(filepath));
55
+ }
56
+ // 文件不存在
57
+ return new Response("Not Found", {status: 404});
58
+ }
59
+
60
+ // 请求服务端数据
61
+ const matchRoute = matches.at(-1)!;
62
+ let serverDataApi = matchRoute.route.serverDataApi;
63
+ const getServerData = async () => {
64
+ if(!serverDataApi)return undefined;
65
+ const prefixUrl = request.url.split("/")[0] + "//" + request.url.split("/")[2];
66
+ // 需要请求服务端数据, 替换动态参数
67
+ for(const [key, value] of Object.entries(matchRoute.params)){
68
+ if(value)serverDataApi = serverDataApi.replace(":" + key, value);
69
+ }
70
+ // 加上当前request的query参数
71
+ for(const [key, value] of new URLSearchParams(request.url.split("?")[1]).entries()){
72
+ if(serverDataApi.includes(key + "="))continue; // 已经有这个参数了
73
+ serverDataApi += (serverDataApi.includes("?") ? "&" : "?") + key + "=" + value;
74
+ }
75
+ const serverRequest = new Request({
76
+ ...request.clone(),
77
+ url: prefixUrl + serverDataApi, // 替换url
78
+ });
79
+ serverRequest.headers.set("wildpig-server-data-api", serverDataApi);
80
+ const pathname = serverDataApi.split("?")[0]; // 获取路径
81
+ const serverData = await apiModules[pathname].GET(serverRequest).then((r: Response) => r.json());
82
+ return serverData;
83
+ };
84
+ let serverData = await getServerData();
85
+
86
+ // 1. 读取 index.html
87
+ const template = fs.readFileSync('./client/index.html', 'utf-8');
88
+ // 2. 获取渲染函数
89
+ const { render } = await import("../entry/server"!);
90
+ // 3. 获取应用程序 HTML
91
+ const appHtml = await render(request)
92
+
93
+ // 4. 注入渲染后的应用程序 HTML 到模板中。
94
+ const html = template
95
+ .replace(`<!--ssr-outlet-->`, () => appHtml)
96
+ .replace(`<!--title-->`, () => serverData?.title || "title")
97
+ .replace(`<!--server-data-->`, () => `<script>window.__SERVER_DATA__ = ${JSON.stringify(serverData)};</script>`);
98
+
99
+ return new Response(html, {
100
+ headers: {
101
+ "content-type": "text/html; charset=utf-8",
102
+ "Access-Control-Allow-Origin": "*",
103
+ }
104
+ });
105
+ },
106
+ },
107
+ development: false,
108
+
109
+ })
110
+ }
111
+
112
+
113
+ startServer();
114
+ afterStart();
package/scripts/server.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import chalk from "chalk";
2
2
  import path from "node:path";
3
3
  import { watch } from "node:fs";
4
- import { startServer } from "./WildPig";
4
+ import { startServer } from "./devServer";
5
+ import viteConfig from "#/vite.config";
5
6
  const env = process.env;
6
7
 
7
8
 
@@ -27,29 +28,10 @@ const startHotServer = async () => {
27
28
  setTimeout(async () => {
28
29
  server = await startServer();
29
30
  console.log(chalk.green("服务已重启"));
30
- }, 500);
31
+ }, 1000);
31
32
  })
32
33
  }
33
34
  }
34
35
  await startHotServer();
35
36
 
36
37
 
37
- const afterStart = () => {
38
- // 启动后的文字
39
- console.log(` __ __ _ _ _ ____ _
40
- \\ \\ / /(_)| | __| | | _ \\ (_) __ _
41
- \\ \\ /\\ / / | || | / _\` | | |_) || | / _\` |
42
- \\ V V / | || || (_| | | __/ | || (_| |
43
- \\_/\\_/ |_||_| \\__,_| |_| |_| \\__, |
44
- |___/ `)
45
- console.log(chalk.blue.bgGreen(` 🐗 WildPig version ${packageInfo?.version} by ${packageInfo?.author} `));
46
- console.log(chalk.green(" Strong & Fast Fullstack Framework\n"));
47
- console.log(chalk.green("✨ WildPig is running on port " + env.PORT || 3000));
48
- if(isDev){
49
- console.log(chalk.yellow("💻 Wildpig is Running in development mode."));
50
- }else{
51
- console.log(chalk.green("💻 Wildpig is Running in production mode."));
52
- }
53
- console.log(chalk.green(`🔗 Click to debug in Browser: http://${hostname}:${port}`));
54
- }
55
- afterStart();
@@ -1,12 +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>WildPig 开发环境</title>
7
- <script src="./render.tsx"></script>
8
- </head>
9
- <body>
10
- <div id="root"></div>
11
- </body>
12
- </html>
package/public/render.tsx DELETED
@@ -1,29 +0,0 @@
1
- import { createRoot, hydrateRoot } from 'react-dom/client';
2
- import { RouterProvider } from 'react-router';
3
- import { serverDataStore } from '../store/serverDataStore';
4
- import { browserRouter } from '../router/index.js';
5
- import "./tailwindcss4.js"
6
-
7
- const isDev = process.env.NODE_ENV === 'development';
8
-
9
-
10
- // 确保在 DOM 加载完成后才进行水合
11
- document?.addEventListener("DOMContentLoaded", () => {
12
- // 获取根元素
13
- const rootElement = document.getElementById('root')!;
14
-
15
- // 从 window 对象获取服务端数据
16
- const serverData = (window as any).__SERVER_DATA__;
17
-
18
- // 设置服务端数据到 store
19
- serverDataStore.set(serverData);
20
-
21
- if(isDev){
22
- // 开发时,使用 createRoot 进行渲染
23
- const root = createRoot(rootElement);
24
- root.render(<RouterProvider router={browserRouter} />);
25
- }else{
26
- // 生产环境,使用 hydrateRoot 进行水合
27
- hydrateRoot(rootElement, (<RouterProvider router={browserRouter} />));
28
- }
29
- });