@aetherframework/template-engine 1.0.2 → 1.0.6
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/package.json +1 -1
- package/src/core/ModeManager.js +0 -3
- package/src/core/TemplateEngineFactory.js +0 -10
- package/src/engines/AetherEngine.js +149 -185
- package/src/engines/TemplateModeEngine.js +0 -4
- package/src/examples/basic-usage.js +217 -0
- package/src/examples/layout-example.js +404 -0
- package/src/examples/ssr-example.js +180 -0
- package/src/utils/ConfigLoader.js +2 -3
package/package.json
CHANGED
package/src/core/ModeManager.js
CHANGED
|
@@ -63,7 +63,6 @@ class ModeManager {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
this.currentMode = mode;
|
|
66
|
-
console.log(`🔄 Mode changed to: ${mode}`);
|
|
67
66
|
return this;
|
|
68
67
|
}
|
|
69
68
|
|
|
@@ -154,7 +153,6 @@ class ModeManager {
|
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
this.modes.get(mode).renderer = renderer;
|
|
157
|
-
console.log(`✅ Renderer registered for mode: ${mode}`);
|
|
158
156
|
return this;
|
|
159
157
|
}
|
|
160
158
|
|
|
@@ -181,7 +179,6 @@ class ModeManager {
|
|
|
181
179
|
if (this.modes.has(mode)) {
|
|
182
180
|
const modeInfo = this.modes.get(mode);
|
|
183
181
|
modeInfo.adapter = adapter;
|
|
184
|
-
console.log(`✅ Adapter "${adapter.name}" registered for mode: ${mode}`);
|
|
185
182
|
}
|
|
186
183
|
});
|
|
187
184
|
|
|
@@ -46,8 +46,6 @@ class TemplateEngineFactory {
|
|
|
46
46
|
|
|
47
47
|
// Track initialization state
|
|
48
48
|
this.initialized = false;
|
|
49
|
-
|
|
50
|
-
console.log(`🚀 Template Engine Factory created in ${this.config.mode} mode`);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
/**
|
|
@@ -65,7 +63,6 @@ class TemplateEngineFactory {
|
|
|
65
63
|
await this.initializeEngines();
|
|
66
64
|
|
|
67
65
|
this.initialized = true;
|
|
68
|
-
console.log(`✅ Template Engine Factory initialized in ${this.config.mode} mode`);
|
|
69
66
|
|
|
70
67
|
return this;
|
|
71
68
|
} catch (error) {
|
|
@@ -83,17 +80,14 @@ class TemplateEngineFactory {
|
|
|
83
80
|
// Import and register Aether Engine (your custom syntax)
|
|
84
81
|
const { default: AetherEngine } = await import('./../engines/AetherEngine.js');
|
|
85
82
|
this.registerEngine('aether', new AetherEngine(this.config));
|
|
86
|
-
console.log('✅ Aether engine registered');
|
|
87
83
|
|
|
88
84
|
// Import and register SSR Mode Engine
|
|
89
85
|
const { default: SSRModeEngine } = await import('./../engines/SSRModeEngine.js');
|
|
90
86
|
this.registerEngine('ssr-mode', new SSRModeEngine(this.config));
|
|
91
|
-
console.log('✅ SSR Mode engine registered');
|
|
92
87
|
|
|
93
88
|
// Import and register Template Mode Engine
|
|
94
89
|
const { default: TemplateModeEngine } = await import('./../engines/TemplateModeEngine.js');
|
|
95
90
|
this.registerEngine('template-mode', new TemplateModeEngine(this.config));
|
|
96
|
-
console.log('✅ Template Mode engine registered');
|
|
97
91
|
|
|
98
92
|
} catch (error) {
|
|
99
93
|
console.error('❌ Engine initialization failed:', error.message);
|
|
@@ -174,7 +168,6 @@ createRenderer(engineName = this.config.defaultEngine, options = {}) {
|
|
|
174
168
|
if (cacheKey && cacheManager.has(cacheKey)) {
|
|
175
169
|
const cached = cacheManager.get(cacheKey);
|
|
176
170
|
if (Date.now() - cached.timestamp < config.cacheTTL) {
|
|
177
|
-
console.log(`📦 Cache hit for key: ${cacheKey.substring(0, 50)}...`);
|
|
178
171
|
return cached.html;
|
|
179
172
|
}
|
|
180
173
|
}
|
|
@@ -208,7 +201,6 @@ createRenderer(engineName = this.config.defaultEngine, options = {}) {
|
|
|
208
201
|
engine: engineName,
|
|
209
202
|
renderTime: Date.now() - startTime
|
|
210
203
|
});
|
|
211
|
-
console.log(`💾 Cached result for key: ${cacheKey.substring(0, 50)}...`);
|
|
212
204
|
}
|
|
213
205
|
|
|
214
206
|
return html;
|
|
@@ -266,7 +258,6 @@ createRenderer(engineName = this.config.defaultEngine, options = {}) {
|
|
|
266
258
|
throw new Error(`Invalid mode: ${mode}. Must be 'ssr' or 'template'`);
|
|
267
259
|
}
|
|
268
260
|
this.config.mode = mode;
|
|
269
|
-
console.log(`🔄 Rendering mode changed to: ${mode}`);
|
|
270
261
|
return this;
|
|
271
262
|
}
|
|
272
263
|
|
|
@@ -350,7 +341,6 @@ createRenderer(engineName = this.config.defaultEngine, options = {}) {
|
|
|
350
341
|
clearAllCaches() {
|
|
351
342
|
this.cacheManager.clear();
|
|
352
343
|
this.registry.clearCaches();
|
|
353
|
-
console.log('🗑️ All caches cleared');
|
|
354
344
|
}
|
|
355
345
|
|
|
356
346
|
/**
|
|
@@ -16,91 +16,77 @@ import crypto from 'crypto';
|
|
|
16
16
|
import CompressionEngine from './CompressionEngine.js';
|
|
17
17
|
|
|
18
18
|
class AetherEngine {
|
|
19
|
-
constructor(options = {}) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.options = {
|
|
25
|
-
templateDir: options.templateDir || './templates',
|
|
26
|
-
cacheEnabled: options.cacheEnabled !== false,
|
|
27
|
-
cacheTTL: options.cacheTTL || 3600,
|
|
28
|
-
compileCache: new Map(),
|
|
29
|
-
templateCache: new Map(),
|
|
30
|
-
debug: options.debug || false,
|
|
31
|
-
csrfToken: options.csrfToken || '',
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.name = 'aether';
|
|
21
|
+
this.version = '1.3.0';
|
|
22
|
+
this.initialized = false;
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
24
|
+
this.options = {
|
|
25
|
+
templateDir: options.templateDir || './templates',
|
|
26
|
+
cacheEnabled: options.cacheEnabled !== false,
|
|
27
|
+
cacheTTL: options.cacheTTL || 3600,
|
|
28
|
+
compileCache: new Map(),
|
|
29
|
+
templateCache: new Map(),
|
|
30
|
+
debug: options.debug || false,
|
|
31
|
+
csrfToken: options.csrfToken || '',
|
|
32
|
+
|
|
33
|
+
// Compression options
|
|
34
|
+
compressionEnabled: options.compressionEnabled !== false,
|
|
35
|
+
minifyHTML: options.minifyHTML !== false,
|
|
36
|
+
minifyCSS: options.minifyCSS !== false,
|
|
37
|
+
minifyJS: options.minifyJS !== false,
|
|
38
|
+
mangleJS: options.mangleJS || false,
|
|
39
|
+
removeComments: options.removeComments || false,
|
|
40
|
+
collapseWhitespace: options.collapseWhitespace || true,
|
|
41
|
+
cacheCompressed: options.cacheCompressed !== false,
|
|
42
|
+
...options
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Initialize compression engine
|
|
46
|
+
this.compressionEngine = new CompressionEngine(this.options);
|
|
47
|
+
|
|
48
|
+
this.filters = new Map();
|
|
49
|
+
this.functions = new Map();
|
|
50
|
+
this.layouts = new Map();
|
|
51
|
+
this.routes = {};
|
|
52
|
+
|
|
53
|
+
this.ensureTemplateDir();
|
|
54
|
+
this.registerDefaultFilters();
|
|
55
|
+
this.registerDefaultFunctions();
|
|
56
|
+
}
|
|
57
57
|
|
|
58
58
|
initialize() {
|
|
59
59
|
if (this.initialized) return;
|
|
60
|
-
console.log(`Aether Engine initialized (v${this.version})`);
|
|
61
60
|
this.initialized = true;
|
|
62
61
|
}
|
|
62
|
+
|
|
63
63
|
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
* Process content with compression based on options
|
|
65
|
+
*/
|
|
66
|
+
processWithCompression(content, options = {}) {
|
|
67
|
+
if (!this.options.compressionEnabled) return content;
|
|
68
|
+
|
|
69
|
+
const processOptions = {
|
|
70
|
+
minifyHTML: this.options.minifyHTML,
|
|
71
|
+
minifyCSS: this.options.minifyCSS,
|
|
72
|
+
minifyJS: this.options.minifyJS,
|
|
73
|
+
mangleJS: this.options.mangleJS,
|
|
74
|
+
removeComments: this.options.removeComments,
|
|
75
|
+
collapseWhitespace: this.options.collapseWhitespace,
|
|
76
|
+
cacheCompressed: this.options.cacheCompressed,
|
|
77
|
+
...options
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return this.compressionEngine.processHTML(content, processOptions);
|
|
72
81
|
}
|
|
73
|
-
|
|
74
|
-
const processOptions = {
|
|
75
|
-
minifyHTML: this.options.minifyHTML,
|
|
76
|
-
minifyCSS: this.options.minifyCSS,
|
|
77
|
-
minifyJS: this.options.minifyJS,
|
|
78
|
-
mangleJS: this.options.mangleJS,
|
|
79
|
-
removeComments: this.options.removeComments,
|
|
80
|
-
collapseWhitespace: this.options.collapseWhitespace,
|
|
81
|
-
cacheCompressed: this.options.cacheCompressed,
|
|
82
|
-
...options
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
return this.compressionEngine.processHTML(content, processOptions);
|
|
86
|
-
}
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*/
|
|
91
|
-
clearCompressionCache() {
|
|
92
|
-
if (this.compressionEngine) {
|
|
93
|
-
this.compressionEngine.clearCompressionCache();
|
|
83
|
+
clearCompressionCache() {
|
|
84
|
+
if (this.compressionEngine) this.compressionEngine.clearCompressionCache();
|
|
94
85
|
}
|
|
95
|
-
}
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*/
|
|
101
|
-
getCompressionStats() {
|
|
102
|
-
return this.compressionEngine ? this.compressionEngine.getStats() : null;
|
|
103
|
-
}
|
|
87
|
+
getCompressionStats() {
|
|
88
|
+
return this.compressionEngine ? this.compressionEngine.getStats() : null;
|
|
89
|
+
}
|
|
104
90
|
|
|
105
91
|
ensureTemplateDir() {
|
|
106
92
|
const dirs = [
|
|
@@ -144,9 +130,7 @@ getCompressionStats() {
|
|
|
144
130
|
});
|
|
145
131
|
|
|
146
132
|
this.function('class', (classes) => {
|
|
147
|
-
if (Array.isArray(classes))
|
|
148
|
-
return classes.filter(c => typeof c === 'string').join(' ');
|
|
149
|
-
}
|
|
133
|
+
if (Array.isArray(classes)) return classes.filter(c => typeof c === 'string').join(' ');
|
|
150
134
|
return '';
|
|
151
135
|
});
|
|
152
136
|
|
|
@@ -368,12 +352,10 @@ getCompressionStats() {
|
|
|
368
352
|
inSectionBlock = false; currentSectionName = ''; sectionContent = '';
|
|
369
353
|
}
|
|
370
354
|
}
|
|
371
|
-
// FIX: Enhance @include syntax to support passing data parameters
|
|
372
355
|
else if (token.startsWith('@include')) {
|
|
373
356
|
const includeMatch = token.match(/@include\s*\(\s*'([^']+)'\s*(?:,\s*([\s\S]*?))?\s*\)/);
|
|
374
357
|
if (includeMatch) {
|
|
375
358
|
const viewName = includeMatch[1];
|
|
376
|
-
// Default to empty object if no params; otherwise evaluate as JS expression
|
|
377
359
|
const dataExpr = includeMatch[2] ? includeMatch[2].trim() : '{}';
|
|
378
360
|
|
|
379
361
|
if (inSectionBlock) sectionContent += token;
|
|
@@ -422,17 +404,22 @@ getCompressionStats() {
|
|
|
422
404
|
return value;
|
|
423
405
|
}
|
|
424
406
|
|
|
425
|
-
// FIX
|
|
407
|
+
// [CRITICAL FIX] Preserve prototype chain to prevent losing root data variables (like langUrls, t, etc.)
|
|
408
|
+
// Object.assign only copies own enumerable properties, ignoring the prototype chain where
|
|
409
|
+
// the original 'data' object properties reside.
|
|
426
410
|
function __include(name, includeData = {}) {
|
|
427
|
-
// 1.
|
|
428
|
-
const mergedScope = Object.
|
|
411
|
+
// 1. Create a new scope that inherits from the current scope's prototype (the original data)
|
|
412
|
+
const mergedScope = Object.create(Object.getPrototypeOf(__scope));
|
|
413
|
+
|
|
414
|
+
// 2. Copy own properties from current scope (e.g., loop variables) and includeData
|
|
415
|
+
Object.assign(mergedScope, __scope, includeData);
|
|
429
416
|
|
|
430
|
-
//
|
|
417
|
+
// 3. Prioritize pre-compiled includes (passed via render options)
|
|
431
418
|
if (helpers.includes && helpers.includes[name]) {
|
|
432
419
|
return helpers.includes[name](mergedScope, helpers);
|
|
433
420
|
}
|
|
434
421
|
|
|
435
|
-
//
|
|
422
|
+
// 4. Synchronously load and compile template from disk
|
|
436
423
|
if (helpers.engine) {
|
|
437
424
|
try {
|
|
438
425
|
const content = helpers.engine.loadTemplateSync(name);
|
|
@@ -544,8 +531,6 @@ getCompressionStats() {
|
|
|
544
531
|
let jsCode = '';
|
|
545
532
|
try {
|
|
546
533
|
jsCode = this.enhancedConvertToJsCode(templateContent);
|
|
547
|
-
if (this.options.debug) console.log('\n--- Generated JS Code ---\n' + jsCode + '\n-------------------------\n');
|
|
548
|
-
|
|
549
534
|
const renderFunc = new Function('data', 'helpers', `
|
|
550
535
|
const __output = [];
|
|
551
536
|
const __sections = {};
|
|
@@ -567,94 +552,81 @@ getCompressionStats() {
|
|
|
567
552
|
}
|
|
568
553
|
}
|
|
569
554
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
555
|
+
async render(templateName, data = {}, options = {}) {
|
|
556
|
+
if (!this.initialized) this.initialize();
|
|
557
|
+
let templateContent = templateName;
|
|
558
|
+
|
|
559
|
+
const isFilePath = typeof templateName === 'string' && !templateName.includes('\n') && !templateName.includes('<') && (templateName.endsWith('.aether') || templateName.endsWith('.html') || templateName.includes('/') || templateName.includes('\\'));
|
|
560
|
+
if (isFilePath) templateContent = await this.loadTemplate(templateName);
|
|
561
|
+
|
|
562
|
+
// [FIX] Correct regex and match groups for layout inheritance
|
|
563
|
+
const extendsMatch = templateContent.match(/@extends\s*\(\s*'([^']+)'\s*\)/);
|
|
564
|
+
if (extendsMatch) {
|
|
565
|
+
const layoutName = extendsMatch[1];
|
|
566
|
+
try {
|
|
567
|
+
const layoutContent = await this.loadTemplate(layoutName);
|
|
568
|
+
const sections = {};
|
|
569
|
+
|
|
570
|
+
const sectionRegex = /@section\s*\(\s*'([^']+)'\s*\)([\s\S]*?)@endsection/g;
|
|
571
|
+
let sectionMatch;
|
|
572
|
+
while ((sectionMatch = sectionRegex.exec(templateContent)) !== null) {
|
|
573
|
+
sections[sectionMatch[1]] = sectionMatch[2].trim();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const inlineSectionRegex = /@section\s*\(\s*'([^']+)'\s*,\s*'([^']*)'\s*\)/g;
|
|
577
|
+
let inlineMatch;
|
|
578
|
+
while ((inlineMatch = inlineSectionRegex.exec(templateContent)) !== null) {
|
|
579
|
+
sections[inlineMatch[1]] = inlineMatch[2];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const yieldRegex = /@yield\s*\(\s*'([^']+)'(?:\s*,\s*'([^']*)')?\s*\)/g;
|
|
583
|
+
templateContent = layoutContent.replace(yieldRegex, (match, name, defaultValue) =>
|
|
584
|
+
sections[name] !== undefined ? sections[name] : (defaultValue || '')
|
|
585
|
+
);
|
|
586
|
+
} catch (error) {
|
|
587
|
+
console.warn(`Warning: Could not load layout '${layoutName}':`, error.message);
|
|
600
588
|
}
|
|
601
|
-
|
|
602
|
-
// 【修复1】:恢复正确的正则表达式
|
|
603
|
-
const yieldRegex = /@yield\s*\(\s*'([^']+)'(?:\s*,\s*'([^']*)')?\s*\)/g;
|
|
604
|
-
templateContent = layoutContent.replace(yieldRegex, (match, name, defaultValue) =>
|
|
605
|
-
sections[name] !== undefined ? sections[name] : (defaultValue || '')
|
|
606
|
-
);
|
|
607
|
-
} catch (error) {
|
|
608
|
-
console.warn(`Warning: Could not load layout '${layoutName}':`, error.message);
|
|
609
589
|
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const renderFunc = this.compile(templateContent);
|
|
613
|
-
|
|
614
|
-
// 【关键修复】: 保存渲染结果到变量,而不是直接 return,以便后续进行压缩处理
|
|
615
|
-
const renderedContent = renderFunc(data, {
|
|
616
|
-
filters: this.filters,
|
|
617
|
-
functions: this.functions,
|
|
618
|
-
includes: options.includes || {},
|
|
619
|
-
engine: this
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
// Apply compression if enabled
|
|
623
|
-
if (this.options.compressionEnabled) {
|
|
624
|
-
const compressionOptions = {
|
|
625
|
-
minifyHTML: this.options.minifyHTML,
|
|
626
|
-
minifyCSS: this.options.minifyCSS,
|
|
627
|
-
minifyJS: this.options.minifyJS,
|
|
628
|
-
mangleJS: this.options.mangleJS,
|
|
629
|
-
removeComments: this.options.removeComments,
|
|
630
|
-
collapseWhitespace: this.options.collapseWhitespace,
|
|
631
|
-
removeAttributeQuotes: this.options.removeAttributeQuotes,
|
|
632
|
-
removeEmptyAttributes: this.options.removeEmptyAttributes,
|
|
633
|
-
cacheCompressed: this.options.cacheCompressed,
|
|
634
|
-
...options.compression // Allow per-render override
|
|
635
|
-
};
|
|
636
590
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
591
|
+
const renderFunc = this.compile(templateContent);
|
|
592
|
+
|
|
593
|
+
const renderedContent = renderFunc(data, {
|
|
594
|
+
filters: this.filters,
|
|
595
|
+
functions: this.functions,
|
|
596
|
+
includes: options.includes || {},
|
|
597
|
+
engine: this
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
if (this.options.compressionEnabled) {
|
|
601
|
+
const compressionOptions = {
|
|
602
|
+
minifyHTML: this.options.minifyHTML,
|
|
603
|
+
minifyCSS: this.options.minifyCSS,
|
|
604
|
+
minifyJS: this.options.minifyJS,
|
|
605
|
+
mangleJS: this.options.mangleJS,
|
|
606
|
+
removeComments: this.options.removeComments,
|
|
607
|
+
collapseWhitespace: this.options.collapseWhitespace,
|
|
608
|
+
removeAttributeQuotes: this.options.removeAttributeQuotes,
|
|
609
|
+
removeEmptyAttributes: this.options.removeEmptyAttributes,
|
|
610
|
+
cacheCompressed: this.options.cacheCompressed,
|
|
611
|
+
...options.compression
|
|
612
|
+
};
|
|
640
613
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
614
|
+
if (this.compressionEngine) {
|
|
615
|
+
const compressedContent = this.compressionEngine.processHTML(renderedContent, compressionOptions);
|
|
616
|
+
|
|
617
|
+
if (this.options.debug) {
|
|
618
|
+
const originalSize = renderedContent.length;
|
|
619
|
+
const compressedSize = compressedContent.length;
|
|
620
|
+
const reduction = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return compressedContent;
|
|
647
624
|
}
|
|
648
|
-
|
|
649
|
-
return compressedContent;
|
|
650
625
|
}
|
|
626
|
+
|
|
627
|
+
return renderedContent;
|
|
651
628
|
}
|
|
652
|
-
|
|
653
|
-
return renderedContent;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
629
|
|
|
657
|
-
// FIX: Extract unified path resolution logic, supporting dot notation (e.g., 'partials.header')
|
|
658
630
|
_getTemplatePaths(templateName) {
|
|
659
631
|
const paths = [
|
|
660
632
|
path.join(this.options.templateDir, 'partials', `${templateName}.aether`),
|
|
@@ -665,7 +637,6 @@ getCompressionStats() {
|
|
|
665
637
|
templateName
|
|
666
638
|
];
|
|
667
639
|
|
|
668
|
-
// Support dot notation resolution: 'partials.header' -> 'partials/header.aether'
|
|
669
640
|
if (templateName.includes('.')) {
|
|
670
641
|
const dotPath = templateName.replace(/\./g, path.sep) + '.aether';
|
|
671
642
|
paths.unshift(path.join(this.options.templateDir, dotPath));
|
|
@@ -688,34 +659,27 @@ getCompressionStats() {
|
|
|
688
659
|
throw new Error(`Template not found: ${templateName}`);
|
|
689
660
|
}
|
|
690
661
|
|
|
691
|
-
|
|
662
|
+
/**
|
|
663
|
+
* [FIX] Synchronously load template from disk for runtime @include usage
|
|
664
|
+
*/
|
|
692
665
|
loadTemplateSync(templateName) {
|
|
693
666
|
if (this.options.cacheEnabled && this.options.templateCache.has(templateName)) {
|
|
694
|
-
|
|
667
|
+
return this.options.templateCache.get(templateName);
|
|
695
668
|
}
|
|
696
669
|
|
|
697
670
|
const possiblePaths = this._getTemplatePaths(templateName);
|
|
698
671
|
for (const templatePath of possiblePaths) {
|
|
699
672
|
try {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
673
|
+
// Use readFileSync for synchronous loading required by __include
|
|
674
|
+
const content = fs.readFileSync(templatePath, 'utf-8');
|
|
675
|
+
if (this.options.cacheEnabled) this.options.templateCache.set(templateName, content);
|
|
676
|
+
return content;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
// Ignore and try next path
|
|
679
|
+
}
|
|
706
680
|
}
|
|
707
681
|
throw new Error(`Template not found: ${templateName}`);
|
|
708
682
|
}
|
|
709
|
-
|
|
710
|
-
clearCache() { this.options.compileCache.clear(); this.options.templateCache.clear(); }
|
|
711
|
-
|
|
712
|
-
getMetadata() {
|
|
713
|
-
return {
|
|
714
|
-
name: this.name, version: this.version, initialized: this.initialized,
|
|
715
|
-
filters: Array.from(this.filters.keys()), functions: Array.from(this.functions.keys()),
|
|
716
|
-
layouts: Array.from(this.layouts.keys()), cacheEnabled: this.options.cacheEnabled, templateDir: this.options.templateDir
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
683
|
}
|
|
720
684
|
|
|
721
685
|
export default AetherEngine;
|
|
@@ -49,7 +49,6 @@ class TemplateModeEngine extends AetherEngine {
|
|
|
49
49
|
// Load default layout if exists
|
|
50
50
|
await this.loadDefaultLayout();
|
|
51
51
|
|
|
52
|
-
console.log(`✅ Template Mode Engine initialized (v${this.version})`);
|
|
53
52
|
this.initialized = true;
|
|
54
53
|
return this;
|
|
55
54
|
}
|
|
@@ -66,7 +65,6 @@ class TemplateModeEngine extends AetherEngine {
|
|
|
66
65
|
if (await fs.pathExists(defaultLayoutPath)) {
|
|
67
66
|
const layoutContent = await fs.readFile(defaultLayoutPath, 'utf-8');
|
|
68
67
|
this.registerLayout('default', layoutContent);
|
|
69
|
-
console.log('📄 Default layout loaded from file');
|
|
70
68
|
} else {
|
|
71
69
|
// Create default layout based on user's template
|
|
72
70
|
const defaultLayout = `<!DOCTYPE html>
|
|
@@ -92,7 +90,6 @@ class TemplateModeEngine extends AetherEngine {
|
|
|
92
90
|
// Save to file for future use
|
|
93
91
|
await fs.ensureDir(`${this.options.templateDir}/layouts`);
|
|
94
92
|
await fs.writeFile(defaultLayoutPath, defaultLayout, 'utf-8');
|
|
95
|
-
console.log('📄 Default layout created and saved');
|
|
96
93
|
}
|
|
97
94
|
} catch (error) {
|
|
98
95
|
console.warn('⚠️ Could not load default layout:', error.message);
|
|
@@ -269,7 +266,6 @@ class TemplateModeEngine extends AetherEngine {
|
|
|
269
266
|
|
|
270
267
|
@section('scripts')
|
|
271
268
|
<script>
|
|
272
|
-
console.log('${name} page loaded');
|
|
273
269
|
// Add your JavaScript here
|
|
274
270
|
</script>
|
|
275
271
|
@endsection`;
|