feat: add context and env

This commit is contained in:
Dmitriy Pleshevskiy 2022-03-21 22:50:07 +03:00
parent 1c4fb5fb44
commit 58c0f976af
11 changed files with 89 additions and 37 deletions

View file

@ -8,7 +8,7 @@ hr:
deno run -A ~/sandbox/hr/server.ts target static deno run -A ~/sandbox/hr/server.ts target static
ts-w: ts-w:
npx tsc-watch --onSuccess "node target/scripts/main.mjs" NODE_ENV=develop npx tsc-watch --onSuccess "node target/scripts/main.mjs"
clean: clean:
rm -rf target rm -rf target

View file

@ -1,4 +1,5 @@
import { AnyNode, E, Ea, Elem } from "ren"; import { AnyNode, E, Ea, Elem } from "ren";
import { config } from "../config.mjs";
import { div } from "../utils.mjs"; import { div } from "../utils.mjs";
export function Layout(page: AnyNode): Elem { export function Layout(page: AnyNode): Elem {
@ -15,15 +16,16 @@ export function Layout(page: AnyNode): Elem {
}), }),
E("title", "hello world"), E("title", "hello world"),
]), ]),
E("body", [ E("body", [div({ id: "root" }, page), config.isDev && HotReloadScript()]),
div({ id: "root" }, page),
E(
"script",
`const ws = new WebSocket("ws://localhost:30001");
ws.addEventListener("message", (m) => {
if (m.data === "RELOAD") location.reload();
});`
),
]),
]); ]);
} }
function HotReloadScript(): Elem {
return E(
"script",
`const ws = new WebSocket("ws://localhost:30001");
ws.addEventListener("message", (m) => {
if (m.data === "RELOAD") location.reload();
});`
);
}

View file

