or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdcompilation.mdcontext.mdfilters.mdhelpers.mdindex.mdparsing.mdrendering.md

helpers.mddocs/

0

# Helper System

1

2

Extensible helper system for implementing complex template logic and custom functionality through user-defined helper functions.

3

4

## Capabilities

5

6

### Helper Registration

7

8

System for registering and managing custom helper functions that extend template capabilities.

9

10

```javascript { .api }

11

/**

12

* Container for all registered helpers

13

* Empty by default - all helpers must be added by users

14

*/

15

const helpers: { [helperName: string]: HelperFunction };

16

17

/**

18

* Helper function signature

19

* @param chunk - Output chunk for writing content

20

* @param context - Template context for data access

21

* @param bodies - Template bodies (block, else, etc.)

22

* @param params - Parameters passed to helper

23

* @returns Modified chunk

24

*/

25

type HelperFunction = (

26

chunk: Chunk,

27

context: Context,

28

bodies: Bodies,

29

params: Params

30

) => Chunk;

31

32

interface Bodies {

33

/** Main template body */

34

block?: TemplateFunction;

35

/** Else template body */

36

else?: TemplateFunction;

37

/** Named bodies for custom helpers */

38

[bodyName: string]: TemplateFunction | undefined;

39

}

40

41

interface Params {

42

/** Parameters passed to helper as key-value pairs */

43

[paramName: string]: any;

44

}

45

```

46

47

**Usage Examples:**

48

49

```javascript

50

const dust = require('dustjs-linkedin');

51

52

// Register a simple helper

53

dust.helpers.uppercase = (chunk, context, bodies, params) => {

54

if (bodies.block) {

55

return chunk.capture(bodies.block, context, (data, chunk) => {

56

return chunk.write(data.toUpperCase());

57

});

58

}

59

return chunk;

60

};

61

62

// Register a helper with parameters

63

dust.helpers.repeat = (chunk, context, bodies, params) => {

64

const times = parseInt(params.times) || 1;

65

66

if (bodies.block) {

67

for (let i = 0; i < times; i++) {

68

chunk.render(bodies.block, context.push({ index: i }));

69

}

70

}

71

72

return chunk;

73

};

74

75

// Register a conditional helper

76

dust.helpers.compare = (chunk, context, bodies, params) => {

77

const left = params.left;

78

const right = params.right;

79

const operator = params.operator || 'eq';

80

81

let result = false;

82

switch (operator) {

83

case 'eq': result = left == right; break;

84

case 'ne': result = left != right; break;

85

case 'gt': result = left > right; break;

86

case 'lt': result = left < right; break;

87

case 'gte': result = left >= right; break;

88

case 'lte': result = left <= right; break;

89

}

90

91

if (result && bodies.block) {

92

return chunk.render(bodies.block, context);

93

} else if (!result && bodies.else) {

94

return chunk.render(bodies.else, context);

95

}

96

97

return chunk;

98

};

99

100

// Usage in templates:

101

// {@uppercase}hello world{/uppercase} → HELLO WORLD

102

// {@repeat times="3"}Item {index}{/repeat} → Item 0Item 1Item 2

103

// {@compare left=age right=18 operator="gte"}Adult{:else}Minor{/compare}

104

```

105

106

### Helper Context Access

107

108

Helpers have full access to the template context and can manipulate data resolution.

109

110

```javascript { .api }

111

interface Context {

112

/** Get data from context */

113

get(path: string): any;

114

/** Get current context data */

115

current(): any;

116

/** Push new context data */

117

push(data: any): Context;

118

/** Template name for debugging */

119

getTemplateName(): string | undefined;

120

}

121

```

122

123

**Usage Examples:**

124

125

