@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aetherframework/template-engine",
3
- "version": "1.0.2",
3
+ "version": "1.0.6",
4
4
  "description": "A lightweight, high-performance template engine with SSR support and custom syntax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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
- this.name = 'aether';
21
- this.version = '1.3.0';
22
- this.initialized = false;
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
- // 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
- }
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
- * Process content with compression based on options
65
- * @param {string} content - Content to process
66
- * @param {Object} options - Processing options
67
- * @returns {string} Processed content
68
- */
69
- processWithCompression(content, options = {}) {
70
- if (!this.options.compressionEnabled) {
71
- return content;
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
- * Clear compression cache
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
- * Get compression statistics
99
- * @returns {Object} Compression statistics
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: Fully implement __include to support synchronous loading, compiling, and rendering of sub-templates from disk
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. Merge current scope with passed include data
428
- const mergedScope = Object.assign({}, __scope, includeData);
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
- // 2. Prioritize pre-compiled includes (passed via render options)
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
- // 3. Synchronously load and compile template from disk
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
- async render(templateName, data = {}, options = {}) {
571
- if (!this.initialized) this.initialize();
572
- let templateContent = templateName;
573
-
574
- const isFilePath = typeof templateName === 'string' && !templateName.includes('\n') && !templateName.includes('<') && (templateName.endsWith('.aether') || templateName.endsWith('.html') || templateName.includes('/') || templateName.includes('\\'));
575
- if (isFilePath) templateContent = await this.loadTemplate(templateName);
576
-
577
- // 【修复1】:恢复正确的正则表达式 \(\)
578
- const extendsMatch = templateContent.match(/@extends\s*\(\s*'([^']+)'\s*\)/);
579
- if (extendsMatch) {
580
- // 【修复2】:恢复正确的匹配组索引 [1]
581
- const layoutName = extendsMatch[1];
582
- try {
583
- const layoutContent = await this.loadTemplate(layoutName);
584
- const sections = {};
585
-
586
- // 【修复1】:恢复正确的正则表达式
587
- const sectionRegex = /@section\s*\(\s*'([^']+)'\s*\)([\s\S]*?)@endsection/g;
588
- let sectionMatch;
589
- while ((sectionMatch = sectionRegex.exec(templateContent)) !== null) {
590
- // 【修复2】:恢复正确的匹配组索引 [1] 和 [2]
591
- sections[sectionMatch[1]] = sectionMatch[2].trim();
592
- }
593
-
594
- // 【修复1】:恢复正确的正则表达式
595
- const inlineSectionRegex = /@section\s*\(\s*'([^']+)'\s*,\s*'([^']*)'\s*\)/g;
596
- let inlineMatch;
597
- while ((inlineMatch = inlineSectionRegex.exec(templateContent)) !== null) {
598
- // 【修复2】:恢复正确的匹配组索引 [1] [2]
599
- sections[inlineMatch[1]] = inlineMatch[2];
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
- // Use the compression engine to process the rendered content
638
- if (this.compressionEngine) {
639
- const compressedContent = this.compressionEngine.processHTML(renderedContent, compressionOptions);
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
- // Log compression statistics in debug mode
642
- if (this.options.debug) {
643
- const originalSize = renderedContent.length;
644
- const compressedSize = compressedContent.length;
645
- const reduction = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);
646
- console.log(`[AetherEngine] Compression applied: ${originalSize} → ${compressedSize} bytes (${reduction}% reduction)`);
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
- // FIX: Add synchronous loading method specifically for runtime @include usage
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
- return this.options.templateCache.get(templateName);
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
- if (fs.existsSync(templatePath)) {
701
- const content = fs.readFileSync(templatePath, 'utf-8');
702
- if (this.options.cacheEnabled) this.options.templateCache.set(templateName, content);
703
- return content;
704
- }
705
- } catch (error) {}
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`;