parent
fcc87648be
commit
3c982e845f
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@ |
|||||||
|
import path from "node:path"; |
||||||
|
import express from "express"; |
||||||
|
import { mainPageHandler } from "./main.tsx"; |
||||||
|
|
||||||
|
export const createApp = () => { |
||||||
|
const app = express(); |
||||||
|
|
||||||
|
app.get("/", mainPageHandler); |
||||||
|
app.use("/frontend", express.static(path.join(import.meta.dirname, "..", "frontend"))); |
||||||
|
app.use("/shared", express.static(path.join(import.meta.dirname, "..", "shared"))); |
||||||
|
|
||||||
|
return app; |
||||||
|
}; |
@ -0,0 +1,30 @@ |
|||||||
|
import type { RequestHandler } from "express"; |
||||||
|
import { safeGetQueryValue, sendHtml } from "./utils.ts"; |
||||||
|
|
||||||
|
export const mainPageHandler: RequestHandler = (req, res) => { |
||||||
|
const pageNumber = parseInt(safeGetQueryValue(req, "page") ?? "0", 10); |
||||||
|
sendHtml( |
||||||
|
res, |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
{/* |
||||||
|
We need to use ".js" here instead of ".ts" (and "incorrect" path) |
||||||
|
because we're loading the compiled file on frontend, from path relative to the root page, |
||||||
|
and because TS won't rewrite the extension here. |
||||||
|
*/} |
||||||
|
<script src="frontend/app.js" type="module"></script> |
||||||
|
<link rel="stylesheet" href="frontend/static/style.css" /> |
||||||
|
<title>Hi</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<p>Hello world!</p> |
||||||
|
<p> |
||||||
|
Page number <span class="pageNumber">{pageNumber}</span> |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
<a href={`?page=${pageNumber + 1}`}>Go to the next page</a> |
||||||
|
</p> |
||||||
|
</body> |
||||||
|
</html>, |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,19 @@ |
|||||||
|
import type { Request, Response } from "express"; |
||||||
|
import type { VNode } from "preact"; |
||||||
|
import { render } from "preact-render-to-string"; |
||||||
|
|
||||||
|
export const sendHtml = (res: Response, document: VNode) => { |
||||||
|
const html = render(document); |
||||||
|
res.send(`<!DOCTYPE html>${html}`); |
||||||
|
}; |
||||||
|
|
||||||
|
// Quick and dirty workaround, should not be used in real world
|
||||||
|
// with real complex query strings and with the need for error handling
|
||||||
|
export const safeGetQueryValue = (req: Request, name: string) => { |
||||||
|
const result = req.query[name]; |
||||||
|
if (typeof result !== "string") { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return result || null; |
||||||
|
}; |
@ -0,0 +1,6 @@ |
|||||||
|
import { computeAllSolutions } from "../shared/solver.ts"; |
||||||
|
import { rules } from "../shared/tictactoe-rules.ts"; |
||||||
|
|
||||||
|
const allSolutions = computeAllSolutions(3, 3, rules); |
||||||
|
console.log(allSolutions.size); |
||||||
|
console.log(allSolutions); |
@ -0,0 +1,4 @@ |
|||||||
|
.pageNumber { |
||||||
|
font-weight: bold; |
||||||
|
color: red; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import { createApp } from "./backend/app.ts"; |
||||||
|
|
||||||
|
const app = createApp(); |
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we actually want to coalesce empty strings here, too
|
||||||
|
const port = process.env["PORT"] || 3000; |
||||||
|
|
||||||
|
app.listen(port, () => { |
||||||
|
console.log(`Example app listening on port ${port}`); |
||||||
|
}); |
@ -1,8 +1,10 @@ |
|||||||
{ |
{ |
||||||
"extends": "./tsconfig.json", |
"extends": "./tsconfig.json", |
||||||
"exclude": ["src/**/*.spec.*"], |
"exclude": ["src/**/*.spec.*"], |
||||||
"compilerOptions": { |
"compilerOptions": { |
||||||
"module": "Preserve" |
"module": "Preserve", |
||||||
} |
"noEmit": false, |
||||||
|
"outDir": "dist", |
||||||
|
"rewriteRelativeImportExtensions": true |
||||||
} |
} |
||||||
|
} |
||||||
|
Loading…
Reference in new issue