```javascript

126

// Helper that accesses context data

127

dust.helpers.userInfo = (chunk, context, bodies, params) => {

128

const user = context.get('user');

129

const currentData = context.current();

130

131

if (user) {

132

const info = `User: ${user.name} (${user.role})`;

133

return chunk.write(info);

134

}

135

136

return chunk.write('No user information');

137

};

138

139

// Helper that modifies context

140

dust.helpers.withDefaults = (chunk, context, bodies, params) => {

141

const defaults = params.defaults || {};

142

const enhanced = context.push(defaults);

143

144

if (bodies.block) {

145

return chunk.render(bodies.block, enhanced);

146

}

147

148

return chunk;

149

};

150

151

// Helper that creates isolated context

152

dust.helpers.isolate = (chunk, context, bodies, params) => {

153

const isolatedData = params.data || {};

154

const isolatedContext = dust.context(isolatedData);

155

156

if (bodies.block) {

157

return chunk.render(bodies.block, isolatedContext);

158

}

159

160

return chunk;

161

};

162

```

163

164

### Chunk Manipulation

165

166

Helpers work with chunks to control template output and handle asynchronous operations.

167

168

```javascript { .api }

169

interface Chunk {

170

/** Write string content to output */

171

write(data: string): Chunk;

172

/** End chunk with optional final content */

173

end(data?: string): Chunk;

174

/** Render template body with context */

175

render(body: TemplateFunction, context: Context): Chunk;

176

/** Capture rendered content for processing */

177

capture(body: TemplateFunction, context: Context, callback: (data: string, chunk: Chunk) => Chunk): Chunk;

178

/** Create asynchronous chunk for async operations */

179

map(callback: (chunk: Chunk) => Chunk): Chunk;

180

/** Set error on chunk */

181

setError(error: Error): Chunk;

182

}

183

```

184

185

**Usage Examples:**

186

187

```javascript

188

// Helper that manipulates output

189

dust.helpers.wrap = (chunk, context, bodies, params) => {

190

const tag = params.tag || 'div';

191

const className = params.class || '';

192

193

chunk.write(`<${tag}${className ? ` class="${className}"` : ''}>`);

194

195

if (bodies.block) {

196

chunk.render(bodies.block, context);

197

}

198

199

return chunk.write(`</${tag}>`);

200

};

201

202

// Helper that processes content

203

dust.helpers.markdown = (chunk, context, bodies, params) => {

204

if (bodies.block) {

205

return chunk.capture(bodies.block, context, (data, chunk) => {

206

// Pseudo-code: convert markdown to HTML

207

const html = convertMarkdownToHtml(data);

208

return chunk.write(html);

209

});

210

}

211

return chunk;

212

};

213

214

// Asynchronous helper

215

dust.helpers.asyncData = (chunk, context, bodies, params) => {

216

const url = params.url;

217

218

return chunk.map((chunk) => {

219

// Async operation (pseudo-code)

220

fetchData(url, (err, data) => {

221

if (err) {

222

chunk.setError(err);

223

} else {

224

const dataContext = context.push({ asyncData: data });

225

if (bodies.block) {

226

chunk.render(bodies.block, dataContext);

227

}

228

}

229

chunk.end();

230

});

231

232

return chunk;

233

});

234

};

235

```

236

237

## Common Helper Patterns

238

239

### Conditional Helpers

240

241

Patterns for implementing conditional logic in templates.

242

243

```javascript

244

// Simple existence check helper

245

dust.helpers.ifExists = (chunk, context, bodies, params) => {

246

const value = context.get(params.key);

247

248

if (value !== undefined && value !== null && value !== '') {

249

if (bodies.block) {

250

return chunk.render(bodies.block, context);

251

}

252

} else {

253

if (bodies.else) {

254

return chunk.render(bodies.else, context);

255

}

256

}

257

258

return chunk;

259

};

260

261

// Multi-condition helper

262

dust.helpers.when = (chunk, context, bodies, params) => {

263

const conditions = params.conditions || [];

264

265

for (const condition of conditions) {

266

if (evaluateCondition(condition, context)) {

267

if (bodies[condition.body]) {

268

return chunk.render(bodies[condition.body], context);

269

}

270

}

271

}

272

273

if (bodies.else) {

274

return chunk.render(bodies.else, context);

275

}

276

277

return chunk;

278

};

279

280

// Usage in templates:

281

// {@ifExists key="user.email"}Has email{:else}No email{/ifExists}

282

```

