🏷️ Section Tags

What are Section Tags?

StoneJS provides special section tags that let you organize your component code into logical blocks with different execution behaviors. These tags use a distinctive <%%tag> syntax that won't conflict with EJS.

Quick Reference

Tag Execution Primary Use
<%%doc> Never (removed) Documentation
<%%once> Once per component load Static initialization
<%%init> Every request (start) Request setup
<%%js> Inline (during body) JavaScript code blocks
<%%cleanup> Every request (end) Resource cleanup

1. <%%doc> Documentation Tag

Documentation blocks are completely removed during preprocessing. Use them for internal notes, explanations, and team documentation.

Syntax:

<%%doc> This is internal documentation. It won't be executed or appear in output. Great for explaining complex logic! </doc%%>
Use Cases:

2. <%%once> One-Time Initialization

Code in <%%once> blocks runs only once when the component is first loaded, not on every request. Perfect for expensive operations that produce static results.

Syntax:

<%%once> const CONFIG = require('./config.json'); const STATIC_DATA = expensiveComputation(); const COMPILED_REGEX = /^[a-z0-9]+$/i; console.log('This only runs once!'); </once%%>

Example:

<%%once> // Load configuration once const APP_CONFIG = { version: '1.0.0', apiUrl: 'https://api.example.com', maxRetries: 3 }; // Compile regex patterns once const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const PHONE_REGEX = /^\+?[1-9]\d{1,14}$/; </once%%> <h1>App Version: <%= APP_CONFIG.version %></h1>
Use Cases:

3. <%%init> Request Initialization

Code in <%%init> blocks runs at the beginning of every request, before any output is generated.

Syntax:

<%%init> const userId = $session.userId; const userData = await $db.query('SELECT * FROM users WHERE id = $1', [userId]); const requestTime = Date.now(); </init%%>

Example:

<%%init> // Set up request-specific variables const requestId = Math.random().toString(36).substring(7); const startTime = Date.now(); // Load user data const userId = $req.params.userId; const user = await $db.query('SELECT * FROM users WHERE id = $1', [userId]); // Validate request if (!user) { $res.status(404).send('User not found'); return; } </init%%> <h1>Welcome, <%= user.name %>!</h1>
Use Cases:

4. <%%js> Inline JavaScript Blocks

The <%%js> tag is a convenient alternative to standard EJS <% %> tags. It's converted directly to EJS code blocks during preprocessing.

Syntax:

<%%js> const items = ['Apple', 'Banana', 'Cherry']; const total = items.length; </js%%>

Converts to:

<% const items = ['Apple', 'Banana', 'Cherry']; const total = items.length; %>

Example:

<h1>Shopping List</h1> <%%js> const items = await $db.query('SELECT * FROM items'); const categorized = {}; items.forEach(item => { if (!categorized[item.category]) { categorized[item.category] = []; } categorized[item.category].push(item); }); </js%%> <% Object.keys(categorized).forEach(category => { %> <h2><%= category %></h2> <ul> <% categorized[category].forEach(item => { %> <li><%= item.name %></li> <% }); %> </ul> <% }); %>
Use Cases:

Note: <%%js> is functionally identical to <% %>. Use whichever you prefer - both work exactly the same way!

5. <%%cleanup> Request Cleanup

Code in <%%cleanup> blocks runs at the end of every request, even if errors occur. Uses try/finally under the hood.

Syntax:

<%%cleanup> // Log request duration const duration = Date.now() - requestStartTime; console.log('Request took', duration, 'ms'); // Close connections await someConnection.close(); // Record metrics await recordAnalytics({ duration, itemsProcessed }); </cleanup%%>

Example:

<%%init> const startTime = Date.now(); let itemsProcessed = 0; </init%%> <!-- Process items --> <% const items = await $db.query('SELECT * FROM items'); itemsProcessed = items.length; %> <%%cleanup> const duration = Date.now() - startTime; console.log(`Processed ${itemsProcessed} items in ${duration}ms`); // Record analytics await $db.query( 'INSERT INTO analytics (duration, items_count) VALUES ($1, $2)', [duration, itemsProcessed] ); </cleanup%%>
Use Cases:

6. Combining Multiple Tags

You can use all tags in the same component for complete control over execution flow:

<%%doc> User Profile Component Displays user information with caching and analytics Author: Development Team Last Updated: 2024-01-20 </doc%%> <%%once> const DEFAULT_AVATAR = '/images/default-avatar.png'; const PROFILE_CACHE_TTL = 3600; const ALLOWED_FIELDS = ['name', 'email', 'bio', 'avatar']; </once%%> <%%init> const userId = $req.params.userId; const viewStartTime = Date.now(); // Load profile with caching const profile = await $cache.remember(`profile:${userId}`, PROFILE_CACHE_TTL, async () => { const result = await $db.query('SELECT * FROM profiles WHERE user_id = $1', [userId]); return result.rows[0]; }); if (!profile) { $res.status(404).send('Profile not found'); return; } </init%%> <h1>Welcome, <%= profile.name %>!</h1> <img src="<%= profile.avatar || DEFAULT_AVATAR %>" alt="Avatar"> <p><%= profile.bio %></p> <%%cleanup> const viewDuration = Date.now() - viewStartTime; await $db.query( 'INSERT INTO profile_views (user_id, viewer_id, duration) VALUES ($1, $2, $3)', [userId, $session.userId, viewDuration] ); console.log(`Profile view for user ${userId} took ${viewDuration}ms`); </cleanup%%>

7. 📚 Best Practices

When to Use Each Tag:

<%%doc> - Use liberally for documentation

<%%once> - Use sparingly for truly static data

<%%init> - Use for request setup

<%%js> - Use for inline code blocks

<%%cleanup> - Use for guaranteed cleanup

8. ⚙️ Technical Details

Tag Syntax:

section tags use a distinctive <%%tagname> syntax to avoid conflicts with EJS:

How It Works:

  1. Preprocessing: Components are processed before EJS compilation
  2. <%%doc> Removal: Completely stripped from output
  3. <%%js> Conversion: Converted to standard <% %> EJS tags
  4. <%%once> Cache: Uses global cache (global.__stoneOnceCache) to track execution
  5. <%%init> Placement: Moved to the top of the component
  6. <%%cleanup> Wrapper: Wrapped in try/finally to ensure execution

Execution Order:

  1. Component loaded from disk
  2. Preprocessor detects section tags
  3. Tags are extracted and transformed
  4. Component is reconstructed with proper order
  5. EJS compiles the preprocessed component
  6. On first request: <%%once> code runs
  7. On every request: <%%init> → main body → <%%cleanup>

Performance: