mnfst-render 0.5.4 → 0.5.5
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/manifest.render.mjs +39 -7
- package/package.json +1 -1
package/manifest.render.mjs
CHANGED
|
@@ -1787,6 +1787,11 @@ async function runPrerender(config) {
|
|
|
1787
1787
|
const browserRecycleEvery = Math.max(0, pre.browserRecycleEvery ?? 40);
|
|
1788
1788
|
let pagesSinceRecycle = 0;
|
|
1789
1789
|
const recycleLock = { busy: false };
|
|
1790
|
+
// Workers block on this promise before touching `browser`. While a recycle
|
|
1791
|
+
// is in progress it's a pending promise; once the new browser is up it
|
|
1792
|
+
// resolves and workers can proceed. This prevents "browser not ready"
|
|
1793
|
+
// errors from racing retries during recycle.
|
|
1794
|
+
let browserReadyPromise = Promise.resolve();
|
|
1790
1795
|
const pathTotal = pathList.length;
|
|
1791
1796
|
const failedPaths = [];
|
|
1792
1797
|
const debugRows = [];
|
|
@@ -1878,11 +1883,11 @@ async function runPrerender(config) {
|
|
|
1878
1883
|
: defaultLocale || 'en'
|
|
1879
1884
|
: defaultLocale || 'en';
|
|
1880
1885
|
|
|
1881
|
-
//
|
|
1882
|
-
//
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
+
// Wait for any in-progress browser recycle to complete before touching
|
|
1887
|
+
// `browser`. This transparently handles the window between the old
|
|
1888
|
+
// browser being closed and the new one being launched — workers block
|
|
1889
|
+
// here instead of throwing "browser not ready".
|
|
1890
|
+
await browserReadyPromise;
|
|
1886
1891
|
const page = await browser.newPage();
|
|
1887
1892
|
try {
|
|
1888
1893
|
// Align <html lang> with the URL being prerendered before any app script runs.
|
|
@@ -2816,14 +2821,23 @@ async function runPrerender(config) {
|
|
|
2816
2821
|
if (pagesSinceRecycle < browserRecycleEvery) return;
|
|
2817
2822
|
if (recycleLock.busy) return;
|
|
2818
2823
|
recycleLock.busy = true;
|
|
2824
|
+
// Wait for all in-flight workers to finish their current page BEFORE
|
|
2825
|
+
// we gate `browserReadyPromise`, so workers already mid-processPath
|
|
2826
|
+
// don't deadlock awaiting a promise we haven't yet started.
|
|
2827
|
+
await waitUntilZero();
|
|
2828
|
+
// Now gate newPage() calls from any worker that enters processPath
|
|
2829
|
+
// after this point.
|
|
2830
|
+
let resolveReady;
|
|
2831
|
+
browserReadyPromise = new Promise((r) => { resolveReady = r; });
|
|
2819
2832
|
try {
|
|
2820
|
-
// Wait for all in-flight workers to finish their current page.
|
|
2821
|
-
await waitUntilZero();
|
|
2822
2833
|
process.stdout.write(`prerender: recycling browser (processed ${pagesSinceRecycle} pages)\n`);
|
|
2823
2834
|
try { await browser.close(); } catch (_) {}
|
|
2824
2835
|
browser = await launchBrowser();
|
|
2825
2836
|
pagesSinceRecycle = 0;
|
|
2826
2837
|
} finally {
|
|
2838
|
+
// Release the gate first so any waiting workers can proceed, then
|
|
2839
|
+
// clear the recycle lock so the outer while loop stops pausing.
|
|
2840
|
+
try { resolveReady(); } catch (_) {}
|
|
2827
2841
|
recycleLock.busy = false;
|
|
2828
2842
|
const r = recycleGate.resume;
|
|
2829
2843
|
recycleGate.resume = null;
|
|
@@ -2835,12 +2849,19 @@ async function runPrerender(config) {
|
|
|
2835
2849
|
while (true) {
|
|
2836
2850
|
// Pause if a recycle is underway.
|
|
2837
2851
|
if (recycleLock.busy) await waitForResume();
|
|
2852
|
+
// Also wait for any pending browser readiness (e.g. another worker
|
|
2853
|
+
// started a recycle while we were processing).
|
|
2854
|
+
await browserReadyPromise;
|
|
2838
2855
|
|
|
2839
2856
|
const i = index++;
|
|
2840
2857
|
if (i >= puppeteerPaths.length) return;
|
|
2841
2858
|
const pathSeg = puppeteerPaths[i];
|
|
2842
2859
|
let attempt = 0;
|
|
2843
2860
|
while (true) {
|
|
2861
|
+
// Re-check recycle state at the start of every retry iteration.
|
|
2862
|
+
if (recycleLock.busy) await waitForResume();
|
|
2863
|
+
await browserReadyPromise;
|
|
2864
|
+
|
|
2844
2865
|
const failureCountBefore = failedPaths.length;
|
|
2845
2866
|
activeWorkers++;
|
|
2846
2867
|
try {
|
|
@@ -2849,6 +2870,17 @@ async function runPrerender(config) {
|
|
|
2849
2870
|
if (seg !== NOT_FOUND_PATH) baseHtmlCache.set(seg || '', html);
|
|
2850
2871
|
},
|
|
2851
2872
|
});
|
|
2873
|
+
} catch (err) {
|
|
2874
|
+
// Unexpected exception escaped processPath (e.g. browser died
|
|
2875
|
+
// mid-call). Record as a failure so the retry logic can handle
|
|
2876
|
+
// it gracefully instead of tearing down the whole worker.
|
|
2877
|
+
failedPaths.push({
|
|
2878
|
+
path: pathSeg === '' ? '/' : '/' + pathSeg,
|
|
2879
|
+
message: err && err.message ? err.message : String(err),
|
|
2880
|
+
});
|
|
2881
|
+
if (failedPaths.length <= 10) {
|
|
2882
|
+
process.stderr.write(`prerender: worker exception on ${pathSeg || '/'}: ${failedPaths[failedPaths.length - 1].message}\n`);
|
|
2883
|
+
}
|
|
2852
2884
|
} finally {
|
|
2853
2885
|
activeWorkers--;
|
|
2854
2886
|
if (activeWorkers === 0 && recycleGate.waitForZero) {
|