Asynchronous templates for the browser and server (LinkedIn fork)
Stack-based context system for data resolution with scoping, inheritance, and block management in Dust templates.
Functions for creating and managing template contexts that provide data resolution during rendering.
/**
* Creates base template context
* @param global - Global data object accessible throughout template
* @param options - Context configuration options
* @returns Context instance for template rendering
*/
function context(global: any, options?: ContextOptions): Context;
/**
* Alias for context() function
* @param global - Global data object
* @param options - Context configuration options
* @returns Context instance
*/
function makeBase(global: any, options?: ContextOptions): Context;
interface ContextOptions {
/** Template name for debugging */
templateName?: string;
/** Block definitions for template inheritance */
blocks?: { [blockName: string]: any };
/** Additional context configuration */
[key: string]: any;
}Usage Examples:
const dust = require('dustjs-linkedin');
// Basic context creation
const ctx = dust.context({
user: { name: 'Alice', role: 'admin' },
site: { title: 'My App', version: '1.0' }
});
// Context with options
const ctxWithOptions = dust.context({
users: [{ name: 'Bob' }, { name: 'Carol' }]
}, {
templateName: 'user-list',
blocks: { header: 'Custom Header' }
});
// Using makeBase alias
const baseCtx = dust.makeBase({
config: { debug: true },
helpers: { customHelper: () => 'Custom' }
});Utility function to determine if an object is a Dust context.
/**
* Tests if object is a Dust context
* @param obj - Object to test
* @returns true if object is a Context instance
*/
function isContext(obj: any): boolean;
/**
* Static method to wrap context data
* @param context - Context data or existing Context instance
* @param name - Template name for debugging
* @returns Context instance
*/
Context.wrap(context: any, name?: string): Context;Usage Examples:
const ctx = dust.context({ data: 'value' });
const plainObject = { data: 'value' };
console.log(dust.isContext(ctx)); // true
console.log(dust.isContext(plainObject)); // false
// Useful in helper functions
function myHelper(chunk, context, bodies, params) {
if (!dust.isContext(context)) {
throw new Error('Invalid context provided to helper');
}
// Helper implementation...
}Methods for accessing and resolving data within the context stack.
interface Context {
/**
* Get value from context by path
* @param path - Dot-separated path or array of keys
* @param cur - Current context data (optional)
* @returns Resolved value or undefined
*/
get(path: string | string[], cur?: any): any;
/**
* Get current context data at top of stack
* @returns Current data object
*/
current(): any;
/**
* Get template name for debugging
* @returns Template name string or undefined
*/
getTemplateName(): string | undefined;
}Usage Examples:
const ctx = dust.context({
user: {
profile: { name: 'Alice', email: 'alice@example.com' },
settings: { theme: 'dark', notifications: true }
},
items: ['apple', 'banana', 'cherry']
});
// Simple path resolution
console.log(ctx.get('user.profile.name')); // 'Alice'
console.log(ctx.get('user.settings.theme')); // 'dark'
// Array path resolution
console.log(ctx.get(['user', 'profile', 'email'])); // 'alice@example.com'
// Array index access
console.log(ctx.get('items.0')); // 'apple'
console.log(ctx.get('items.2')); // 'cherry'
// Non-existent path
console.log(ctx.get('user.invalid.path')); // undefined
// Current context data
console.log(ctx.current()); // Full context objectMethods for manipulating the context stack during template execution.
interface Context {
/**
* Push new data onto context stack
* @param head - New context data to push
* @param idx - Index for array iterations (optional)
* @param len - Length for array iterations (optional)
* @returns New context with pushed data
*/
push(head: any, idx?: number, len?: number): Context;
/**
* Remove top item from context stack
* @returns Context with popped data
*/
pop(): Context;
/**
* Create new context with different base data
* @param head - New base data
* @returns New context instance
*/
rebase(head: any): Context;
/**
* Clone current context
* @returns New context instance with same data
*/
clone(): Context;
}Usage Examples:
const baseCtx = dust.context({
global: 'value',
config: { debug: true }
});
// Push new context data
const userCtx = baseCtx.push({
name: 'Bob',
role: 'editor'
});
console.log(userCtx.get('name')); // 'Bob' (from pushed data)
console.log(userCtx.get('global')); // 'value' (from base context)
// Push with array iteration info
const items = ['a', 'b', 'c'];
items.forEach((item, idx) => {
const itemCtx = baseCtx.push(item, idx, items.length);
console.log(itemCtx.get('@index')); // Current index
console.log(itemCtx.get('@length')); // Array length
});
// Pop context
const poppedCtx = userCtx.pop();
console.log(poppedCtx.get('name')); // undefined (popped)
console.log(poppedCtx.get('global')); // 'value' (still available)
// Rebase context
const rebasedCtx = baseCtx.rebase({
newBase: 'data',
different: 'values'
});
// Clone context
const clonedCtx = baseCtx.clone();
console.log(clonedCtx.get('global')); // 'value'Methods for managing template blocks used in template inheritance.
interface Context {
/**
* Get block by name
* @param key - Block name
* @returns Block definition or undefined
*/
getBlock(key: string): any;
/**
* Add local blocks to context
* @param locals - Object containing block definitions
* @returns New context with added blocks
*/
shiftBlocks(locals: { [blockName: string]: any }): Context;
}Usage Examples:
// Context with blocks
const ctx = dust.context({ data: 'value' }, {
blocks: {
header: 'Custom Header Block',
footer: 'Custom Footer Block'
}
});
// Get block by name
console.log(ctx.getBlock('header')); // 'Custom Header Block'
console.log(ctx.getBlock('sidebar')); // undefined
// Add local blocks
const extendedCtx = ctx.shiftBlocks({
sidebar: 'Sidebar Block',
navigation: 'Nav Block'
});
console.log(extendedCtx.getBlock('sidebar')); // 'Sidebar Block'
console.log(extendedCtx.getBlock('header')); // 'Custom Header Block' (inherited)Method for resolving template bodies to string output.
interface Context {
/**
* Resolve template body to string output
* @param body - Template body function or content
* @returns Promise resolving to rendered string
*/
resolve(body: any): Promise<string>;
}Usage Examples:
// Used internally by Dust during template rendering
// Typically not called directly by user code
const ctx = dust.context({ name: 'Alice' });
// In a helper function
function customHelper(chunk, context, bodies, params) {
if (bodies.block) {
return context.resolve(bodies.block).then(content => {
return chunk.write(`<div class="wrapper">${content}</div>`);
});
}
return chunk;
}Understanding how Dust resolves data paths within the context stack:
const ctx = dust.context({
// Global data
global: 'global-value',
user: { name: 'Alice' }
}).push({
// Local data (top of stack)
local: 'local-value',
user: { role: 'admin' } // Shadows global user
});
// Resolution priority (top of stack first)
console.log(ctx.get('local')); // 'local-value' (local only)
console.log(ctx.get('global')); // 'global-value' (global only)
console.log(ctx.get('user.name')); // undefined (local user has no name)
console.log(ctx.get('user.role')); // 'admin' (local user.role)
// Explicit current context access
console.log(ctx.current().local); // 'local-value'Dust provides special variables during iterations and template execution:
// Array iteration provides special variables
const items = ['apple', 'banana', 'cherry'];
const ctx = dust.context({ items });
// When iterating over array (internally handled by sections)
items.forEach((item, idx) => {
const iterCtx = ctx.push(item, idx, items.length);
console.log(iterCtx.get('@index')); // Current index (0, 1, 2)
console.log(iterCtx.get('@length')); // Array length (3)
console.log(iterCtx.get('@first')); // true for first item
console.log(iterCtx.get('@last')); // true for last item
console.log(iterCtx.get('@odd')); // true for odd indices
console.log(iterCtx.get('@even')); // true for even indices
});
// Object iteration provides key access
const obj = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach((key, idx) => {
const iterCtx = ctx.push(obj[key], idx, Object.keys(obj).length);
iterCtx.key = key; // Key is available as property
console.log(iterCtx.get('@key')); // Current key ('a', 'b', 'c')
console.log(iterCtx.get('.')); // Current value (1, 2, 3)
});How context is used within helper functions:
// Register a helper that uses context
dust.helpers.debugContext = (chunk, context, bodies, params) => {
// Access current data
const currentData = context.current();
// Get specific values
const userName = context.get('user.name');
const templateName = context.getTemplateName();
// Push new context for body execution
const newContext = context.push({
debug: true,
timestamp: Date.now()
});
// Render body with new context
return chunk.render(bodies.block, newContext);
};
// Usage in template: {@debugContext}Content with debug info{/debugContext}Best practices for efficient context usage:
// Efficient: Reuse base contexts
const baseCtx = dust.context({
config: appConfig,
helpers: customHelpers
});
// For each render, push specific data
function renderUserTemplate(userData) {
const userCtx = baseCtx.push(userData);
return dust.render('user-template', userCtx, callback);
}
// Avoid: Creating new base contexts repeatedly
function inefficientRender(userData) {
const ctx = dust.context({
config: appConfig, // Recreated each time
...userData
});
return dust.render('user-template', ctx, callback);
}Utilities for debugging context resolution:
// Debug helper to inspect context
dust.helpers.inspectContext = (chunk, context) => {
const debug = {
current: context.current(),
templateName: context.getTemplateName(),
stackDepth: 'not directly accessible' // Internal implementation detail
};
return chunk.write(`<!--Context: ${JSON.stringify(debug, null, 2)}-->`);
};
// Usage in template: {@inspectContext/}Install with Tessl CLI
npx tessl i tessl/npm-dustjs-linkedin