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
|
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
|
||||||
|
|
|
@ -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();
|
||||||
|
});`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
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";
|
import { createServer } from "./server.mjs";
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
||||||
function main(): void {
|
function main(): void {
|
||||||
const config = createConfig();
|
createServer();
|
||||||
createServer(config.server);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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("Привет!"),
|
||||||
|
|
|
@ -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" }, "Страница не найдена")
|
||||||
|
|
|
@ -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")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue