genai-lite 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/templateEngine.d.ts +15 -0
- package/dist/utils/templateEngine.js +194 -0
- package/dist/utils/templateEngine.test.d.ts +1 -0
- package/dist/utils/templateEngine.test.js +134 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A lightweight, portable Node.js/TypeScript library providing a unified interface
|
|
|
11
11
|
- ⚡ **Lightweight** - Minimal dependencies, focused functionality
|
|
12
12
|
- 🛡️ **Provider Normalization** - Consistent responses across different AI APIs
|
|
13
13
|
- 🎨 **Configurable Model Presets** - Built-in presets with full customization options
|
|
14
|
+
- 🎭 **Template Engine** - Sophisticated templating with conditionals and variable substitution
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -388,6 +389,108 @@ const response = await llm.sendMessage({
|
|
|
388
389
|
});
|
|
389
390
|
```
|
|
390
391
|
|
|
392
|
+
### Template Engine
|
|
393
|
+
|
|
394
|
+
Generate dynamic prompts and content using the built-in template engine that supports variable substitution and conditional logic:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
import { renderTemplate } from 'genai-lite/utils';
|
|
398
|
+
|
|
399
|
+
// Simple variable substitution
|
|
400
|
+
const greeting = renderTemplate('Hello, {{ name }}!', { name: 'World' });
|
|
401
|
+
// Result: "Hello, World!"
|
|
402
|
+
|
|
403
|
+
// Conditional rendering with ternary syntax
|
|
404
|
+
const prompt = renderTemplate(
|
|
405
|
+
'Analyze this {{ language }} code:\n{{ hasContext ? `Context: {{context}}\n` : `` }}```\n{{ code }}\n```',
|
|
406
|
+
{
|
|
407
|
+
language: 'TypeScript',
|
|
408
|
+
hasContext: true,
|
|
409
|
+
context: 'React component for user authentication',
|
|
410
|
+
code: 'export const Login = () => { ... }'
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
// Result includes the context line when hasContext is true
|
|
414
|
+
|
|
415
|
+
// Complex template with multiple conditionals
|
|
416
|
+
const complexTemplate = `
|
|
417
|
+
System: You are a {{ role }} assistant.
|
|
418
|
+
{{ hasExpertise ? `Expertise: {{expertise}}` : `General knowledge assistant` }}
|
|
419
|
+
|
|
420
|
+
Task: {{ task }}
|
|
421
|
+
{{ hasFiles ? `
|
|
422
|
+
Files to analyze:
|
|
423
|
+
{{ fileList }}` : `` }}
|
|
424
|
+
{{ requiresOutput ? `
|
|
425
|
+
Expected output format:
|
|
426
|
+
{{ outputFormat }}` : `` }}
|
|
427
|
+
`;
|
|
428
|
+
|
|
429
|
+
const result = renderTemplate(complexTemplate, {
|
|
430
|
+
role: 'coding',
|
|
431
|
+
hasExpertise: true,
|
|
432
|
+
expertise: 'TypeScript, React, Node.js',
|
|
433
|
+
task: 'Review the code for best practices',
|
|
434
|
+
hasFiles: true,
|
|
435
|
+
fileList: '- src/index.ts\n- src/utils.ts',
|
|
436
|
+
requiresOutput: false
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Template syntax supports:
|
|
441
|
+
- **Simple substitution**: `{{ variableName }}`
|
|
442
|
+
- **Ternary conditionals**: `{{ condition ? `true result` : `false result` }}`
|
|
443
|
+
- **Nested variables**: `{{ show ? `Name: {{name}}` : `Anonymous` }}`
|
|
444
|
+
- **Multi-line strings**: Use backticks to preserve formatting
|
|
445
|
+
- **Intelligent newline handling**: Empty results remove trailing newlines
|
|
446
|
+
|
|
447
|
+
### Example: Building Dynamic LLM Prompts
|
|
448
|
+
|
|
449
|
+
Combine the template engine with other utilities for powerful prompt generation:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
import { LLMService, fromEnvironment } from 'genai-lite';
|
|
453
|
+
import { renderTemplate, countTokens } from 'genai-lite/utils';
|
|
454
|
+
|
|
455
|
+
const llm = new LLMService(fromEnvironment);
|
|
456
|
+
|
|
457
|
+
// Define a reusable prompt template
|
|
458
|
+
const codeReviewTemplate = `
|
|
459
|
+
You are an expert {{ language }} developer.
|
|
460
|
+
|
|
461
|
+
{{ hasGuidelines ? `Follow these coding guidelines:
|
|
462
|
+
{{ guidelines }}
|
|
463
|
+
|
|
464
|
+
` : `` }}Review the following code:
|
|
465
|
+
\`\`\`{{ language }}
|
|
466
|
+
{{ code }}
|
|
467
|
+
\`\`\`
|
|
468
|
+
|
|
469
|
+
{{ hasFocus ? `Focus on: {{ focusAreas }}` : `Provide a comprehensive review covering all aspects.` }}
|
|
470
|
+
`;
|
|
471
|
+
|
|
472
|
+
// Render the prompt with specific values
|
|
473
|
+
const prompt = renderTemplate(codeReviewTemplate, {
|
|
474
|
+
language: 'TypeScript',
|
|
475
|
+
hasGuidelines: true,
|
|
476
|
+
guidelines: '- Use functional components\n- Prefer composition over inheritance',
|
|
477
|
+
code: sourceCode,
|
|
478
|
+
hasFocus: true,
|
|
479
|
+
focusAreas: 'performance optimizations and error handling'
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Check token count before sending
|
|
483
|
+
const tokenCount = countTokens(prompt, 'gpt-4.1-mini');
|
|
484
|
+
console.log(`Prompt uses ${tokenCount} tokens`);
|
|
485
|
+
|
|
486
|
+
// Send to LLM
|
|
487
|
+
const response = await llm.sendMessage({
|
|
488
|
+
providerId: 'openai',
|
|
489
|
+
modelId: 'gpt-4.1-mini',
|
|
490
|
+
messages: [{ role: 'user', content: prompt }]
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
391
494
|
## Contributing
|
|
392
495
|
|
|
393
496
|
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.fromEnvironment = exports.LLMService = void 0;
|
|
17
|
+
exports.renderTemplate = exports.fromEnvironment = exports.LLMService = void 0;
|
|
18
18
|
// --- LLM Service ---
|
|
19
19
|
var LLMService_1 = require("./llm/LLMService");
|
|
20
20
|
Object.defineProperty(exports, "LLMService", { enumerable: true, get: function () { return LLMService_1.LLMService; } });
|
|
@@ -25,3 +25,6 @@ __exportStar(require("./llm/clients/types"), exports);
|
|
|
25
25
|
// --- API Key Providers ---
|
|
26
26
|
var fromEnvironment_1 = require("./providers/fromEnvironment");
|
|
27
27
|
Object.defineProperty(exports, "fromEnvironment", { enumerable: true, get: function () { return fromEnvironment_1.fromEnvironment; } });
|
|
28
|
+
// --- Utilities ---
|
|
29
|
+
var templateEngine_1 = require("./utils/templateEngine");
|
|
30
|
+
Object.defineProperty(exports, "renderTemplate", { enumerable: true, get: function () { return templateEngine_1.renderTemplate; } });
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a template string by substituting variables and evaluating conditional expressions.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Simple variable substitution: {{ variableName }}
|
|
6
|
+
* - Conditional rendering: {{ condition ? `true result` : `false result` }}
|
|
7
|
+
* - Conditional with only true branch: {{ condition ? `true result` }}
|
|
8
|
+
* - Multi-line strings in backticks
|
|
9
|
+
* - Intelligent newline handling (removes empty lines when result is empty)
|
|
10
|
+
*
|
|
11
|
+
* @param template The template string containing placeholders
|
|
12
|
+
* @param variables Object containing variable values
|
|
13
|
+
* @returns The rendered template string
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderTemplate(template: string, variables: Record<string, any>): string;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderTemplate = renderTemplate;
|
|
4
|
+
/**
|
|
5
|
+
* Renders a template string by substituting variables and evaluating conditional expressions.
|
|
6
|
+
*
|
|
7
|
+
* Supports:
|
|
8
|
+
* - Simple variable substitution: {{ variableName }}
|
|
9
|
+
* - Conditional rendering: {{ condition ? `true result` : `false result` }}
|
|
10
|
+
* - Conditional with only true branch: {{ condition ? `true result` }}
|
|
11
|
+
* - Multi-line strings in backticks
|
|
12
|
+
* - Intelligent newline handling (removes empty lines when result is empty)
|
|
13
|
+
*
|
|
14
|
+
* @param template The template string containing placeholders
|
|
15
|
+
* @param variables Object containing variable values
|
|
16
|
+
* @returns The rendered template string
|
|
17
|
+
*/
|
|
18
|
+
function renderTemplate(template, variables) {
|
|
19
|
+
// We need to handle nested {{ }} properly, so we'll use a custom approach
|
|
20
|
+
let result = '';
|
|
21
|
+
let lastIndex = 0;
|
|
22
|
+
while (lastIndex < template.length) {
|
|
23
|
+
// Find the next {{
|
|
24
|
+
const startIndex = template.indexOf('{{', lastIndex);
|
|
25
|
+
if (startIndex === -1) {
|
|
26
|
+
// No more placeholders
|
|
27
|
+
result += template.substring(lastIndex);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
// Add everything before the placeholder
|
|
31
|
+
result += template.substring(lastIndex, startIndex);
|
|
32
|
+
// Check for leading newline
|
|
33
|
+
const leadingNewline = startIndex > 0 && template[startIndex - 1] === '\n' ? '\n' : '';
|
|
34
|
+
// Find the matching }}
|
|
35
|
+
let depth = 1;
|
|
36
|
+
let i = startIndex + 2;
|
|
37
|
+
let inBacktick = false;
|
|
38
|
+
let expression = '';
|
|
39
|
+
while (i < template.length && depth > 0) {
|
|
40
|
+
if (template[i] === '`' && (i === startIndex + 2 || template[i - 1] !== '\\')) {
|
|
41
|
+
inBacktick = !inBacktick;
|
|
42
|
+
}
|
|
43
|
+
else if (!inBacktick) {
|
|
44
|
+
if (template[i] === '{' && i + 1 < template.length && template[i + 1] === '{') {
|
|
45
|
+
depth++;
|
|
46
|
+
i++; // Skip the second {
|
|
47
|
+
}
|
|
48
|
+
else if (template[i] === '}' && i + 1 < template.length && template[i + 1] === '}') {
|
|
49
|
+
depth--;
|
|
50
|
+
if (depth === 0) {
|
|
51
|
+
expression = template.substring(startIndex + 2, i);
|
|
52
|
+
i++; // Skip the second }
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
i++; // Skip the second }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
if (depth > 0) {
|
|
61
|
+
// Unmatched {{, treat as literal
|
|
62
|
+
result += template.substring(startIndex, i);
|
|
63
|
+
lastIndex = i;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Check for trailing newline
|
|
67
|
+
const nextCharIndex = i + 1;
|
|
68
|
+
const trailingNewline = nextCharIndex < template.length && template[nextCharIndex] === '\n' ? '\n' : '';
|
|
69
|
+
// Process the expression
|
|
70
|
+
const processedResult = processExpression(expression.trim(), variables, leadingNewline, trailingNewline);
|
|
71
|
+
// Add the processed result (which already includes newlines when appropriate)
|
|
72
|
+
result += processedResult;
|
|
73
|
+
// Update lastIndex, skipping the trailing newline if the result was empty
|
|
74
|
+
if (processedResult === '' && trailingNewline) {
|
|
75
|
+
lastIndex = nextCharIndex + 1;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
lastIndex = nextCharIndex;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
function processExpression(expression, variables, leadingNewline, trailingNewline) {
|
|
84
|
+
const conditionalMarkerIndex = expression.indexOf('?');
|
|
85
|
+
let result;
|
|
86
|
+
if (conditionalMarkerIndex === -1) {
|
|
87
|
+
// --- Simple variable substitution (backward compatible) ---
|
|
88
|
+
const key = expression;
|
|
89
|
+
const value = variables[key];
|
|
90
|
+
// Handle task context specially - only include if it exists
|
|
91
|
+
if (key === 'task_context' && (!value || (typeof value === 'string' && value.trim() === ''))) {
|
|
92
|
+
result = '';
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
result = value !== undefined ? String(value) : '';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// --- Conditional 'ternary' substitution ---
|
|
100
|
+
const conditionKey = expression.substring(0, conditionalMarkerIndex).trim();
|
|
101
|
+
const rest = expression.substring(conditionalMarkerIndex + 1).trim();
|
|
102
|
+
// Parse ternary expression with backtick-delimited strings
|
|
103
|
+
// We need to handle nested {{ }} within backticks
|
|
104
|
+
let trueText;
|
|
105
|
+
let falseText = ''; // Default to empty string if no 'else' part
|
|
106
|
+
// Try to match backtick format with better handling of nested content
|
|
107
|
+
if (rest.startsWith('`')) {
|
|
108
|
+
// Find the matching closing backtick, accounting for escaped backticks
|
|
109
|
+
let i = 1;
|
|
110
|
+
let depth = 1;
|
|
111
|
+
while (i < rest.length && depth > 0) {
|
|
112
|
+
if (rest[i] === '\\' && i + 1 < rest.length && rest[i + 1] === '`') {
|
|
113
|
+
i += 2; // Skip escaped backtick
|
|
114
|
+
}
|
|
115
|
+
else if (rest[i] === '`') {
|
|
116
|
+
depth--;
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
i++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (depth === 0) {
|
|
124
|
+
// Found matching backtick
|
|
125
|
+
trueText = rest.substring(1, i - 1);
|
|
126
|
+
const afterTrue = rest.substring(i).trim();
|
|
127
|
+
// Check for false part
|
|
128
|
+
if (afterTrue.startsWith(':')) {
|
|
129
|
+
const falsePart = afterTrue.substring(1).trim();
|
|
130
|
+
if (falsePart.startsWith('`')) {
|
|
131
|
+
// Find matching backtick for false part
|
|
132
|
+
let j = 1;
|
|
133
|
+
let falseDepth = 1;
|
|
134
|
+
while (j < falsePart.length && falseDepth > 0) {
|
|
135
|
+
if (falsePart[j] === '\\' && j + 1 < falsePart.length && falsePart[j + 1] === '`') {
|
|
136
|
+
j += 2;
|
|
137
|
+
}
|
|
138
|
+
else if (falsePart[j] === '`') {
|
|
139
|
+
falseDepth--;
|
|
140
|
+
j++;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
j++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (falseDepth === 0) {
|
|
147
|
+
falseText = falsePart.substring(1, j - 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Unescape any escaped backticks
|
|
152
|
+
trueText = trueText.replace(/\\`/g, '`');
|
|
153
|
+
falseText = falseText.replace(/\\`/g, '`');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// No matching backtick found
|
|
157
|
+
trueText = rest;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Fallback to old quote-based parsing for backward compatibility
|
|
162
|
+
const elseMarkerIndex = rest.indexOf(':');
|
|
163
|
+
if (elseMarkerIndex === -1) {
|
|
164
|
+
trueText = rest;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
trueText = rest.substring(0, elseMarkerIndex).trim();
|
|
168
|
+
falseText = rest.substring(elseMarkerIndex + 1).trim();
|
|
169
|
+
}
|
|
170
|
+
// Remove quotes from the start and end of the text parts
|
|
171
|
+
const unquote = (text) => {
|
|
172
|
+
if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
|
|
173
|
+
return text.slice(1, -1);
|
|
174
|
+
}
|
|
175
|
+
return text;
|
|
176
|
+
};
|
|
177
|
+
trueText = unquote(trueText);
|
|
178
|
+
falseText = unquote(falseText);
|
|
179
|
+
}
|
|
180
|
+
const conditionValue = !!variables[conditionKey]; // Evaluate truthiness
|
|
181
|
+
result = conditionValue ? trueText : falseText;
|
|
182
|
+
// Recursively process the result to handle nested variables
|
|
183
|
+
if (result.includes('{{')) {
|
|
184
|
+
result = renderTemplate(result, variables);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// If result is empty, return empty string without newlines
|
|
188
|
+
if (result === '') {
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
// If result is not empty, return just the result without the captured newlines
|
|
192
|
+
// The newlines are already part of the template structure
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const templateEngine_1 = require("./templateEngine");
|
|
4
|
+
describe('renderTemplate', () => {
|
|
5
|
+
it('should handle simple variable substitution', () => {
|
|
6
|
+
const template = 'Hello, {{ name }}!';
|
|
7
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { name: 'World' });
|
|
8
|
+
expect(result).toBe('Hello, World!');
|
|
9
|
+
});
|
|
10
|
+
it('should handle intelligent newline trimming for empty results', () => {
|
|
11
|
+
const template = 'Start\n{{ showDetails ? `Details here` : `` }}\nEnd';
|
|
12
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { showDetails: false });
|
|
13
|
+
expect(result).toBe('Start\nEnd');
|
|
14
|
+
});
|
|
15
|
+
it('should preserve newlines for non-empty results', () => {
|
|
16
|
+
const template = 'Start\n{{ showDetails ? `Details here` : `` }}\nEnd';
|
|
17
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { showDetails: true });
|
|
18
|
+
expect(result).toBe('Start\nDetails here\nEnd');
|
|
19
|
+
});
|
|
20
|
+
it('should correctly parse ternary logic with backtick-delimited strings', () => {
|
|
21
|
+
const template = '{{ condition ? `A` : `B` }}';
|
|
22
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: true })).toBe('A');
|
|
23
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: false })).toBe('B');
|
|
24
|
+
});
|
|
25
|
+
it('should handle multi-line strings in backticks', () => {
|
|
26
|
+
const template = '{{ condition ? `Line 1\nLine 2` : `Single Line` }}';
|
|
27
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { condition: true });
|
|
28
|
+
expect(result).toBe('Line 1\nLine 2');
|
|
29
|
+
});
|
|
30
|
+
it('should handle ternary expressions with only a true part', () => {
|
|
31
|
+
const template = 'Data: {{ data ? `{{data}}` : `` }}';
|
|
32
|
+
expect((0, templateEngine_1.renderTemplate)(template, { data: 'Exists' })).toBe('Data: Exists');
|
|
33
|
+
expect((0, templateEngine_1.renderTemplate)(template, { data: false })).toBe('Data: ');
|
|
34
|
+
});
|
|
35
|
+
it('should handle escaped backticks within the template strings', () => {
|
|
36
|
+
const template = '{{ flag ? `This is a \\`backtick\\`` : `` }}';
|
|
37
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { flag: true });
|
|
38
|
+
expect(result).toBe('This is a `backtick`');
|
|
39
|
+
});
|
|
40
|
+
it('should handle undefined variables', () => {
|
|
41
|
+
const template = 'Value: {{ undefinedVar }}';
|
|
42
|
+
const result = (0, templateEngine_1.renderTemplate)(template, {});
|
|
43
|
+
expect(result).toBe('Value: ');
|
|
44
|
+
});
|
|
45
|
+
it('should handle boolean values', () => {
|
|
46
|
+
const template = 'Boolean: {{ boolValue }}';
|
|
47
|
+
expect((0, templateEngine_1.renderTemplate)(template, { boolValue: true })).toBe('Boolean: true');
|
|
48
|
+
expect((0, templateEngine_1.renderTemplate)(template, { boolValue: false })).toBe('Boolean: false');
|
|
49
|
+
});
|
|
50
|
+
it('should handle numeric values', () => {
|
|
51
|
+
const template = 'Number: {{ numValue }}';
|
|
52
|
+
expect((0, templateEngine_1.renderTemplate)(template, { numValue: 42 })).toBe('Number: 42');
|
|
53
|
+
expect((0, templateEngine_1.renderTemplate)(template, { numValue: 0 })).toBe('Number: 0');
|
|
54
|
+
});
|
|
55
|
+
it('should handle nested variable substitution in ternary true branch', () => {
|
|
56
|
+
const template = '{{ showName ? `Hello, {{name}}!` : `Hello, stranger!` }}';
|
|
57
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { showName: true, name: 'Alice' });
|
|
58
|
+
expect(result).toBe('Hello, Alice!');
|
|
59
|
+
});
|
|
60
|
+
it('should handle nested variable substitution in ternary false branch', () => {
|
|
61
|
+
const template = '{{ showName ? `Hello, {{name}}!` : `Hello, {{fallback}}!` }}';
|
|
62
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { showName: false, fallback: 'Guest' });
|
|
63
|
+
expect(result).toBe('Hello, Guest!');
|
|
64
|
+
});
|
|
65
|
+
it('should handle multiple variable substitutions', () => {
|
|
66
|
+
const template = '{{ greeting }}, {{ name }}! {{ farewell }}';
|
|
67
|
+
const result = (0, templateEngine_1.renderTemplate)(template, {
|
|
68
|
+
greeting: 'Hello',
|
|
69
|
+
name: 'World',
|
|
70
|
+
farewell: 'Goodbye'
|
|
71
|
+
});
|
|
72
|
+
expect(result).toBe('Hello, World! Goodbye');
|
|
73
|
+
});
|
|
74
|
+
it('should handle special task_context variable with empty string', () => {
|
|
75
|
+
const template = 'Context: {{ task_context }}';
|
|
76
|
+
expect((0, templateEngine_1.renderTemplate)(template, { task_context: '' })).toBe('Context: ');
|
|
77
|
+
expect((0, templateEngine_1.renderTemplate)(template, { task_context: ' ' })).toBe('Context: ');
|
|
78
|
+
expect((0, templateEngine_1.renderTemplate)(template, { task_context: 'Some context' })).toBe('Context: Some context');
|
|
79
|
+
});
|
|
80
|
+
it('should handle complex nested templates', () => {
|
|
81
|
+
const template = `
|
|
82
|
+
Project: {{ projectName }}
|
|
83
|
+
{{ hasFiles ? \`## Files
|
|
84
|
+
{{ fileList }}\` : \`No files selected\` }}
|
|
85
|
+
{{ showSummary ? \`
|
|
86
|
+
## Summary
|
|
87
|
+
{{ summary }}\` : \`\` }}
|
|
88
|
+
End
|
|
89
|
+
`;
|
|
90
|
+
const result = (0, templateEngine_1.renderTemplate)(template, {
|
|
91
|
+
projectName: 'MyProject',
|
|
92
|
+
hasFiles: true,
|
|
93
|
+
fileList: '- file1.js\n- file2.js',
|
|
94
|
+
showSummary: false,
|
|
95
|
+
summary: 'This is a summary'
|
|
96
|
+
});
|
|
97
|
+
expect(result).toBe(`
|
|
98
|
+
Project: MyProject
|
|
99
|
+
## Files
|
|
100
|
+
- file1.js
|
|
101
|
+
- file2.js
|
|
102
|
+
End
|
|
103
|
+
`);
|
|
104
|
+
});
|
|
105
|
+
it('should handle backward compatible quote-based ternary syntax', () => {
|
|
106
|
+
const template = '{{ condition ? "True value" : "False value" }}';
|
|
107
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: true })).toBe('True value');
|
|
108
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: false })).toBe('False value');
|
|
109
|
+
});
|
|
110
|
+
it('should handle single quotes in backward compatible syntax', () => {
|
|
111
|
+
const template = "{{ condition ? 'True value' : 'False value' }}";
|
|
112
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: true })).toBe('True value');
|
|
113
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: false })).toBe('False value');
|
|
114
|
+
});
|
|
115
|
+
it('should handle whitespace around expressions', () => {
|
|
116
|
+
const template = '{{ name }}';
|
|
117
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { name: 'Test' });
|
|
118
|
+
expect(result).toBe('Test');
|
|
119
|
+
});
|
|
120
|
+
it('should handle whitespace in ternary expressions', () => {
|
|
121
|
+
const template = '{{ condition ? `True` : `False` }}';
|
|
122
|
+
expect((0, templateEngine_1.renderTemplate)(template, { condition: true })).toBe('True');
|
|
123
|
+
});
|
|
124
|
+
it('should handle empty template', () => {
|
|
125
|
+
const template = '';
|
|
126
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { any: 'value' });
|
|
127
|
+
expect(result).toBe('');
|
|
128
|
+
});
|
|
129
|
+
it('should handle template with no placeholders', () => {
|
|
130
|
+
const template = 'Just plain text';
|
|
131
|
+
const result = (0, templateEngine_1.renderTemplate)(template, { any: 'value' });
|
|
132
|
+
expect(result).toBe('Just plain text');
|
|
133
|
+
});
|
|
134
|
+
});
|