@matthesketh/utopia-router 0.2.0 → 0.3.1
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/dist/index.cjs +52 -35
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +52 -35
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -401,33 +401,41 @@ function createRouterView() {
|
|
|
401
401
|
container.setAttribute("data-utopia-router-view", "");
|
|
402
402
|
let currentCleanup = null;
|
|
403
403
|
let currentMatch = null;
|
|
404
|
+
let loadId = 0;
|
|
404
405
|
(0, import_utopia_core2.effect)(() => {
|
|
405
406
|
const match = currentRoute();
|
|
406
407
|
if (match === currentMatch) {
|
|
407
408
|
return;
|
|
408
409
|
}
|
|
409
410
|
currentMatch = match;
|
|
410
|
-
|
|
411
|
-
currentCleanup();
|
|
412
|
-
currentCleanup = null;
|
|
413
|
-
}
|
|
414
|
-
while (container.firstChild) {
|
|
415
|
-
container.removeChild(container.firstChild);
|
|
416
|
-
}
|
|
411
|
+
const thisLoadId = ++loadId;
|
|
417
412
|
if (!match) {
|
|
413
|
+
if (currentCleanup) {
|
|
414
|
+
currentCleanup();
|
|
415
|
+
currentCleanup = null;
|
|
416
|
+
}
|
|
417
|
+
clearContainer(container);
|
|
418
418
|
const notFound = document.createElement("div");
|
|
419
419
|
notFound.setAttribute("data-utopia-not-found", "");
|
|
420
420
|
notFound.textContent = "Page not found";
|
|
421
421
|
container.appendChild(notFound);
|
|
422
422
|
return;
|
|
423
423
|
}
|
|
424
|
-
loadRouteComponent(match
|
|
425
|
-
|
|
424
|
+
loadRouteComponent(match).then((result) => {
|
|
425
|
+
if (thisLoadId !== loadId) return;
|
|
426
|
+
if (!result) return;
|
|
427
|
+
if (currentCleanup) {
|
|
428
|
+
currentCleanup();
|
|
429
|
+
currentCleanup = null;
|
|
430
|
+
}
|
|
431
|
+
clearContainer(container);
|
|
432
|
+
container.appendChild(result.node);
|
|
433
|
+
currentCleanup = result.cleanup;
|
|
426
434
|
});
|
|
427
435
|
});
|
|
428
436
|
return container;
|
|
429
437
|
}
|
|
430
|
-
async function loadRouteComponent(match
|
|
438
|
+
async function loadRouteComponent(match) {
|
|
431
439
|
try {
|
|
432
440
|
const promises = [match.route.component()];
|
|
433
441
|
if (match.route.layout) {
|
|
@@ -441,26 +449,26 @@ async function loadRouteComponent(match, container) {
|
|
|
441
449
|
}
|
|
442
450
|
const PageComponent = pageModule.default ?? pageModule;
|
|
443
451
|
const LayoutComponent = layoutModule ? layoutModule.default ?? layoutModule : null;
|
|
444
|
-
while (container.firstChild) {
|
|
445
|
-
container.removeChild(container.firstChild);
|
|
446
|
-
}
|
|
447
452
|
const pageNode = renderComponent(PageComponent, {
|
|
448
453
|
params: match.params,
|
|
449
454
|
url: match.url
|
|
450
455
|
});
|
|
456
|
+
let node;
|
|
451
457
|
if (LayoutComponent) {
|
|
452
|
-
|
|
458
|
+
node = renderComponent(LayoutComponent, {
|
|
453
459
|
params: match.params,
|
|
454
460
|
url: match.url,
|
|
455
461
|
children: pageNode
|
|
456
462
|
});
|
|
457
|
-
container.appendChild(layoutNode);
|
|
458
463
|
} else {
|
|
459
|
-
|
|
464
|
+
node = pageNode;
|
|
460
465
|
}
|
|
461
|
-
return
|
|
462
|
-
|
|
463
|
-
|
|
466
|
+
return {
|
|
467
|
+
node,
|
|
468
|
+
cleanup: () => {
|
|
469
|
+
if (node.parentNode) {
|
|
470
|
+
node.parentNode.removeChild(node);
|
|
471
|
+
}
|
|
464
472
|
}
|
|
465
473
|
};
|
|
466
474
|
} catch (err) {
|
|
@@ -468,26 +476,33 @@ async function loadRouteComponent(match, container) {
|
|
|
468
476
|
try {
|
|
469
477
|
const errorModule = await match.route.error();
|
|
470
478
|
const ErrorComponent = errorModule.default ?? errorModule;
|
|
471
|
-
while (container.firstChild) {
|
|
472
|
-
container.removeChild(container.firstChild);
|
|
473
|
-
}
|
|
474
479
|
const errorNode = renderComponent(ErrorComponent, {
|
|
475
480
|
error: err,
|
|
476
481
|
params: match.params,
|
|
477
482
|
url: match.url
|
|
478
483
|
});
|
|
479
|
-
|
|
484
|
+
return {
|
|
485
|
+
node: errorNode,
|
|
486
|
+
cleanup: () => {
|
|
487
|
+
if (errorNode.parentNode) {
|
|
488
|
+
errorNode.parentNode.removeChild(errorNode);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
480
492
|
} catch {
|
|
481
|
-
|
|
493
|
+
return {
|
|
494
|
+
node: createFallbackErrorNode(err),
|
|
495
|
+
cleanup: () => {
|
|
496
|
+
}
|
|
497
|
+
};
|
|
482
498
|
}
|
|
483
499
|
} else {
|
|
484
|
-
|
|
500
|
+
return {
|
|
501
|
+
node: createFallbackErrorNode(err),
|
|
502
|
+
cleanup: () => {
|
|
503
|
+
}
|
|
504
|
+
};
|
|
485
505
|
}
|
|
486
|
-
return () => {
|
|
487
|
-
while (container.firstChild) {
|
|
488
|
-
container.removeChild(container.firstChild);
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
506
|
}
|
|
492
507
|
}
|
|
493
508
|
function renderComponent(component, props) {
|
|
@@ -510,10 +525,7 @@ function renderComponent(component, props) {
|
|
|
510
525
|
div.textContent = "[Component render error]";
|
|
511
526
|
return div;
|
|
512
527
|
}
|
|
513
|
-
function
|
|
514
|
-
while (container.firstChild) {
|
|
515
|
-
container.removeChild(container.firstChild);
|
|
516
|
-
}
|
|
528
|
+
function createFallbackErrorNode(error) {
|
|
517
529
|
const errorDiv = document.createElement("div");
|
|
518
530
|
errorDiv.setAttribute("data-utopia-error", "");
|
|
519
531
|
errorDiv.style.cssText = "padding:2rem;color:#dc2626;font-family:monospace;";
|
|
@@ -523,7 +535,7 @@ function renderFallbackError(container, error) {
|
|
|
523
535
|
error instanceof Error ? error.message : String(error)
|
|
524
536
|
)}</pre>
|
|
525
537
|
`;
|
|
526
|
-
|
|
538
|
+
return errorDiv;
|
|
527
539
|
}
|
|
528
540
|
function createLink(props) {
|
|
529
541
|
const anchor = document.createElement("a");
|
|
@@ -550,6 +562,11 @@ function createLink(props) {
|
|
|
550
562
|
}
|
|
551
563
|
return anchor;
|
|
552
564
|
}
|
|
565
|
+
function clearContainer(container) {
|
|
566
|
+
while (container.firstChild) {
|
|
567
|
+
container.removeChild(container.firstChild);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
553
570
|
function escapeHtml(str) {
|
|
554
571
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
555
572
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -171,8 +171,8 @@ declare function destroy(): void;
|
|
|
171
171
|
* Creates a DOM node that renders the current route's component.
|
|
172
172
|
*
|
|
173
173
|
* When the route changes:
|
|
174
|
-
* 1. The
|
|
175
|
-
* 2.
|
|
174
|
+
* 1. The new component is lazily loaded (old content stays visible)
|
|
175
|
+
* 2. Once loaded, the old component is swapped out atomically
|
|
176
176
|
* 3. If the route has a layout, the page is wrapped in the layout
|
|
177
177
|
* 4. If loading fails and an error component exists, it is shown instead
|
|
178
178
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -171,8 +171,8 @@ declare function destroy(): void;
|
|
|
171
171
|
* Creates a DOM node that renders the current route's component.
|
|
172
172
|
*
|
|
173
173
|
* When the route changes:
|
|
174
|
-
* 1. The
|
|
175
|
-
* 2.
|
|
174
|
+
* 1. The new component is lazily loaded (old content stays visible)
|
|
175
|
+
* 2. Once loaded, the old component is swapped out atomically
|
|
176
176
|
* 3. If the route has a layout, the page is wrapped in the layout
|
|
177
177
|
* 4. If loading fails and an error component exists, it is shown instead
|
|
178
178
|
*
|
package/dist/index.js
CHANGED
|
@@ -362,33 +362,41 @@ function createRouterView() {
|
|
|
362
362
|
container.setAttribute("data-utopia-router-view", "");
|
|
363
363
|
let currentCleanup = null;
|
|
364
364
|
let currentMatch = null;
|
|
365
|
+
let loadId = 0;
|
|
365
366
|
effect(() => {
|
|
366
367
|
const match = currentRoute();
|
|
367
368
|
if (match === currentMatch) {
|
|
368
369
|
return;
|
|
369
370
|
}
|
|
370
371
|
currentMatch = match;
|
|
371
|
-
|
|
372
|
-
currentCleanup();
|
|
373
|
-
currentCleanup = null;
|
|
374
|
-
}
|
|
375
|
-
while (container.firstChild) {
|
|
376
|
-
container.removeChild(container.firstChild);
|
|
377
|
-
}
|
|
372
|
+
const thisLoadId = ++loadId;
|
|
378
373
|
if (!match) {
|
|
374
|
+
if (currentCleanup) {
|
|
375
|
+
currentCleanup();
|
|
376
|
+
currentCleanup = null;
|
|
377
|
+
}
|
|
378
|
+
clearContainer(container);
|
|
379
379
|
const notFound = document.createElement("div");
|
|
380
380
|
notFound.setAttribute("data-utopia-not-found", "");
|
|
381
381
|
notFound.textContent = "Page not found";
|
|
382
382
|
container.appendChild(notFound);
|
|
383
383
|
return;
|
|
384
384
|
}
|
|
385
|
-
loadRouteComponent(match
|
|
386
|
-
|
|
385
|
+
loadRouteComponent(match).then((result) => {
|
|
386
|
+
if (thisLoadId !== loadId) return;
|
|
387
|
+
if (!result) return;
|
|
388
|
+
if (currentCleanup) {
|
|
389
|
+
currentCleanup();
|
|
390
|
+
currentCleanup = null;
|
|
391
|
+
}
|
|
392
|
+
clearContainer(container);
|
|
393
|
+
container.appendChild(result.node);
|
|
394
|
+
currentCleanup = result.cleanup;
|
|
387
395
|
});
|
|
388
396
|
});
|
|
389
397
|
return container;
|
|
390
398
|
}
|
|
391
|
-
async function loadRouteComponent(match
|
|
399
|
+
async function loadRouteComponent(match) {
|
|
392
400
|
try {
|
|
393
401
|
const promises = [match.route.component()];
|
|
394
402
|
if (match.route.layout) {
|
|
@@ -402,26 +410,26 @@ async function loadRouteComponent(match, container) {
|
|
|
402
410
|
}
|
|
403
411
|
const PageComponent = pageModule.default ?? pageModule;
|
|
404
412
|
const LayoutComponent = layoutModule ? layoutModule.default ?? layoutModule : null;
|
|
405
|
-
while (container.firstChild) {
|
|
406
|
-
container.removeChild(container.firstChild);
|
|
407
|
-
}
|
|
408
413
|
const pageNode = renderComponent(PageComponent, {
|
|
409
414
|
params: match.params,
|
|
410
415
|
url: match.url
|
|
411
416
|
});
|
|
417
|
+
let node;
|
|
412
418
|
if (LayoutComponent) {
|
|
413
|
-
|
|
419
|
+
node = renderComponent(LayoutComponent, {
|
|
414
420
|
params: match.params,
|
|
415
421
|
url: match.url,
|
|
416
422
|
children: pageNode
|
|
417
423
|
});
|
|
418
|
-
container.appendChild(layoutNode);
|
|
419
424
|
} else {
|
|
420
|
-
|
|
425
|
+
node = pageNode;
|
|
421
426
|
}
|
|
422
|
-
return
|
|
423
|
-
|
|
424
|
-
|
|
427
|
+
return {
|
|
428
|
+
node,
|
|
429
|
+
cleanup: () => {
|
|
430
|
+
if (node.parentNode) {
|
|
431
|
+
node.parentNode.removeChild(node);
|
|
432
|
+
}
|
|
425
433
|
}
|
|
426
434
|
};
|
|
427
435
|
} catch (err) {
|
|
@@ -429,26 +437,33 @@ async function loadRouteComponent(match, container) {
|
|
|
429
437
|
try {
|
|
430
438
|
const errorModule = await match.route.error();
|
|
431
439
|
const ErrorComponent = errorModule.default ?? errorModule;
|
|
432
|
-
while (container.firstChild) {
|
|
433
|
-
container.removeChild(container.firstChild);
|
|
434
|
-
}
|
|
435
440
|
const errorNode = renderComponent(ErrorComponent, {
|
|
436
441
|
error: err,
|
|
437
442
|
params: match.params,
|
|
438
443
|
url: match.url
|
|
439
444
|
});
|
|
440
|
-
|
|
445
|
+
return {
|
|
446
|
+
node: errorNode,
|
|
447
|
+
cleanup: () => {
|
|
448
|
+
if (errorNode.parentNode) {
|
|
449
|
+
errorNode.parentNode.removeChild(errorNode);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
441
453
|
} catch {
|
|
442
|
-
|
|
454
|
+
return {
|
|
455
|
+
node: createFallbackErrorNode(err),
|
|
456
|
+
cleanup: () => {
|
|
457
|
+
}
|
|
458
|
+
};
|
|
443
459
|
}
|
|
444
460
|
} else {
|
|
445
|
-
|
|
461
|
+
return {
|
|
462
|
+
node: createFallbackErrorNode(err),
|
|
463
|
+
cleanup: () => {
|
|
464
|
+
}
|
|
465
|
+
};
|
|
446
466
|
}
|
|
447
|
-
return () => {
|
|
448
|
-
while (container.firstChild) {
|
|
449
|
-
container.removeChild(container.firstChild);
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
467
|
}
|
|
453
468
|
}
|
|
454
469
|
function renderComponent(component, props) {
|
|
@@ -471,10 +486,7 @@ function renderComponent(component, props) {
|
|
|
471
486
|
div.textContent = "[Component render error]";
|
|
472
487
|
return div;
|
|
473
488
|
}
|
|
474
|
-
function
|
|
475
|
-
while (container.firstChild) {
|
|
476
|
-
container.removeChild(container.firstChild);
|
|
477
|
-
}
|
|
489
|
+
function createFallbackErrorNode(error) {
|
|
478
490
|
const errorDiv = document.createElement("div");
|
|
479
491
|
errorDiv.setAttribute("data-utopia-error", "");
|
|
480
492
|
errorDiv.style.cssText = "padding:2rem;color:#dc2626;font-family:monospace;";
|
|
@@ -484,7 +496,7 @@ function renderFallbackError(container, error) {
|
|
|
484
496
|
error instanceof Error ? error.message : String(error)
|
|
485
497
|
)}</pre>
|
|
486
498
|
`;
|
|
487
|
-
|
|
499
|
+
return errorDiv;
|
|
488
500
|
}
|
|
489
501
|
function createLink(props) {
|
|
490
502
|
const anchor = document.createElement("a");
|
|
@@ -511,6 +523,11 @@ function createLink(props) {
|
|
|
511
523
|
}
|
|
512
524
|
return anchor;
|
|
513
525
|
}
|
|
526
|
+
function clearContainer(container) {
|
|
527
|
+
while (container.firstChild) {
|
|
528
|
+
container.removeChild(container.firstChild);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
514
531
|
function escapeHtml(str) {
|
|
515
532
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
516
533
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthesketh/utopia-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "File-based routing for UtopiaJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dist"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@matthesketh/utopia-core": "0.
|
|
44
|
-
"@matthesketh/utopia-runtime": "0.
|
|
43
|
+
"@matthesketh/utopia-core": "0.3.1",
|
|
44
|
+
"@matthesketh/utopia-runtime": "0.3.1"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|