@uniweb/runtime 0.6.24 → 0.6.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/runtime",
3
- "version": "0.6.24",
3
+ "version": "0.6.25",
4
4
  "description": "Minimal runtime for loading Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -41,7 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@vitejs/plugin-react": "^4.5.2",
43
43
  "vite": "^7.3.1",
44
- "@uniweb/build": "0.8.27"
44
+ "@uniweb/build": "0.8.28"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "react": "^18.0.0 || ^19.0.0",
@@ -122,18 +122,10 @@ export default function PageRenderer() {
122
122
  let page = website?.getPage(location.pathname)
123
123
  if (page && website) website.setActivePage(location.pathname)
124
124
 
125
- // Handle redirect pages navigate to target immediately
126
- const redirectTarget = page?.redirect
127
- useEffect(() => {
128
- if (!redirectTarget) return
129
- if (redirectTarget.startsWith('http')) {
130
- window.location.replace(redirectTarget)
131
- } else {
132
- navigate(redirectTarget, { replace: true })
133
- }
134
- }, [redirectTarget, navigate])
125
+ // ─── Compute navigation targets (before hooks, no early returns) ───
135
126
 
136
- if (redirectTarget) return null
127
+ // Explicit redirect: in page.yml
128
+ const redirectTarget = page?.redirect || null
137
129
 
138
130
  // Auto-redirect for content-less pages (structural containers).
139
131
  // Folders with page.yml but no markdown keep their route in the hierarchy;
@@ -141,36 +133,36 @@ export default function PageRenderer() {
141
133
  const autoRedirectRoute = page && !page.redirect && !page.hasContent()
142
134
  ? page.getNavigableRoute()
143
135
  : null
144
- const shouldAutoRedirect = autoRedirectRoute && autoRedirectRoute !== page?.route
145
-
146
- useEffect(() => {
147
- if (!shouldAutoRedirect) return
148
- navigate(autoRedirectRoute, { replace: true })
149
- }, [shouldAutoRedirect, autoRedirectRoute, navigate])
150
-
151
- if (shouldAutoRedirect) return null
152
-
153
- // Rewrite pages are served by an external site — the host handles routing.
154
- // In SPA mode this shouldn't be reached (host proxies before JS loads),
155
- // but if it is (e.g., dev mode), do a full page reload to let the host handle it.
156
- if (page?.rewrite) {
157
- window.location.reload()
158
- return null
159
- }
136
+ const shouldAutoRedirect = !!(autoRedirectRoute && autoRedirectRoute !== page?.route)
160
137
 
161
138
  // If no page found, try the 404 page (do NOT fall back to activePage/homepage)
162
- const isNotFound = !page
139
+ const isNotFound = !page && !redirectTarget
163
140
  if (isNotFound) {
164
141
  page = website?.getNotFoundPage?.() || null
165
142
  }
166
143
 
167
- // Get head metadata from page (uses Page.getHeadMeta() if available)
144
+ // Head metadata compute for all cases (redirect pages get a fallback)
168
145
  const headMeta = page?.getHeadMeta?.() || {
169
146
  title: isNotFound ? 'Page Not Found' : (page?.title || 'Website'),
170
147
  description: page?.description || ''
171
148
  }
172
149
 
173
- // Manage head meta tags
150
+ // ─── All hooks called unconditionally (React rules of hooks) ───
151
+
152
+ useEffect(() => {
153
+ if (!redirectTarget) return
154
+ if (redirectTarget.startsWith('http')) {
155
+ window.location.replace(redirectTarget)
156
+ } else {
157
+ navigate(redirectTarget, { replace: true })
158
+ }
159
+ }, [redirectTarget, navigate])
160
+
161
+ useEffect(() => {
162
+ if (!shouldAutoRedirect) return
163
+ navigate(autoRedirectRoute, { replace: true })
164
+ }, [shouldAutoRedirect, autoRedirectRoute, navigate])
165
+
174
166
  useHeadMeta(headMeta, { siteName })
175
167
 
176
168
  // For dynamic pages created before collection data was available (hard reload),
@@ -183,6 +175,19 @@ export default function PageRenderer() {
183
175
  return website.dataStore.onUpdate(forceUpdate)
184
176
  }, [isDynamicPending, website])
185
177
 
178
+ // ─── Early returns (after all hooks) ───
179
+
180
+ if (redirectTarget) return null
181
+ if (shouldAutoRedirect) return null
182
+
183
+ // Rewrite pages are served by an external site — the host handles routing.
184
+ // In SPA mode this shouldn't be reached (host proxies before JS loads),
185
+ // but if it is (e.g., dev mode), do a full page reload to let the host handle it.
186
+ if (page?.rewrite) {
187
+ window.location.reload()
188
+ return null
189
+ }
190
+
186
191
  if (!page) {
187
192
  // No page and no 404 page defined - show shared fallback + dev debug info
188
193
  const requestedPath = location.pathname