0
# Template Parsing
1
2
Template parsing functionality that converts Dust template source code into Abstract Syntax Trees (AST) using a PEG.js generated parser.
3
4
## Capabilities
5
6
### Parse Function
7
8
Parses Dust template source into an Abstract Syntax Tree for compilation or analysis.
9
10
```javascript { .api }
11
/**
12
* Parses template source into Abstract Syntax Tree
13
* @param source - Dust template source code
14
* @returns Abstract Syntax Tree object representation
15
* @throws SyntaxError with detailed location information for invalid syntax
16
*/
17
function parse(source: string): ASTNode;
18
19
interface ASTNode {
20
/** Node type identifier */
21
[0]: string;
22
/** Node attributes and properties */
23
[1]?: any;
24
/** Child nodes array */
25
[2]?: ASTNode[];
26
}
27
```
28
29
**Usage Examples:**
30
31
```javascript
32
const dust = require('dustjs-linkedin');
33
34
// Basic template parsing
35
const source = 'Hello {name}!';
36
const ast = dust.parse(source);
37
console.log(ast);
38
// Output: ['body', {}, [['buffer', 'Hello '], ['reference', ['path', ['key', 'name']], [], {}], ['buffer', '!']]]
39
40
// Complex template with sections
41
const complexSource = `
42
{#users}
43
<li>{name} - {role}</li>
44
{:else}
45
<li>No users</li>
46
{/users}
47
`;
48
49
try {
50
const complexAst = dust.parse(complexSource);
51
console.log('AST nodes:', complexAst[2].length);
52
console.log('First node type:', complexAst[2][0][0]);
53
} catch (err) {
54
console.error('Parse error:', err.message);
55
}
56
```
57
58
### AST Node Structure
59
60
Understanding the structure of parsed AST nodes for template analysis and manipulation.
61
62
```javascript { .api }
63
// Common AST node types:
64
65
// Body node (root container)
66
type BodyNode = ['body', {}, ASTNode[]];
67
68
// Buffer node (static text)
69
type BufferNode = ['buffer', string];
70
71
// Reference node (variable substitution)
72
type ReferenceNode = ['reference', PathNode, FilterNode[], ParamsNode];
73
74
// Section node (conditional/loop)
75
type SectionNode = ['section', PathNode, ContextNode, ParamsNode, BodyNode];
76
77
// Exists/NotExists nodes (conditional checks)
78
type ExistsNode = ['exists', PathNode, BodyNode];
79
type NotExistsNode = ['notexists', PathNode, BodyNode];
80
81
// Block node (template inheritance)
82
type BlockNode = ['block', KeyNode, BodyNode];
83
84
// Partial node (template inclusion)
85
type PartialNode = ['partial', PathNode | InlineNode, ContextNode, ParamsNode];
86
87
// Helper node (custom function call)
88
type HelperNode = ['helper', KeyNode, ContextNode, ParamsNode, BodyNode];
89
90
// Path node (variable path)
91
type PathNode = ['path', KeyNode[], FiltersNode[]];
92
93
// Key node (identifier)
94
type KeyNode = ['key', string];
95
96
// Inline node (literal value)
97
type InlineNode = ['inline', string];
98
```
99
100
**Usage Examples:**
101
102
```javascript
103
// Analyze AST structure
104
function analyzeTemplate(source) {
105
const ast = dust.parse(source);
106
107
function walkNodes(node, depth = 0) {
108
const indent = ' '.repeat(depth);
109
console.log(`${indent}Node: ${node[0]}`);
110
111
if (node[0] === 'reference') {
112
const path = node[1];
113
console.log(`${indent} Variable: ${path[1][0][1]}`); // Extract key name
114
}
115
116
if (node[0] === 'section') {
117
const path = node[1];
118
console.log(`${indent} Section: ${path[1][0][1]}`); // Extract section name
119
}
120
121
// Walk child nodes
122
if (node[2] && Array.isArray(node[2])) {
123
node[2].forEach(child => walkNodes(child, depth + 1));
124
}
125
}
126
127
walkNodes(ast);
128
}
129
130
// Analyze a template
131
analyzeTemplate('{#users}{name}{/users}');
132
// Output:
133
// Node: body
134
// Node: section
135
// Section: users
136
// Node: body
137
// Node: reference
138
// Variable: name
139
```
140
141
### Parser Error Handling
142
143
Comprehensive error handling with detailed location information for debugging template syntax issues.
144
145
```javascript { .api }
146
interface ParseSyntaxError extends SyntaxError {
147
/** Error message describing the syntax issue */
148
message: string;
149
/** Expected tokens or syntax elements */
150
expected: any[];
151
/** Actually found token or character */
152
found: string;
153
/** Location information for the error */
154
location: {
155
start: { offset: number; line: number; column: number };
156
end: { offset: number; line: number; column: number };
157
};
158
/** Error type name */
159
name: 'SyntaxError';
160
}
161
```
162
163
**Usage Examples:**
164
165
```javascript
166
// Handle parsing errors with location information
167
function parseWithErrorHandling(source, templateName) {
168
try {
169
return dust.parse(source);
170
} catch (err) {
171
if (err instanceof SyntaxError && err.location) {
172
console.error(`Parse error in template "${templateName}":
173
Message: ${err.message}
174
Line: ${err.location.start.line}
175
Column: ${err.location.start.column}
176
Expected: ${JSON.stringify(err.expected)}
177
Found: ${err.found}
178
Position: ${err.location.start.offset}`);
179
180
// Show source context
181
const lines = source.split('\n');
182
const errorLine = lines[err.location.start.line - 1];
183
const pointer = ' '.repeat(err.location.start.column - 1) + '^';
184
185
console.error(`Source: ${errorLine}`);
186
console.error(` ${pointer}`);
187
} else {
188
console.error('Unexpected parse error:', err);
189
}
190
throw err;
191
}
192
}
193
194
// Usage with error handling
195
try {
196
parseWithErrorHandling('{#unclosed', 'my-template');
197
} catch (err) {
198
// Error details logged above, handle appropriately
199
}
200
```
201
202
### AST Manipulation
203
204
Advanced AST manipulation for template transformation and analysis.
205
206
```javascript { .api }
207
/**
208
* Filter and transform AST nodes during compilation
209
* @param context - Compilation context
210
* @param node - AST node to filter
211
* @returns Transformed AST node
212
*/
213
function filterNode(context: any, node: ASTNode): ASTNode;
214
```
215
216
**Usage Examples:**
217
218
```javascript
219
// Custom AST transformation
220
function transformTemplate(source) {
221
// Parse to AST
222
const ast = dust.parse(source);
223
224
// Apply transformations using compiler
225
const context = {}; // Compilation context
226
const transformedAst = dust.compiler.filterNode(context, ast);
227
228
// Compile back to executable code
229
const code = dust.compiler.compileNode(context, transformedAst);
230
231
return code;
232
}
233
234
// Custom node visitor
235
function visitNodes(node, visitor) {
236
// Visit current node
237
visitor(node);
238
239
// Visit child nodes recursively
240
if (node[2] && Array.isArray(node[2])) {
241
node[2].forEach(child => visitNodes(child, visitor));
242
}
243
}
244
245
// Example: Find all variable references
246
function findReferences(source) {
247
const ast = dust.parse(source);
248
const references = [];
249
250
visitNodes(ast, (node) => {
251
if (node[0] === 'reference') {
252
const path = node[1];
253
if (path && path[1] && path[1][0] && path[1][0][1]) {
254
references.push(path[1][0][1]); // Extract variable name
255
}
256
}
257
});
258
259
return references;
260
}
261
262
// Find variables in template
263
const variables = findReferences('Hello {name}! You have {count} messages.');
264
console.log(variables); // ['name', 'count']
265
```
266
267
## Template Syntax Analysis
268
269
Understanding how different Dust template syntax elements are represented in the AST:
270
271
```javascript
272
// Variable references: {name}
273
const refAst = dust.parse('{name}');
274
// ['body', {}, [['reference', ['path', ['key', 'name']], [], {}]]]
275
276
// Sections: {#items}...{/items}
277
const sectionAst = dust.parse('{#items}Item: {name}{/items}');
278
// ['body', {}, [['section', ['path', ['key', 'items']], [...], {}]]]
279
280
// Conditionals: {?exists}...{/exists}
281
const existsAst = dust.parse('{?exists}Content{/exists}');
282
// ['body', {}, [['exists', ['path', ['key', 'exists']], [...]]]]
283
284
// Filters: {name|capitalize}
285
const filteredAst = dust.parse('{name|capitalize}');
286
// Reference node with filters array populated
287
288
// Parameters: {#section param="value"}...{/section}
289
const paramAst = dust.parse('{#section param="value"}Content{/section}');
290
// Section node with params object containing parameter definitions
291
292
// Blocks: {+title}Default Title{/title}
293
const blockAst = dust.parse('{+title}Default{/title}');
294
// ['body', {}, [['block', ['key', 'title'], [...]]]]
295
296
// Partials: {>"template" data=context /}
297
const partialAst = dust.parse('{>"template"/}');
298
// ['body', {}, [['partial', ['inline', 'template'], [...], {}]]]
299
300
// Helpers: {@helper param="value"}...{/helper}
301
const helperAst = dust.parse('{@helper param="value"}Content{/helper}');
302
// ['body', {}, [['helper', ['key', 'helper'], [...], {...}, [...]]]]
303
```
304
305
## Advanced Parser Features
306
307
### Context-Aware Parsing
308
309
The parser understands Dust template context and nesting rules:
310
311
```javascript
312
// Nested sections are properly parsed
313
const nested = dust.parse(`
314
{#users}
315
{name}
316
{#posts}
317
{title}
318
{/posts}
319
{/users}
320
`);
321
322
// Alternative section syntax
323
const alternative = dust.parse(`
324
{#users}
325
Active user: {name}
326
{:else}
327
No users found
328
{/users}
329
`);
330
331
// Complex path references
332
const paths = dust.parse('{user.profile.name} and {items[0].title}');
333
```
334
335
### Performance Considerations
336
337
The parser is optimized for template compilation performance:
338
339
- Templates are typically parsed once during compilation
340
- AST structures are lightweight for fast traversal
341
- Error reporting includes precise location information
342
- Parser supports all Dust syntax features comprehensively
343
344
```javascript
345
// For performance-critical applications, cache parsed ASTs
346
const astCache = new Map();
347
348
function getCachedAst(source, templateName) {
349
if (astCache.has(templateName)) {
350
return astCache.get(templateName);
351
}
352
353
const ast = dust.parse(source);
354
astCache.set(templateName, ast);
355
return ast;
356
}
357
```