0
# Template Processing
1
2
EJS template processing for dynamic file generation with context variables and template options, enabling creation of customized files from templates.
3
4
## Capabilities
5
6
### Copy with Template Processing
7
8
Copy files and process them as EJS templates, substituting variables with provided context data.
9
10
```typescript { .api }
11
/**
12
* Copy files with EJS template processing
13
* @param from - Source template files or glob pattern
14
* @param to - Destination path
15
* @param context - Template variables for substitution
16
* @param tplSettings - EJS template configuration options
17
* @param options - Copy options
18
*/
19
function copyTpl(
20
from: string | string[],
21
to: string,
22
context?: Record<string, any>,
23
tplSettings?: EJSOptions,
24
options?: CopySingleOptions
25
): void;
26
27
interface EJSOptions {
28
/** Template filename for error reporting */
29
filename?: string;
30
/** Enable template caching for performance */
31
cache?: boolean;
32
/** Custom opening delimiter (default: "<%") */
33
openDelimiter?: string;
34
/** Custom closing delimiter (default: "%>") */
35
closeDelimiter?: string;
36
/** Additional EJS configuration options */
37
[key: string]: any;
38
}
39
40
interface CopySingleOptions {
41
/** Append to destination instead of overwriting */
42
append?: boolean;
43
/** Process file contents during copy */
44
process?: (contents: string | Buffer, filepath: string, destination: string) => string | Buffer;
45
}
46
```
47
48
**Usage Examples:**
49
50
```typescript
51
import { create as createMemFs } from "mem-fs";
52
import { create as createEditor } from "mem-fs-editor";
53
54
const store = createMemFs();
55
const fs = createEditor(store);
56
57
// Basic template processing
58
const context = {
59
name: "MyProject",
60
version: "1.0.0",
61
author: "John Doe"
62
};
63
64
fs.copyTpl("templates/package.json.ejs", "package.json", context);
65
66
// Note: .ejs extensions are automatically removed from destination paths
67
fs.copyTpl("template.txt.ejs", "output/", context);
68
// Creates: output/template.txt (not output/template.txt.ejs)
69
70
// Multiple template files
71
fs.copyTpl("templates/**/*.ejs", "output/", {
72
projectName: "awesome-app",
73
features: ["auth", "database", "api"],
74
config: {
75
port: 3000,
76
database: "mongodb"
77
}
78
});
79
80
// Template with custom delimiters
81
fs.copyTpl("template.txt", "output.txt", { name: "World" }, {
82
openDelimiter: "{{",
83
closeDelimiter: "}}"
84
});
85
```
86
87
### Async Template Copy
88
89
Asynchronous template processing for better performance with large template sets.
90
91
```typescript { .api }
92
/**
93
* Async version of template copy
94
* @param from - Source template files or glob pattern
95
* @param to - Destination path
96
* @param context - Template variables for substitution
97
* @param tplSettings - EJS template configuration options
98
* @param options - Copy options
99
* @returns Promise that resolves when template processing is complete
100
*/
101
function copyTplAsync(
102
from: string | string[],
103
to: string,
104
context?: Record<string, any>,
105
tplSettings?: EJSOptions,
106
options?: CopySingleOptions
107
): Promise<void>;
108
```
109
110
**Usage Examples:**
111
112
```typescript
113
// Async template processing
114
await fs.copyTplAsync("templates/**/*.ejs", "generated/", {
115
timestamp: new Date().toISOString(),
116
environment: "production",
117
features: {
118
authentication: true,
119
logging: true,
120
monitoring: false
121
}
122
});
123
124
// Process large template sets
125
const contexts = [
126
{ name: "frontend", type: "react" },
127
{ name: "backend", type: "node" },
128
{ name: "database", type: "postgres" }
129
];
130
131
for (const ctx of contexts) {
132
await fs.copyTplAsync(`templates/${ctx.type}/**/*.ejs`, `services/${ctx.name}/`, ctx);
133
}
134
```
135
136
### Append Template Content
137
138
Append templated content to existing files, useful for building up files incrementally.
139
140
```typescript { .api }
141
/**
142
* Append templated content to file
143
* @param filepath - Target file path
144
* @param contents - Template content string
145
* @param context - Template variables for substitution
146
* @param templateOptions - EJS template settings
147
* @param options - Append configuration options
148
*/
149
function appendTpl(
150
filepath: string,
151
contents: string,
152
context: Record<string, any>,
153
templateOptions?: EJSOptions,
154
options?: AppendOptions
155
): void;
156
157
interface AppendOptions {
158
/** Create file if it doesn't exist (default: true) */
159
create?: boolean;
160
/** Remove trailing whitespace before appending (default: false) */
161
trimEnd?: boolean;
162
/** Separator to add between existing and new content */
163
separator?: string;
164
}
165
```
166
167
**Usage Examples:**
168
169
```typescript
170
// Build up a file with multiple template appends
171
const baseContext = { projectName: "MyApp" };
172
173
// Initialize file
174
fs.write("config.js", "// Generated configuration\n");
175
176
// Append database config
177
fs.appendTpl("config.js", `
178
const database = {
179
host: '<%= host %>',
180
port: <%= port %>,
181
name: '<%= dbName %>'
182
};
183
`, {
184
host: "localhost",
185
port: 5432,
186
dbName: baseContext.projectName.toLowerCase()
187
}, null, { separator: "\n" });
188
189
// Append server config
190
fs.appendTpl("config.js", `
191
const server = {
192
port: <%= serverPort %>,
193
env: '<%= environment %>'
194
};
195
`, {
196
serverPort: 3000,
197
environment: "development"
198
}, null, { separator: "\n" });
199
```
200
201
### Internal Template Processing
202
203
Direct template processing function for custom workflows.
204
205
```typescript { .api }
206
/**
207
* Process file contents as EJS template
208
* @param params - Template processing parameters
209
* @returns Processed template content
210
*/
211
function _processTpl(params: {
212
contents: string | Buffer;
213
filename: string;
214
context?: Record<string, any>;
215
tplSettings?: EJSOptions;
216
}): string;
217
```
218
219
**Usage Examples:**
220
221
```typescript
222
// Process template content directly
223
const templateContent = `
224
Hello <%= name %>!
225
Your project <%= projectName %> is ready.
226
Features: <% features.forEach(feature => { %>
227
- <%= feature %>
228
<% }); %>
229
`;
230
231
const processed = fs._processTpl({
232
contents: templateContent,
233
filename: "greeting.txt",
234
context: {
235
name: "Developer",
236
projectName: "AwesomeApp",
237
features: ["Authentication", "Database", "API"]
238
}
239
});
240
241
fs.write("output.txt", processed);
242
```
243
244
## Template Syntax and Examples
245
246
### Basic Variable Substitution
247
248
```ejs
249
<!-- Template: greeting.ejs -->
250
Hello <%= name %>!
251
Welcome to <%= projectName %>.
252
```
253
254
```typescript
255
// Usage
256
fs.copyTpl("greeting.ejs", "greeting.txt", {
257
name: "Alice",
258
projectName: "Amazing App"
259
});
260
// Output: "Hello Alice!\nWelcome to Amazing App."
261
```
262
263
### Conditional Content
264
265
```ejs
266
<!-- Template: config.ejs -->
267
{
268
"name": "<%= name %>",
269
"version": "<%= version %>"<% if (features.includes('auth')) { %>,
270
"authentication": {
271
"provider": "<%= authProvider || 'local' %>"
272
}<% } %><% if (features.includes('database')) { %>,
273
"database": {
274
"type": "<%= dbType || 'sqlite' %>"
275
}<% } %>
276
}
277
```
278
279
```typescript
280
fs.copyTpl("config.ejs", "config.json", {
281
name: "my-app",
282
version: "1.0.0",
283
features: ["auth", "database"],
284
authProvider: "oauth2",
285
dbType: "postgresql"
286
});
287
```
288
289
### Loops and Iteration
290
291
```ejs
292
<!-- Template: routes.ejs -->
293
<% routes.forEach(route => { %>
294
app.<%= route.method %>('<%= route.path %>', <%= route.handler %>);
295
<% }); %>
296
297
<!-- Generated imports -->
298
<% dependencies.forEach(dep => { %>
299
const <%= dep.name %> = require('<%= dep.package %>');
300
<% }); %>
301
```
302
303
```typescript
304
fs.copyTpl("routes.ejs", "routes.js", {
305
routes: [
306
{ method: "get", path: "/users", handler: "getUsersHandler" },
307
{ method: "post", path: "/users", handler: "createUserHandler" },
308
{ method: "delete", path: "/users/:id", handler: "deleteUserHandler" }
309
],
310
dependencies: [
311
{ name: "express", package: "express" },
312
{ name: "cors", package: "cors" }
313
]
314
});
315
```
316
317
### Complex Context Objects
318
319
```typescript
320
// Complex template context
321
const context = {
322
project: {
323
name: "e-commerce-api",
324
version: "2.1.0",
325
description: "REST API for e-commerce platform"
326
},
327
database: {
328
type: "postgresql",
329
host: "localhost",
330
port: 5432,
331
ssl: false
332
},
333
features: {
334
authentication: { enabled: true, provider: "jwt" },
335
logging: { enabled: true, level: "info" },
336
monitoring: { enabled: false }
337
},
338
endpoints: [
339
{ path: "/api/products", methods: ["GET", "POST"] },
340
{ path: "/api/orders", methods: ["GET", "POST", "PUT"] },
341
{ path: "/api/users", methods: ["GET", "POST", "DELETE"] }
342
]
343
};
344
345
fs.copyTpl("templates/**/*.ejs", "generated/", context);
346
```
347
348
## Error Handling and Debugging
349
350
```typescript
351
// Template processing with error handling
352
try {
353
fs.copyTpl("template.ejs", "output.txt", context, {
354
filename: "template.ejs" // Helps with error reporting
355
});
356
} catch (error) {
357
if (error.message.includes("ReferenceError")) {
358
console.error("Template variable not found:", error.message);
359
} else {
360
console.error("Template processing failed:", error.message);
361
}
362
}
363
364
// Debug template context
365
const debugContext = {
366
...context,
367
_debug: true,
368
_timestamp: new Date().toISOString()
369
};
370
371
fs.copyTpl("debug-template.ejs", "debug-output.txt", debugContext);
372
```
373
374
## Custom Delimiters
375
376
```typescript
377
// Use custom delimiters for templates that conflict with default EJS syntax
378
fs.copyTpl("template.txt", "output.txt", { name: "World" }, {
379
openDelimiter: "{{",
380
closeDelimiter: "}}"
381
});
382
383
// Template content with custom delimiters:
384
// "Hello {{ name }}! Today is {{ new Date().toDateString() }}."
385
```