rasengan 1.2.1-beta.4 → 1.2.2
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/CHANGELOG.md +13 -0
- package/lib/esm/core/plugins/index.js +10 -13
- package/lib/esm/entries/client/render.js +24 -7
- package/lib/esm/entries/server/entry.server.js +1 -1
- package/lib/esm/entries/server/error-template.js +14 -0
- package/lib/esm/entries/server/index.js +2 -1
- package/lib/esm/routing/components/index.js +12 -69
- package/lib/esm/routing/error-overlay/ErrorBoundaryFallback.js +42 -0
- package/lib/esm/routing/error-overlay/ErrorOverlay.js +60 -0
- package/lib/esm/routing/error-overlay/ErrorOverlayProvider.js +39 -0
- package/lib/esm/routing/error-overlay/error-overlay.css +472 -0
- package/lib/esm/routing/error-overlay/error-store.js +44 -0
- package/lib/esm/routing/error-overlay/index.js +2 -0
- package/lib/esm/routing/error-overlay/stack-utils.js +72 -0
- package/lib/esm/routing/utils/define-router.js +10 -4
- package/lib/esm/routing/utils/flat-routes.js +10 -5
- package/lib/esm/routing/utils/generate-routes.js +3 -2
- package/lib/esm/server/dev/handlers.js +9 -5
- package/lib/esm/server/dev/server.js +8 -1
- package/lib/esm/server/node/index.js +2 -4
- package/lib/esm/server/node/rendering.js +6 -4
- package/lib/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/tsconfig.types.tsbuildinfo +1 -1
- package/lib/types/entries/server/entry.server.d.ts +2 -0
- package/lib/types/entries/server/error-template.d.ts +1 -0
- package/lib/types/routing/error-overlay/ErrorBoundaryFallback.d.ts +14 -0
- package/lib/types/routing/error-overlay/ErrorOverlay.d.ts +2 -0
- package/lib/types/routing/error-overlay/ErrorOverlayProvider.d.ts +7 -0
- package/lib/types/routing/error-overlay/error-store.d.ts +24 -0
- package/lib/types/routing/error-overlay/index.d.ts +3 -0
- package/lib/types/routing/error-overlay/stack-utils.d.ts +31 -0
- package/lib/types/routing/utils/define-router.d.ts +6 -0
- package/lib/types/server/node/rendering.d.ts +7 -1
- package/package.json +13 -12
- package/types/client.d.ts +1 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--re-bg: #0d1117;
|
|
3
|
+
--re-surface: #161b22;
|
|
4
|
+
--re-surface-subtle: #21262d;
|
|
5
|
+
--re-border: #30363d;
|
|
6
|
+
--re-text: #e6edf3;
|
|
7
|
+
--re-text-secondary: #8b949e;
|
|
8
|
+
--re-error: #f85149;
|
|
9
|
+
--re-error-hover: #ff7b72;
|
|
10
|
+
--re-accent: #2f81f7;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.rasengan-error-backdrop {
|
|
14
|
+
position: fixed;
|
|
15
|
+
inset: 0;
|
|
16
|
+
background: rgba(1, 4, 9, 0.8);
|
|
17
|
+
backdrop-filter: blur(4px);
|
|
18
|
+
z-index: 99998;
|
|
19
|
+
animation: re-fade-in 0.2s ease;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.rasengan-error-overlay {
|
|
23
|
+
position: fixed;
|
|
24
|
+
top: 50%;
|
|
25
|
+
left: 50%;
|
|
26
|
+
transform: translate(-50%, -50%);
|
|
27
|
+
z-index: 99999;
|
|
28
|
+
width: 92%;
|
|
29
|
+
max-width: 840px;
|
|
30
|
+
max-height: 82vh;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
background: var(--re-bg);
|
|
34
|
+
border: 1px solid var(--re-border);
|
|
35
|
+
border-radius: 12px;
|
|
36
|
+
color: var(--re-text);
|
|
37
|
+
font-family:
|
|
38
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,
|
|
39
|
+
Arial, sans-serif;
|
|
40
|
+
font-size: 14px;
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
animation: re-overlay-in 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@keyframes re-fade-in {
|
|
48
|
+
from {
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
to {
|
|
52
|
+
opacity: 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@keyframes re-overlay-in {
|
|
57
|
+
from {
|
|
58
|
+
opacity: 0;
|
|
59
|
+
transform: translate(-50%, -50%) scale(0.96);
|
|
60
|
+
}
|
|
61
|
+
to {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
transform: translate(-50%, -50%) scale(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes re-glow-pulse {
|
|
68
|
+
0%,
|
|
69
|
+
100% {
|
|
70
|
+
box-shadow: 0 0 0 0 rgba(248, 81, 73, 0.3);
|
|
71
|
+
}
|
|
72
|
+
50% {
|
|
73
|
+
box-shadow: 0 0 12px 2px rgba(248, 81, 73, 0.15);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@keyframes re-dot-pulse {
|
|
78
|
+
0%,
|
|
79
|
+
100% {
|
|
80
|
+
opacity: 1;
|
|
81
|
+
box-shadow: 0 0 0 0 rgba(63, 185, 80, 0.5);
|
|
82
|
+
}
|
|
83
|
+
50% {
|
|
84
|
+
opacity: 0.8;
|
|
85
|
+
box-shadow: 0 0 6px 2px rgba(63, 185, 80, 0.3);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.rasengan-error-overlay-header {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
padding: 12px 16px;
|
|
94
|
+
background: var(--re-surface);
|
|
95
|
+
border-bottom: 1px solid var(--re-border);
|
|
96
|
+
border-left: 3px solid var(--re-error);
|
|
97
|
+
flex-shrink: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.rasengan-error-pagination {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: 6px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.rasengan-error-version-label {
|
|
107
|
+
display: inline-flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: 6px;
|
|
110
|
+
font-size: 11px;
|
|
111
|
+
color: var(--re-text-secondary);
|
|
112
|
+
font-weight: 500;
|
|
113
|
+
user-select: none;
|
|
114
|
+
letter-spacing: 0.02em;
|
|
115
|
+
flex-shrink: 0;
|
|
116
|
+
padding: 0 8px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.rasengan-error-version-dot {
|
|
120
|
+
width: 7px;
|
|
121
|
+
height: 7px;
|
|
122
|
+
border-radius: 50%;
|
|
123
|
+
background: #3fb950;
|
|
124
|
+
animation: re-dot-pulse 2s ease-in-out infinite;
|
|
125
|
+
flex-shrink: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.rasengan-error-pagination-btn {
|
|
129
|
+
background: transparent;
|
|
130
|
+
border: 1px solid var(--re-border);
|
|
131
|
+
color: var(--re-text-secondary);
|
|
132
|
+
width: 28px;
|
|
133
|
+
height: 28px;
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
139
|
+
font-size: 16px;
|
|
140
|
+
font-family: inherit;
|
|
141
|
+
transition:
|
|
142
|
+
background 0.15s,
|
|
143
|
+
color 0.15s,
|
|
144
|
+
border-color 0.15s;
|
|
145
|
+
line-height: 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.rasengan-error-pagination-btn:hover:not(:disabled) {
|
|
149
|
+
background: var(--re-surface-subtle);
|
|
150
|
+
color: var(--re-text);
|
|
151
|
+
border-color: var(--re-text-secondary);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.rasengan-error-pagination-btn:disabled {
|
|
155
|
+
opacity: 0.25;
|
|
156
|
+
cursor: not-allowed;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.rasengan-error-pagination-text {
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
color: var(--re-text-secondary);
|
|
162
|
+
min-width: 64px;
|
|
163
|
+
text-align: center;
|
|
164
|
+
user-select: none;
|
|
165
|
+
font-variant-numeric: tabular-nums;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.rasengan-error-overlay-actions {
|
|
169
|
+
display: flex;
|
|
170
|
+
gap: 4px;
|
|
171
|
+
flex-shrink: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.rasengan-error-overlay-close {
|
|
175
|
+
background: transparent;
|
|
176
|
+
border: 1px solid var(--re-border);
|
|
177
|
+
color: var(--re-text-secondary);
|
|
178
|
+
width: 28px;
|
|
179
|
+
height: 28px;
|
|
180
|
+
border-radius: 6px;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
font-size: 15px;
|
|
183
|
+
display: flex;
|
|
184
|
+
align-items: center;
|
|
185
|
+
justify-content: center;
|
|
186
|
+
transition:
|
|
187
|
+
background 0.15s,
|
|
188
|
+
color 0.15s,
|
|
189
|
+
border-color 0.15s;
|
|
190
|
+
line-height: 1;
|
|
191
|
+
font-family: inherit;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.rasengan-error-overlay-close:hover {
|
|
195
|
+
background: var(--re-surface-subtle);
|
|
196
|
+
color: var(--re-text);
|
|
197
|
+
border-color: var(--re-text-secondary);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.rasengan-error-overlay-body {
|
|
201
|
+
flex: 1;
|
|
202
|
+
overflow: auto;
|
|
203
|
+
padding: 24px 24px 20px;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.rasengan-error-overlay-source {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: row;
|
|
209
|
+
align-items: center;
|
|
210
|
+
gap: 8px;
|
|
211
|
+
font-size: 11px;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
text-transform: uppercase;
|
|
214
|
+
letter-spacing: 0.08em;
|
|
215
|
+
color: var(--re-text-secondary);
|
|
216
|
+
margin-bottom: 4px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.rasengan-error-overlay-message {
|
|
220
|
+
font-size: 15px;
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
color: var(--re-error);
|
|
223
|
+
margin-bottom: 20px;
|
|
224
|
+
line-height: 1.6;
|
|
225
|
+
word-break: break-word;
|
|
226
|
+
/* animation: re-glow-pulse 2.5s ease-in-out infinite; */
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.rasengan-error-code-preview {
|
|
230
|
+
margin-bottom: 20px;
|
|
231
|
+
border: 1px solid var(--re-border);
|
|
232
|
+
border-radius: 8px;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
background: var(--re-surface);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.rasengan-error-code-preview-header {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 6px;
|
|
241
|
+
padding: 7px 12px;
|
|
242
|
+
background: var(--re-surface-subtle);
|
|
243
|
+
border-bottom: 1px solid var(--re-border);
|
|
244
|
+
font-size: 12px;
|
|
245
|
+
color: var(--re-text-secondary);
|
|
246
|
+
overflow: hidden;
|
|
247
|
+
text-overflow: ellipsis;
|
|
248
|
+
white-space: nowrap;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.rasengan-error-code-preview-file {
|
|
252
|
+
font-weight: 500;
|
|
253
|
+
color: var(--re-text);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.rasengan-error-code-preview-line {
|
|
257
|
+
color: var(--re-text-secondary);
|
|
258
|
+
margin-left: auto;
|
|
259
|
+
font-variant-numeric: tabular-nums;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.rasengan-error-code-preview-content {
|
|
263
|
+
padding: 8px 0;
|
|
264
|
+
overflow-x: auto;
|
|
265
|
+
font-family:
|
|
266
|
+
'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Courier New', monospace;
|
|
267
|
+
font-size: 12px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.rasengan-error-code-line {
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
min-height: 24px;
|
|
274
|
+
padding: 0 12px;
|
|
275
|
+
line-height: 1.6;
|
|
276
|
+
color: var(--re-text);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.rasengan-error-code-line.error {
|
|
280
|
+
background: rgba(248, 81, 73, 0.08);
|
|
281
|
+
border-left: 2px solid var(--re-error);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.rasengan-error-code-line-number {
|
|
285
|
+
min-width: 30px;
|
|
286
|
+
text-align: right;
|
|
287
|
+
padding-right: 12px;
|
|
288
|
+
color: var(--re-text-secondary);
|
|
289
|
+
user-select: none;
|
|
290
|
+
font-size: 11px;
|
|
291
|
+
opacity: 0.6;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.rasengan-error-code-line-arrow {
|
|
295
|
+
color: var(--re-error);
|
|
296
|
+
margin-right: 6px;
|
|
297
|
+
font-weight: 700;
|
|
298
|
+
font-size: 14px;
|
|
299
|
+
line-height: 1;
|
|
300
|
+
width: 12px;
|
|
301
|
+
flex-shrink: 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.rasengan-error-code-line-content {
|
|
305
|
+
white-space: pre;
|
|
306
|
+
tab-size: 2;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.rasengan-error-overlay-section {
|
|
310
|
+
margin-top: 16px;
|
|
311
|
+
border: 1px solid var(--re-border);
|
|
312
|
+
border-radius: 8px;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
background: var(--re-surface);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.rasengan-error-overlay-section-title {
|
|
318
|
+
font-size: 11px;
|
|
319
|
+
font-weight: 600;
|
|
320
|
+
text-transform: uppercase;
|
|
321
|
+
letter-spacing: 0.08em;
|
|
322
|
+
color: var(--re-text-secondary);
|
|
323
|
+
padding: 8px 14px;
|
|
324
|
+
background: var(--re-surface-subtle);
|
|
325
|
+
border-bottom: 1px solid var(--re-border);
|
|
326
|
+
cursor: pointer;
|
|
327
|
+
user-select: none;
|
|
328
|
+
transition: color 0.15s;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* .rasengan-error-overlay-section-title::-webkit-details-marker {
|
|
332
|
+
display: none;
|
|
333
|
+
} */
|
|
334
|
+
|
|
335
|
+
/* .rasengan-error-overlay-section-title::before {
|
|
336
|
+
content: '\203A';
|
|
337
|
+
display: inline-block;
|
|
338
|
+
margin-right: 8px;
|
|
339
|
+
font-size: 14px;
|
|
340
|
+
font-weight: 700;
|
|
341
|
+
color: var(--re-text-secondary);
|
|
342
|
+
transition: transform 0.2s;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.rasengan-error-overlay-section[open] > .rasengan-error-overlay-section-title::before {
|
|
346
|
+
transform: rotate(90deg);
|
|
347
|
+
} */
|
|
348
|
+
|
|
349
|
+
.rasengan-error-overlay-section-title:hover {
|
|
350
|
+
color: var(--re-text);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.rasengan-error-overlay-stack {
|
|
354
|
+
margin: 0;
|
|
355
|
+
padding: 14px 16px;
|
|
356
|
+
font-family:
|
|
357
|
+
'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Courier New', monospace;
|
|
358
|
+
font-size: 12px;
|
|
359
|
+
line-height: 1.7;
|
|
360
|
+
color: var(--re-text-secondary);
|
|
361
|
+
white-space: pre-wrap;
|
|
362
|
+
word-break: break-word;
|
|
363
|
+
overflow-x: auto;
|
|
364
|
+
tab-size: 2;
|
|
365
|
+
background: var(--re-bg);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.rasengan-error-overlay-stack::-webkit-scrollbar {
|
|
369
|
+
width: 6px;
|
|
370
|
+
height: 6px;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.rasengan-error-overlay-stack::-webkit-scrollbar-track {
|
|
374
|
+
background: transparent;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.rasengan-error-overlay-stack::-webkit-scrollbar-thumb {
|
|
378
|
+
background: var(--re-border);
|
|
379
|
+
border-radius: 3px;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.rasengan-error-fab {
|
|
383
|
+
position: fixed;
|
|
384
|
+
bottom: 24px;
|
|
385
|
+
left: 24px;
|
|
386
|
+
z-index: 99999;
|
|
387
|
+
width: 48px;
|
|
388
|
+
height: 48px;
|
|
389
|
+
border-radius: 50%;
|
|
390
|
+
background: var(--re-error);
|
|
391
|
+
border: none;
|
|
392
|
+
color: #fff;
|
|
393
|
+
cursor: pointer;
|
|
394
|
+
display: flex;
|
|
395
|
+
align-items: center;
|
|
396
|
+
justify-content: center;
|
|
397
|
+
box-shadow: 0 4px 16px rgba(248, 81, 73, 0.35);
|
|
398
|
+
transition:
|
|
399
|
+
transform 0.2s,
|
|
400
|
+
box-shadow 0.2s,
|
|
401
|
+
background 0.2s;
|
|
402
|
+
animation: re-fab-in 0.35s cubic-bezier(0.16, 1, 0.3, 1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.rasengan-error-fab:hover {
|
|
406
|
+
transform: scale(1.08);
|
|
407
|
+
background: var(--re-error-hover);
|
|
408
|
+
box-shadow: 0 6px 24px rgba(248, 81, 73, 0.45);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.rasengan-error-fab:active {
|
|
412
|
+
transform: scale(0.95);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.rasengan-error-fab-icon {
|
|
416
|
+
font-size: 20px;
|
|
417
|
+
font-weight: 700;
|
|
418
|
+
line-height: 1;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.rasengan-error-fab-count {
|
|
422
|
+
position: absolute;
|
|
423
|
+
top: -4px;
|
|
424
|
+
right: -4px;
|
|
425
|
+
background: var(--re-text);
|
|
426
|
+
color: var(--re-bg);
|
|
427
|
+
font-size: 10px;
|
|
428
|
+
font-weight: 700;
|
|
429
|
+
min-width: 18px;
|
|
430
|
+
height: 18px;
|
|
431
|
+
border-radius: 9px;
|
|
432
|
+
display: flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
justify-content: center;
|
|
435
|
+
padding: 0 4px;
|
|
436
|
+
font-family:
|
|
437
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,
|
|
438
|
+
Arial, sans-serif;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.rasengan-route-error {
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 8px;
|
|
445
|
+
padding: 10px 16px;
|
|
446
|
+
background: var(--re-surface);
|
|
447
|
+
border-bottom: 1px solid var(--re-border);
|
|
448
|
+
border-left: 3px solid var(--re-error);
|
|
449
|
+
color: var(--re-error);
|
|
450
|
+
font-family:
|
|
451
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica,
|
|
452
|
+
Arial, sans-serif;
|
|
453
|
+
font-size: 13px;
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.rasengan-route-error-detail {
|
|
458
|
+
color: var(--re-text-secondary);
|
|
459
|
+
font-size: 12px;
|
|
460
|
+
font-weight: 400;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
@keyframes re-fab-in {
|
|
464
|
+
from {
|
|
465
|
+
transform: scale(0);
|
|
466
|
+
opacity: 0;
|
|
467
|
+
}
|
|
468
|
+
to {
|
|
469
|
+
transform: scale(1);
|
|
470
|
+
opacity: 1;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
class ErrorStore {
|
|
2
|
+
errors = [];
|
|
3
|
+
listeners = new Set();
|
|
4
|
+
nextId = 1;
|
|
5
|
+
minimized = false;
|
|
6
|
+
addError(error, source = 'global', componentStack) {
|
|
7
|
+
this.errors = [
|
|
8
|
+
...this.errors,
|
|
9
|
+
{
|
|
10
|
+
id: this.nextId++,
|
|
11
|
+
error,
|
|
12
|
+
source,
|
|
13
|
+
timestamp: Date.now(),
|
|
14
|
+
componentStack,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
this.notify();
|
|
18
|
+
}
|
|
19
|
+
clearAll() {
|
|
20
|
+
this.errors = [];
|
|
21
|
+
this.minimized = false;
|
|
22
|
+
this.notify();
|
|
23
|
+
}
|
|
24
|
+
toggleMinimize() {
|
|
25
|
+
this.minimized = !this.minimized;
|
|
26
|
+
this.notify();
|
|
27
|
+
}
|
|
28
|
+
getErrors() {
|
|
29
|
+
return this.errors;
|
|
30
|
+
}
|
|
31
|
+
isMinimized() {
|
|
32
|
+
return this.minimized;
|
|
33
|
+
}
|
|
34
|
+
subscribe(listener) {
|
|
35
|
+
this.listeners.add(listener);
|
|
36
|
+
return () => {
|
|
37
|
+
this.listeners.delete(listener);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
notify() {
|
|
41
|
+
this.listeners.forEach((fn) => fn());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const errorStore = new ErrorStore();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse an error stack trace and extract the first meaningful user frame.
|
|
3
|
+
* Handles Chrome/V8 and Firefox stack trace formats.
|
|
4
|
+
* Skips the "Error:" header line.
|
|
5
|
+
*
|
|
6
|
+
* @param stack - The full error.stack string
|
|
7
|
+
* @returns The first parsed StackFrame, or null if none found
|
|
8
|
+
*/
|
|
9
|
+
export function parseStackFrame(stack) {
|
|
10
|
+
const lines = stack.split('\n');
|
|
11
|
+
for (const rawLine of lines) {
|
|
12
|
+
const line = rawLine.trim();
|
|
13
|
+
if (!line || line.startsWith('Error:'))
|
|
14
|
+
continue;
|
|
15
|
+
// Match: /path/file.ext:line:col (absolute path, any extension)
|
|
16
|
+
const match = line.match(/(\/[^\s:]+\.(?:tsx?|jsx?|m?js|mdx?|css|json|html)):(\d+):(\d+)/);
|
|
17
|
+
if (match) {
|
|
18
|
+
const file = match[1].split('?')[0].split('#')[0];
|
|
19
|
+
return { file, line: parseInt(match[2]), column: parseInt(match[3]) };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Vite plugin transforms may wrap a file's original source in
|
|
26
|
+
* `export default "..."` with escape sequences. Detect and unwrap.
|
|
27
|
+
*/
|
|
28
|
+
function unwrapExportDefaultString(source) {
|
|
29
|
+
const match = source.match(/^export default\s+"((?:[^"\\]|\\.)*)"/s);
|
|
30
|
+
if (match) {
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse('"' + match[1] + '"');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return source;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return source;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetch the source file from the Vite dev server (using ?raw to get original
|
|
42
|
+
* source) and return a snippet of lines surrounding the error line.
|
|
43
|
+
*
|
|
44
|
+
* @param file - File path (absolute, e.g. /src/app/page.tsx)
|
|
45
|
+
* @param errorLine - 1-indexed line number where the error occurred
|
|
46
|
+
* @param contextLines - Number of lines to show above and below the error
|
|
47
|
+
* @returns An object with the snippet string, the 0-indexed error line
|
|
48
|
+
* position within the snippet, and the total file line count,
|
|
49
|
+
* or null on failure
|
|
50
|
+
*/
|
|
51
|
+
export async function fetchSourceSnippet(file, errorLine, contextLines = 2) {
|
|
52
|
+
try {
|
|
53
|
+
const url = file + '?raw';
|
|
54
|
+
const response = await fetch(url);
|
|
55
|
+
if (!response.ok)
|
|
56
|
+
return null;
|
|
57
|
+
let source = await response.text();
|
|
58
|
+
// Some Vite transforms wrap the source in `export default "..."`,
|
|
59
|
+
// escaping newlines and quotes. Detect and unescape it.
|
|
60
|
+
source = unwrapExportDefaultString(source);
|
|
61
|
+
const lines = source.split('\n');
|
|
62
|
+
const totalLines = lines.length;
|
|
63
|
+
const start = Math.max(0, errorLine - contextLines - 1);
|
|
64
|
+
const end = Math.min(lines.length, errorLine + contextLines);
|
|
65
|
+
const snippet = lines.slice(start, end).join('\n');
|
|
66
|
+
const errorLineIndex = errorLine - 1 - start;
|
|
67
|
+
return { snippet, errorLineIndex, totalLines };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -16,12 +16,12 @@ export const defineRouter = (option) => {
|
|
|
16
16
|
// Check if p is an array
|
|
17
17
|
if (Array.isArray(p)) {
|
|
18
18
|
for (let page of p) {
|
|
19
|
-
if (
|
|
19
|
+
if (page.source) {
|
|
20
20
|
pageComponentList.push(page);
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
23
23
|
// When p is a MDXPageComponent
|
|
24
|
-
// type property holds the "MDXPageComponent" value, coming from @rasenganjs/mdx plugin
|
|
24
|
+
// the "type" property holds the "MDXPageComponent" value, coming from @rasenganjs/mdx plugin
|
|
25
25
|
if (isMDXPage(page)) {
|
|
26
26
|
const Page = await convertMDXPageToPageComponent(page);
|
|
27
27
|
pageComponentList.push(Page);
|
|
@@ -32,7 +32,7 @@ export const defineRouter = (option) => {
|
|
|
32
32
|
}
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
if (
|
|
35
|
+
if (p.source) {
|
|
36
36
|
pageComponentList.push(p);
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
@@ -57,9 +57,15 @@ export const defineRouter = (option) => {
|
|
|
57
57
|
return router;
|
|
58
58
|
};
|
|
59
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* This function helps to convert the data provided by @rasenganjs/mdx into a PageComponent component
|
|
62
|
+
* The MDXPage arg has to follow exactly the type returned by the @rasenganjs/mdx plugin
|
|
63
|
+
* @param MDXPage
|
|
64
|
+
* @returns
|
|
65
|
+
*/
|
|
60
66
|
export const convertMDXPageToPageComponent = async (MDXPage) => {
|
|
61
67
|
const Page = () => {
|
|
62
|
-
return (_jsx(MDXPage.Renderer, { config: MDXPage.config, toc: MDXPage.toc, children: MDXPage.Content }));
|
|
68
|
+
return (_jsx(MDXPage.Renderer, { config: MDXPage.config, toc: MDXPage.toc, raw: MDXPage.raw, children: MDXPage.Content }));
|
|
63
69
|
};
|
|
64
70
|
Page.path = MDXPage.metadata.path;
|
|
65
71
|
Page.metadata = MDXPage.metadata.metadata;
|
|
@@ -179,7 +179,7 @@ async function generateRouter(tree) {
|
|
|
179
179
|
let layout;
|
|
180
180
|
// Get layout if defined
|
|
181
181
|
if (root.isLayout) {
|
|
182
|
-
if (
|
|
182
|
+
if (root.source) {
|
|
183
183
|
layout = root;
|
|
184
184
|
}
|
|
185
185
|
else {
|
|
@@ -216,7 +216,7 @@ async function generateRoutes(tree) {
|
|
|
216
216
|
// Handle layout
|
|
217
217
|
if (node.isLayout) {
|
|
218
218
|
let layout;
|
|
219
|
-
if (
|
|
219
|
+
if (node.source) {
|
|
220
220
|
layout = node;
|
|
221
221
|
}
|
|
222
222
|
else {
|
|
@@ -257,7 +257,7 @@ async function generateRoutes(tree) {
|
|
|
257
257
|
}
|
|
258
258
|
catch (error) {
|
|
259
259
|
console.error(error);
|
|
260
|
-
|
|
260
|
+
throw error;
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
/**
|
|
@@ -284,8 +284,14 @@ export async function flatRoutes(fn) {
|
|
|
284
284
|
const layoutModulesMap = new Map([...modulesMap.entries()].filter(([filePath]) => filePath.includes('layout.')));
|
|
285
285
|
// Filter out pages
|
|
286
286
|
const pageModulesMap = new Map([...modulesMap.entries()].filter(([filePath]) => filePath.includes('.page.')));
|
|
287
|
+
const isRootLayoutExists = Object.keys(layoutModulesMap).find((path) => [
|
|
288
|
+
`${basePath}layout.ts`,
|
|
289
|
+
`${basePath}layout.tsx`,
|
|
290
|
+
`${basePath}layout.jsx`,
|
|
291
|
+
`${basePath}layout.js`,
|
|
292
|
+
].includes(path));
|
|
287
293
|
// Handle the case where modules are empty
|
|
288
|
-
if (
|
|
294
|
+
if (!isRootLayoutExists) {
|
|
289
295
|
insertNodeToTree(tree, ['_'], {
|
|
290
296
|
component: DefaultLayout,
|
|
291
297
|
metadata: {},
|
|
@@ -315,6 +321,5 @@ export async function flatRoutes(fn) {
|
|
|
315
321
|
catch (error) {
|
|
316
322
|
console.error(error);
|
|
317
323
|
throw error;
|
|
318
|
-
// TODO: Handle error
|
|
319
324
|
}
|
|
320
325
|
}
|
|
@@ -183,7 +183,7 @@ export const generateRoutes = (router, isRoot = true, parentLayoutPath = undefin
|
|
|
183
183
|
let layoutPath;
|
|
184
184
|
try {
|
|
185
185
|
// Check if the layout is coming from the file-based routing system
|
|
186
|
-
if (
|
|
186
|
+
if (router.layout.source) {
|
|
187
187
|
const layoutNode = router.layout;
|
|
188
188
|
layoutPath = layoutNode.path;
|
|
189
189
|
route = {
|
|
@@ -322,7 +322,7 @@ export const generateRoutes = (router, isRoot = true, parentLayoutPath = undefin
|
|
|
322
322
|
// Get informations about pages
|
|
323
323
|
const pages = router.pages.map((p) => {
|
|
324
324
|
// Check if the page is coming from file-based routing system
|
|
325
|
-
if (
|
|
325
|
+
if (p.source) {
|
|
326
326
|
const pageNode = p;
|
|
327
327
|
// /home => home
|
|
328
328
|
// / => /
|
|
@@ -474,6 +474,7 @@ export const generateRoutes = (router, isRoot = true, parentLayoutPath = undefin
|
|
|
474
474
|
}
|
|
475
475
|
catch (error) {
|
|
476
476
|
console.error(error);
|
|
477
|
+
throw error;
|
|
477
478
|
}
|
|
478
479
|
finally {
|
|
479
480
|
// Return the formated router
|