@ -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"; import { div } from "../utils.mjs";
export function PageLayout(...children: AnyNode[]): Elem { export function PageLayout(ctx: Context, ...children: AnyNode[]): Elem {
return Ea("div", { id: "main" }, [ return Ea("div", { id: "main" }, [
Header(), Header(ctx.locPath),
Ea("div", { class: "content" }, children), Ea("div", { class: "content" }, children),
Footer(), // Footer(),
]); ]);
} }
export function Header(): Elem { export function Header(locPath: string): Elem {
return Ea("header", { class: "header" }, [ return Ea("header", { class: "header" }, [
div({ class: "content-width" }, HeaderNav()), div({ class: "content-width" }, HeaderNav(locPath)),
]); ]);
} }
export function HeaderNav(): Elem { export function HeaderNav(locPath: string): Elem {
return E("nav", [ return Ea("nav", { class: "main-menu" }, [
Ea("a", { href: "/" }, "Обо мне"), NavLink(locPath, "/", "Обо мне"),
Ea("a", { href: "/works" }, "Работы"), 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 { export function Footer(): Elem {
return Ea("footer", { class: "footer" }, "footer"); return Ea("footer", { class: "footer" }, "footer");
} }

View file

@ -1,8 +1,14 @@
export const config = createConfig();
export function createConfig(): Config { export function createConfig(): Config {
return { server: { port: 30000 } }; return {
isDev: process.env.NODE_ENV === "develop",
server: { port: 30000 },
};
} }
export interface Config { export interface Config {
isDev: boolean;
server: ServerConfig; server: ServerConfig;
} }

3
src/context.mts Normal file
View file

@ -0,0 +1,3 @@
export interface Context {
locPath: string;
}

View file

@ -1,9 +1,7 @@
import { createConfig } from "./config.mjs";
import { createServer } from "./server.mjs"; import { createServer } from "./server.mjs";
main(); main();
function main(): void { function main(): void {
const config = createConfig(); createServer();
createServer(config.server);
} }

View file

@ -2,17 +2,21 @@ import * as http from "http";
import * as fs from "fs/promises"; import * as fs from "fs/promises";
import * as path from "path"; import * as path from "path";
import { Layout } from "./components/layout.mjs"; import { Layout } from "./components/layout.mjs";
import { ServerConfig } from "./config.mjs"; import { config } from "./config.mjs";
import { debug, info } from "./log.mjs"; import { debug, info } from "./log.mjs";
import { StrRenderer } from "ren"; import { StrRenderer } from "ren";
import { AboutPage } from "./views/about.mjs"; import { AboutPage } from "./views/about.mjs";
import { E404 } from "./views/e404.mjs"; import { E404 } from "./views/e404.mjs";
import { WorksPage } from "./views/works.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); const server = http.createServer(handleHttpReq);
server.listen(cfg.port, () => { server.listen(config.server.port, () => {
info("[server]", `Server listening at http://localhost:${cfg.port}`); info(
"[server]",
`Server listening at http://localhost:${config.server.port}`
);
}); });
} }
@ -39,19 +43,20 @@ async function handleHttpReq(
httpRes.writeHead(404).end("Not found"); httpRes.writeHead(404).end("Not found");
} }
} else { } else {
const ctx: Context = { locPath: req.url };
const ren = new StrRenderer(); const ren = new StrRenderer();
if (/^[/](?:about[/]?)?$/.test(req.url)) { if (/^[/](?:about[/]?)?$/.test(req.url)) {
httpRes httpRes
.writeHead(200, { "content-type": "text/html" }) .writeHead(200, { "content-type": "text/html" })
.end(ren.render(Layout(AboutPage()))); .end(ren.render(Layout(AboutPage(ctx))));
} else if (/^[/]works[/]?$/.test(req.url)) { } else if (/^[/]works[/]?$/.test(req.url)) {
httpRes httpRes
.writeHead(200, { "content-type": "text/html" }) .writeHead(200, { "content-type": "text/html" })
.end(ren.render(Layout(WorksPage()))); .end(ren.render(Layout(WorksPage(ctx))));
} else { } else {
httpRes httpRes
.writeHead(404, { "content-type": "text/html" }) .writeHead(404, { "content-type": "text/html" })
.end(ren.render(Layout(E404()))); .end(ren.render(Layout(E404(ctx))));
} }
} }
} catch (err) { } catch (err) {

View file

@ -1,9 +1,11 @@
import { PageLayout } from "../components/page_layout.mjs"; import { PageLayout } from "../components/page_layout.mjs";
import { AnyNode } from "ren"; import { AnyNode } from "ren";
import { div, h3, li, p, ul } from "../utils.mjs"; 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( return PageLayout(
ctx,
div({ class: "content-width responsive-typography" }, [ div({ class: "content-width responsive-typography" }, [
div({}, [ div({}, [
p("Привет!"), p("Привет!"),

View file

@ -1,9 +1,11 @@
import { PageLayout } from "../components/page_layout.mjs"; import { PageLayout } from "../components/page_layout.mjs";
import { AnyNode, Ea } from "ren"; import { AnyNode, Ea } from "ren";
import { div } from "../utils.mjs"; import { div } from "../utils.mjs";
import { Context } from "../context.mjs";
export function E404(): AnyNode { export function E404(ctx: Context): AnyNode {
return PageLayout( return PageLayout(
ctx,
div( div(
{ class: "content-width" }, { class: "content-width" },
Ea("h3", { class: "font-h3" }, "Страница не найдена") Ea("h3", { class: "font-h3" }, "Страница не найдена")

View file

@ -1,7 +1,8 @@
import { PageLayout } from "../components/page_layout.mjs"; import { PageLayout } from "../components/page_layout.mjs";
import { AnyNode } from "ren"; import { AnyNode } from "ren";
import { div, p } from "../utils.mjs"; import { div, p } from "../utils.mjs";
import { Context } from "../context.mjs";
export function WorksPage(): AnyNode { export function WorksPage(ctx: Context): AnyNode {
return PageLayout(div({ class: "content-width" }, p("Works"))); return PageLayout(ctx, div({ class: "content-width" }, p("Works")));
} }

View file

@ -2,7 +2,7 @@
--min-width: 320px; --min-width: 320px;
--max-width: 1024px; --max-width: 1024px;
--default-color-white: hsl(0, 0, 100%); --default-color-white: #ffffff;
--color-blue: #1966df; --color-blue: #1966df;
--default-font-size: 16px; --default-font-size: 16px;
@ -61,6 +61,32 @@ ul, ol {
padding-bottom: 1.5rem; 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 { .content-width {
width: 100%; width: 100%;
max-width: var(--max-width); max-width: var(--max-width);