@moku-labs/web 0.5.4 → 0.5.6
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/browser.mjs +1 -2
- package/dist/index.cjs +79 -3
- package/dist/index.d.cts +7 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.mjs +79 -3
- package/dist/render-BNe0s7fr.mjs +32 -0
- package/dist/render-DLZEOe4M.cjs +32 -0
- package/package.json +1 -1
- package/dist/render-BL9Fv6G6.mjs +0 -20
- package/dist/render-BSTM0Akv.cjs +0 -20
package/dist/browser.mjs
CHANGED
|
@@ -3303,11 +3303,10 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
3303
3303
|
const region = document.querySelector(resolved.swapSelector);
|
|
3304
3304
|
if (!region) return false;
|
|
3305
3305
|
handleStart(pathname);
|
|
3306
|
-
const { renderVNode } = await import("./render-
|
|
3306
|
+
const { renderVNode } = await import("./render-BNe0s7fr.mjs");
|
|
3307
3307
|
syncDataHead(hit.route, routeContext);
|
|
3308
3308
|
unmountPageSpecific(state, emit);
|
|
3309
3309
|
runSwap(() => {
|
|
3310
|
-
region.replaceChildren();
|
|
3311
3310
|
renderVNode(vnode, region);
|
|
3312
3311
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
3313
3312
|
notifyNavEnd(state);
|
package/dist/index.cjs
CHANGED
|
@@ -1335,6 +1335,23 @@ function calculateReadingTime(text) {
|
|
|
1335
1335
|
function articleToUrl(locale, slug) {
|
|
1336
1336
|
return `/${locale}/${slug}/`;
|
|
1337
1337
|
}
|
|
1338
|
+
/** Matches an `<img>` `src` that points at the co-located `images/` dir (relative or root-relative). */
|
|
1339
|
+
const RELATIVE_IMAGE_SRC = /(<img\b[^>]*?\bsrc=")(?:\.?\/)?images\//g;
|
|
1340
|
+
/**
|
|
1341
|
+
* Rewrite relative co-located image URLs (`./images/x.webp`) in rendered article HTML to the shared
|
|
1342
|
+
* absolute path the build copies them to (`/<slug>/images/...`), so they resolve from any locale page.
|
|
1343
|
+
*
|
|
1344
|
+
* @param html - The rendered article HTML.
|
|
1345
|
+
* @param slug - Article directory name.
|
|
1346
|
+
* @returns The HTML with image `src`s rewritten to absolute paths.
|
|
1347
|
+
* @example
|
|
1348
|
+
* ```ts
|
|
1349
|
+
* rewriteImageUrls('<img src="./images/a.webp">', "post"); // '<img src="/post/images/a.webp">'
|
|
1350
|
+
* ```
|
|
1351
|
+
*/
|
|
1352
|
+
function rewriteImageUrls(html, slug) {
|
|
1353
|
+
return html.replaceAll(RELATIVE_IMAGE_SRC, `$1/${slug}/images/`);
|
|
1354
|
+
}
|
|
1338
1355
|
/**
|
|
1339
1356
|
* Build the canonical "article not found" error for {@link createContentApi.load}.
|
|
1340
1357
|
* Centralised so the null-resolve path and the production draft-suppression path
|
|
@@ -1450,7 +1467,7 @@ async function readArticle(ctx, slug, fileLocale, outLocale, isFallback) {
|
|
|
1450
1467
|
ctx.state.dirtyPaths.delete(filePath);
|
|
1451
1468
|
const { frontmatter, body } = parseFrontmatter(raw, ctx.config);
|
|
1452
1469
|
const processor = ensureProcessor(ctx.state, ctx.config);
|
|
1453
|
-
const html = String(await processor.process(body));
|
|
1470
|
+
const html = rewriteImageUrls(String(await processor.process(body)), slug);
|
|
1454
1471
|
const { readingTime, wordCount } = calculateReadingTime(body);
|
|
1455
1472
|
return {
|
|
1456
1473
|
frontmatter,
|
|
@@ -1662,6 +1679,18 @@ function createContentApi(ctx) {
|
|
|
1662
1679
|
*/
|
|
1663
1680
|
articleToCard(article) {
|
|
1664
1681
|
return toCard(article);
|
|
1682
|
+
},
|
|
1683
|
+
/**
|
|
1684
|
+
* The configured content source directory (e.g. `"./content"`).
|
|
1685
|
+
*
|
|
1686
|
+
* @returns The content directory path from config.
|
|
1687
|
+
* @example
|
|
1688
|
+
* ```ts
|
|
1689
|
+
* api.contentDir(); // "./content"
|
|
1690
|
+
* ```
|
|
1691
|
+
*/
|
|
1692
|
+
contentDir() {
|
|
1693
|
+
return ctx.config.contentDir;
|
|
1665
1694
|
}
|
|
1666
1695
|
};
|
|
1667
1696
|
}
|
|
@@ -3445,6 +3474,52 @@ function readCachedContent(ctx) {
|
|
|
3445
3474
|
return cached instanceof Map ? cached : /* @__PURE__ */ new Map();
|
|
3446
3475
|
}
|
|
3447
3476
|
//#endregion
|
|
3477
|
+
//#region src/plugins/build/phases/content-images.ts
|
|
3478
|
+
/**
|
|
3479
|
+
* @file build phase — content-images. Copies each article's co-located image directory
|
|
3480
|
+
* (`<contentDir>/<slug>/images/`) to a single shared output dir (`<outDir>/<slug>/images/`) reused by
|
|
3481
|
+
* every locale, matching the absolute `/<slug>/images/...` URLs the content renderer emits. Gated by
|
|
3482
|
+
* `config.images`.
|
|
3483
|
+
*/
|
|
3484
|
+
/** Conventional per-article image subdirectory name (alongside `<slug>/<locale>.md`). */
|
|
3485
|
+
const ARTICLE_IMAGE_DIR = "images";
|
|
3486
|
+
/**
|
|
3487
|
+
* Copy every article's co-located `images/` directory to `<outDir>/<slug>/images/`. No-op when
|
|
3488
|
+
* `config.images` is false or the content directory does not exist.
|
|
3489
|
+
*
|
|
3490
|
+
* @param ctx - Plugin context (provides `config`, `log`, `require`).
|
|
3491
|
+
* @returns The number of directories copied (one per article that has an `images/` dir).
|
|
3492
|
+
* @example
|
|
3493
|
+
* ```ts
|
|
3494
|
+
* const copied = await copyContentImages(ctx);
|
|
3495
|
+
* ```
|
|
3496
|
+
*/
|
|
3497
|
+
async function copyContentImages(ctx) {
|
|
3498
|
+
if (!ctx.config.images) {
|
|
3499
|
+
ctx.log.debug("build:content-images", { skipped: true });
|
|
3500
|
+
return 0;
|
|
3501
|
+
}
|
|
3502
|
+
const contentDir = ctx.require(contentPlugin).contentDir();
|
|
3503
|
+
if (!(0, node_fs.existsSync)(contentDir)) {
|
|
3504
|
+
ctx.log.debug("build:content-images", {
|
|
3505
|
+
skipped: true,
|
|
3506
|
+
reason: "no content dir"
|
|
3507
|
+
});
|
|
3508
|
+
return 0;
|
|
3509
|
+
}
|
|
3510
|
+
const entries = await (0, node_fs_promises.readdir)(contentDir, { withFileTypes: true });
|
|
3511
|
+
let copied = 0;
|
|
3512
|
+
for (const entry of entries) {
|
|
3513
|
+
if (!entry.isDirectory()) continue;
|
|
3514
|
+
const source = node_path$1.default.join(contentDir, entry.name, ARTICLE_IMAGE_DIR);
|
|
3515
|
+
if (!(0, node_fs.existsSync)(source)) continue;
|
|
3516
|
+
await (0, node_fs_promises.cp)(source, node_path$1.default.join(ctx.config.outDir, entry.name, ARTICLE_IMAGE_DIR), { recursive: true });
|
|
3517
|
+
copied += 1;
|
|
3518
|
+
}
|
|
3519
|
+
ctx.log.debug("build:content-images", { copied });
|
|
3520
|
+
return copied;
|
|
3521
|
+
}
|
|
3522
|
+
//#endregion
|
|
3448
3523
|
//#region src/plugins/build/phases/feeds.ts
|
|
3449
3524
|
/**
|
|
3450
3525
|
* @file build phase 4 — feeds. Generates RSS/Atom/JSON from cached content plus
|
|
@@ -4690,6 +4765,7 @@ const PHASE_ORDER = [
|
|
|
4690
4765
|
"content",
|
|
4691
4766
|
"images",
|
|
4692
4767
|
"pages",
|
|
4768
|
+
"content-images",
|
|
4693
4769
|
"feeds",
|
|
4694
4770
|
"sitemap",
|
|
4695
4771
|
"og-images",
|
|
@@ -4796,6 +4872,7 @@ async function runPipeline(ctx, options) {
|
|
|
4796
4872
|
await withPhase(phaseContext, "bundle", () => bundle(phaseContext));
|
|
4797
4873
|
await Promise.all([withPhase(phaseContext, "content", () => loadContent(phaseContext)), withPhase(phaseContext, "images", () => processImages(phaseContext))]);
|
|
4798
4874
|
const pages = await withPhase(phaseContext, "pages", () => renderPages(phaseContext));
|
|
4875
|
+
await withPhase(phaseContext, "content-images", () => copyContentImages(phaseContext));
|
|
4799
4876
|
await runOutputs(phaseContext);
|
|
4800
4877
|
await withPhase(phaseContext, "root-index", async () => {
|
|
4801
4878
|
if (pages.rootHtml !== null) await (0, node_fs_promises.writeFile)(node_path$1.default.join(outDir, "index.html"), pages.rootHtml, "utf8");
|
|
@@ -6701,11 +6778,10 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
6701
6778
|
const region = document.querySelector(resolved.swapSelector);
|
|
6702
6779
|
if (!region) return false;
|
|
6703
6780
|
handleStart(pathname);
|
|
6704
|
-
const { renderVNode } = await Promise.resolve().then(() => require("./render-
|
|
6781
|
+
const { renderVNode } = await Promise.resolve().then(() => require("./render-DLZEOe4M.cjs"));
|
|
6705
6782
|
syncDataHead(hit.route, routeContext);
|
|
6706
6783
|
unmountPageSpecific(state, emit);
|
|
6707
6784
|
runSwap(() => {
|
|
6708
|
-
region.replaceChildren();
|
|
6709
6785
|
renderVNode(vnode, region);
|
|
6710
6786
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
6711
6787
|
notifyNavEnd(state);
|
package/dist/index.d.cts
CHANGED
|
@@ -1575,7 +1575,7 @@ interface State$2 {
|
|
|
1575
1575
|
* const phase: PhaseName = "bundle";
|
|
1576
1576
|
* ```
|
|
1577
1577
|
*/
|
|
1578
|
-
type PhaseName = "bundle" | "content" | "images" | "pages" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
|
|
1578
|
+
type PhaseName = "bundle" | "content" | "images" | "pages" | "content-images" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
|
|
1579
1579
|
/**
|
|
1580
1580
|
* Result of a completed build run.
|
|
1581
1581
|
*
|
|
@@ -1849,6 +1849,12 @@ type Api$1 = {
|
|
|
1849
1849
|
* @param article - The source article.
|
|
1850
1850
|
*/
|
|
1851
1851
|
articleToCard(article: Article): ArticleCard;
|
|
1852
|
+
/**
|
|
1853
|
+
* The configured content source directory (e.g. `"./content"`). Lets the build copy each
|
|
1854
|
+
* article's co-located assets (`<contentDir>/<slug>/images/`) into the output so the absolute
|
|
1855
|
+
* image URLs the renderer emits resolve.
|
|
1856
|
+
*/
|
|
1857
|
+
contentDir(): string;
|
|
1852
1858
|
};
|
|
1853
1859
|
//#endregion
|
|
1854
1860
|
//#region src/plugins/content/index.d.ts
|
package/dist/index.d.mts
CHANGED
|
@@ -1575,7 +1575,7 @@ interface State$2 {
|
|
|
1575
1575
|
* const phase: PhaseName = "bundle";
|
|
1576
1576
|
* ```
|
|
1577
1577
|
*/
|
|
1578
|
-
type PhaseName = "bundle" | "content" | "images" | "pages" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
|
|
1578
|
+
type PhaseName = "bundle" | "content" | "images" | "pages" | "content-images" | "feeds" | "sitemap" | "og-images" | "public" | "not-found" | "locale-redirects" | "root-index";
|
|
1579
1579
|
/**
|
|
1580
1580
|
* Result of a completed build run.
|
|
1581
1581
|
*
|
|
@@ -1849,6 +1849,12 @@ type Api$1 = {
|
|
|
1849
1849
|
* @param article - The source article.
|
|
1850
1850
|
*/
|
|
1851
1851
|
articleToCard(article: Article): ArticleCard;
|
|
1852
|
+
/**
|
|
1853
|
+
* The configured content source directory (e.g. `"./content"`). Lets the build copy each
|
|
1854
|
+
* article's co-located assets (`<contentDir>/<slug>/images/`) into the output so the absolute
|
|
1855
|
+
* image URLs the renderer emits resolve.
|
|
1856
|
+
*/
|
|
1857
|
+
contentDir(): string;
|
|
1852
1858
|
};
|
|
1853
1859
|
//#endregion
|
|
1854
1860
|
//#region src/plugins/content/index.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1322,6 +1322,23 @@ function calculateReadingTime(text) {
|
|
|
1322
1322
|
function articleToUrl(locale, slug) {
|
|
1323
1323
|
return `/${locale}/${slug}/`;
|
|
1324
1324
|
}
|
|
1325
|
+
/** Matches an `<img>` `src` that points at the co-located `images/` dir (relative or root-relative). */
|
|
1326
|
+
const RELATIVE_IMAGE_SRC = /(<img\b[^>]*?\bsrc=")(?:\.?\/)?images\//g;
|
|
1327
|
+
/**
|
|
1328
|
+
* Rewrite relative co-located image URLs (`./images/x.webp`) in rendered article HTML to the shared
|
|
1329
|
+
* absolute path the build copies them to (`/<slug>/images/...`), so they resolve from any locale page.
|
|
1330
|
+
*
|
|
1331
|
+
* @param html - The rendered article HTML.
|
|
1332
|
+
* @param slug - Article directory name.
|
|
1333
|
+
* @returns The HTML with image `src`s rewritten to absolute paths.
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```ts
|
|
1336
|
+
* rewriteImageUrls('<img src="./images/a.webp">', "post"); // '<img src="/post/images/a.webp">'
|
|
1337
|
+
* ```
|
|
1338
|
+
*/
|
|
1339
|
+
function rewriteImageUrls(html, slug) {
|
|
1340
|
+
return html.replaceAll(RELATIVE_IMAGE_SRC, `$1/${slug}/images/`);
|
|
1341
|
+
}
|
|
1325
1342
|
/**
|
|
1326
1343
|
* Build the canonical "article not found" error for {@link createContentApi.load}.
|
|
1327
1344
|
* Centralised so the null-resolve path and the production draft-suppression path
|
|
@@ -1437,7 +1454,7 @@ async function readArticle(ctx, slug, fileLocale, outLocale, isFallback) {
|
|
|
1437
1454
|
ctx.state.dirtyPaths.delete(filePath);
|
|
1438
1455
|
const { frontmatter, body } = parseFrontmatter(raw, ctx.config);
|
|
1439
1456
|
const processor = ensureProcessor(ctx.state, ctx.config);
|
|
1440
|
-
const html = String(await processor.process(body));
|
|
1457
|
+
const html = rewriteImageUrls(String(await processor.process(body)), slug);
|
|
1441
1458
|
const { readingTime, wordCount } = calculateReadingTime(body);
|
|
1442
1459
|
return {
|
|
1443
1460
|
frontmatter,
|
|
@@ -1649,6 +1666,18 @@ function createContentApi(ctx) {
|
|
|
1649
1666
|
*/
|
|
1650
1667
|
articleToCard(article) {
|
|
1651
1668
|
return toCard(article);
|
|
1669
|
+
},
|
|
1670
|
+
/**
|
|
1671
|
+
* The configured content source directory (e.g. `"./content"`).
|
|
1672
|
+
*
|
|
1673
|
+
* @returns The content directory path from config.
|
|
1674
|
+
* @example
|
|
1675
|
+
* ```ts
|
|
1676
|
+
* api.contentDir(); // "./content"
|
|
1677
|
+
* ```
|
|
1678
|
+
*/
|
|
1679
|
+
contentDir() {
|
|
1680
|
+
return ctx.config.contentDir;
|
|
1652
1681
|
}
|
|
1653
1682
|
};
|
|
1654
1683
|
}
|
|
@@ -3432,6 +3461,52 @@ function readCachedContent(ctx) {
|
|
|
3432
3461
|
return cached instanceof Map ? cached : /* @__PURE__ */ new Map();
|
|
3433
3462
|
}
|
|
3434
3463
|
//#endregion
|
|
3464
|
+
//#region src/plugins/build/phases/content-images.ts
|
|
3465
|
+
/**
|
|
3466
|
+
* @file build phase — content-images. Copies each article's co-located image directory
|
|
3467
|
+
* (`<contentDir>/<slug>/images/`) to a single shared output dir (`<outDir>/<slug>/images/`) reused by
|
|
3468
|
+
* every locale, matching the absolute `/<slug>/images/...` URLs the content renderer emits. Gated by
|
|
3469
|
+
* `config.images`.
|
|
3470
|
+
*/
|
|
3471
|
+
/** Conventional per-article image subdirectory name (alongside `<slug>/<locale>.md`). */
|
|
3472
|
+
const ARTICLE_IMAGE_DIR = "images";
|
|
3473
|
+
/**
|
|
3474
|
+
* Copy every article's co-located `images/` directory to `<outDir>/<slug>/images/`. No-op when
|
|
3475
|
+
* `config.images` is false or the content directory does not exist.
|
|
3476
|
+
*
|
|
3477
|
+
* @param ctx - Plugin context (provides `config`, `log`, `require`).
|
|
3478
|
+
* @returns The number of directories copied (one per article that has an `images/` dir).
|
|
3479
|
+
* @example
|
|
3480
|
+
* ```ts
|
|
3481
|
+
* const copied = await copyContentImages(ctx);
|
|
3482
|
+
* ```
|
|
3483
|
+
*/
|
|
3484
|
+
async function copyContentImages(ctx) {
|
|
3485
|
+
if (!ctx.config.images) {
|
|
3486
|
+
ctx.log.debug("build:content-images", { skipped: true });
|
|
3487
|
+
return 0;
|
|
3488
|
+
}
|
|
3489
|
+
const contentDir = ctx.require(contentPlugin).contentDir();
|
|
3490
|
+
if (!existsSync(contentDir)) {
|
|
3491
|
+
ctx.log.debug("build:content-images", {
|
|
3492
|
+
skipped: true,
|
|
3493
|
+
reason: "no content dir"
|
|
3494
|
+
});
|
|
3495
|
+
return 0;
|
|
3496
|
+
}
|
|
3497
|
+
const entries = await readdir(contentDir, { withFileTypes: true });
|
|
3498
|
+
let copied = 0;
|
|
3499
|
+
for (const entry of entries) {
|
|
3500
|
+
if (!entry.isDirectory()) continue;
|
|
3501
|
+
const source = path.join(contentDir, entry.name, ARTICLE_IMAGE_DIR);
|
|
3502
|
+
if (!existsSync(source)) continue;
|
|
3503
|
+
await cp(source, path.join(ctx.config.outDir, entry.name, ARTICLE_IMAGE_DIR), { recursive: true });
|
|
3504
|
+
copied += 1;
|
|
3505
|
+
}
|
|
3506
|
+
ctx.log.debug("build:content-images", { copied });
|
|
3507
|
+
return copied;
|
|
3508
|
+
}
|
|
3509
|
+
//#endregion
|
|
3435
3510
|
//#region src/plugins/build/phases/feeds.ts
|
|
3436
3511
|
/**
|
|
3437
3512
|
* @file build phase 4 — feeds. Generates RSS/Atom/JSON from cached content plus
|
|
@@ -4677,6 +4752,7 @@ const PHASE_ORDER = [
|
|
|
4677
4752
|
"content",
|
|
4678
4753
|
"images",
|
|
4679
4754
|
"pages",
|
|
4755
|
+
"content-images",
|
|
4680
4756
|
"feeds",
|
|
4681
4757
|
"sitemap",
|
|
4682
4758
|
"og-images",
|
|
@@ -4783,6 +4859,7 @@ async function runPipeline(ctx, options) {
|
|
|
4783
4859
|
await withPhase(phaseContext, "bundle", () => bundle(phaseContext));
|
|
4784
4860
|
await Promise.all([withPhase(phaseContext, "content", () => loadContent(phaseContext)), withPhase(phaseContext, "images", () => processImages(phaseContext))]);
|
|
4785
4861
|
const pages = await withPhase(phaseContext, "pages", () => renderPages(phaseContext));
|
|
4862
|
+
await withPhase(phaseContext, "content-images", () => copyContentImages(phaseContext));
|
|
4786
4863
|
await runOutputs(phaseContext);
|
|
4787
4864
|
await withPhase(phaseContext, "root-index", async () => {
|
|
4788
4865
|
if (pages.rootHtml !== null) await writeFile(path.join(outDir, "index.html"), pages.rootHtml, "utf8");
|
|
@@ -6688,11 +6765,10 @@ function createSpaKernel(state, config, emit, deps) {
|
|
|
6688
6765
|
const region = document.querySelector(resolved.swapSelector);
|
|
6689
6766
|
if (!region) return false;
|
|
6690
6767
|
handleStart(pathname);
|
|
6691
|
-
const { renderVNode } = await import("./render-
|
|
6768
|
+
const { renderVNode } = await import("./render-BNe0s7fr.mjs");
|
|
6692
6769
|
syncDataHead(hit.route, routeContext);
|
|
6693
6770
|
unmountPageSpecific(state, emit);
|
|
6694
6771
|
runSwap(() => {
|
|
6695
|
-
region.replaceChildren();
|
|
6696
6772
|
renderVNode(vnode, region);
|
|
6697
6773
|
scanAndMount(state, emit, resolved.swapSelector);
|
|
6698
6774
|
notifyNavEnd(state);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { render } from "preact";
|
|
2
|
+
//#region src/plugins/spa/render.ts
|
|
3
|
+
/**
|
|
4
|
+
* Render a route's `VNode` into the live swap region, starting from a clean slate
|
|
5
|
+
* each time. Preact keeps the previous vdom tree on the container and diffs the
|
|
6
|
+
* next render against it — but the kernel clears the region between navs to drop
|
|
7
|
+
* the static SSR markup. A raw `replaceChildren()` would delete the live DOM out
|
|
8
|
+
* from under Preact's retained vdom, so the next diff patches detached nodes → an
|
|
9
|
+
* empty region (the bug where a SECOND consecutive client nav went blank).
|
|
10
|
+
*
|
|
11
|
+
* To stay correct without tracking element identity, first `render(null, region)`
|
|
12
|
+
* — this unmounts any Preact tree Preact owns AND resets its retained vdom pointer
|
|
13
|
+
* (a no-op the first time, when the region still holds raw SSR/HTML). Then clear
|
|
14
|
+
* whatever static children remain, then mount the new VNode fresh. Reuses the
|
|
15
|
+
* build's component output verbatim (same `route.render`), so the client paint
|
|
16
|
+
* matches the SSG paint.
|
|
17
|
+
*
|
|
18
|
+
* @param vnode - The VNode produced by the matched route's `.render(ctx)`.
|
|
19
|
+
* @param region - The swap-region element to render into.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const { renderVNode } = await import("./render");
|
|
23
|
+
* renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function renderVNode(vnode, region) {
|
|
27
|
+
render(null, region);
|
|
28
|
+
region.replaceChildren();
|
|
29
|
+
render(vnode, region);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { renderVNode };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
let preact = require("preact");
|
|
2
|
+
//#region src/plugins/spa/render.ts
|
|
3
|
+
/**
|
|
4
|
+
* Render a route's `VNode` into the live swap region, starting from a clean slate
|
|
5
|
+
* each time. Preact keeps the previous vdom tree on the container and diffs the
|
|
6
|
+
* next render against it — but the kernel clears the region between navs to drop
|
|
7
|
+
* the static SSR markup. A raw `replaceChildren()` would delete the live DOM out
|
|
8
|
+
* from under Preact's retained vdom, so the next diff patches detached nodes → an
|
|
9
|
+
* empty region (the bug where a SECOND consecutive client nav went blank).
|
|
10
|
+
*
|
|
11
|
+
* To stay correct without tracking element identity, first `render(null, region)`
|
|
12
|
+
* — this unmounts any Preact tree Preact owns AND resets its retained vdom pointer
|
|
13
|
+
* (a no-op the first time, when the region still holds raw SSR/HTML). Then clear
|
|
14
|
+
* whatever static children remain, then mount the new VNode fresh. Reuses the
|
|
15
|
+
* build's component output verbatim (same `route.render`), so the client paint
|
|
16
|
+
* matches the SSG paint.
|
|
17
|
+
*
|
|
18
|
+
* @param vnode - The VNode produced by the matched route's `.render(ctx)`.
|
|
19
|
+
* @param region - The swap-region element to render into.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const { renderVNode } = await import("./render");
|
|
23
|
+
* renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function renderVNode(vnode, region) {
|
|
27
|
+
(0, preact.render)(null, region);
|
|
28
|
+
region.replaceChildren();
|
|
29
|
+
(0, preact.render)(vnode, region);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
exports.renderVNode = renderVNode;
|
package/package.json
CHANGED
package/dist/render-BL9Fv6G6.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { render } from "preact";
|
|
2
|
-
//#region src/plugins/spa/render.ts
|
|
3
|
-
/**
|
|
4
|
-
* Render a route's `VNode` into the live swap region, replacing its contents.
|
|
5
|
-
* Reuses the build's component output verbatim (same `route.render`), so the
|
|
6
|
-
* client paint matches the SSG paint.
|
|
7
|
-
*
|
|
8
|
-
* @param vnode - The VNode produced by the matched route's `.render(ctx)`.
|
|
9
|
-
* @param region - The swap-region element to render into.
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const { renderVNode } = await import("./render");
|
|
13
|
-
* renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
function renderVNode(vnode, region) {
|
|
17
|
-
render(vnode, region);
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { renderVNode };
|
package/dist/render-BSTM0Akv.cjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
let preact = require("preact");
|
|
2
|
-
//#region src/plugins/spa/render.ts
|
|
3
|
-
/**
|
|
4
|
-
* Render a route's `VNode` into the live swap region, replacing its contents.
|
|
5
|
-
* Reuses the build's component output verbatim (same `route.render`), so the
|
|
6
|
-
* client paint matches the SSG paint.
|
|
7
|
-
*
|
|
8
|
-
* @param vnode - The VNode produced by the matched route's `.render(ctx)`.
|
|
9
|
-
* @param region - The swap-region element to render into.
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const { renderVNode } = await import("./render");
|
|
13
|
-
* renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
function renderVNode(vnode, region) {
|
|
17
|
-
(0, preact.render)(vnode, region);
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
exports.renderVNode = renderVNode;
|