feat: add context and env
This commit is contained in:
parent
1c4fb5fb44
commit
58c0f976af
11 changed files with 89 additions and 37 deletions
2
makefile
2
makefile
|
@ -8,7 +8,7 @@ hr:
|
|||
deno run -A ~/sandbox/hr/server.ts target static
|
||||
|
||||
ts-w:
|
||||
npx tsc-watch --onSuccess "node target/scripts/main.mjs"
|
||||
NODE_ENV=develop npx tsc-watch --onSuccess "node target/scripts/main.mjs"
|
||||
|
||||
clean:
|
||||
rm -rf target
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { AnyNode, E, Ea, Elem } from "ren";
|
||||
import { config } from "../config.mjs";
|
||||
import { div } from "../utils.mjs";
|
||||
|
||||
export function Layout(page: AnyNode): Elem {
|
||||
|
@ -15,15 +16,16 @@ export function Layout(page: AnyNode): Elem {
|
|||
}),
|
||||
E("title", "hello world"),
|
||||
]),
|
||||
E("body", [
|
||||
div({ id: "root" }, page),
|
||||
E(
|
||||
E("body", [div({ id: "root" }, page), config.isDev && HotReloadScript()]),
|
||||
]);
|
||||
}
|
||||
|
||||
function HotReloadScript(): Elem {
|
||||
return E(
|
||||
"script",
|
||||
`const ws = new WebSocket("ws://localhost:30001");
|
||||
ws.addEventListener("message", (m) => {
|
||||
if (m.data === "RELOAD") location.reload();
|
||||
});`
|
||||
),
|
||||
]),
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,27 +1,34 @@
|
|||
import { AnyNode, E, Ea, Elem } from "ren";
|
||||
import { AnyNode, Ea, Elem } from "ren";
|
||||
import { Context } from "../context.mjs";
|
||||
import { div } from "../utils.mjs";
|
||||
|
||||
export function PageLayout(...children: AnyNode[]): Elem {
|
||||
export function PageLayout(ctx: Context, ...children: AnyNode[]): Elem {
|
||||
return Ea("div", { id: "main" }, [
|
||||
Header(),
|
||||
Header(ctx.locPath),
|
||||
Ea("div", { class: "content" }, children),
|
||||
Footer(),
|
||||
// Footer(),
|
||||
]);
|
||||
}
|
||||
|
||||
export function Header(): Elem {
|
||||
export function Header(locPath: string): Elem {
|
||||
return Ea("header", { class: "header" }, [
|
||||
div({ class: "content-width" }, HeaderNav()),
|
||||
div({ class: "content-width" }, HeaderNav(locPath)),
|
||||
]);
|
||||
}
|
||||
|
||||
export function HeaderNav(): Elem {
|
||||
return E("nav", [
|
||||
Ea("a", { href: "/" }, "Обо мне"),
|
||||
Ea("a", { href: "/works" }, "Работы"),
|
||||
export function HeaderNav(locPath: string): Elem {
|
||||
return Ea("nav", { class: "main-menu" }, [
|
||||
NavLink(locPath, "/", "Обо мне"),
|
||||
NavLink(locPath, "/works", "Работы"),
|
||||
]);
|
||||
}
|
||||
|
||||
export function NavLink(locPath: string, href: string, text: string): Elem {
|
||||
const attrs = { href };
|
||||
if (locPath === href) attrs["aria-current"] = "true";
|
||||
return Ea("a", attrs, text);
|
||||
}
|
||||
|
||||
export function Footer(): Elem {
|
||||
return Ea("footer", { class: "footer" }, "footer");
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
export const config = createConfig();
|
||||
|
||||
export function createConfig(): Config {
|
||||
return { server: { port: 30000 } };
|
||||
return {
|
||||
isDev: process.env.NODE_ENV === "develop",
|
||||
server: { port: 30000 },
|
||||
};
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
isDev: boolean;
|
||||
server: ServerConfig;
|
||||
}
|
||||
|
||||
|
|
3
src/context.mts
Normal file
3
src/context.mts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export interface Context {
|
||||
locPath: string;
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
import { createConfig } from "./config.mjs";
|
||||
import { createServer } from "./server.mjs";
|
||||
|
||||
main();
|
||||
|
||||
function main(): void {
|
||||
const config = createConfig();
|
||||
createServer(config.server);
|
||||
createServer();
|
||||
}
|
||||
|
|
|
@ -2,17 +2,21 @@ import * as http from "http";
|
|||
import * as fs from "fs/promises";
|
||||
import * as path from "path";
|
||||
import { Layout } from "./components/layout.mjs";
|
||||
import { ServerConfig } from "./config.mjs";
|
||||
import { config } from "./config.mjs";
|
||||
import { debug, info } from "./log.mjs";
|
||||
import { StrRenderer } from "ren";
|
||||
import { AboutPage } from "./views/about.mjs";
|
||||
import { E404 } from "./views/e404.mjs";
|
||||
import { WorksPage } from "./views/works.mjs";
|
||||
import { Context } from "./context.mjs";
|
||||
|
||||
export function createServer(cfg: ServerConfig): void {
|
||||
export function createServer(): void {
|
||||
const server = http.createServer(handleHttpReq);
|
||||
server.listen(cfg.port, () => {
|
||||
info("[server]", `Server listening at http://localhost:${cfg.port}`);
|
||||
server.listen(config.server.port, () => {
|
||||
info(
|
||||
"[server]",
|
||||
`Server listening at http://localhost:${config.server.port}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -39,19 +43,20 @@ async function handleHttpReq(
|
|||
httpRes.writeHead(404).end("Not found");
|
||||
}
|
||||
} else {
|
||||
const ctx: Context = { locPath: req.url };
|
||||
const ren = new StrRenderer();
|
||||
if (/^[/](?:about[/]?)?$/.test(req.url)) {
|
||||
httpRes
|
||||
.writeHead(200, { "content-type": "text/html" })
|
||||
.end(ren.render(Layout(AboutPage())));
|
||||
.end(ren.render(Layout(AboutPage(ctx))));
|
||||
} else if (/^[/]works[/]?$/.test(req.url)) {
|
||||
httpRes
|
||||
.writeHead(200, { "content-type": "text/html" })
|
||||
.end(ren.render(Layout(WorksPage())));
|
||||
.end(ren.render(Layout(WorksPage(ctx))));
|
||||
} else {
|
||||
httpRes
|
||||
.writeHead(404, { "content-type": "text/html" })
|
||||
.end(ren.render(Layout(E404())));
|
||||
.end(ren.render(Layout(E404(ctx))));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { PageLayout } from "../components/page_layout.mjs";
|
||||
import { AnyNode } from "ren";
|
||||
import { div, h3, li, p, ul } from "../utils.mjs";
|
||||
import { Context } from "../context.mjs";
|
||||
|
||||
export function AboutPage(): AnyNode {
|
||||
export function AboutPage(ctx: Context): AnyNode {
|
||||
return PageLayout(
|
||||
ctx,
|
||||
div({ class: "content-width responsive-typography" }, [
|
||||
div({}, [
|
||||
p("Привет!"),
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { PageLayout } from "../components/page_layout.mjs";
|
||||
import { AnyNode, Ea } from "ren";
|
||||
import { div } from "../utils.mjs";
|
||||
import { Context } from "../context.mjs";
|
||||
|
||||
export function E404(): AnyNode {
|
||||
export function E404(ctx: Context): AnyNode {
|
||||
return PageLayout(
|
||||
ctx,
|
||||
div(
|
||||
{ class: "content-width" },
|
||||
Ea("h3", { class: "font-h3" }, "Страница не найдена")
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { PageLayout } from "../components/page_layout.mjs";
|
||||
import { AnyNode } from "ren";
|
||||
import { div, p } from "../utils.mjs";
|
||||
import { Context } from "../context.mjs";
|
||||
|
||||
export function WorksPage(): AnyNode {
|
||||
return PageLayout(div({ class: "content-width" }, p("Works")));
|
||||
export function WorksPage(ctx: Context): AnyNode {
|
||||
return PageLayout(ctx, div({ class: "content-width" }, p("Works")));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
--min-width: 320px;
|
||||
--max-width: 1024px;
|
||||
|
||||
--default-color-white: hsl(0, 0, 100%);
|
||||
--default-color-white: #ffffff;
|
||||
--color-blue: #1966df;
|
||||
|
||||
--default-font-size: 16px;
|
||||
|
@ -61,6 +61,32 @@ ul, ol {
|
|||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.main-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
// anim
|
||||
.main-menu > a {
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
|
||||
.main-menu > a {
|
||||
color: var(--color-blue);
|
||||
padding: 0.5rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--color-blue);
|
||||
text-decoration: none;
|
||||
}
|
||||
.main-menu > a:hover,
|
||||
.main-menu > a[aria-current]:not([aria-current=""]) {
|
||||
color: var(--default-color-white);
|
||||
background-color: var(--color-blue);
|
||||
}
|
||||
.main-menu > a:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.content-width {
|
||||
width: 100%;
|
||||
max-width: var(--max-width);
|
||||
|
|
Loading…
Reference in a new issue