@warlock.js/web 5.2.1 → 5.2.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to `@warlock.js/web` are documented here.
4
4
 
5
+ ## 5.2.3 - 2026-09-02
6
+
7
+ ### Fixed
8
+
9
+ - The generated Web starter now projects and hydrates unchanged with one `index` page identity, universal localization registration, and deterministic browser markup.
10
+
11
+ ## 5.2.2
12
+
13
+ ### Fixed
14
+
15
+ - Restored exact Core and Seal peer pins at the family's shared 5.2.2 version.
16
+ The partial 5.2.1 release could not satisfy reciprocal family peer pins.
17
+
5
18
  ## 5.2.1
6
19
 
7
20
  ### Fixed
package/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: add-web-to-an-app
11
- description: 'Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/home.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/home.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.'
11
+ description: 'Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/index.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/index.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.'
12
12
  ---
13
13
 
14
14
  # Warlock — add web to an app
@@ -27,11 +27,13 @@ On the ordinary path it leaves this application-owned shape:
27
27
  src/
28
28
  web/
29
29
  root.tsx
30
- home.page.tsx
30
+ index.page.tsx
31
31
  warlock.config.ts
32
32
  ```
33
33
 
34
- The generated home page declares `route = "/"`. The generated root owns the document, renders `<Head />`, keeps `{children}` inside `#root`, and renders `<Scripts />`.
34
+ The generated index page declares `route = { path: "/", name: "index" }`, so the server payload and client registry consume one explicit identity. Universal localization setup is inside its synchronous `register()` hook. The generated root owns the document, renders `<Head />`, keeps `{children}` inside `#root`, and renders `<Scripts />`.
35
+
36
+ Every added `@warlock.js/*` package is saved at the exact version of the Core CLI executing the command. Third-party dependency ranges stay as declared by the feature.
35
37
 
36
38
  ## Connector registration
37
39
 
@@ -76,9 +78,9 @@ Indented `router.get("/", ...)` declarations inside a prefixed group are not roo
76
78
 
77
79
  - No `src/app/shared/routes.ts`, or no top-level `GET "/"`: create the root page normally.
78
80
  - Exactly one recognized top-level `GET "/"` and no top-level `GET "/welcome"`: move it to `/welcome`, then create the home page.
79
- - More than one top-level `GET "/"`, an existing top-level `GET "/welcome"`, an unreadable/unwritable routes file, or an unrecognized rewrite: create `root.tsx`, do not create `home.page.tsx`, set a failing exit code, and still register the connector.
81
+ - More than one top-level `GET "/"`, an existing top-level `GET "/welcome"`, an unreadable/unwritable routes file, or an unrecognized rewrite: create `root.tsx`, do not create `index.page.tsx`, set a failing exit code, and still register the connector.
80
82
 
81
- On the refusal path, free `/` yourself and create a page with either `route = "/"` or another literal route.
83
+ On the refusal path, free `/` yourself and create a page with either `route = { path: "/", name: "index" }` or another literal route.
82
84
 
83
85
  ## Re-running the command
84
86
 
@@ -319,6 +321,24 @@ The browser boundary is decided by the import graph, not by the file's location.
319
321
 
320
322
  Keep server-only repository and service reads inside `loader`. Do not read them from module-scope initializers or the default component.
321
323
 
324
+ ### `.client` does not isolate SSR in 5.2
325
+
326
+ A `.client.tsx` suffix is a naming convention, not an SSR-isolation boundary. A
327
+ module statically imported by a page, layout, root, or any of their imports is
328
+ still evaluated by the server. Top-level browser globals such as `window`
329
+ therefore crash SSR boot. Warlock 5.2 does not ship a client-only component
330
+ primitive; code that requires browser globals at module scope cannot be part of
331
+ the SSR page graph.
332
+
333
+ ### Static assets use `public/` in 5.2
334
+
335
+ The production server build does not support imported non-stylesheet assets. An
336
+ import such as `import logo from "./logo.svg"` works under Vite in development
337
+ but `warlock build` refuses it rather than emit a server URL that disagrees with
338
+ the client bundle. Put the file in the application's `public/` directory and
339
+ reference it by root URL: `public/logo.svg` is `/logo.svg`. Stylesheet imports
340
+ are the exception and remain supported.
341
+
322
342
  ## Editing a page in development
