@@ -14,59 +14,67 @@ Unlike traditional template engines, Boxwood templates are **just JavaScript fun
1414const HomePage = ({ posts }) => {
1515 return Div ([
1616 H1 (" Blog" ),
17- posts .map (post => Article ([
18- H2 (post .title ),
19- P (post .summary )
20- ]))
17+ posts .map ((post ) => Article ([H2 (post .title ), P (post .summary )])),
2118 ])
2219}
2320```
2421
2522## Key Advantages
2623
2724### Zero Learning Curve
25+
2826If you know JavaScript, you already know Boxwood. Use ` map ` , ` filter ` , ` if/else ` , and all standard JS features naturally.
2927
3028### IDE Support
29+
3130Get autocomplete, refactoring, and go-to-definition out of the box. Your templates are just code, so your editor understands them.
3231
3332### True Composition
33+
3434Components are functions. Compose them like functions. No slots, no special APIs - just parameters and return values.
3535
3636### Performance
37+
3738No template parsing at runtime. Templates are already JavaScript functions, eliminating parsing overhead.
3839
3940### Security Helpers
41+
4042- Automatic HTML escaping by default
4143- Basic sanitization for loaded SVG/HTML files
4244- Path traversal protection for file operations
4345- Remember: security is ultimately your responsibility
4446
4547### Integrated CSS Management
48+
4649- Automatic CSS scoping with hash-based class names
4750- CSS-in-JS with zero runtime
4851- Critical CSS inlining
4952- Automatic minification
5053
5154### Built-in i18n Support
55+
5256First-class internationalization support with a simple, component-friendly API for multi-language applications.
5357
5458### Asset Handling
59+
5560- Inline images as base64
5661- SVG loading with automatic sanitization
5762- JSON data loading
5863- Raw HTML imports with XSS protection
5964
6065### SEO Friendly
66+
6167- Pure server-side rendering - search engines see fully rendered HTML
6268- Lightning fast pages with inlined critical CSS
6369- Minimal payload size improves Core Web Vitals scores
6470- No client-side hydration delays
6571
6672### Minimal Footprint
67- Single file implementation (~ 950 lines). No complex build process or heavy dependencies.
73+
74+ Short implementation. No complex build process or heavy dependencies.
6875
6976### Testable by Design
77+
7078Templates are pure functions - easy to unit test with any testing framework.
7179
7280## Table of Contents
@@ -91,10 +99,7 @@ Create a template file:
9199const { Div , H1 , P } = require (" boxwood" )
92100
93101module .exports = ({ name, message }) => {
94- return Div ([
95- H1 (` Hello, ${ name} !` ),
96- P (message)
97- ])
102+ return Div ([H1 (` Hello, ${ name} !` ), P (message)])
98103}
99104```
100105
@@ -105,9 +110,9 @@ Compile and render it:
105110const { compile } = require (" boxwood" )
106111
107112const { template } = compile (" ./templates/greeting.js" )
108- const html = template ({
113+ const html = template ({
109114 name: " World" ,
110- message: " Welcome to Boxwood"
115+ message: " Welcome to Boxwood" ,
111116})
112117
113118console .log (html)
@@ -119,40 +124,41 @@ console.log(html)
119124Boxwood includes built-in Express support:
120125
121126``` js
122- import express from ' express'
123- import engine from ' boxwood/adapters/express'
124- import crypto from ' crypto'
127+ import express from " express"
128+ import engine from " boxwood/adapters/express"
129+ import crypto from " crypto"
125130
126131const app = express ()
127132
128133// Register Boxwood as template engine
129- app .engine (' js ' , engine ())
130- app .set (' views' , ' ./views' )
131- app .set (' view engine' , ' js ' )
134+ app .engine (" js " , engine ())
135+ app .set (" views" , " ./views" )
136+ app .set (" view engine" , " js " )
132137
133138// CSP (Content Security Policy) nonce for inline scripts
134139// A nonce is a unique random value generated for each request that allows
135140// specific inline scripts to execute while blocking potential XSS attacks
136141app .use ((req , res , next ) => {
137142 // Generate a cryptographically secure random nonce
138- res .locals .nonce = crypto .randomBytes (16 ).toString (' base64' )
139-
143+ res .locals .nonce = crypto .randomBytes (16 ).toString (" base64" )
144+
140145 // Set CSP header - only scripts with this exact nonce can execute
141146 res .setHeader (
142- ' Content-Security-Policy' ,
147+ " Content-Security-Policy" ,
143148 ` script-src 'nonce-${ res .locals .nonce } ' 'strict-dynamic';`
144149 )
145150 next ()
146151})
147152
148153// Render templates - nonce is automatically injected into all inline scripts
149- app .get (' / ' , (req , res ) => {
150- res .render (' home' , { title: ' Welcome' })
154+ app .get (" / " , (req , res ) => {
155+ res .render (" home" , { title: " Welcome" })
151156 // Boxwood automatically adds nonce="${res.locals.nonce}" to script tags
152157})
153158```
154159
155160The Express adapter automatically:
161+
156162- Handles template caching in production
157163- Hot reloads templates in development
158164- Injects CSP nonces from ` res.locals.nonce ` into all inline scripts and styles
@@ -171,6 +177,7 @@ A Content Security Policy (CSP) nonce is a security feature that helps prevent C
171177 - Attackers can't guess the nonce, so injected scripts are blocked
172178
173179Example output:
180+
174181``` html
175182<!-- HTTP Header -->
176183Content-Security-Policy: script-src 'nonce-rAnd0m123' 'strict-dynamic';
@@ -205,10 +212,13 @@ const styles = css`
205212`
206213
207214const Button = ({ variant, children }) => {
208- return ButtonTag ({
209- // className accepts arrays - falsy values are automatically filtered
210- className: [styles .button , variant === ' secondary' && styles .secondary ]
211- }, children)
215+ return ButtonTag (
216+ {
217+ // className accepts arrays - falsy values are automatically filtered
218+ className: [styles .button , variant === " secondary" && styles .secondary ],
219+ },
220+ children
221+ )
212222}
213223
214224module .exports = component (Button, { styles })
@@ -223,12 +233,12 @@ const { component, i18n, H1, P } = require("boxwood")
223233const Welcome = ({ translate, username }) => {
224234 return [
225235 H1 (translate (" greeting" ).replace (" {name}" , username)),
226- P (translate (" intro" ))
236+ P (translate (" intro" )),
227237 ]
228238}
229239
230- module .exports = component (Welcome, {
231- i18n: i18n .load (__dirname )
240+ module .exports = component (Welcome, {
241+ i18n: i18n .load (__dirname ),
232242})
233243```
234244
0 commit comments