@jqhtml/core 2.2.222
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 +24 -0
- package/dist/component-registry.d.ts +64 -0
- package/dist/component-registry.d.ts.map +1 -0
- package/dist/component.d.ts +336 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/debug-entry.d.ts +36 -0
- package/dist/debug-entry.d.ts.map +1 -0
- package/dist/debug-overlay.d.ts +61 -0
- package/dist/debug-overlay.d.ts.map +1 -0
- package/dist/debug.d.ts +19 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/index.cjs +4384 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4347 -0
- package/dist/index.js.map +1 -0
- package/dist/instruction-processor.d.ts +31 -0
- package/dist/instruction-processor.d.ts.map +1 -0
- package/dist/jqhtml-core.esm.js +4352 -0
- package/dist/jqhtml-core.esm.js.map +1 -0
- package/dist/jqhtml-debug.esm.js +575 -0
- package/dist/jqhtml-debug.esm.js.map +1 -0
- package/dist/jquery-plugin.d.ts +30 -0
- package/dist/jquery-plugin.d.ts.map +1 -0
- package/dist/lifecycle-manager.d.ts +34 -0
- package/dist/lifecycle-manager.d.ts.map +1 -0
- package/dist/load-coordinator.d.ts +79 -0
- package/dist/load-coordinator.d.ts.map +1 -0
- package/dist/local-storage.d.ts +147 -0
- package/dist/local-storage.d.ts.map +1 -0
- package/dist/template-renderer.d.ts +17 -0
- package/dist/template-renderer.d.ts.map +1 -0
- package/laravel-bridge/README.md +242 -0
- package/laravel-bridge/autoload.php +51 -0
- package/laravel-bridge/composer.json +34 -0
- package/laravel-bridge/config/jqhtml.php +82 -0
- package/laravel-bridge/examples/node-integration.js +201 -0
- package/laravel-bridge/src/JqhtmlErrorFormatter.php +187 -0
- package/laravel-bridge/src/JqhtmlException.php +173 -0
- package/laravel-bridge/src/JqhtmlExceptionRenderer.php +93 -0
- package/laravel-bridge/src/JqhtmlServiceProvider.php +72 -0
- package/laravel-bridge/src/Middleware/JqhtmlErrorMiddleware.php +90 -0
- package/laravel-bridge/tests/ExceptionFormattingTest.php +219 -0
- package/package.json +74 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example Node.js integration for sending JQHTML errors to Laravel
|
|
5
|
+
*
|
|
6
|
+
* This shows how to compile JQHTML templates and send any errors
|
|
7
|
+
* to a Laravel backend for proper error handling and display.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Lexer, Parser, CodeGenerator, JQHTMLParseError } from '@jqhtml/parser';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import fetch from 'node-fetch'; // or axios, etc.
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compile a JQHTML template and handle errors
|
|
16
|
+
*/
|
|
17
|
+
async function compileTemplate(templatePath, laravelEndpoint) {
|
|
18
|
+
try {
|
|
19
|
+
// Read template file
|
|
20
|
+
const source = fs.readFileSync(templatePath, 'utf8');
|
|
21
|
+
const filename = templatePath;
|
|
22
|
+
|
|
23
|
+
// Compile with source maps
|
|
24
|
+
const lexer = new Lexer(source);
|
|
25
|
+
const tokens = lexer.tokenize();
|
|
26
|
+
const parser = new Parser(tokens, source, filename);
|
|
27
|
+
const ast = parser.parse();
|
|
28
|
+
|
|
29
|
+
const generator = new CodeGenerator();
|
|
30
|
+
const result = generator.generateWithSourceMap(ast, filename, source);
|
|
31
|
+
|
|
32
|
+
// Success - return compiled code
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
code: result.code,
|
|
36
|
+
sourceMap: result.map
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Format error for Laravel
|
|
41
|
+
const errorData = formatErrorForLaravel(error, templatePath);
|
|
42
|
+
|
|
43
|
+
// Send to Laravel if endpoint provided
|
|
44
|
+
if (laravelEndpoint) {
|
|
45
|
+
await sendErrorToLaravel(errorData, laravelEndpoint);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Return error response
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: errorData
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Format a JavaScript error for Laravel consumption
|
|
58
|
+
*/
|
|
59
|
+
function formatErrorForLaravel(error, templatePath) {
|
|
60
|
+
// Check if it's a JQHTML parse error with full details
|
|
61
|
+
if (error instanceof JQHTMLParseError || error.name === 'JQHTMLParseError') {
|
|
62
|
+
return {
|
|
63
|
+
message: error.message,
|
|
64
|
+
filename: error.filename || templatePath,
|
|
65
|
+
line: error.line,
|
|
66
|
+
column: error.column,
|
|
67
|
+
source: error.source,
|
|
68
|
+
suggestion: error.suggestion,
|
|
69
|
+
severity: error.severity || 'error',
|
|
70
|
+
endLine: error.endLine,
|
|
71
|
+
endColumn: error.endColumn
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Generic error - try to extract what we can
|
|
76
|
+
const errorData = {
|
|
77
|
+
message: error.message || String(error),
|
|
78
|
+
filename: templatePath,
|
|
79
|
+
severity: 'error'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Try to parse location from error message
|
|
83
|
+
const locationMatch = error.message.match(/at line (\d+), column (\d+)/);
|
|
84
|
+
if (locationMatch) {
|
|
85
|
+
errorData.line = parseInt(locationMatch[1]);
|
|
86
|
+
errorData.column = parseInt(locationMatch[2]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return errorData;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Send error data to Laravel backend
|
|
94
|
+
*/
|
|
95
|
+
async function sendErrorToLaravel(errorData, endpoint) {
|
|
96
|
+
try {
|
|
97
|
+
const response = await fetch(endpoint, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
'Accept': 'application/json'
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
error: errorData,
|
|
105
|
+
context: {
|
|
106
|
+
node_version: process.version,
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
environment: process.env.NODE_ENV || 'development'
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
console.error('Failed to send error to Laravel:', response.statusText);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return response.json();
|
|
118
|
+
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.error('Error communicating with Laravel:', err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Express/HTTP endpoint for template compilation
|
|
126
|
+
*
|
|
127
|
+
* This could be used in a Node.js service that compiles templates
|
|
128
|
+
* for a Laravel application.
|
|
129
|
+
*/
|
|
130
|
+
export function createCompilationEndpoint(app, laravelErrorEndpoint) {
|
|
131
|
+
app.post('/compile-jqhtml', async (req, res) => {
|
|
132
|
+
const { template, filename, source } = req.body;
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// Compile template
|
|
136
|
+
const lexer = new Lexer(source || template);
|
|
137
|
+
const tokens = lexer.tokenize();
|
|
138
|
+
const parser = new Parser(tokens, source || template, filename);
|
|
139
|
+
const ast = parser.parse();
|
|
140
|
+
|
|
141
|
+
const generator = new CodeGenerator();
|
|
142
|
+
const result = generator.generateWithSourceMap(
|
|
143
|
+
ast,
|
|
144
|
+
filename || 'template.jqhtml',
|
|
145
|
+
source || template
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Success response
|
|
149
|
+
res.json({
|
|
150
|
+
success: true,
|
|
151
|
+
compiled: {
|
|
152
|
+
code: result.code,
|
|
153
|
+
map: result.map
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
// Format error for Laravel
|
|
159
|
+
const errorData = formatErrorForLaravel(error, filename);
|
|
160
|
+
|
|
161
|
+
// Send error to Laravel for logging/display
|
|
162
|
+
if (laravelErrorEndpoint) {
|
|
163
|
+
sendErrorToLaravel(errorData, laravelErrorEndpoint);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Return error response
|
|
167
|
+
res.status(400).json({
|
|
168
|
+
success: false,
|
|
169
|
+
error: errorData
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* CLI usage example
|
|
177
|
+
*/
|
|
178
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
179
|
+
const templatePath = process.argv[2];
|
|
180
|
+
const laravelEndpoint = process.argv[3] || process.env.LARAVEL_ERROR_ENDPOINT;
|
|
181
|
+
|
|
182
|
+
if (!templatePath) {
|
|
183
|
+
console.error('Usage: node-integration.js <template-file> [laravel-endpoint]');
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
compileTemplate(templatePath, laravelEndpoint).then(result => {
|
|
188
|
+
if (result.success) {
|
|
189
|
+
console.log('Compilation successful!');
|
|
190
|
+
console.log('Code length:', result.code.length);
|
|
191
|
+
console.log('Source map:', result.sourceMap ? 'Generated' : 'Not generated');
|
|
192
|
+
} else {
|
|
193
|
+
console.error('Compilation failed!');
|
|
194
|
+
console.error(result.error);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Export for use as module
|
|
201
|
+
export { compileTemplate, formatErrorForLaravel, sendErrorToLaravel };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Jqhtml\LaravelBridge;
|
|
4
|
+
|
|
5
|
+
use Throwable;
|
|
6
|
+
|
|
7
|
+
class JqhtmlErrorFormatter
|
|
8
|
+
{
|
|
9
|
+
protected $sourceMapPath;
|
|
10
|
+
protected $showSourceContext;
|
|
11
|
+
protected $sourceMapCache = [];
|
|
12
|
+
|
|
13
|
+
public function __construct(?string $sourceMapPath = null, bool $showSourceContext = true)
|
|
14
|
+
{
|
|
15
|
+
$this->sourceMapPath = $sourceMapPath;
|
|
16
|
+
$this->showSourceContext = $showSourceContext;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Format a JQHTML exception for Laravel's error handler
|
|
21
|
+
*/
|
|
22
|
+
public function format(JqhtmlException $exception): array
|
|
23
|
+
{
|
|
24
|
+
$data = [
|
|
25
|
+
'message' => $exception->getMessage(),
|
|
26
|
+
'exception' => get_class($exception),
|
|
27
|
+
'file' => $exception->getTemplateFile() ?? $exception->getFile(),
|
|
28
|
+
'line' => $exception->getTemplateLine() ?? $exception->getLine(),
|
|
29
|
+
'column' => $exception->getTemplateColumn(),
|
|
30
|
+
'error_type' => $exception->getErrorType(),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
if ($exception->getSuggestion()) {
|
|
34
|
+
$data['suggestion'] = $exception->getSuggestion();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if ($this->showSourceContext && $exception->getSourceCode()) {
|
|
38
|
+
$data['source_context'] = $this->getSourceContext($exception);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Try to resolve source map if we have a compiled file location
|
|
42
|
+
if ($exception->getCompiledFile() && $this->sourceMapPath) {
|
|
43
|
+
$data['source_map'] = $this->resolveSourceMap($exception);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return $data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Format for JSON responses
|
|
51
|
+
*/
|
|
52
|
+
public function formatForJson(JqhtmlException $exception): array
|
|
53
|
+
{
|
|
54
|
+
return [
|
|
55
|
+
'error' => true,
|
|
56
|
+
'type' => 'jqhtml_error',
|
|
57
|
+
'message' => $exception->getMessage(),
|
|
58
|
+
'details' => $this->format($exception),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get source code context for display
|
|
64
|
+
*/
|
|
65
|
+
protected function getSourceContext(JqhtmlException $exception): array
|
|
66
|
+
{
|
|
67
|
+
$source = $exception->getSourceCode();
|
|
68
|
+
$line = $exception->getTemplateLine();
|
|
69
|
+
$column = $exception->getTemplateColumn();
|
|
70
|
+
|
|
71
|
+
if (!$source || !$line) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$lines = explode("\n", $source);
|
|
76
|
+
$lineIndex = $line - 1;
|
|
77
|
+
|
|
78
|
+
// Get 5 lines before and after for context
|
|
79
|
+
$contextSize = 5;
|
|
80
|
+
$start = max(0, $lineIndex - $contextSize);
|
|
81
|
+
$end = min(count($lines) - 1, $lineIndex + $contextSize);
|
|
82
|
+
|
|
83
|
+
$context = [];
|
|
84
|
+
for ($i = $start; $i <= $end; $i++) {
|
|
85
|
+
$context[] = [
|
|
86
|
+
'line_number' => $i + 1,
|
|
87
|
+
'content' => $lines[$i],
|
|
88
|
+
'is_error_line' => $i === $lineIndex,
|
|
89
|
+
'error_column' => $i === $lineIndex ? $column : null,
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return $context;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Attempt to resolve source map for better error location
|
|
98
|
+
*/
|
|
99
|
+
protected function resolveSourceMap(JqhtmlException $exception): ?array
|
|
100
|
+
{
|
|
101
|
+
$compiledFile = $exception->getCompiledFile();
|
|
102
|
+
if (!$compiledFile || !file_exists($compiledFile)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Look for source map file
|
|
107
|
+
$mapFile = $compiledFile . '.map';
|
|
108
|
+
if (!file_exists($mapFile)) {
|
|
109
|
+
// Try in the configured source map directory
|
|
110
|
+
if ($this->sourceMapPath) {
|
|
111
|
+
$mapFile = $this->sourceMapPath . '/' . basename($compiledFile) . '.map';
|
|
112
|
+
if (!file_exists($mapFile)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Load and parse source map
|
|
121
|
+
if (!isset($this->sourceMapCache[$mapFile])) {
|
|
122
|
+
$mapContent = file_get_contents($mapFile);
|
|
123
|
+
$this->sourceMapCache[$mapFile] = json_decode($mapContent, true);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
$sourceMap = $this->sourceMapCache[$mapFile];
|
|
127
|
+
if (!$sourceMap) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
'version' => $sourceMap['version'] ?? null,
|
|
133
|
+
'sources' => $sourceMap['sources'] ?? [],
|
|
134
|
+
'file' => $sourceMap['file'] ?? null,
|
|
135
|
+
'has_mappings' => !empty($sourceMap['mappings']),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Convert a generic exception to JQHTML exception if it contains JQHTML error data
|
|
141
|
+
*/
|
|
142
|
+
public function wrapException(Throwable $exception): Throwable
|
|
143
|
+
{
|
|
144
|
+
// Check if the exception message contains JQHTML error data
|
|
145
|
+
$message = $exception->getMessage();
|
|
146
|
+
|
|
147
|
+
// Look for JQHTML error patterns
|
|
148
|
+
if (strpos($message, 'JQHTMLParseError') !== false ||
|
|
149
|
+
strpos($message, 'at line') !== false && strpos($message, 'column') !== false) {
|
|
150
|
+
|
|
151
|
+
// Try to extract error details from the message
|
|
152
|
+
$templateFile = null;
|
|
153
|
+
$templateLine = null;
|
|
154
|
+
$templateColumn = null;
|
|
155
|
+
|
|
156
|
+
// Extract location info from "at filename:line:column" format
|
|
157
|
+
if (preg_match('/at\s+([^:]+):(\d+):(\d+)/', $message, $matches)) {
|
|
158
|
+
$templateFile = $matches[1];
|
|
159
|
+
$templateLine = (int)$matches[2];
|
|
160
|
+
$templateColumn = (int)$matches[3];
|
|
161
|
+
} elseif (preg_match('/at\s+line\s+(\d+),\s+column\s+(\d+)/', $message, $matches)) {
|
|
162
|
+
$templateLine = (int)$matches[1];
|
|
163
|
+
$templateColumn = (int)$matches[2];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Extract suggestion if present
|
|
167
|
+
$suggestion = null;
|
|
168
|
+
if (preg_match('/Did you\s+(.+?)\?/', $message, $matches)) {
|
|
169
|
+
$suggestion = 'Did you ' . $matches[1] . '?';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return new JqhtmlException(
|
|
173
|
+
$message,
|
|
174
|
+
$templateFile,
|
|
175
|
+
$templateLine,
|
|
176
|
+
$templateColumn,
|
|
177
|
+
null,
|
|
178
|
+
$suggestion,
|
|
179
|
+
'parse',
|
|
180
|
+
$exception->getCode(),
|
|
181
|
+
$exception
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return $exception;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Jqhtml\LaravelBridge;
|
|
4
|
+
|
|
5
|
+
use Illuminate\View\ViewException;
|
|
6
|
+
use Throwable;
|
|
7
|
+
|
|
8
|
+
class JqhtmlException extends ViewException
|
|
9
|
+
{
|
|
10
|
+
protected $templateFile;
|
|
11
|
+
protected $templateLine;
|
|
12
|
+
protected $templateColumn;
|
|
13
|
+
protected $sourceCode;
|
|
14
|
+
protected $suggestion;
|
|
15
|
+
protected $errorType;
|
|
16
|
+
protected $compiledFile;
|
|
17
|
+
|
|
18
|
+
public function __construct(
|
|
19
|
+
string $message,
|
|
20
|
+
?string $templateFile = null,
|
|
21
|
+
?int $templateLine = null,
|
|
22
|
+
?int $templateColumn = null,
|
|
23
|
+
?string $sourceCode = null,
|
|
24
|
+
?string $suggestion = null,
|
|
25
|
+
string $errorType = 'parse',
|
|
26
|
+
int $code = 0,
|
|
27
|
+
?Throwable $previous = null
|
|
28
|
+
) {
|
|
29
|
+
// Call ViewException constructor with template file and line info
|
|
30
|
+
// ViewException signature: __construct($message, $code = 0, $severity = 1, $filename = '', $lineno = 0, $previous = null)
|
|
31
|
+
parent::__construct(
|
|
32
|
+
$message,
|
|
33
|
+
$code,
|
|
34
|
+
1, // severity
|
|
35
|
+
$templateFile ?? '',
|
|
36
|
+
$templateLine ?? 0,
|
|
37
|
+
$previous
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
$this->templateFile = $templateFile;
|
|
41
|
+
$this->templateLine = $templateLine;
|
|
42
|
+
$this->templateColumn = $templateColumn;
|
|
43
|
+
$this->sourceCode = $sourceCode;
|
|
44
|
+
$this->suggestion = $suggestion;
|
|
45
|
+
$this->errorType = $errorType;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create from a JavaScript error object or JSON string
|
|
50
|
+
*/
|
|
51
|
+
public static function createFromJsError($jsError, ?string $compiledFile = null): self
|
|
52
|
+
{
|
|
53
|
+
if (is_string($jsError)) {
|
|
54
|
+
$jsError = json_decode($jsError, true);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return new self(
|
|
58
|
+
$jsError['message'] ?? 'Unknown JQHTML error',
|
|
59
|
+
$jsError['filename'] ?? $jsError['templateFile'] ?? null,
|
|
60
|
+
$jsError['line'] ?? null,
|
|
61
|
+
$jsError['column'] ?? null,
|
|
62
|
+
$jsError['source'] ?? null,
|
|
63
|
+
$jsError['suggestion'] ?? null,
|
|
64
|
+
$jsError['severity'] ?? 'error'
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public function getTemplateFile(): ?string
|
|
69
|
+
{
|
|
70
|
+
return $this->templateFile;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public function getTemplateLine(): ?int
|
|
74
|
+
{
|
|
75
|
+
return $this->templateLine;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public function getTemplateColumn(): ?int
|
|
79
|
+
{
|
|
80
|
+
return $this->templateColumn;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public function getSourceCode(): ?string
|
|
84
|
+
{
|
|
85
|
+
return $this->sourceCode;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public function getSuggestion(): ?string
|
|
89
|
+
{
|
|
90
|
+
return $this->suggestion;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public function getErrorType(): string
|
|
94
|
+
{
|
|
95
|
+
return $this->errorType;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public function getCompiledFile(): ?string
|
|
99
|
+
{
|
|
100
|
+
return $this->compiledFile;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public function setCompiledFile(string $file): self
|
|
104
|
+
{
|
|
105
|
+
$this->compiledFile = $file;
|
|
106
|
+
return $this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get formatted error message with context
|
|
111
|
+
*/
|
|
112
|
+
public function getFormattedMessage(): string
|
|
113
|
+
{
|
|
114
|
+
$message = $this->getMessage();
|
|
115
|
+
|
|
116
|
+
if ($this->suggestion) {
|
|
117
|
+
$message .= "\n" . $this->suggestion;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if ($this->templateFile) {
|
|
121
|
+
$message .= sprintf(
|
|
122
|
+
"\n at %s:%d:%d",
|
|
123
|
+
$this->templateFile,
|
|
124
|
+
$this->templateLine ?? 0,
|
|
125
|
+
$this->templateColumn ?? 0
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if ($this->sourceCode && $this->templateLine) {
|
|
130
|
+
$message .= "\n\n" . $this->getCodeSnippet();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return $message;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get code snippet with error highlighting
|
|
138
|
+
*/
|
|
139
|
+
protected function getCodeSnippet(): string
|
|
140
|
+
{
|
|
141
|
+
if (!$this->sourceCode || !$this->templateLine) {
|
|
142
|
+
return '';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
$lines = explode("\n", $this->sourceCode);
|
|
146
|
+
$lineIndex = $this->templateLine - 1;
|
|
147
|
+
|
|
148
|
+
// Show 3 lines before and after for context
|
|
149
|
+
$contextLines = 3;
|
|
150
|
+
$startLine = max(0, $lineIndex - $contextLines);
|
|
151
|
+
$endLine = min(count($lines) - 1, $lineIndex + $contextLines);
|
|
152
|
+
|
|
153
|
+
$snippet = '';
|
|
154
|
+
for ($i = $startLine; $i <= $endLine; $i++) {
|
|
155
|
+
$lineNum = $i + 1;
|
|
156
|
+
$isErrorLine = $i === $lineIndex;
|
|
157
|
+
$prefix = $isErrorLine ? '>' : ' ';
|
|
158
|
+
|
|
159
|
+
// Line number with padding
|
|
160
|
+
$lineNumStr = str_pad((string)$lineNum, 5, ' ', STR_PAD_LEFT);
|
|
161
|
+
$snippet .= sprintf("%s %s | %s\n", $prefix, $lineNumStr, $lines[$i]);
|
|
162
|
+
|
|
163
|
+
// Add pointer to error column
|
|
164
|
+
if ($isErrorLine && $this->templateColumn) {
|
|
165
|
+
$spaces = str_repeat(' ', $this->templateColumn + 8);
|
|
166
|
+
$carets = str_repeat('^', min(strlen($lines[$i]) - $this->templateColumn + 1, 20));
|
|
167
|
+
$snippet .= $spaces . $carets . "\n";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return $snippet;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Jqhtml\LaravelBridge;
|
|
4
|
+
|
|
5
|
+
use Illuminate\Contracts\View\View;
|
|
6
|
+
use Illuminate\Http\JsonResponse;
|
|
7
|
+
use Illuminate\Http\Request;
|
|
8
|
+
use Illuminate\Http\Response;
|
|
9
|
+
use Throwable;
|
|
10
|
+
|
|
11
|
+
class JqhtmlExceptionRenderer
|
|
12
|
+
{
|
|
13
|
+
protected $formatter;
|
|
14
|
+
|
|
15
|
+
public function __construct(JqhtmlErrorFormatter $formatter)
|
|
16
|
+
{
|
|
17
|
+
$this->formatter = $formatter;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Render a JQHTML exception for display
|
|
22
|
+
*/
|
|
23
|
+
public function render(Request $request, JqhtmlException $exception): ?Response
|
|
24
|
+
{
|
|
25
|
+
if ($request->expectsJson()) {
|
|
26
|
+
return $this->renderJson($exception);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// For development, enhance the error page with JQHTML-specific info
|
|
30
|
+
if (app()->hasDebugModeEnabled()) {
|
|
31
|
+
return $this->renderDebugView($exception);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// In production, return null to let Laravel handle it normally
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Render JSON error response
|
|
40
|
+
*/
|
|
41
|
+
protected function renderJson(JqhtmlException $exception): JsonResponse
|
|
42
|
+
{
|
|
43
|
+
return response()->json(
|
|
44
|
+
$this->formatter->formatForJson($exception),
|
|
45
|
+
500
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Render debug view with enhanced JQHTML error information
|
|
51
|
+
*/
|
|
52
|
+
protected function renderDebugView(JqhtmlException $exception): ?Response
|
|
53
|
+
{
|
|
54
|
+
// Get formatted error data
|
|
55
|
+
$errorData = $this->formatter->format($exception);
|
|
56
|
+
|
|
57
|
+
// If using Laravel's Ignition error page, enhance it with our data
|
|
58
|
+
if (class_exists(\Spatie\LaravelIgnition\Facades\Flare::class)) {
|
|
59
|
+
\Spatie\LaravelIgnition\Facades\Flare::context('JQHTML Error', $errorData);
|
|
60
|
+
return null; // Let Ignition handle the rendering
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If using older Laravel or custom error handling
|
|
64
|
+
try {
|
|
65
|
+
// Check if we have a custom view
|
|
66
|
+
if (view()->exists('jqhtml::error')) {
|
|
67
|
+
return response()->view('jqhtml::error', [
|
|
68
|
+
'exception' => $exception,
|
|
69
|
+
'error_data' => $errorData,
|
|
70
|
+
], 500);
|
|
71
|
+
}
|
|
72
|
+
} catch (Throwable $e) {
|
|
73
|
+
// Fall back to letting Laravel handle it
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if an exception should be handled by this renderer
|
|
81
|
+
*/
|
|
82
|
+
public function shouldHandle(Throwable $exception): bool
|
|
83
|
+
{
|
|
84
|
+
if ($exception instanceof JqhtmlException) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if it's a wrapped JQHTML error
|
|
89
|
+
$message = $exception->getMessage();
|
|
90
|
+
return strpos($message, 'JQHTMLParseError') !== false ||
|
|
91
|
+
strpos($message, 'JQHTML') !== false;
|
|
92
|
+
}
|
|
93
|
+
}
|