commit 53e7c46749bcd7903a78b90e92b5fa9614cd1477 Author: Nulo Date: Tue Dec 20 17:53:19 2022 -0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..97c56e9 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@nulo:registry=https://gitea.nulo.in/api/packages/nulo/npm/ diff --git a/html.ts b/html.ts new file mode 100644 index 0000000..0d736f5 --- /dev/null +++ b/html.ts @@ -0,0 +1,81 @@ +// Inspirado en https://gitea.nulo.in/Nulo/html.lua/src/commit/cb7e35dcca0e45b397baf628f5e1a162a2269638/html.lua + +interface VirtualElement { + __element_type: string; + things: Thing[]; +} +interface RawHtml { + __raw: string; +} +interface Attributes { + [key: string]: string | number; +} +type Thing = VirtualElement | RawHtml | Attributes | string; +type Renderable = VirtualElement | RawHtml | string; + +export const basicElement = + (__element_type: string) => + (...things: Thing[]): VirtualElement => ({ __element_type, things }); +export const raw = (html: string): RawHtml => ({ __raw: html }); + +export const doctype = basicElement("!doctype html"); +export const head = basicElement("head"); +export const body = basicElement("body"); +export const meta = basicElement("meta"); +export const title = basicElement("title"); +export const link = basicElement("link"); +export const metaUtf8 = meta({ charset: "utf-8" }); +export const h1 = basicElement("h1"); +export const h2 = basicElement("h2"); +export const h3 = basicElement("h3"); +export const h4 = basicElement("h4"); +export const h5 = basicElement("h5"); +export const h6 = basicElement("h6"); +export const p = basicElement("p"); +export const ul = basicElement("ul"); +export const ol = basicElement("ol"); +export const li = basicElement("li"); +export const a = basicElement("a"); +export const img = basicElement("img"); +export const span = basicElement("span"); +export const picture = basicElement("picture"); +export const source = basicElement("source"); +export const figure = basicElement("figure"); +export const figcaption = basicElement("figcaption"); +export const article = basicElement("article"); +export const nav = basicElement("nav"); +export const main = basicElement("main"); + +// TODO: actually escape +const escapeHTML = (string: string) => string; +export const render = (...elements: Renderable[]): string => { + if (elements.length > 1) return elements.map((e) => render(e)).join(""); + if (typeof elements[0] == "string") return escapeHTML(elements[0]); + if ("__raw" in elements[0]) return elements[0].__raw; + const { __element_type, things } = elements[0]; + + const attributes = things.filter( + (t) => + !(typeof t == "string") && !("__element_type" in t) && !("__raw" in t) + ); + const children = things.filter( + (t) => + typeof t == "string" || + ("__element_type" in t && "things" in t) || + "__raw" in t + ) as Renderable[]; + const selfClosing = + ["meta", "img", "source", "link", "br"].includes(__element_type) || + __element_type.startsWith("!"); // doctype + + // TODO: escape attribute values + return `<${__element_type}${attributes.length ? " " : ""}${attributes + .flatMap((t) => + Object.entries(t).map(([name, value]) => `${name}="${value}"`) + ) + .join("")}${ + selfClosing + ? ">" + : `>${children.map((c) => render(c)).join("")}` + }`; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ff059b --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "@nulo/html.js", + "version": "0.0.2", + "description": "", + "main": "dist/html.js", + "files": [ + "dist/**.js", + "dist/**.d.ts" + ], + "scripts": { + "prepublish": "tsc" + }, + "author": "Nulo ", + "devDependencies": { + "@types/node": "^18.11.17", + "typescript": "^4.9.4" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..1ca0093 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,21 @@ +lockfileVersion: 5.4 + +specifiers: + '@types/node': ^18.11.17 + typescript: ^4.9.4 + +devDependencies: + '@types/node': 18.11.17 + typescript: 4.9.4 + +packages: + + /@types/node/18.11.17: + resolution: {integrity: sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==} + dev: true + + /typescript/4.9.4: + resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1b8b0f1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "target": "es2022", + "module": "es2022", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "outDir": "dist/" + } +}