-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
912 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import fs from 'node:fs'; | ||
import { fileURLToPath } from 'node:url'; | ||
import path from 'node:path'; | ||
import type { Context } from 'koa'; | ||
import { getMime } from 'ranuts'; | ||
import { ssr } from '@/app/lib/vite'; | ||
import { HtmlWritable, getEnv } from '@/app/lib/index'; | ||
import { FORMAT, HTML_PATH_MAP, RENDER_PATH_MAP, TEMPLATE_REPLACE } from '@/app/lib/constant'; | ||
|
||
const env = getEnv(); | ||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url)); | ||
|
||
export default class ServerRender { | ||
async index(ctx: Context): Promise<void> { | ||
try { | ||
const fsTemplate = fs.readFileSync(path.resolve(__dirname, HTML_PATH_MAP[env]), FORMAT); | ||
const template = await ssr.transformIndexHtml(ctx.path, fsTemplate); | ||
const { render } = await ssr.ssrLoadModule(path.resolve(__dirname, RENDER_PATH_MAP[env])); | ||
const writable = new HtmlWritable(); | ||
const stream = render(ctx, { | ||
onShellReady() { | ||
stream.pipe(writable); | ||
}, | ||
onShellError() { | ||
ctx.res.write('<h1>Something went wrong</h1>'); | ||
}, | ||
onError(error: Error) { | ||
ctx.errorHandler({ error }); | ||
}, | ||
onAllReady() { | ||
const type = getMime(ctx.url); | ||
if (type) { | ||
ctx.type = type; | ||
} else { | ||
ctx.res.setHeader('Content-Type', 'text/html'); | ||
ctx.res.setHeader('Transfer-Encoding', 'chunked'); | ||
} | ||
}, | ||
}); | ||
const writeableFinish = (): Promise<{ success: true; data: string }> => { | ||
return new Promise((resolve, reject) => { | ||
writable.on('finish', () => { | ||
const stream = writable.getHtml(); | ||
const html = template.replace(TEMPLATE_REPLACE, stream); | ||
resolve({ success: true, data: html }); | ||
}); | ||
writable.on('error', (error) => { | ||
reject({ success: false, data: error }); | ||
}); | ||
}); | ||
}; | ||
const result = await writeableFinish(); | ||
ctx.res.end(result.data); | ||
} catch (error) { | ||
console.log('home:', error); | ||
// ctx.errorHandler({ error }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { createServer } from 'node:http'; | ||
import type { IncomingMessage, ServerResponse } from 'node:http'; | ||
import { readController, routing } from '@/app/routes'; | ||
import type { Context } from '@/app/types/index'; | ||
|
||
readController().then((controller) => { | ||
const server = createServer((req: IncomingMessage, res: ServerResponse) => { | ||
const ctx: Context = { | ||
req, | ||
res, | ||
controller, | ||
path: req.url || '', | ||
}; | ||
routing(ctx); | ||
// if (req.url === '/home') { | ||
// res.writeHead(200, { 'Content-Type': 'application/json' }); | ||
// res.end(JSON.stringify({ message: 'Hello, JSON!' })); | ||
// } else { | ||
// res.writeHead(404, { 'Content-Type': 'text/plain' }); | ||
// res.end('404 Not Found\n'); | ||
// } | ||
}); | ||
|
||
const PORT = 3000; | ||
|
||
server.listen(PORT, () => { | ||
console.log(`Server is running at http://localhost:${PORT}/`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// transmitnotice | ||
export const DDTOKEN = '4e6add99ad420323a61b0ad4caa4940ec2806670a20ff00d32adbe84b5346b6c'; | ||
export const ADMINISTRATOR = 1; | ||
// serverRenderServer | ||
const HTML_PATH_DEV = '../../views/index.html'; | ||
export const FORMAT = 'utf-8'; | ||
export const TEMPLATE_REPLACE = '<!--ssr-outlet-->'; | ||
const RENDER_PATH_DEV = '/client/server.tsx'; | ||
const HTML_PATH_PROD = '../../dist/client/views/index.html'; | ||
const RENDER_PATH_PROD = '../../dist/server/server.js'; | ||
export const HTML_PATH_MAP: Record<string, string> = { | ||
dev: HTML_PATH_DEV, | ||
prod: HTML_PATH_PROD, | ||
}; | ||
export const RENDER_PATH_MAP: Record<string, string> = { | ||
dev: RENDER_PATH_DEV, | ||
prod: RENDER_PATH_PROD, | ||
}; | ||
// serverRender | ||
export const PRODUCTION = 'production'; | ||
// static server dir | ||
export const PRODUCTION_DIR = '../../dist/'; | ||
export const DEVELOPMENT_DIR = '../../dist/views/'; | ||
|
||
export const PORT = 30102; | ||
export const LOCAL_URL = `http://localhost:${PORT}`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Writable } from 'node:stream'; | ||
|
||
export const noop = (): void => {}; | ||
|
||
export class HtmlWritable extends Writable { | ||
chunks: Uint8Array[]; | ||
html: string; | ||
constructor() { | ||
super(); | ||
this.chunks = []; | ||
this.html = ''; | ||
} | ||
|
||
getHtml(): string { | ||
return this.html; | ||
} | ||
|
||
getFragment(): string { | ||
return Buffer.concat(this.chunks).toString(); | ||
} | ||
|
||
_write(chunk: Uint8Array, _: string, callback = noop): void { | ||
this.chunks.push(chunk); | ||
callback(); | ||
} | ||
|
||
_final(callback = noop): void { | ||
this.html = Buffer.concat(this.chunks).toString(); | ||
callback(); | ||
} | ||
} | ||
|
||
/** | ||
* @description: Gets the current environment configuration | ||
* @return {string} | ||
*/ | ||
export const getEnv = (): string => { | ||
const env = process.env.NODE_ENV; | ||
switch (env) { | ||
case 'development': | ||
case 'dev': | ||
case 'local': | ||
return 'dev'; | ||
case 'test': | ||
return 'test'; | ||
case 'staging': | ||
return 'staging'; | ||
case 'production': | ||
case 'prod': | ||
return 'prod'; | ||
default: | ||
return 'prod'; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { createServer } from 'vite'; | ||
|
||
const viteServer = async () => { | ||
return await createServer({ | ||
server: { middlewareMode: true }, | ||
appType: 'custom', | ||
}); | ||
}; | ||
|
||
export const ssr = await viteServer(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* 路由配置: '${method}=>${path}: ${controller}#${controllerMethod}#${env}' | ||
* method: 请求方法 GET|POST | ||
* path: 请求链接 /api/index | ||
* controller: 本次请求对应的处理 controller | ||
* controllerMethod: 本次请求对应的 controller 中的具体的处理方法 | ||
* env: 该路由在什么环境中生效 local|test|staging|prod 不同环境的路由可以放到一起 | ||
*/ | ||
const serverRender = { | ||
// 获取主页 | ||
'get=>#/^(?!/api).*$/#': 'home#index', | ||
}; | ||
|
||
export default { | ||
...serverRender, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import path, { resolve } from 'node:path'; | ||
import fs from 'node:fs'; | ||
import { fileURLToPath } from 'node:url'; | ||
import { noop } from 'ranuts'; | ||
import routes from '@/app/router.config'; | ||
import type { Context, Controller } from '@/app/types/index'; | ||
|
||
const regex = /#\/(.*?)\/#/; | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
|
||
const __dirname = path.dirname(__filename); | ||
|
||
const dir = resolve(__dirname, 'controllers'); | ||
|
||
export const readController = async (): Promise<Controller> => { | ||
const controller: Controller = {}; | ||
const fileList = fs.readdirSync(dir); | ||
for (const file of fileList) { | ||
if (file.length > 0 && file.startsWith('.')) continue; | ||
const { default: route } = await import(`${dir}/${file}`); | ||
const type = Reflect.toString.call(route); | ||
const [key, _] = file.split('.'); | ||
if (type === '[object Object]') controller[key] = route; | ||
if (type === '[object Function]') controller[key] = new route(); | ||
} | ||
return controller; | ||
}; | ||
|
||
export const routing = async (ctx: Context): Promise<void> => { | ||
const { req, controller } = ctx; | ||
for (const [key, value] of Object.entries(routes)) { | ||
const [method, path] = key.split('=>'); | ||
// 验证路由是否合法 | ||
const [_, url] = new RegExp(regex).exec(path) || []; | ||
if (req.method === method.toUpperCase() && req.url && new RegExp(url).test(req.url)) { | ||
const [name, func] = value.split('#'); | ||
const handler = controller[name][func] || noop; | ||
handler(ctx); | ||
return; | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { IncomingMessage, ServerResponse } from 'node:http'; | ||
|
||
export type Controller = Record<string, Record<string, Function>>; | ||
|
||
export interface Context { | ||
req: IncomingMessage; | ||
res: ServerResponse<IncomingMessage>; | ||
controller: Controller; | ||
path: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
|
||
bin=./node_modules/.bin | ||
$bin/tsx ./app/index.ts | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { JSX } from 'react'; | ||
import React, { createContext, useState } from 'react'; | ||
import { useRoutes } from 'react-router-dom'; | ||
import routes from '@/client/router'; | ||
import '@/client/assets/base.css'; | ||
|
||
const Context = createContext({}); | ||
|
||
const { Provider } = Context; | ||
|
||
const Noop = () => <></>; | ||
|
||
function RoutesContent(): JSX.Element { | ||
return useRoutes(routes) || <Noop />; | ||
} | ||
|
||
const App = (): JSX.Element => { | ||
const [state, setState] = useState({}); | ||
return ( | ||
<Provider value={{ state, setState }}> | ||
<RoutesContent /> | ||
</Provider> | ||
); | ||
}; | ||
|
||
export default App; |
Oops, something went wrong.