SSR API
The Server Side Rendering (SSR) module provides utilities for rendering WinnetouJs constructos on the server and working with server-side content.
Import
// CommonJS
const {
loadPartial,
escapeHTML,
joinConstructos,
} = require("winnetoujs/modules/ssr");
// ES Modules
import {
loadPartial,
escapeHTML,
joinConstructos,
} from "winnetoujs/modules/ssr";
loadPartial()
Loads and returns the content of a file from the server's file system. This function is essential for loading HTML templates, partials, or any text-based content for server-side rendering.
Syntax
loadPartial(filePath: string): string
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
filePath |
string |
Yes | The absolute or relative path to the file to load |
Return Value
- Type:
string - Description: The content of the file as a string. Returns an empty string if the file doesn't exist or cannot be read.
Examples
Basic Usage
const content = loadPartial("./views/home.html");
console.log(content); // HTML content as string
Loading Different File Types
// Load HTML template
const template = loadPartial("./views/template.html");
// Load EJS file
const ejsContent = loadPartial("./views/page.ejs");
// Load Handlebars template
const hbsContent = loadPartial("./views/component.hbs");
// Load plain text
const textContent = loadPartial("./content/article.txt");
// Load Markdown
const mdContent = loadPartial("./docs/readme.md");
Using with Path Module
const path = require("path");
const content = loadPartial(
path.join(__dirname, "./views/docs/getting-started.html")
);
Loading Partials into Constructos
const header = loadPartial("./views/partials/header.html");
const footer = loadPartial("./views/partials/footer.html");
const content = loadPartial("./views/content.html");
const html = new $html({
header,
content,
footer,
}).constructoString();
Dynamic File Loading
app.get("/docs/:page", (req, res) => {
const pageName = req.params.page;
const content = loadPartial(`./views/docs/${pageName}.html`);
if (!content) {
return res.status(404).send("Page not found");
}
res.send(content);
});
Loading Multiple Partials
const partials = {
navigation: loadPartial("./views/partials/nav.html"),
sidebar: loadPartial("./views/partials/sidebar.html"),
main: loadPartial("./views/partials/main.html"),
footer: loadPartial("./views/partials/footer.html"),
};
const page = new $pageLayout({
nav: partials.navigation,
sidebar: partials.sidebar,
content: partials.main,
footer: partials.footer,
}).constructoString();
Use Cases
- Loading HTML template files for rendering
- Reading EJS, Handlebars, or other template engine files
- Loading static content for pages
- Including reusable partial views
- Reading configuration or data files
- Loading email templates
- Reading documentation or article content
Error Handling
The function returns an empty string if the file cannot be read. Always check the result:
const content = loadPartial("./views/page.html");
if (!content) {
console.error("Failed to load template");
// Handle error appropriately
}
Best Practices
Use Absolute Paths: Avoid path resolution issues by using
path.join(__dirname, ...)const content = loadPartial(path.join(__dirname, "./views/template.html"));Cache Frequently Used Files: Cache templates that are loaded multiple times
const templateCache = new Map(); function getCachedPartial(filePath) { if (!templateCache.has(filePath)) { templateCache.set(filePath, loadPartial(filePath)); } return templateCache.get(filePath); }Validate File Existence: Check if content was loaded successfully
const content = loadPartial(filePath); if (!content) { throw new Error(`Template not found: ${filePath}`); }Organize Templates: Keep templates in a dedicated directory structure
views/ ├── layouts/ ├── partials/ ├── pages/ └── components/
escapeHTML()
Escapes HTML special characters in a string to prevent HTML injection and to display HTML markup as plain text in the browser. This is crucial for security and for displaying code examples.
Syntax
escapeHTML(html: string): string
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
html |
string |
Yes | The string containing HTML to escape |
Return Value
- Type:
string - Description: The input string with HTML special characters converted to their HTML entity equivalents.
Character Conversions
| Character | HTML Entity | Description |
|---|---|---|
< |
< |
Less than |
> |
> |
Greater than |
& |
& |
Ampersand |
" |
" |
Double quote |
' |
' |
Single quote (apostrophe) |
Examples
Basic Usage
const escaped = escapeHTML("<div>Hello World</div>");
console.log(escaped);
// Output: "<div>Hello World</div>"
Displaying Code Examples
const codeSnippet = `
<button onclick="handleClick()">
Click me
</button>
`;
const escaped = escapeHTML(codeSnippet);
const html = new $codeBlock({
code: escaped,
}).constructoString();
Preventing XSS Attacks
app.post("/comment", (req, res) => {
const userComment = req.body.comment;
// Escape user input to prevent XSS
const safeComment = escapeHTML(userComment);
const commentHTML = new $comment({
text: safeComment,
author: escapeHTML(req.body.author),
}).constructoString();
res.send(commentHTML);
});
Escaping Multiple Strings
const userInput = {
name: escapeHTML(req.body.name),
bio: escapeHTML(req.body.bio),
website: escapeHTML(req.body.website),
};
const profile = new $userProfile(userInput).constructoString();
Displaying HTML in Documentation
const htmlExample = escapeHTML(`
<winnetou>
<div id="[[myDiv]]">
{{content}}
</div>
</winnetou>
`);
const docPage = new $documentationPage({
title: "Constructo Examples",
codeExample: htmlExample,
}).constructoString();
Escaping All Characters
const complexHTML = '<script>alert("XSS")</script>';
const escaped = escapeHTML(complexHTML);
console.log(escaped);
// Output: "<script>alert("XSS")</script>"
Using in API Responses
app.get("/api/snippet", (req, res) => {
const snippet = `
<div class="container">
<h1>Title</h1>
<p>Content here</p>
</div>
`;
res.json({
code: escapeHTML(snippet),
language: "html",
});
});
Use Cases
- Security: Sanitize user-generated content to prevent XSS attacks
- Code Display: Show HTML, XML, or other markup code as text
- Documentation: Display code examples in tutorials and docs
- Debugging: Show HTML structure without rendering it
- Email Content: Safely include user input in emails
- Form Validation: Display validation error messages safely
- API Responses: Return HTML code as JSON data
Security Considerations
Always Escape User Input: Never trust user-provided data
// Bad const html = `<div>${userInput}</div>`; // Good const html = `<div>${escapeHTML(userInput)}</div>`;Escape Before Storage or Display: Escape when rendering, not when storing
// When displaying const displayText = escapeHTML(storedUserInput);Don't Double-Escape: Avoid escaping already escaped content
const text = "Hello <World>"; const escaped = escapeHTML(text); // Don't do: escapeHTML(escaped)
Best Practices
Escape All User Input: When displaying user-generated content
const safeContent = escapeHTML(userGeneratedContent);Use for Code Examples: Display code snippets properly
new $codeBlock({ code: escapeHTML(codeSnippet), language: "html", }).create("#examples");Combine with Validation: Validate input, then escape for display
if (isValid(userInput)) { const safe = escapeHTML(userInput); // Use safe content }
joinConstructos()
Flattens and joins multiple parts (strings, arrays, or nested structures) into a single HTML string. This function is particularly useful for combining multiple constructos or HTML fragments in server-side rendering.
Syntax
joinConstructos(...parts: any[]): string
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
...parts |
any[] |
Yes | Variable number of arguments including strings, arrays, or nested arrays to be joined |
Return Value
- Type:
string - Description: A single concatenated string with all parts joined together. Nested arrays are automatically flattened.
Examples
Basic Usage
const combined = joinConstructos(
"<header>Header</header>",
"<main>Content</main>",
"<footer>Footer</footer>"
);
console.log(combined);
// Output: <header>Header</header><main>Content</main><footer>Footer</footer>
Combining Constructos
const pageHTML = joinConstructos(
new $header({ title: "Welcome" }).constructoString(),
new $content({ text: "Main content here" }).constructoString(),
new $footer({ year: 2024 }).constructoString()
);
res.send(pageHTML);
Joining Arrays of Constructos
const menuItems = [
new $menuItem({ text: "Home", href: "/" }).constructoString(),
new $menuItem({ text: "About", href: "/about" }).constructoString(),
new $menuItem({ text: "Contact", href: "/contact" }).constructoString(),
];
const menu = joinConstructos(...menuItems);
Nested Arrays (Auto-Flattening)
const header = new $header().constructoString();
const sidebar = new $sidebar().constructoString();
const content = new $content().constructoString();
const aside = new $aside().constructoString();
const footer = new $footer().constructoString();
// Nested arrays are automatically flattened
const page = joinConstructos(header, [sidebar, [content, aside]], footer);
Building Complex Layouts
const sections = [
new $hero({ title: "Welcome" }).constructoString(),
new $features({ items: featureList }).constructoString(),
new $testimonials({ reviews }).constructoString(),
new $cta({ text: "Get Started" }).constructoString(),
];
const landingPage = joinConstructos(
new $navbar().constructoString(),
sections,
new $footer().constructoString()
);
Dynamic Content Generation
app.get("/products", async (req, res) => {
const products = await getProducts();
const productCards = products.map(product =>
new $productCard({
name: product.name,
price: product.price,
image: product.image,
}).constructoString()
);
const pageHTML = joinConstructos(
new $header({ title: "Products" }).constructoString(),
new $productGrid({
cards: joinConstructos(...productCards),
}).constructoString(),
new $footer().constructoString()
);
res.send(pageHTML);
});
Email Template Generation
const emailHTML = joinConstructos(
new $emailHeader({
logo: "https://example.com/logo.png",
}).constructoString(),
new $emailBody({
greeting: "Hello User",
message: "Thank you for signing up!",
}).constructoString(),
new $emailFooter({
unsubscribeLink: "https://example.com/unsubscribe",
year: new Date().getFullYear(),
}).constructoString()
);
sendEmail(recipientEmail, emailHTML);
Conditional Parts
const pageHTML = joinConstructos(
new $header().constructoString(),
isLoggedIn
? new $userDashboard().constructoString()
: new $loginForm().constructoString(),
showAds ? new $adBanner().constructoString() : "",
new $footer().constructoString()
);
Combining with loadPartial
const pageHTML = joinConstructos(
loadPartial("./views/partials/header.html"),
new $content({ text: pageContent }).constructoString(),
loadPartial("./views/partials/footer.html")
);
Multiple Sections with Separators
const articles = articlesData.map(article =>
new $article({
title: article.title,
content: article.content,
}).constructoString()
);
const blogPage = joinConstructos(
new $blogHeader().constructoString(),
...articles.flatMap(article => [article, "<hr>"]),
new $blogFooter().constructoString()
);
Building Navigation Menus
const menuSections = [
{
title: "Getting Started",
items: ["Installation", "Quick Start", "Tutorial"],
},
{
title: "API",
items: ["Core API", "Router", "Translations"],
},
];
const menu = joinConstructos(
...menuSections.map(section =>
joinConstructos(
new $menuTitle({ title: section.title }).constructoString(),
...section.items.map(item =>
new $menuItem({ text: item }).constructoString()
)
)
)
);
Use Cases
- Page Assembly: Combine header, content, and footer
- Component Composition: Build complex components from smaller ones
- List Generation: Join multiple list items or cards
- Email Templates: Assemble email HTML from parts
- Dynamic Layouts: Conditionally include sections
- Menu Generation: Build navigation from multiple items
- Report Generation: Combine multiple data sections
- API Responses: Build HTML responses from fragments
Array Handling
The function automatically flattens nested arrays:
// These are equivalent:
joinConstructos("a", "b", "c");
joinConstructos(["a", "b", "c"]);
joinConstructos("a", ["b", "c"]);
joinConstructos(["a", ["b", ["c"]]]);
// All output: "abc"
Performance Considerations
- Efficient String Concatenation: The function efficiently handles large numbers of parts
- Memory Usage: Be mindful when joining very large arrays
- Pre-computation: Consider caching frequently used combinations
// Cache static parts
const cachedLayout = joinConstructos(
staticHeader,
"{{DYNAMIC_CONTENT}}",
staticFooter
);
// Replace dynamic part
const page = cachedLayout.replace("{{DYNAMIC_CONTENT}}", dynamicContent);
Best Practices
Organize Parts Logically: Group related constructos together
const pageHTML = joinConstructos( // Header section navbar, hero, // Main content content, sidebar, // Footer section footer );Use Spread Operator for Arrays: Spread arrays when joining
const items = [item1, item2, item3]; const result = joinConstructos(...items);Handle Empty Arrays: joinConstructos handles empty arrays gracefully
const items = products.length > 0 ? products.map(p => new $productCard(p).constructoString()) : [new $emptyState().constructoString()]; const html = joinConstructos(...items);Combine with Other SSR Functions: Use together with
loadPartialandescapeHTMLconst page = joinConstructos( loadPartial("./header.html"), new $content({ text: escapeHTML(userContent) }).constructoString(), loadPartial("./footer.html") );
Constructo Methods for SSR
When using constructos in server-side rendering, you'll use the .constructoString() method instead of .create().
constructoString()
Returns the HTML string representation of a constructo without attaching it to the DOM.
Syntax
constructoString(): string
Example
import { $myComponent } from "./components.wcto";
// Client-side (attaches to DOM)
new $myComponent({ text: "Hello" }).create("#app");
// Server-side (returns HTML string)
const html = new $myComponent({ text: "Hello" }).constructoString();
Complete SSR Example
const express = require("express");
const { joinConstructos, loadPartial } = require("winnetoujs/modules/ssr");
const { $html, $navbar, $footer } = require("./winnetou-ssr/dist/app.js");
const app = express();
app.get("/page", (req, res) => {
const content = loadPartial("./views/page.html");
const html = new $html({
title: "My Page",
navbar: new $navbar({ items: menuItems }).constructoString(),
content: content,
footer: new $footer({ year: 2024 }).constructoString(),
}).constructoString();
res.send(html);
});
app.listen(3000);
Type Definitions
For TypeScript users, the SSR module exports the following types:
/**
* Loads content from a file
* @param filePath - Path to the file to load
* @returns The file content as a string
*/
export function loadPartial(filePath: string): string;
/**
* Escapes HTML special characters
* @param html - The HTML string to escape
* @returns The escaped HTML string
*/
export function escapeHTML(html: string): string;
/**
* Joins multiple parts into a single string
* @param parts - Variable number of parts to join
* @returns The joined string
*/
export function joinConstructos(...parts: any[]): string;
Complete API Reference
Summary Table
| Function | Purpose | Input | Output |
|---|---|---|---|
loadPartial() |
Load file content | File path (string) | File content (string) |
escapeHTML() |
Escape HTML characters | HTML string | Escaped HTML string |
joinConstructos() |
Join multiple parts | Variable parts | Single string |
.constructoString() |
Get constructo HTML | None (method) | HTML string |
Module Availability
The SSR module is available when compiling with:
-nor--nodeflag (CommonJS format)-eor--node-esmflag (ES Module format)
Browser Compatibility
⚠️ Important: The SSR module is designed for Node.js server environments only. It will not work in browser environments as it requires Node.js file system access.
For browser-side rendering, use the standard .create() method instead of .constructoString().
Common Patterns
Pattern 1: Complete Page Rendering
app.get("/page/:id", async (req, res) => {
const pageData = await getPageData(req.params.id);
const html = new $html({
title: pageData.title,
description: pageData.description,
content: joinConstructos(
new $header({ title: pageData.title }).constructoString(),
loadPartial(`./views/pages/${req.params.id}.html`),
new $footer({ year: new Date().getFullYear() }).constructoString()
),
}).constructoString();
res.send(html);
});
Pattern 2: Rendering Lists
const items = data.map(item =>
new $listItem({
title: escapeHTML(item.title),
description: escapeHTML(item.description),
}).constructoString()
);
const list = new $list({
items: joinConstructos(...items),
}).constructoString();
Pattern 3: Conditional Rendering
const content = isAuthenticated
? new $userDashboard({ user }).constructoString()
: new $loginPrompt().constructoString();
const page = new $html({
content,
navbar: new $navbar({ isAuth: isAuthenticated }).constructoString(),
}).constructoString();
Pattern 4: Template Caching
const cache = new Map();
function renderPage(template, data) {
const key = `${template}-${JSON.stringify(data)}`;
if (!cache.has(key)) {
const html = new $page({
content: loadPartial(`./views/${template}.html`),
...data,
}).constructoString();
cache.set(key, html);
}
return cache.get(key);
}
Migration Guide
From Client-Side to Server-Side
Client-Side:
import { $myComponent } from "./components.wcto";
new $myComponent({ text: "Hello" }).create("#app");
Server-Side:
const { $myComponent } = require("./components.wcto");
const html = new $myComponent({ text: "Hello" }).constructoString();
res.send(html);
Key Differences
| Aspect | Client-Side | Server-Side |
|---|---|---|
| Import | ES6 import | CommonJS require or ES6 import |
| Method | .create('#selector') |
.constructoString() |
| Return | Object with ids | HTML string |
| Environment | Browser | Node.js |
| Compilation | wbr -b -p |
wbr -b -n -p or wbr -b -e -p |
This API reference provides complete documentation for all SSR utilities available in WinnetouJs. Use these functions to build high-performance, SEO-friendly server-rendered applications.