283

284

### Data Manipulation Helpers

285

286

Helpers for transforming and processing data within templates.

287

288

```javascript

289

// Array manipulation helper

290

dust.helpers.slice = (chunk, context, bodies, params) => {

291

const array = context.get(params.array) || [];

292

const start = parseInt(params.start) || 0;

293

const end = params.end ? parseInt(params.end) : array.length;

294

295

const sliced = array.slice(start, end);

296

const sliceContext = context.push({ items: sliced });

297

298

if (bodies.block) {

299

return chunk.render(bodies.block, sliceContext);

300

}

301

302

return chunk;

303

};

304

305

// Object manipulation helper

306

dust.helpers.pick = (chunk, context, bodies, params) => {

307

const obj = context.get(params.from) || {};

308

const keys = params.keys ? params.keys.split(',') : [];

309

310

const picked = {};

311

keys.forEach(key => {

312

if (obj.hasOwnProperty(key.trim())) {

313

picked[key.trim()] = obj[key.trim()];

314

}

315

});

316

317

const pickedContext = context.push(picked);

318

319

if (bodies.block) {

320

return chunk.render(bodies.block, pickedContext);

321

}

322

323

return chunk;

324

};

325

326

// String manipulation helper

327

dust.helpers.truncate = (chunk, context, bodies, params) => {

328

const text = params.text || '';

329

const length = parseInt(params.length) || 50;

330

const suffix = params.suffix || '...';

331

332

const truncated = text.length > length

333

? text.substring(0, length) + suffix

334

: text;

335

336

return chunk.write(truncated);

337

};

338

```

339

340

### Formatting Helpers

341

342

Helpers for formatting data output in templates.

343

344

```javascript

345

// Date formatting helper

346

dust.helpers.formatDate = (chunk, context, bodies, params) => {

347

const date = new Date(params.date);

348

const format = params.format || 'YYYY-MM-DD';

349

350

if (isNaN(date.getTime())) {

351

return chunk.write('Invalid Date');

352

}

353

354

// Pseudo-code: format date according to format string

355

const formatted = formatDate(date, format);

356

return chunk.write(formatted);

357

};

358

359

// Number formatting helper

360

dust.helpers.formatNumber = (chunk, context, bodies, params) => {

361

const number = parseFloat(params.number);

362

const decimals = parseInt(params.decimals) || 2;

363

const locale = params.locale || 'en-US';

364

365

if (isNaN(number)) {

366

return chunk.write('Invalid Number');

367

}

368

369

const formatted = number.toLocaleString(locale, {

370

minimumFractionDigits: decimals,

371

maximumFractionDigits: decimals

372

});

373

374

return chunk.write(formatted);

375

};

376

377

// Currency formatting helper

378

dust.helpers.formatCurrency = (chunk, context, bodies, params) => {

379

const amount = parseFloat(params.amount);

380

const currency = params.currency || 'USD';

381

const locale = params.locale || 'en-US';

382

383

if (isNaN(amount)) {

384

return chunk.write('Invalid Amount');

385

}

386

387

const formatted = new Intl.NumberFormat(locale, {

388

style: 'currency',

389

currency: currency

390

}).format(amount);

391

392

return chunk.write(formatted);

393

};

394

```

395

396

## Advanced Helper Features

397

398

### Helper with Named Bodies

399

400

Helpers can define multiple named bodies for complex template structures.

401

402

