@threew/view
v1.0.3
Published
Production-ready View Engine and Template Rendering System for Threew ecosystem
Maintainers
Readme
@threew/view
Introduction
@threew/view is the official View Engine and Template Rendering System for the Threew ecosystem. Designed to be modular, extensible, and lightweight, it provides robust template rendering capabilities with excellent type safety. It supports multiple popular template engines and can be used independently or seamlessly integrated with @threew/app.
This package focuses exclusively on view rendering, deliberately avoiding HTTP frameworks, routers, servers, or middleware engines to maintain a clear and focused scope.
Features
- Modular & Extensible: Easily integrate new template engines via a simple adapter interface.
- Lightweight: Minimal dependencies and focused functionality.
- Type Safety: Built with TypeScript, ensuring a robust and predictable development experience.
- Multi-Engine Support: Out-of-the-box support for EJS, Handlebars, Nunjucks, and Pug.
- Caching: Optional in-memory caching for improved performance by avoiding redundant file system reads.
- Flexible Template Resolution: Supports relative and absolute paths, nested folders, layouts, and partials.
- Independent or Integrated: Usable as a standalone library or within the Threew application framework.
Installation
To install @threew/view, use your preferred package manager:
npm install @threew/view
# or
yarn add @threew/view
# or
pnpm add @threew/viewQuick Start
Here's a quick example to get you started:
import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'
async function bootstrap() {
const views = new ViewManager({
views: path.join(__dirname, 'views'), // Your templates directory
cache: true // Enable caching
})
// Register an EJS adapter
views.register('ejs', new EjsAdapter())
// Render a template
const html = await views.render('home', {
title: 'Welcome to Threew View',
message: 'This is a sample page.'
})
console.log(html)
}
bootstrap().catch(console.error)Engine Registration
@threew/view allows you to register multiple template engines and switch between them. Each engine must implement the ViewEngine interface.
import { ViewManager, EjsAdapter, PugAdapter, HandlebarsAdapter, NunjucksAdapter } from '@threew/view'
const views = new ViewManager()
views.register('ejs', new EjsAdapter())
views.register('pug', new PugAdapter())
views.register('handlebars', new HandlebarsAdapter())
views.register('nunjucks', new NunjucksAdapter())
// Set the default engine
views.setEngine('ejs')
// Check if an engine is registered
console.log(views.hasEngine('pug')) // true
// Get a registered engine
const ejsEngine = views.getEngine('ejs')
// Remove an engine
views.removeEngine('nunjucks')
// Clear all engines
views.clearEngines()Rendering
The ViewManager provides a simple render method to process your templates.
import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'
const views = new ViewManager({
views: path.join(__dirname, 'views')
})
views.register('ejs', new EjsAdapter())
// Assuming 'views/dashboard.ejs' exists
const userData = { user: { name: 'John Doe', role: 'Admin' } }
const dashboardHtml = await views.render('dashboard', userData)
console.log(dashboardHtml)Layouts and Partials
The ViewResolver and ViewRenderer components are designed to support layouts and partials, allowing for reusable template structures. While the core render method handles basic template resolution, advanced layout and partial handling will depend on the specific capabilities of the registered ViewEngine adapters.
For example, an EJS template might include a partial like this:
<!-- views/layout.ejs -->
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<%- body %>
<%- include('partials/footer') %>
</body>
</html>And a partial:
<!-- views/partials/footer.ejs -->
<footer>
<p>© 2023 Threew</p>
</footer>Cache
Enable in-memory caching to prevent repeated file system reads for templates. This can significantly improve performance in production environments.
import { ViewManager, EjsAdapter } from '@threew/view'
const views = new ViewManager({
cache: true // Enable caching
})
views.register('ejs', new EjsAdapter())
// Templates will be cached after the first render
await views.render('home', { title: 'Cached Page' })EJS Example
// examples/ejs.ts
import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function run() {
const views = new ViewManager({
views: path.join(__dirname, '../tests/views'),
cache: true
})
views.register('ejs', new EjsAdapter())
const html = await views.render('home', {
title: 'Hello from EJS'
})
console.log('Rendered HTML:', html)
}
run().catch(console.error)Handlebars Example
// examples/handlebars.ts
import { ViewManager, HandlebarsAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function run() {
const views = new ViewManager({
views: path.join(__dirname, '../tests/views'),
cache: true
})
views.register('handlebars', new HandlebarsAdapter())
// Assuming a template 'views/welcome.hbs' with content '<h1>Hello, {{name}}!</h1>'
const html = await views.render('welcome', {
name: 'Handlebars User'
})
console.log('Rendered HTML:', html)
}
run().catch(console.error)Nunjucks Example
// examples/nunjucks.ts
import { ViewManager, NunjucksAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function run() {
const views = new ViewManager({
views: path.join(__dirname, '../tests/views'),
cache: true
})
views.register('nunjucks', new NunjucksAdapter())
// Assuming a template 'views/greeting.njk' with content '<h1>Hello, {{ name }}!</h1>'
const html = await views.render('greeting', {
name: 'Nunjucks User'
})
console.log('Rendered HTML:', html)
}
run().catch(console.error)Pug Example
// examples/pug.ts
import { ViewManager, PugAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function run() {
const views = new ViewManager({
views: path.join(__dirname, '../tests/views'),
cache: true
})
views.register('pug', new PugAdapter())
// Assuming a template 'views/profile.pug' with content 'h1= user.name'
const html = await views.render('profile', {
user: { name: 'Pug User' }
})
console.log('Rendered HTML:', html)
}
run().catch(console.error)