@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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/dist/component-registry.d.ts +64 -0
  4. package/dist/component-registry.d.ts.map +1 -0
  5. package/dist/component.d.ts +336 -0
  6. package/dist/component.d.ts.map +1 -0
  7. package/dist/debug-entry.d.ts +36 -0
  8. package/dist/debug-entry.d.ts.map +1 -0
  9. package/dist/debug-overlay.d.ts +61 -0
  10. package/dist/debug-overlay.d.ts.map +1 -0
  11. package/dist/debug.d.ts +19 -0
  12. package/dist/debug.d.ts.map +1 -0
  13. package/dist/index.cjs +4384 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.d.ts +91 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +4347 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/instruction-processor.d.ts +31 -0
  20. package/dist/instruction-processor.d.ts.map +1 -0
  21. package/dist/jqhtml-core.esm.js +4352 -0
  22. package/dist/jqhtml-core.esm.js.map +1 -0
  23. package/dist/jqhtml-debug.esm.js +575 -0
  24. package/dist/jqhtml-debug.esm.js.map +1 -0
  25. package/dist/jquery-plugin.d.ts +30 -0
  26. package/dist/jquery-plugin.d.ts.map +1 -0
  27. package/dist/lifecycle-manager.d.ts +34 -0
  28. package/dist/lifecycle-manager.d.ts.map +1 -0
  29. package/dist/load-coordinator.d.ts +79 -0
  30. package/dist/load-coordinator.d.ts.map +1 -0
  31. package/dist/local-storage.d.ts +147 -0
  32. package/dist/local-storage.d.ts.map +1 -0
  33. package/dist/template-renderer.d.ts +17 -0
  34. package/dist/template-renderer.d.ts.map +1 -0
  35. package/laravel-bridge/README.md +242 -0
  36. package/laravel-bridge/autoload.php +51 -0
  37. package/laravel-bridge/composer.json +34 -0
  38. package/laravel-bridge/config/jqhtml.php +82 -0
  39. package/laravel-bridge/examples/node-integration.js +201 -0
  40. package/laravel-bridge/src/JqhtmlErrorFormatter.php +187 -0
  41. package/laravel-bridge/src/JqhtmlException.php +173 -0
  42. package/laravel-bridge/src/JqhtmlExceptionRenderer.php +93 -0
  43. package/laravel-bridge/src/JqhtmlServiceProvider.php +72 -0
  44. package/laravel-bridge/src/Middleware/JqhtmlErrorMiddleware.php +90 -0
  45. package/laravel-bridge/tests/ExceptionFormattingTest.php +219 -0
  46. 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
+ }