```javascript

403

dust.helpers.switch = (chunk, context, bodies, params) => {

404

const value = context.get(params.value);

405

const cases = params.cases || {};

406

407

// Look for matching case body

408

if (cases[value] && bodies[cases[value]]) {

409

return chunk.render(bodies[cases[value]], context);

410

}

411

412

// Fall back to default

413

if (bodies.default) {

414

return chunk.render(bodies.default, context);

415

}

416

417

return chunk;

418

};

419

420

// Usage in template:

421

// {@switch value=status cases.active="activeBody" cases.inactive="inactiveBody"}

422

// {<activeBody}Status is active{/activeBody}

423

// {<inactiveBody}Status is inactive{/inactiveBody}

424

// {<default}Unknown status{/default}

425

// {/switch}

426

```

427

428

### Error Handling in Helpers

429

430

Best practices for handling errors within helper functions.

431

432

```javascript

433

dust.helpers.safeHelper = (chunk, context, bodies, params) => {

434

try {

435

// Potentially error-prone operation

436

const result = riskyOperation(params.input);

437

return chunk.write(result);

438

} catch (err) {

439

// Log error for debugging

440

dust.log('Helper error: ' + err.message, 'ERROR');

441

442

// Render fallback content

443

if (bodies.error) {

444

return chunk.render(bodies.error, context.push({ error: err.message }));

445

}

446

447

// Or set error on chunk

448

return chunk.setError(err);

449

}

450

};

451

452

// Helper that validates parameters

453

dust.helpers.requireParams = (chunk, context, bodies, params) => {

454

const required = ['name', 'email'];

455

const missing = required.filter(param => !params[param]);

456

457

if (missing.length > 0) {

458

const error = new Error(`Missing required parameters: ${missing.join(', ')}`);

459

return chunk.setError(error);

460

}

461

462

if (bodies.block) {

463

return chunk.render(bodies.block, context);

464

}

465

466

return chunk;

467

};

468

```

469

470

### Helper Testing

471

472

Patterns for testing custom helpers in isolation.

473

474

```javascript

475

// Test helper function

476

function testHelper() {

477

const mockChunk = {

478

output: '',

479

write(data) { this.output += data; return this; },

480

render(body, ctx) { /* mock implementation */ return this; },

481

end() { return this; }

482

};

483

484

const mockContext = dust.context({ test: 'data' });

485

const mockBodies = { block: () => 'rendered content' };

486

const mockParams = { param1: 'value1' };

487

488

// Test helper

489

const result = dust.helpers.myHelper(mockChunk, mockContext, mockBodies, mockParams);

490

491

console.log('Helper output:', mockChunk.output);

492

return result;

493

}

494

```

495

496

## Helper Best Practices

497

498

### Performance Considerations

499

500

- Cache expensive computations within helpers

501

- Avoid creating new objects/functions in helper execution

502

- Use chunk operations efficiently

503

504

### Security Considerations

505

506

- Always sanitize user input in helpers

507

- Validate parameters before processing

508

- Be cautious with dynamic code execution

509

510

### Maintainability

511

512

- Keep helpers focused on single responsibilities

513

- Document helper parameters and usage

514

- Provide error handling and fallbacks

515

516

```javascript

517

// Well-structured helper example

518

dust.helpers.bestPracticeHelper = (chunk, context, bodies, params) => {

519

// 1. Validate required parameters

520

if (!params.required) {

521

return chunk.setError(new Error('Missing required parameter'));

522

}

523

524

// 2. Sanitize inputs

525

const sanitized = sanitizeInput(params.input);

526

527

// 3. Perform operation with error handling

528

try {

529

const result = processData(sanitized);

530

531

// 4. Use appropriate chunk operations

532

if (bodies.block) {

533

const resultContext = context.push({ result });

534

return chunk.render(bodies.block, resultContext);

535

} else {

536

return chunk.write(result);

537

}

538

} catch (err) {

539

// 5. Handle errors gracefully

540

dust.log(`Helper error: ${err.message}`, 'ERROR');

541

return bodies.error

542

? chunk.render(bodies.error, context)

543

: chunk.write('Error processing data');

544

}

545

};

546

```