Rendu: A JavaScript Hypertext Preprocessor

1 month ago 1

🏎️ JavaScript Hypertext Preprocessor.

Rendu is a lightweight toolkit for mixing HTML and JavaScript with a focus on simplicity, standards and progressive rendering.

Warning

This is an experimental PoC.

Using the rendu CLI, you can start a local web server to serve static files and render .html files as templates (powered by srvx).

compileTemplate(template, opts)

Compile a template string into a render function.

Example:

import { compileTemplate } from "rendu"; const template = ` <h1>{{ title }}</h1> <ul> <? for (const item of items) { ?> <li>{{ item }}</li> <? } ?> </ul> `; const render = compileTemplate(template, { stream: false }); const html = await render({ title: "My List", items: ["Item 1", "Item 2", "Item 3"] }); console.log(html); // Output: // <h1>My List</h1> // <ul> // <li>Item 1</li> // <li>Item 2</li> // <li>Item 3</li> // </ul>

compileTemplateToString(template, opts, asyncWrapper?)

Compile a template string into a render function code string.

Note: This function is for advanced use cases where you need the generated code as a string.

createRenderContext(options)

hasTemplateSyntax(template)

Check if a template string contains template syntax.

  • Type: array
  • Default: ["htmlspecialchars","setCookie","redirect","$REQUEST","$METHOD","$URL","$HEADERS","$COOKIES","$RESPONSE"]

renderToResponse(htmlTemplate, opts)

Renders an HTML template to a Response object.

Example:

import { compileTemplate, renderToResponse } from "rendu"; const render = compileTemplate(template, { stream: true }); const response = await renderToResponse(render, { request });

Rendu uses PHP-style tags to embed JavaScript within HTML templates:

Use <script server> to execute JavaScript on the server where it appears:

<script server> globalThis.visitedPagesCount ??= 0; globalThis.visitedPagesCount++; </script>

Use {{ expression }} for HTML-escaped output, or {{{ expression }}} or <?= expression ?> for unescaped (raw) output:

<h1><?= title ?></h1> <div>Page visited: {{ visitedPagesCount }}</div>

Use <? ... ?> for JavaScript control flow:

<? if (items.length === 0) { ?> <p>No items found.</p> <? } ?> <? for (const item of items) { ?> <li>{{ item.name }}</li> <? } ?>

Use the echo() function for streaming content. Accepts: strings, functions, Promises, Response objects, or ReadableStreams:

Examples:

<script server> echo("Welcome to our site!"); </script> <script server> echo("Hello"); echo(async () => fetch("https://api.example.com/data")); echo(() => "World"); </script>'><!-- Simple string output --> <script server> echo("Welcome to our site!"); </script> <!-- Async content from API (non-blocking)--> <script server> echo("Hello"); echo(async () => fetch("https://api.example.com/data")); echo(() => "World"); </script>

Access request context and global state:

  • $REQUEST: The incoming Request object
  • $METHOD: HTTP method (GET, POST, etc.)
  • $URL: Request URL object
  • $HEADERS: Request headers
  • $RESPONSE: Response configuration object
  • $COOKIES: Read-only object containing request cookies

Use setCookie() function to set cookies in the response:

<script server> setCookie("user", "RenduUser"); setCookie("session", "abc123", { maxAge: 3600, httpOnly: true }); </script>

Access cookies from the request using $COOKIES:

<div>Welcome, <?= $COOKIES["user"] || "Guest" ?>!</div>

Use redirect() function to redirect the user:

<script server> if (!$COOKIES["auth"]) { redirect("/login"); } </script>

The htmlspecialchars() function is available for escaping HTML content:

Tip

When using curly {{ }} syntax, htmlspecialchars will be automatically applied.

<div><?= htmlspecialchars(userInput) ?></div>
local development
  • Clone this repository
  • Install the latest LTS version of Node.js
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

Published under the MIT license.

Read Entire Article