mnfst-render 0.2.8 → 0.2.9

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 (2) hide show
  1. package/manifest.render.mjs +86 -110
  2. package/package.json +1 -1
@@ -77,133 +77,109 @@ async function flushAlpineEffects(page) {
77
77
  }
78
78
 
79
79
  /**
80
- * After locale + route sync, wait until (1) Manifest fires manifest:components-processed for that pass
81
- * (component swapping finished) and (2) no Alpine data _*_state is still loading / _localeChanging.
82
- * Listener is registered before dispatch so the initial page load event is ignored.
80
+ * After locale + route sync: run component swapping explicitly, then wait until Alpine data stores
81
+ * are settled. We call ManifestComponentsSwapping.processAll() directly because swapping only
82
+ * subscribes to manifest:route-change if window.ManifestRouting already exists at initialize() time;
83
+ * when the router loads after components, that listener is never registered and no second
84
+ * manifest:components-processed fires.
83
85
  */
84
86
  async function waitForManifestPrerenderPipeline(page, { allLocales, currentLocale, timeoutMs }) {
85
87
  const result = await page
86
88
  .evaluate(
87
- ({ localeList, loc, ms }) => {
88
- return new Promise((resolve) => {
89
- const deadline = Date.now() + ms;
90
- let dispatched = false;
91
- let sawComponents = false;
92
- let poll = 0;
89
+ async ({ localeList, loc, ms }) => {
90
+ const sleep = (n) => new Promise((r) => setTimeout(r, n));
91
+ const deadline = Date.now() + ms;
92
+
93
+ const checkData = () => {
94
+ try {
95
+ const Alpine = window.Alpine;
96
+ if (!Alpine?.store) return true;
97
+ const d = Alpine.store('data');
98
+ if (!d) return true;
99
+ if (d._localeChanging) return false;
100
+ for (const k of Object.keys(d)) {
101
+ if (!k.startsWith('_') || !k.endsWith('_state')) continue;
102
+ const s = d[k];
103
+ if (s && typeof s === 'object' && s.loading) return false;
104
+ }
105
+ return true;
106
+ } catch {
107
+ return true;
108
+ }
109
+ };
93
110
 
94
- const checkData = () => {
111
+ try {
112
+ const locales = Array.isArray(localeList) ? localeList : [];
113
+ if (loc && typeof loc === 'string') {
95
114
  try {
96
- const Alpine = window.Alpine;
97
- if (!Alpine?.store) return true;
98
- const d = Alpine.store('data');
99
- if (!d) return true;
100
- if (d._localeChanging) return false;
101
- for (const k of Object.keys(d)) {
102
- if (!k.startsWith('_') || !k.endsWith('_state')) continue;
103
- const s = d[k];
104
- if (s && typeof s === 'object' && s.loading) return false;
105
- }
106
- return true;
115
+ document.documentElement.lang = loc;
107
116
  } catch {
108
- return true;
117
+ /* no-op */
109
118
  }
110
- };
111
-
112
- const skipComponentsWait = () =>
113
- !window.__manifestComponentsInitialized || !window.ManifestComponentsSwapping;
114
-
115
- const cleanup = () => {
116
- if (poll) clearInterval(poll);
117
- window.removeEventListener('manifest:components-processed', onComponents);
118
- };
119
-
120
- const tryFinish = () => {
121
- if (!dispatched) return;
122
- if (skipComponentsWait()) {
123
- sawComponents = true;
119
+ }
120
+ const store = typeof Alpine !== 'undefined' && Alpine.store ? Alpine.store('locale') : null;
121
+ if (store) {
122
+ if (!Array.isArray(store.available) || store.available.length === 0) {
123
+ store.available = locales.slice();
124
+ } else {
125
+ store.available = Array.from(new Set([...store.available, ...locales]));
124
126
  }
125
- if (sawComponents && checkData()) {
126
- cleanup();
127
- resolve({ ok: true });
127
+ if (loc && typeof loc === 'string') {
128
+ store.current = loc;
128
129
  }
129
- };
130
+ }
130
131
 
131
- function onComponents() {
132
- if (!dispatched) return;
133
- sawComponents = true;
134
- tryFinish();
132
+ if (loc && typeof loc === 'string') {
133
+ window.dispatchEvent(new CustomEvent('localechange', { detail: { locale: loc } }));
135
134
  }
136
135
 
137
- window.addEventListener('manifest:components-processed', onComponents);
138
-
139
- poll = setInterval(() => {
140
- if (Date.now() > deadline) {
141
- cleanup();
142
- resolve({
143
- ok: false,
144
- reason: 'timeout',
145
- sawComponents,
146
- dataOk: checkData(),
147
- skippedComponents: skipComponentsWait(),
148
- });
149
- return;
150
- }
151
- tryFinish();
152
- }, 50);
136
+ const rawRoute = window.ManifestRoutingNavigation?.getCurrentRoute?.() ?? window.location.pathname;
137
+ const clean = String(rawRoute || '/').replace(/^\/+|\/+$/g, '');
138
+ const parts = clean ? clean.split('/') : [];
139
+ const logical =
140
+ parts.length > 0 && locales.includes(parts[0])
141
+ ? '/' + parts.slice(1).join('/')
142
+ : clean
143
+ ? '/' + clean
144
+ : '/';
145
+ const to = logical === '//' ? '/' : logical;
146
+ const normalizedPath =
147
+ typeof to === 'string' && to !== '/' ? to.replace(/^\/|\/$/g, '') : '/';
148
+
149
+ window.dispatchEvent(
150
+ new CustomEvent('manifest:route-change', {
151
+ detail: {
152
+ from: to,
153
+ to,
154
+ normalizedPath,
155
+ },
156
+ })
157
+ );
158
+ window.dispatchEvent(new PopStateEvent('popstate'));
153
159
 
154
- try {
155
- const locales = Array.isArray(localeList) ? localeList : [];
156
- if (loc && typeof loc === 'string') {
157
- try {
158
- document.documentElement.lang = loc;
159
- } catch {
160
- /* no-op */
161
- }
162
- }
163
- const store = typeof Alpine !== 'undefined' && Alpine.store ? Alpine.store('locale') : null;
164
- if (store) {
165
- if (!Array.isArray(store.available) || store.available.length === 0) {
166
- store.available = locales.slice();
167
- } else {
168
- store.available = Array.from(new Set([...store.available, ...locales]));
169
- }
170
- if (loc && typeof loc === 'string') {
171
- store.current = loc;
172
- }
160
+ if (window.ManifestComponentsSwapping?.processAll) {
161
+ try {
162
+ await window.ManifestComponentsSwapping.processAll(normalizedPath);
163
+ } catch (e) {
164
+ return { ok: false, reason: 'processAll-error', message: String(e?.message || e) };
173
165
  }
166
+ }
174
167
 
175
- if (loc && typeof loc === 'string') {
176
- window.dispatchEvent(new CustomEvent('localechange', { detail: { locale: loc } }));
168
+ while (Date.now() < deadline) {
169
+ if (checkData()) {
170
+ return { ok: true };
177
171
  }
178
-
179
- const rawRoute = window.ManifestRoutingNavigation?.getCurrentRoute?.() ?? window.location.pathname;
180
- const clean = String(rawRoute || '/').replace(/^\/+|\/+$/g, '');
181
- const parts = clean ? clean.split('/') : [];
182
- const logical =
183
- parts.length > 0 && locales.includes(parts[0])
184
- ? '/' + parts.slice(1).join('/')
185
- : clean
186
- ? '/' + clean
187
- : '/';
188
- const to = logical === '//' ? '/' : logical;
189
-
190
- dispatched = true;
191
- window.dispatchEvent(
192
- new CustomEvent('manifest:route-change', {
193
- detail: {
194
- from: to,
195
- to,
196
- normalizedPath: typeof to === 'string' && to !== '/' ? to.replace(/^\/|\/$/g, '') : '/',
197
- },
198
- })
199
- );
200
- window.dispatchEvent(new PopStateEvent('popstate'));
201
- tryFinish();
202
- } catch (err) {
203
- cleanup();
204
- resolve({ ok: false, reason: 'error', message: String(err?.message || err) });
172
+ await sleep(50);
205
173
  }
206
- });
174
+ return {
175
+ ok: false,
176
+ reason: 'timeout',
177
+ dataOk: checkData(),
178
+ hadSwapping: !!window.ManifestComponentsSwapping?.processAll,
179
+ };
180
+ } catch (err) {
181
+ return { ok: false, reason: 'error', message: String(err?.message || err) };
182
+ }
207
183
  },
208
184
  {
209
185
  localeList: allLocales,
@@ -216,7 +192,7 @@ async function waitForManifestPrerenderPipeline(page, { allLocales, currentLocal
216
192
  if (!result?.ok) {
217
193
  const parts = [`prerender: pipeline wait incomplete (${result?.reason ?? 'unknown'})`];
218
194
  if (result?.dataOk === false) parts.push('data still loading');
219
- if (result?.sawComponents === false && !result?.skippedComponents) parts.push('no manifest:components-processed');
195
+ if (result?.message) parts.push(result.message);
220
196
  process.stdout.write(`${parts.join('; ')}\n`);
221
197
  }
222
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst-render",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Render Manifest sites to static HTML for SEO",
5
5
  "type": "module",
6
6
  "bin": {