323
343
 
324
344
  `warlock dev` decides Fast Refresh vs. a full reload by comparing the module's *skeleton* — its source with every component body masked out — across the edit. Everything outside a component body is part of the skeleton: imports, module-level declarations, and all server exports (`route`, `middleware`, `validation`, `loader`, `metadata`). The skeleton moving, with or without a simultaneous JSX change, forces a full reload; the skeleton holding still defers to Fast Refresh.
@@ -341,6 +361,8 @@ Changed in 5.2: through 5.1 the route table was built once at boot and never aga
341
361
 
342
362
  - **A page with no `route` is not unreachable.** It derives a real URL from its file location — see [Filesystem routing](#filesystem-routing). Nothing about a route-less page is refused anymore.
343
363
  - **A page with no default export IS refused.** Named exports alone fail the build naming the file, instead of serving a blank 200.
364
+ - **`.client.tsx` does not prevent SSR evaluation.** It is a naming convention in 5.2, not a client-only component primitive.
365
+ - **Imported static assets do not build in 5.2.** Put them in `public/` and reference their root URL; CSS imports remain supported.
344
366
  - **`[...slug]` is not a catch-all.** It is read as a literal path segment and silently produces an unreachable route — see [Catch-all segments are NOT supported](#catch-all-segments-are-not-supported).
345
367
  - **`process.env` is refused in the client/universal graph, `PUBLIC_` prefix included.** Read env values in a loader and return them as page data; see [`load-page-data/SKILL.md`](../load-page-data/SKILL.md).
346
368
  - **Keep `route` literal.** A computed route cannot be discovered without executing app code and is refused.
@@ -508,7 +530,7 @@ When `validating` is absent or empty, pages validate query + params, with params
508
530
 
509
531
  ## Execution order
510
532
 
511
- Loaders run **sequentially, root to leaf, each one awaited before the next starts**: `root.tsx`'s `AppLoader`, then the layout's `LayoutLoader`, then the page's `PageLoader`. There are exactly three levels a page has at most one _rendering_ layout (nested rendering layouts are a build error), so there is no outer-layout/inner-layout loader chain to reason about. Layout _middleware_ still composes across the whole ancestry; only the loader levels are capped at three.
533
+ Loaders run **sequentially, root to leaf, each one awaited before the next starts**: `root.tsx`'s `AppLoader`, then every matched `LayoutLoader` from outermost to innermost, then the page's `PageLoader`. The runtime has three top-level slots (`app`, `layout`, `page`), but the layout slot composes the full matched layout chain. A page may have only one _rendering_ layout; prefix-, middleware-, and loader-only layouts may still appear at multiple ancestry levels.
512
534
 
513
535
  **The first core `Response` a loader returns is terminal.** Returning a `Response` object stops the pipeline immediately: no lower loader starts, `metadata` is not resolved, and that response is sent as-is. It is more terminal than a short-circuit — because the response is returned whole, the buffered header/cookie writes made at that same level are discarded along with everything below it. Use `response.redirect()` / `response.notFound()` (which produce a `LoaderShortCircuit`, committing that level's buffer inclusively) when you want your buffered writes to survive; return a raw `Response` only when you mean "this exact response, nothing else."
514
536
 
@@ -902,7 +924,9 @@ export default function ProductsPage() {
902
924
  }
903
925
  ```
904
926
 
905
- Recognized asset imports survive the page's client projection. The client boundary is determined by the import graph, not by the file living under `web/`.
927
+ Recognized stylesheet imports survive the page's client projection. The client boundary is determined by the import graph, not by the file living under `web/`.
928
+
929
+ That support is specific to stylesheets in the production page graph. An imported non-stylesheet asset such as `import logo from "./logo.svg"` works under Vite in development but is refused by `warlock build` in 5.2. Put it in the application's `public/` directory and reference its root URL instead: `public/logo.svg` is `/logo.svg`.
906
930
 
907
931
  For each matched handler, Warlock builds one ordered CSS chain: `[root, ...matched layouts, page]`. A stylesheet imported directly by any member of that chain becomes a render-blocking link in the initial document in both development and production. Unrelated pages and layouts do not contribute CSS to this response.
908
932
 
@@ -1069,7 +1093,7 @@ Client navigation rebuilds the Layout + Page element tree at the same `#root` po
1069
1093
 
1070
1094
  ## Loader data
1071
1095
 
1072
- `LayoutLoader` receives the full `PageContext` and its return reaches `LayoutProps<typeof loader>["data"]`. Layout, App, and Page loaders run in parallel; a layout loader cannot read the page loader's result.
1096
+ `LayoutLoader` receives the full `PageContext` and its return reaches `LayoutProps<typeof loader>["data"]`. Loaders run sequentially as App, every matched layout from outermost to innermost, then Page. Each loader's return belongs only to its own component, so a layout loader still cannot read another loader's result.
1073
1097
 
1074
1098
  Use `shared` when multiple levels need one request-derived value, and write it in middleware before loaders run. See [load-page-data](../load-page-data/SKILL.md).
1075
1099
 
@@ -1212,7 +1236,7 @@ export default function App({ data, children }: AppProps<typeof loader>) {
1212
1236
  }
1213
1237
  ```
1214
1238
 
1215
- The App loader runs in parallel with layout and page loaders. Its return is for the App component; use `shared` for request data that multiple levels need.
1239
+ The App loader runs first and is awaited before the outermost layout loader starts; matched layout loaders then run outermost to innermost before the page loader. Its return is for the App component; use `shared` for request data that multiple levels need.
1216
1240
 
1217
1241
  ## Gotchas
1218
1242
 
package/llms.txt CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  ## Skills
18
18
 
19
- - [add-web-to-an-app](@warlock.js/web/add-web-to-an-app/SKILL.md): Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/home.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/home.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.
19
+ - [add-web-to-an-app](@warlock.js/web/add-web-to-an-app/SKILL.md): Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/index.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/index.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.
20
20
  - [create-a-page](@warlock.js/web/create-a-page/SKILL.md): Create an SSR React page under `src/web/**`, with either a literal `route` or a filesystem-derived one, a default component, an optional typed `loader`, page `metadata`, the `error.page.tsx` boundary, and the universal `register()` hook. Triggers: `*.page.tsx`, `route`, `PageLoader`, `PageProps`, `PageMetadata`, `error.page.tsx`, `register`, `[...slug]`; "create a page", "add an SSR route", "make a React page", "type page loader data", "add an error boundary", "catch-all route", "page renders blank 200", "page has no default export"; typical import `import type { PageLoader, PageProps } from "@warlock.js/web"`. Skip: root document shell — `@warlock.js/web/write-the-root/SKILL.md`; layout wrappers and prefixes — `@warlock.js/web/use-layouts/SKILL.md`; loader lifecycle and `shared` — `@warlock.js/web/load-page-data/SKILL.md`; competing frameworks `next`, `remix`, `react-router` file routes.
21
21
  - [load-page-data](@warlock.js/web/load-page-data/SKILL.md): Load App, Layout, and Page data with `AppLoader`, `LayoutLoader`, and `PageLoader`; type component `data`, validate page input, short-circuit with the buffered response, and publish request-scoped browser-safe values through `shared`. Triggers: `PageLoader`, `LayoutLoader`, `AppLoader`, `PageProps`, `shared`, `useShared`, `validation`, `request.validated`, `process.env`, `env("PUBLIC_...")`, `import.meta.env.PUBLIC_`; "load page data", "pass server data to React", "share request data", "redirect from a loader", "read an environment variable in a page", "process.env refused in the client build", "loader execution order", "return a Response from a loader". Skip: page module basics — `@warlock.js/web/create-a-page/SKILL.md`; layouts — `@warlock.js/web/use-layouts/SKILL.md`; mutation follow-up — `@warlock.js/web/navigate-on-the-client/SKILL.md`; competing loaders Next data functions, Remix loaders, React Server Components.
22
22
  - [navigate-on-the-client](@warlock.js/web/navigate-on-the-client/SKILL.md): Navigate hydrated pages with `<Link>`, resolve named URLs with `href()`, use `navigateTo` / `navigateBack`, prefetch on interaction, inspect the server match with `currentRoute()`, and re-fetch loaders after a mutation with `refresh()`. Triggers: `Link`, `href`, `navigateTo`, `navigateBack`, `refresh`, `currentRoute`, `previousRoute`; "navigate without a reload", "link to a named route", "refresh page data", "revalidate loaders", "client-side back"; typical import `import { Link, refresh } from "@warlock.js/web"`. Skip: define a page route — `@warlock.js/web/create-a-page/SKILL.md`; loader mechanics — `@warlock.js/web/load-page-data/SKILL.md`; root hydration boundary — `@warlock.js/web/write-the-root/SKILL.md`; competing routers `@mongez/react-router`, `react-router-dom`, Next navigation.
package/package.json CHANGED
@@ -11,8 +11,8 @@
11
11
  },
12
12
  "peerDependencies": {
13
13
  "@vitejs/plugin-react": "^5.2.0",
14
- "@warlock.js/core": "^5.2.0",
15
- "@warlock.js/seal": "^5.2.0",
14
+ "@warlock.js/core": "5.2.3",
15
+ "@warlock.js/seal": "5.2.3",
16
16
  "react": "*",
17
17
  "react-dom": "*",
18
18
  "vite": ">=7.3.5 <8"
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "author": "hassanzohdy",
39
39
  "license": "MIT",
40
- "version": "5.2.1",
40
+ "version": "5.2.3",
41
41
  "type": "module",
42
42
  "main": "./esm/index.mjs",
43
43
  "module": "./esm/index.mjs",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: add-web-to-an-app
3
- description: 'Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/home.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/home.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.'
3
+ description: 'Install the SSR page layer with `warlock add web`: add React/Vite peers, scaffold `src/web/root.tsx` and `src/web/index.page.tsx`, register `webConnector()`, and safely relocate the stock top-level `GET "/"` JSON route to `/welcome`. Triggers: `warlock add web`, `webConnector`, `src/web/root.tsx`, `src/web/index.page.tsx`, `GET "/welcome"`; "add web to an app", "install Warlock web", "scaffold SSR", "homepage route collision". Skip: author a page — `@warlock.js/web/create-a-page/SKILL.md`; customize the document — `@warlock.js/web/write-the-root/SKILL.md`; dev/build/start commands — `@warlock.js/core/run-app/SKILL.md`; competing installers `create-next-app`, `vite create`, `remix init`.'
4
4
  ---
5
5
 
6
6
  # Warlock — add web to an app
@@ -19,11 +19,13 @@ On the ordinary path it leaves this application-owned shape:
19
19
  src/
20
20
  web/
21
21
  root.tsx
22
- home.page.tsx
22
+ index.page.tsx
23
23
  warlock.config.ts
24
24
  ```
25
25
 
26
- The generated home page declares `route = "/"`. The generated root owns the document, renders `<Head />`, keeps `{children}` inside `#root`, and renders `<Scripts />`.
26
+ The generated index page declares `route = { path: "/", name: "index" }`, so the server payload and client registry consume one explicit identity. Universal localization setup is inside its synchronous `register()` hook. The generated root owns the document, renders `<Head />`, keeps `{children}` inside `#root`, and renders `<Scripts />`.
27
+
28
+ Every added `@warlock.js/*` package is saved at the exact version of the Core CLI executing the command. Third-party dependency ranges stay as declared by the feature.
27
29
 
28
30
  ## Connector registration
29
31
 
@@ -68,9 +70,9 @@ Indented `router.get("/", ...)` declarations inside a prefixed group are not roo
68
70
 
69
71
  - No `src/app/shared/routes.ts`, or no top-level `GET "/"`: create the root page normally.
70
72
  - Exactly one recognized top-level `GET "/"` and no top-level `GET "/welcome"`: move it to `/welcome`, then create the home page.
71
- - More than one top-level `GET "/"`, an existing top-level `GET "/welcome"`, an unreadable/unwritable routes file, or an unrecognized rewrite: create `root.tsx`, do not create `home.page.tsx`, set a failing exit code, and still register the connector.
73
+ - More than one top-level `GET "/"`, an existing top-level `GET "/welcome"`, an unreadable/unwritable routes file, or an unrecognized rewrite: create `root.tsx`, do not create `index.page.tsx`, set a failing exit code, and still register the connector.
72
74
 
73
- On the refusal path, free `/` yourself and create a page with either `route = "/"` or another literal route.
75
+ On the refusal path, free `/` yourself and create a page with either `route = { path: "/", name: "index" }` or another literal route.
74
76
 
75
77
  ## Re-running the command
76
78