@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,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/template-engine/src/utils/ConfigLoader
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Configuration Loader - Loads and manages configuration from environment variables and configuration files
|
|
10
|
+
* Supports loading from .env files, environment variables, and setting defaults
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
|
|
15
|
+
class ConfigLoader {
|
|
16
|
+
/**
|
|
17
|
+
* Constructor for ConfigLoader
|
|
18
|
+
* @param {Object} options - Configuration options
|
|
19
|
+
* @param {string} options.configFile - Configuration file name (default: '.env')
|
|
20
|
+
* @param {string} options.configDir - Configuration directory path (default: current working directory)
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
// Initialize configuration options with defaults
|
|
24
|
+
this.options = {
|
|
25
|
+
configFile: options.configFile || '.env',
|
|
26
|
+
configDir: options.configDir || process.cwd(),
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Configuration storage object
|
|
31
|
+
this.config = {};
|
|
32
|
+
|
|
33
|
+
// Flag to track if configuration has been loaded
|
|
34
|
+
this.loaded = false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load configuration from environment variables and configuration files
|
|
39
|
+
* @returns {Promise<Object>} Configuration object containing all loaded settings
|
|
40
|
+
*/
|
|
41
|
+
async load() {
|
|
42
|
+
// Return cached configuration if already loaded
|
|
43
|
+
if (this.loaded) {
|
|
44
|
+
return this.config;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Step 1: Load configuration from environment variables
|
|
48
|
+
this.loadFromEnv();
|
|
49
|
+
|
|
50
|
+
// Step 2: Load configuration from configuration file
|
|
51
|
+
await this.loadFromFile();
|
|
52
|
+
|
|
53
|
+
// Step 3: Set default values for any missing configuration options
|
|
54
|
+
this.setDefaults();
|
|
55
|
+
|
|
56
|
+
// Mark configuration as loaded
|
|
57
|
+
this.loaded = true;
|
|
58
|
+
|
|
59
|
+
return this.config;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Load configuration from environment variables
|
|
64
|
+
* Environment variables take precedence over file configuration
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
loadFromEnv() {
|
|
68
|
+
// Template Engine Configuration
|
|
69
|
+
this.config.mode = process.env.TEMPLATE_ENGINE_MODE || 'template';
|
|
70
|
+
this.config.defaultEngine = process.env.TEMPLATE_ENGINE || 'aether';
|
|
71
|
+
this.config.templateDir = process.env.TEMPLATE_DIR || './templates';
|
|
72
|
+
|
|
73
|
+
// Cache Configuration
|
|
74
|
+
this.config.cacheEnabled = process.env.CACHE_ENABLED !== 'false';
|
|
75
|
+
this.config.cacheTTL = parseInt(process.env.CACHE_TTL) || 300000; // 5 minutes default
|
|
76
|
+
this.config.cacheMaxSize = parseInt(process.env.CACHE_MAX_SIZE) || 1000;
|
|
77
|
+
|
|
78
|
+
// Debug Configuration
|
|
79
|
+
this.config.debug = process.env.NODE_ENV === 'development' || process.env.DEBUG === 'true';
|
|
80
|
+
|
|
81
|
+
// Server-Side Rendering (SSR) Configuration
|
|
82
|
+
this.config.ssrHydrate = process.env.SSR_HYDRATE !== 'false';
|
|
83
|
+
this.config.ssrStream = process.env.SSR_STREAM === 'true';
|
|
84
|
+
|
|
85
|
+
// Template Configuration
|
|
86
|
+
this.config.layoutSupport = process.env.LAYOUT_SUPPORT !== 'false';
|
|
87
|
+
this.config.includeSupport = process.env.INCLUDE_SUPPORT !== 'false';
|
|
88
|
+
this.config.cacheTemplates = process.env.CACHE_TEMPLATES !== 'false';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Load configuration from configuration file
|
|
93
|
+
* File configuration is used as fallback when environment variables are not set
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
async loadFromFile() {
|
|
97
|
+
// Construct full path to configuration file
|
|
98
|
+
const configPath = path.join(this.options.configDir, this.options.configFile);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Check if configuration file exists
|
|
102
|
+
if (await fs.pathExists(configPath)) {
|
|
103
|
+
// Read configuration file content
|
|
104
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
105
|
+
const lines = content.split('\n');
|
|
106
|
+
|
|
107
|
+
// Parse each line in the configuration file
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
const trimmed = line.trim();
|
|
110
|
+
|
|
111
|
+
// Skip comments (lines starting with #) and empty lines
|
|
112
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Parse key=value pairs
|
|
117
|
+
const equalsIndex = trimmed.indexOf('=');
|
|
118
|
+
if (equalsIndex > 0) {
|
|
119
|
+
const key = trimmed.substring(0, equalsIndex).trim();
|
|
120
|
+
const value = trimmed.substring(equalsIndex + 1).trim();
|
|
121
|
+
|
|
122
|
+
// Remove surrounding quotes if present
|
|
123
|
+
const cleanValue = value.replace(/['"]|['"]$/g, '');
|
|
124
|
+
|
|
125
|
+
// Only set configuration from file if environment variable is not already set
|
|
126
|
+
if (process.env[key] === undefined) {
|
|
127
|
+
this.config[key] = this.parseValue(cleanValue);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`📁 Configuration loaded from: ${configPath}`);
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
// Log warning but don't fail if configuration file cannot be loaded
|
|
136
|
+
console.warn(`⚠️ Could not load config file: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Parse configuration value from string to appropriate type
|
|
142
|
+
* @param {string} value - Raw string value from configuration
|
|
143
|
+
* @returns {any} Parsed value (boolean, number, or string)
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
parseValue(value) {
|
|
147
|
+
// Parse boolean values
|
|
148
|
+
if (value.toLowerCase() === 'true') return true;
|
|
149
|
+
if (value.toLowerCase() === 'false') return false;
|
|
150
|
+
|
|
151
|
+
// Parse numeric values
|
|
152
|
+
if (!isNaN(value) && value.trim() !== '') {
|
|
153
|
+
const num = Number(value);
|
|
154
|
+
if (!isNaN(num)) return num;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Return as string if not boolean or number
|
|
158
|
+
return value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Set default configuration values for any missing options
|
|
163
|
+
* This ensures all required configuration options have values
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
setDefaults() {
|
|
167
|
+
// Default configuration values
|
|
168
|
+
const defaults = {
|
|
169
|
+
mode: 'template',
|
|
170
|
+
defaultEngine: 'aether',
|
|
171
|
+
templateDir: './templates',
|
|
172
|
+
cacheEnabled: true,
|
|
173
|
+
cacheTTL: 300000, // 5 minutes in milliseconds
|
|
174
|
+
cacheMaxSize: 1000,
|
|
175
|
+
debug: false,
|
|
176
|
+
ssrHydrate: true,
|
|
177
|
+
ssrStream: false,
|
|
178
|
+
layoutSupport: true,
|
|
179
|
+
includeSupport: true,
|
|
180
|
+
cacheTemplates: true
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Apply defaults only for configuration options that are not already set
|
|
184
|
+
for (const [key, defaultValue] of Object.entries(defaults)) {
|
|
185
|
+
if (this.config[key] === undefined) {
|
|
186
|
+
this.config[key] = defaultValue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get a specific configuration value
|
|
193
|
+
* @param {string} key - Configuration key to retrieve
|
|
194
|
+
* @param {any} defaultValue - Default value to return if key is not found
|
|
195
|
+
* @returns {any} Configuration value or default value
|
|
196
|
+
*/
|
|
197
|
+
get(key, defaultValue = null) {
|
|
198
|
+
return this.config[key] !== undefined ? this.config[key] : defaultValue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Set a configuration value
|
|
203
|
+
* @param {string} key - Configuration key to set
|
|
204
|
+
* @param {any} value - Value to set for the configuration key
|
|
205
|
+
* @returns {ConfigLoader} This instance for method chaining
|
|
206
|
+
*/
|
|
207
|
+
set(key, value) {
|
|
208
|
+
this.config[key] = value;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get all configuration as an object
|
|
214
|
+
* @returns {Object} Complete configuration object
|
|
215
|
+
*/
|
|
216
|
+
getAll() {
|
|
217
|
+
// Return a copy to prevent external modification
|
|
218
|
+
return { ...this.config };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Reload configuration from sources
|
|
223
|
+
* Useful when configuration files have been updated
|
|
224
|
+
* @returns {Promise<Object>} Updated configuration object
|
|
225
|
+
*/
|
|
226
|
+
async reload() {
|
|
227
|
+
// Reset loaded flag and clear existing configuration
|
|
228
|
+
this.loaded = false;
|
|
229
|
+
this.config = {};
|
|
230
|
+
|
|
231
|
+
// Load configuration again
|
|
232
|
+
return this.load();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Validate the current configuration
|
|
237
|
+
* Checks for required settings and valid values
|
|
238
|
+
* @returns {Object} Validation result with errors and warnings
|
|
239
|
+
*/
|
|
240
|
+
validate() {
|
|
241
|
+
const errors = [];
|
|
242
|
+
const warnings = [];
|
|
243
|
+
|
|
244
|
+
// Validate rendering mode
|
|
245
|
+
if (!['ssr', 'template', 'disabled'].includes(this.config.mode)) {
|
|
246
|
+
errors.push(`Invalid mode: ${this.config.mode}. Must be 'ssr', 'template', or 'disabled'`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Validate template directory configuration
|
|
250
|
+
if (!this.config.templateDir) {
|
|
251
|
+
errors.push('Template directory not specified');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Validate cache TTL (must be positive)
|
|
255
|
+
if (this.config.cacheTTL < 0) {
|
|
256
|
+
errors.push(`Invalid cache TTL: ${this.config.cacheTTL}. Must be positive number`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Validate cache maximum size (must be at least 1)
|
|
260
|
+
if (this.config.cacheMaxSize < 1) {
|
|
261
|
+
errors.push(`Invalid cache max size: ${this.config.cacheMaxSize}. Must be at least 1`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check if template directory exists (warning only, not an error)
|
|
265
|
+
if (this.config.templateDir && !fs.existsSync(this.config.templateDir)) {
|
|
266
|
+
warnings.push(`Template directory does not exist: ${this.config.templateDir}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Return validation results
|
|
270
|
+
return {
|
|
271
|
+
valid: errors.length === 0,
|
|
272
|
+
errors,
|
|
273
|
+
warnings,
|
|
274
|
+
config: this.config
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export default ConfigLoader;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/template-engine/src/utils/ErrorHandler
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error Handler - Handles and formats template engine errors
|
|
10
|
+
* This class provides comprehensive error handling for template engine operations,
|
|
11
|
+
* including error formatting, logging, and user-friendly error messages.
|
|
12
|
+
*/
|
|
13
|
+
class ErrorHandler {
|
|
14
|
+
/**
|
|
15
|
+
* Constructor for ErrorHandler class
|
|
16
|
+
* @param {Object} options - Configuration options for error handling
|
|
17
|
+
* @param {boolean} options.debug - Enable debug mode for detailed error information
|
|
18
|
+
* @param {boolean} options.logErrors - Enable error logging to console
|
|
19
|
+
* @param {boolean} options.formatErrors - Enable error formatting with context
|
|
20
|
+
*/
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.options = {
|
|
23
|
+
debug: options.debug || false,
|
|
24
|
+
logErrors: options.logErrors !== false,
|
|
25
|
+
formatErrors: options.formatErrors !== false,
|
|
26
|
+
...options
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Static method to handle template engine errors
|
|
32
|
+
* @param {Error} error - Original error object
|
|
33
|
+
* @param {Object} context - Error context information
|
|
34
|
+
* @returns {Error} Formatted error object
|
|
35
|
+
*/
|
|
36
|
+
static handle(error, context = {}) {
|
|
37
|
+
const handler = new ErrorHandler();
|
|
38
|
+
return handler.formatError(error, context);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format error with additional context information
|
|
43
|
+
* @param {Error} error - Original error object
|
|
44
|
+
* @param {Object} context - Error context information
|
|
45
|
+
* @returns {Error} Formatted error object
|
|
46
|
+
*/
|
|
47
|
+
formatError(error, context = {}) {
|
|
48
|
+
const errorInfo = {
|
|
49
|
+
message: error.message,
|
|
50
|
+
stack: error.stack,
|
|
51
|
+
timestamp: new Date().toISOString(),
|
|
52
|
+
context: context
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Log error to console if logging is enabled
|
|
56
|
+
if (this.options.logErrors) {
|
|
57
|
+
console.error('Template Engine Error:', errorInfo);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Format error if formatting is enabled
|
|
61
|
+
if (this.options.formatErrors) {
|
|
62
|
+
return this.createFormattedError(error, context);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create formatted error with helpful message and context
|
|
70
|
+
* @param {Error} error - Original error object
|
|
71
|
+
* @param {Object} context - Error context information
|
|
72
|
+
* @returns {Error} Formatted error object
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
createFormattedError(error, context) {
|
|
76
|
+
let message = error.message;
|
|
77
|
+
|
|
78
|
+
// Add template context information to error message
|
|
79
|
+
if (context.template) {
|
|
80
|
+
const templatePreview = typeof context.template === 'string'
|
|
81
|
+
? context.template.substring(0, 100) + (context.template.length > 100 ? '...' : '')
|
|
82
|
+
: 'Function';
|
|
83
|
+
message += `\nTemplate: ${templatePreview}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add engine context information
|
|
87
|
+
if (context.engine) {
|
|
88
|
+
message += `\nEngine: ${context.engine}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Add mode context information
|
|
92
|
+
if (context.mode) {
|
|
93
|
+
message += `\nMode: ${context.mode}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Add data keys context information
|
|
97
|
+
if (context.data && Object.keys(context.data).length > 0) {
|
|
98
|
+
message += `\nData Keys: ${Object.keys(context.data).join(', ')}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Create new error with formatted message
|
|
102
|
+
const formattedError = new Error(message);
|
|
103
|
+
formattedError.originalError = error;
|
|
104
|
+
formattedError.context = context;
|
|
105
|
+
formattedError.stack = error.stack;
|
|
106
|
+
|
|
107
|
+
return formattedError;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Handle template compilation errors
|
|
112
|
+
* @param {Error} error - Compilation error object
|
|
113
|
+
* @param {string} template - Template content
|
|
114
|
+
* @param {string} templateName - Name of the template
|
|
115
|
+
* @returns {Error} Formatted compilation error
|
|
116
|
+
*/
|
|
117
|
+
handleCompilationError(error, template, templateName = 'anonymous') {
|
|
118
|
+
const context = {
|
|
119
|
+
type: 'compilation',
|
|
120
|
+
templateName,
|
|
121
|
+
templateLength: template.length,
|
|
122
|
+
errorLocation: this.findErrorLocation(error, template)
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return this.formatError(error, context);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handle template rendering errors
|
|
130
|
+
* @param {Error} error - Rendering error object
|
|
131
|
+
* @param {string} templateName - Name of the template
|
|
132
|
+
* @param {Object} data - Template data object
|
|
133
|
+
* @returns {Error} Formatted rendering error
|
|
134
|
+
*/
|
|
135
|
+
handleRenderingError(error, templateName, data = {}) {
|
|
136
|
+
const context = {
|
|
137
|
+
type: 'rendering',
|
|
138
|
+
templateName,
|
|
139
|
+
dataKeys: Object.keys(data),
|
|
140
|
+
dataSize: JSON.stringify(data).length
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return this.formatError(error, context);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Handle file system errors
|
|
148
|
+
* @param {Error} error - File system error object
|
|
149
|
+
* @param {string} filePath - Path to the file
|
|
150
|
+
* @param {string} operation - File operation being performed
|
|
151
|
+
* @returns {Error} Formatted file system error
|
|
152
|
+
*/
|
|
153
|
+
handleFileSystemError(error, filePath, operation = 'read') {
|
|
154
|
+
const context = {
|
|
155
|
+
type: 'filesystem',
|
|
156
|
+
filePath,
|
|
157
|
+
operation,
|
|
158
|
+
errorCode: error.code
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return this.formatError(error, context);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Find error location in template content
|
|
166
|
+
* @param {Error} error - Error object
|
|
167
|
+
* @param {string} template - Template content
|
|
168
|
+
* @returns {Object|null} Error location information or null if not found
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
findErrorLocation(error, template) {
|
|
172
|
+
const stack = error.stack || '';
|
|
173
|
+
const lines = template.split('\n');
|
|
174
|
+
|
|
175
|
+
// Try to find line number from error message
|
|
176
|
+
const lineMatch = error.message.match(/line (\d+)/i) || stack.match(/line (\d+)/i);
|
|
177
|
+
if (lineMatch) {
|
|
178
|
+
const lineNumber = parseInt(lineMatch[1]) - 1;
|
|
179
|
+
if (lineNumber >= 0 && lineNumber < lines.length) {
|
|
180
|
+
return {
|
|
181
|
+
line: lineNumber + 1,
|
|
182
|
+
column: 0,
|
|
183
|
+
snippet: lines[lineNumber].substring(0, 100)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Try to find column from error message
|
|
189
|
+
const columnMatch = error.message.match(/column (\d+)/i) || stack.match(/column (\d+)/i);
|
|
190
|
+
if (columnMatch) {
|
|
191
|
+
const column = parseInt(columnMatch[1]);
|
|
192
|
+
return {
|
|
193
|
+
line: 1,
|
|
194
|
+
column,
|
|
195
|
+
snippet: template.substring(column - 10, column + 10)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create user-friendly error message for end users
|
|
204
|
+
* @param {Error} error - Error object
|
|
205
|
+
* @returns {string} User-friendly error message
|
|
206
|
+
*/
|
|
207
|
+
getUserFriendlyMessage(error) {
|
|
208
|
+
const errorType = error.originalError ? error.originalError.name : error.name;
|
|
209
|
+
|
|
210
|
+
switch (errorType) {
|
|
211
|
+
case 'SyntaxError':
|
|
212
|
+
return 'Template syntax error, please check if the template syntax is correct.';
|
|
213
|
+
case 'ReferenceError':
|
|
214
|
+
return 'Template variable reference error, please check if the variable name is correct.';
|
|
215
|
+
case 'TypeError':
|
|
216
|
+
return 'Template type error, please check if the data types match.';
|
|
217
|
+
case 'RangeError':
|
|
218
|
+
return 'Template range error, please check loops or conditional statements.';
|
|
219
|
+
case 'EvalError':
|
|
220
|
+
return 'Template execution error, please check if the expressions are correct.';
|
|
221
|
+
case 'URIError':
|
|
222
|
+
return 'Template URL error, please check URL-related functions.';
|
|
223
|
+
default:
|
|
224
|
+
return 'An error occurred during template processing, please check the template and data.';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get error recovery suggestions based on error type
|
|
230
|
+
* @param {Error} error - Error object
|
|
231
|
+
* @returns {Array<string>} Array of recovery suggestions
|
|
232
|
+
*/
|
|
233
|
+
getRecoverySuggestions(error) {
|
|
234
|
+
const suggestions = [];
|
|
235
|
+
const message = error.message.toLowerCase();
|
|
236
|
+
|
|
237
|
+
if (message.includes('not found') || message.includes('cannot find')) {
|
|
238
|
+
suggestions.push('Check if the template file path is correct');
|
|
239
|
+
suggestions.push('Confirm that the template file exists');
|
|
240
|
+
suggestions.push('Check template file permissions');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (message.includes('syntax') || message.includes('syntax error')) {
|
|
244
|
+
suggestions.push('Check if the template syntax is correct');
|
|
245
|
+
suggestions.push('Confirm all directives are properly closed');
|
|
246
|
+
suggestions.push('Check variable reference format');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (message.includes('variable') || message.includes('undefined')) {
|
|
250
|
+
suggestions.push('Check if the variable name is correct');
|
|
251
|
+
suggestions.push('Confirm the variable is defined in the data');
|
|
252
|
+
suggestions.push('Check variable scope');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (message.includes('cache') || message.includes('caching')) {
|
|
256
|
+
suggestions.push('Try clearing the cache');
|
|
257
|
+
suggestions.push('Check cache configuration');
|
|
258
|
+
suggestions.push('Restart the template engine');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (message.includes('permission') || message.includes('access denied')) {
|
|
262
|
+
suggestions.push('Check file read/write permissions');
|
|
263
|
+
suggestions.push('Confirm the running user has sufficient permissions');
|
|
264
|
+
suggestions.push('Check directory permissions');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Always add general suggestions
|
|
268
|
+
suggestions.push('View detailed error logs');
|
|
269
|
+
suggestions.push('Check template engine configuration');
|
|
270
|
+
suggestions.push('Simplify the template and debug step by step');
|
|
271
|
+
|
|
272
|
+
return suggestions;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default ErrorHandler;
|