@aetherframework/template-engine 1.0.0
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/LICENSE +21 -0
- package/README.md +662 -0
- package/examples/basic-usage.js +217 -0
- package/examples/dist/basic-usage-result.html +31 -0
- package/examples/dist/layout-example-result.html +210 -0
- package/examples/dist/templates/layouts/main.aether +58 -0
- package/examples/dist/templates/pages/home.aether +116 -0
- package/examples/layout-example.js +404 -0
- package/examples/ssr-example.js +180 -0
- package/index.js +179 -0
- package/package.json +42 -0
- package/src/core/CacheManager.js +245 -0
- package/src/core/EngineRegistry.js +148 -0
- package/src/core/ModeManager.js +231 -0
- package/src/core/TemplateEngineFactory.js +373 -0
- package/src/engines/AetherEngine.js +582 -0
- package/src/engines/BaseEngine.js +101 -0
- package/src/engines/SSRModeEngine.js +139 -0
- package/src/engines/TemplateModeEngine.js +320 -0
- package/src/utils/ConfigLoader.js +279 -0
- package/src/utils/ErrorHandler.js +276 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @license MIT
|
|
4
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
5
|
+
* SPDX-License-Identifier: MIT
|
|
6
|
+
* @module @aetherframework/template-engine/src/engines/SSrModeEngine
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import AetherEngine from './AetherEngine.js'; // Adjust this path if AetherEngine is in a different folder
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* SSR Mode Engine - Server-Side Rendering engine for Aether templates
|
|
13
|
+
* Converts Aether templates to full HTML documents with SSR optimizations
|
|
14
|
+
*/
|
|
15
|
+
class SSRModeEngine extends AetherEngine {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
super(options);
|
|
18
|
+
this.name = 'ssr-mode';
|
|
19
|
+
this.version = '1.0.0';
|
|
20
|
+
|
|
21
|
+
// SSR-specific options
|
|
22
|
+
this.ssrOptions = {
|
|
23
|
+
hydrate: options.hydrate !== false,
|
|
24
|
+
stream: options.stream || false,
|
|
25
|
+
...options
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Render template with SSR optimizations
|
|
31
|
+
* @param {string} templateName - Template name or content
|
|
32
|
+
* @param {Object} data - Template data
|
|
33
|
+
* @param {Object} options - Render options
|
|
34
|
+
* @returns {Promise<string>} Rendered HTML with SSR enhancements
|
|
35
|
+
*/
|
|
36
|
+
async render(templateName, data = {}, options = {}) {
|
|
37
|
+
// First, render the inner content using the parent Aether engine
|
|
38
|
+
const content = await super.render(templateName, data, options);
|
|
39
|
+
|
|
40
|
+
// Wrap and enhance for SSR
|
|
41
|
+
return this.enhanceForSSR(content, data, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Enhance rendered content for SSR by wrapping it in a full HTML document
|
|
46
|
+
* @param {string} content - Rendered HTML content
|
|
47
|
+
* @param {Object} data - Template data
|
|
48
|
+
* @param {Object} options - SSR options
|
|
49
|
+
* @returns {string} Enhanced HTML
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
enhanceForSSR(content, data, options) {
|
|
53
|
+
// Extract meta information from data with safe fallbacks
|
|
54
|
+
const title = data.title || 'Aether Framework';
|
|
55
|
+
const description = data.description || 'Next-generation lightweight full-stack solution';
|
|
56
|
+
const keywords = data.keywords || 'Aether,Framework,Web';
|
|
57
|
+
|
|
58
|
+
// Safely serialize data for hydration (prevent XSS in script tags)
|
|
59
|
+
const safeDataJson = JSON.stringify(data).replace(/</g, '\\u003c');
|
|
60
|
+
|
|
61
|
+
// Build full HTML document with SSR optimizations
|
|
62
|
+
return `<!DOCTYPE html>
|
|
63
|
+
<html lang="en">
|
|
64
|
+
<head>
|
|
65
|
+
<meta charset="UTF-8">
|
|
66
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
67
|
+
<title>${this.escapeHtml(title)}</title>
|
|
68
|
+
<meta name="description" content="${this.escapeHtml(description)}">
|
|
69
|
+
<meta name="keywords" content="${this.escapeHtml(keywords)}">
|
|
70
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
71
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
72
|
+
|
|
73
|
+
${data.head || ''}
|
|
74
|
+
|
|
75
|
+
<!-- SSR Hydration Data -->
|
|
76
|
+
<script type="application/json" id="ssr-data">
|
|
77
|
+
${safeDataJson}
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<!-- SSR Styles -->
|
|
81
|
+
<style>
|
|
82
|
+
body {
|
|
83
|
+
margin: 0;
|
|
84
|
+
padding: 0;
|
|
85
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
86
|
+
}
|
|
87
|
+
.ssr-hydrated { opacity: 1; transition: opacity 0.3s ease; }
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body class="antialiased bg-[#020617] text-white">
|
|
91
|
+
<div id="app" class="ssr-hydrated">
|
|
92
|
+
${content}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
${data.scripts || ''}
|
|
96
|
+
|
|
97
|
+
<!-- SSR Hydration Script -->
|
|
98
|
+
<script>
|
|
99
|
+
(function() {
|
|
100
|
+
// Hydrate SSR data
|
|
101
|
+
const ssrData = document.getElementById('ssr-data');
|
|
102
|
+
if (ssrData) {
|
|
103
|
+
try {
|
|
104
|
+
window.__SSR_DATA__ = JSON.parse(ssrData.textContent);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error('Failed to parse SSR data', e);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Mark as hydrated
|
|
111
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
112
|
+
const app = document.getElementById('app');
|
|
113
|
+
if (app) {
|
|
114
|
+
app.classList.add('ssr-hydrated');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
})();
|
|
118
|
+
</script>
|
|
119
|
+
</body>
|
|
120
|
+
</html>`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get engine metadata
|
|
125
|
+
* @returns {Object} Engine information
|
|
126
|
+
*/
|
|
127
|
+
getMetadata() {
|
|
128
|
+
const baseMetadata = super.getMetadata();
|
|
129
|
+
return {
|
|
130
|
+
...baseMetadata,
|
|
131
|
+
mode: 'ssr',
|
|
132
|
+
hydrate: this.ssrOptions.hydrate,
|
|
133
|
+
stream: this.ssrOptions.stream
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Use 'export default' to match the import in your index.js
|
|
139
|
+
export default SSRModeEngine;
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/template-engine/src/engines/TemplateModeEngine
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Template Mode Engine - Template rendering engine for Aether templates
|
|
9
|
+
* Extends AetherEngine with template-specific functionality
|
|
10
|
+
*/
|
|
11
|
+
import AetherEngine from './AetherEngine.js';
|
|
12
|
+
|
|
13
|
+
class TemplateModeEngine extends AetherEngine {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
super(options);
|
|
16
|
+
this.name = 'template-mode';
|
|
17
|
+
this.version = '1.0.0';
|
|
18
|
+
|
|
19
|
+
// Template mode specific options
|
|
20
|
+
this.templateOptions = {
|
|
21
|
+
layoutSupport: options.layoutSupport !== false,
|
|
22
|
+
includeSupport: options.includeSupport !== false,
|
|
23
|
+
cacheTemplates: options.cacheTemplates !== false,
|
|
24
|
+
...options
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Initialize template directories
|
|
28
|
+
this.ensureTemplateDir();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Initialize the template engine
|
|
33
|
+
* @returns {Promise<TemplateModeEngine>} Initialized engine instance
|
|
34
|
+
*/
|
|
35
|
+
async initialize() {
|
|
36
|
+
if (this.initialized) return this;
|
|
37
|
+
|
|
38
|
+
await super.initialize();
|
|
39
|
+
|
|
40
|
+
// Load default layout if exists
|
|
41
|
+
await this.loadDefaultLayout();
|
|
42
|
+
|
|
43
|
+
console.log(`✅ Template Mode Engine initialized (v${this.version})`);
|
|
44
|
+
this.initialized = true;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load default layout from file
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
async loadDefaultLayout() {
|
|
53
|
+
try {
|
|
54
|
+
const defaultLayoutPath = `${this.options.templateDir}/layouts/default.aether`;
|
|
55
|
+
const fs = await import('fs-extra');
|
|
56
|
+
|
|
57
|
+
if (await fs.pathExists(defaultLayoutPath)) {
|
|
58
|
+
const layoutContent = await fs.readFile(defaultLayoutPath, 'utf-8');
|
|
59
|
+
this.registerLayout('default', layoutContent);
|
|
60
|
+
console.log('📄 Default layout loaded from file');
|
|
61
|
+
} else {
|
|
62
|
+
// Create default layout based on user's template
|
|
63
|
+
const defaultLayout = `<!DOCTYPE html>
|
|
64
|
+
<html lang="zh-CN">
|
|
65
|
+
<head>
|
|
66
|
+
<meta charset="UTF-8">
|
|
67
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
68
|
+
<title>@yield('title', 'Aether Framework')</title>
|
|
69
|
+
<meta name="description" content="@yield('description', '下一代轻量级全栈解决方案')">
|
|
70
|
+
<meta name="keywords" content="@yield('keywords', 'Aether,Framework,Web')">
|
|
71
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
72
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
73
|
+
@yield('head')
|
|
74
|
+
</head>
|
|
75
|
+
<body class="antialiased bg-[#020617] text-white">
|
|
76
|
+
@yield('content')
|
|
77
|
+
@yield('scripts')
|
|
78
|
+
</body>
|
|
79
|
+
</html>`;
|
|
80
|
+
|
|
81
|
+
this.registerLayout('default', defaultLayout);
|
|
82
|
+
|
|
83
|
+
// Save to file for future use
|
|
84
|
+
await fs.ensureDir(`${this.options.templateDir}/layouts`);
|
|
85
|
+
await fs.writeFile(defaultLayoutPath, defaultLayout, 'utf-8');
|
|
86
|
+
console.log('📄 Default layout created and saved');
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn('⚠️ Could not load default layout:', error.message);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Render template with template mode enhancements
|
|
95
|
+
* @param {string} templateName - Template name or content
|
|
96
|
+
* @param {Object} data - Template data
|
|
97
|
+
* @param {Object} options - Render options
|
|
98
|
+
* @returns {Promise<string>} Rendered HTML
|
|
99
|
+
*/
|
|
100
|
+
async render(templateName, data = {}, options = {}) {
|
|
101
|
+
if (!this.initialized) {
|
|
102
|
+
await this.initialize();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add template mode specific data
|
|
106
|
+
const enhancedData = {
|
|
107
|
+
...data,
|
|
108
|
+
_mode: 'template',
|
|
109
|
+
_timestamp: new Date().toISOString(),
|
|
110
|
+
_engine: this.name
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Render using parent Aether engine
|
|
114
|
+
const content = await super.render(templateName, enhancedData, options);
|
|
115
|
+
|
|
116
|
+
// Add template mode specific enhancements
|
|
117
|
+
return this.enhanceForTemplateMode(content, enhancedData, options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Enhance rendered content for template mode
|
|
122
|
+
* @param {string} content - Rendered HTML content
|
|
123
|
+
* @param {Object} data - Template data
|
|
124
|
+
* @param {Object} options - Template options
|
|
125
|
+
* @returns {string} Enhanced HTML
|
|
126
|
+
* @private
|
|
127
|
+
*/
|
|
128
|
+
enhanceForTemplateMode(content, data, options) {
|
|
129
|
+
// Add template mode specific meta tags
|
|
130
|
+
const metaTags = `
|
|
131
|
+
<!-- Template Mode: ${this.name} -->
|
|
132
|
+
<!-- Rendered at: ${new Date().toISOString()} -->
|
|
133
|
+
<!-- Cache: ${this.options.cacheEnabled ? 'Enabled' : 'Disabled'} -->`;
|
|
134
|
+
|
|
135
|
+
// Insert meta tags before closing head tag
|
|
136
|
+
if (content.includes('</head>')) {
|
|
137
|
+
content = content.replace('</head>', `${metaTags}\n</head>`);
|
|
138
|
+
} else {
|
|
139
|
+
// If no head tag, add at the beginning
|
|
140
|
+
content = `${metaTags}\n${content}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add debug information if enabled
|
|
144
|
+
if (this.options.debug) {
|
|
145
|
+
const debugInfo = `
|
|
146
|
+
<!-- DEBUG INFO -->
|
|
147
|
+
<!-- Template: ${data._template || 'unknown'} -->
|
|
148
|
+
<!-- Data Keys: ${Object.keys(data).join(', ')} -->
|
|
149
|
+
<!-- Render Time: ${Date.now() - (data._startTime || Date.now())}ms -->`;
|
|
150
|
+
|
|
151
|
+
if (content.includes('</body>')) {
|
|
152
|
+
content = content.replace('</body>', `${debugInfo}\n</body>`);
|
|
153
|
+
} else {
|
|
154
|
+
content = `${content}\n${debugInfo}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return content;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Compile template for template mode
|
|
163
|
+
* @param {string} template - Template content
|
|
164
|
+
* @param {Object} options - Compile options
|
|
165
|
+
* @returns {Function} Compiled function
|
|
166
|
+
*/
|
|
167
|
+
compile(template, options = {}) {
|
|
168
|
+
return super.compile(template, options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Create a template file
|
|
173
|
+
* @param {string} name - Template name
|
|
174
|
+
* @param {string} type - Template type ('page', 'component', 'partial', 'layout')
|
|
175
|
+
* @param {string} content - Template content
|
|
176
|
+
* @returns {Promise<string>} File path
|
|
177
|
+
*/
|
|
178
|
+
async createTemplate(name, type = 'page', content = null) {
|
|
179
|
+
const fs = await import('fs-extra');
|
|
180
|
+
const path = await import('path');
|
|
181
|
+
|
|
182
|
+
const typeDirs = {
|
|
183
|
+
page: 'pages',
|
|
184
|
+
component: 'components',
|
|
185
|
+
partial: 'partials',
|
|
186
|
+
layout: 'layouts'
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const dir = typeDirs[type] || 'pages';
|
|
190
|
+
const fileName = `${name}.aether`;
|
|
191
|
+
const filePath = path.join(this.options.templateDir, dir, fileName);
|
|
192
|
+
|
|
193
|
+
// Default content if not provided
|
|
194
|
+
if (!content) {
|
|
195
|
+
switch (type) {
|
|
196
|
+
case 'page':
|
|
197
|
+
content = `@extends('layouts.default')
|
|
198
|
+
|
|
199
|
+
@section('title', '${name.charAt(0).toUpperCase() + name.slice(1)} Page')
|
|
200
|
+
|
|
201
|
+
@section('description', 'This is a ${name} page')
|
|
202
|
+
|
|
203
|
+
@section('keywords', '${name}, page, example')
|
|
204
|
+
|
|
205
|
+
@section('content')
|
|
206
|
+
<div class="container mx-auto px-4 py-8">
|
|
207
|
+
<h1 class="text-3xl font-bold mb-6">Welcome to ${name} Page</h1>
|
|
208
|
+
<p class="text-gray-300 mb-4">This is an example page created with Aether Template Engine.</p>
|
|
209
|
+
|
|
210
|
+
<div class="bg-gray-800 rounded-lg p-6 mb-6">
|
|
211
|
+
<h2 class="text-xl font-semibold mb-4">Example Data</h2>
|
|
212
|
+
<ul class="space-y-2">
|
|
213
|
+
{{#each items as item}}
|
|
214
|
+
<li class="flex items-center space-x-2">
|
|
215
|
+
<i class="fas fa-check text-green-500"></i>
|
|
216
|
+
<span>{{item}}</span>
|
|
217
|
+
</li>
|
|
218
|
+
{{/each}}
|
|
219
|
+
</ul>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
223
|
+
<div class="bg-blue-900/30 rounded-lg p-6">
|
|
224
|
+
<h3 class="text-lg font-semibold mb-3">Template Features</h3>
|
|
225
|
+
<ul class="space-y-2">
|
|
226
|
+
<li>Layout inheritance with @extends</li>
|
|
227
|
+
<li>Section management with @section/@yield</li>
|
|
228
|
+
<li>Conditional rendering with @if/@else</li>
|
|
229
|
+
<li>Loop rendering with @foreach</li>
|
|
230
|
+
<li>Variable output with {{variable}}</li>
|
|
231
|
+
</ul>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div class="bg-purple-900/30 rounded-lg p-6">
|
|
235
|
+
<h3 class="text-lg font-semibold mb-3">Current Data</h3>
|
|
236
|
+
<pre class="bg-black/50 p-4 rounded text-sm overflow-x-auto">
|
|
237
|
+
{{json data}}
|
|
238
|
+
</pre>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
@endsection
|
|
243
|
+
|
|
244
|
+
@section('scripts')
|
|
245
|
+
<script>
|
|
246
|
+
console.log('${name} page loaded');
|
|
247
|
+
// Add your JavaScript here
|
|
248
|
+
</script>
|
|
249
|
+
@endsection`;
|
|
250
|
+
break;
|
|
251
|
+
|
|
252
|
+
case 'component':
|
|
253
|
+
content = `<div class="{{class}}">
|
|
254
|
+
<h3 class="text-lg font-semibold mb-2">{{title}}</h3>
|
|
255
|
+
<p class="text-gray-300">{{content}}</p>
|
|
256
|
+
{{#if buttonText}}
|
|
257
|
+
<button class="mt-3 px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded transition">
|
|
258
|
+
{{buttonText}}
|
|
259
|
+
</button>
|
|
260
|
+
{{/if}}
|
|
261
|
+
</div>`;
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
case 'partial':
|
|
265
|
+
content = `<div class="bg-gray-800 rounded-lg p-4 mb-4">
|
|
266
|
+
<h4 class="font-semibold mb-2">{{title}}</h4>
|
|
267
|
+
<p>{{content}}</p>
|
|
268
|
+
</div>`;
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case 'layout':
|
|
272
|
+
content = `<!DOCTYPE html>
|
|
273
|
+
<html lang="zh-CN">
|
|
274
|
+
<head>
|
|
275
|
+
<meta charset="UTF-8">
|
|
276
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
277
|
+
<title>@yield('title', 'Aether Framework')</title>
|
|
278
|
+
<meta name="description" content="@yield('description', '下一代轻量级全栈解决方案')">
|
|
279
|
+
<meta name="keywords" content="@yield('keywords', 'Aether,Framework,Web')">
|
|
280
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
281
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
282
|
+
@yield('head')
|
|
283
|
+
</head>
|
|
284
|
+
<body class="antialiased bg-[#020617] text-white">
|
|
285
|
+
@yield('content')
|
|
286
|
+
@yield('scripts')
|
|
287
|
+
</body>
|
|
288
|
+
</html>`;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
294
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
295
|
+
|
|
296
|
+
// Cache the template if caching is enabled
|
|
297
|
+
if (this.options.cacheEnabled) {
|
|
298
|
+
this.options.templateCache.set(name, content);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return filePath;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get engine metadata
|
|
306
|
+
* @returns {Object} Engine information
|
|
307
|
+
*/
|
|
308
|
+
getMetadata() {
|
|
309
|
+
const baseMetadata = super.getMetadata();
|
|
310
|
+
return {
|
|
311
|
+
...baseMetadata,
|
|
312
|
+
mode: 'template',
|
|
313
|
+
layoutSupport: this.templateOptions.layoutSupport,
|
|
314
|
+
includeSupport: this.templateOptions.includeSupport,
|
|
315
|
+
cacheTemplates: this.templateOptions.cacheTemplates
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export default TemplateModeEngine;
|