lovable-ssr 0.1.8 → 0.1.10

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # lovable-ssr
2
2
 
3
- SSR and route data engine for [Lovable](https://lovable.dev) projects. Provides a route registry (singleton), `getServerData`-based data loading for SSR and SPA navigation, and an Express + Vite server.
3
+ SSR and route data engine for [Lovable](https://lovable.dev) projects: route registry, `getServerData`, Express + Vite server.
4
4
 
5
5
  **Documentation:** [Documentação completa](https://calm-meadow-5cf6.github-8c8.workers.dev/)
6
6
 
@@ -14,123 +14,11 @@ Peer dependencies: `react`, `react-dom`, `react-router-dom` (^18 / ^6).
14
14
 
15
15
  ## Quick start
16
16
 
17
- ### 1. Register routes
17
+ 1. Register routes with `registerRoutes(routes)`.
18
+ 2. Wrap your app with `BrowserRouteDataProvider` and render `AppRoutes` (inside `BrowserRouter`).
19
+ 3. (Optional) SSR: entry module + `createServer` from `lovable-ssr/server`.
18
20
 
19
- Define your routes and call `registerRoutes` so the framework can match paths and run `getServerData`:
20
-
21
- ```ts
22
- // src/routes.ts (or wherever you define routes)
23
- import { registerRoutes, type RouteConfig, type ComponentWithGetServerData } from 'lovable-ssr';
24
- import HomePage from '@/pages/HomePage';
25
- import VideoPage from '@/pages/VideoPage';
26
-
27
- export const routes: RouteConfig[] = [
28
- { path: '/', Component: HomePage, isSSR: true },
29
- { path: '/video/:id', Component: VideoPage, isSSR: true },
30
- { path: '*', Component: NotFound, isSSR: false },
31
- ];
32
-
33
- registerRoutes(routes);
34
- ```
35
-
36
- ### 2. App shell
37
-
38
- Ensure routes are loaded (so the registry is filled), then use `BrowserRouteDataProvider` and `AppRoutes`. The framework reads `window.__PRELOADED_DATA__` and the current pathname and fills the route data context for you (no need to declare `Window` or compute initial route/params in the app):
39
-
40
- ```tsx
41
- // src/App.tsx
42
- import './routes'; // runs registerRoutes(routes)
43
- import { BrowserRouteDataProvider, AppRoutes } from 'lovable-ssr';
44
- import { BrowserRouter } from 'react-router-dom';
45
-
46
- export default function App() {
47
- return (
48
- <BrowserRouter>
49
- <BrowserRouteDataProvider>
50
- <AppRoutes />
51
- </BrowserRouteDataProvider>
52
- </BrowserRouter>
53
- );
54
- }
55
- ```
56
-
57
- The package augments `Window` with `__PRELOADED_DATA__?: Record<string, unknown>` so you don't need to declare it. If you prefer to wire `RouteDataProvider` yourself (e.g. for testing), use `RouterService`, `RouteDataProvider`, and the same initial data logic.
58
-
59
- ### 3. SSR entry (optional)
60
-
61
- For SSR, add an entry module that registers routes and calls the framework’s `render` with your app wrappers (e.g. QueryClient, Toaster):
62
-
63
- ```tsx
64
- // src/entry-server.tsx
65
- import { registerRoutes, render as frameworkRender } from 'lovable-ssr';
66
- import { routes } from '@/routes';
67
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
68
-
69
- registerRoutes(routes);
70
-
71
- export async function render(url: string) {
72
- return frameworkRender(url, {
73
- wrap: (children) => (
74
- <QueryClientProvider client={new QueryClient()}>
75
- {children}
76
- </QueryClientProvider>
77
- ),
78
- });
79
- }
80
- ```
81
-
82
- ### 4. SSR server (optional)
83
-
84
- Run the Express + Vite server using the framework’s `createServer` (import from the `server` subpath so Node-only code is not bundled in the client). The server **preloads your entry module at startup** so `registerRoutes(routes)` runs and the route registry is filled before the first request; that way `isSsrRoute(pathname)` works correctly. You do **not** import the routes module in `server.ts` (that would pull React components into Node before Vite is ready and can cause “React is not defined” or similar).
85
-
86
- ```ts
87
- // src/ssr/server.ts
88
- import path from 'node:path';
89
- import { fileURLToPath } from 'node:url';
90
- import { createServer } from 'lovable-ssr/server';
91
-
92
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
93
- const root = path.resolve(__dirname, '../..');
94
-
95
- createServer({
96
- root,
97
- entryPath: 'src/ssr/entry-server.tsx',
98
- port: process.env.PORT ? Number(process.env.PORT) : 5173,
99
- })
100
- .then((s) => s.listen())
101
- .catch(console.error);
102
- ```
103
-
104
- Scripts:
105
-
106
- - **Dev SPA:** `vite`
107
- - **Dev SSR:** `tsx src/ssr/server.ts`
108
- - **Dev SSR with watch:** use `nodemon` to watch `src/ssr` and your app (e.g. `src/application` or `src/pages`); run with `NODE_OPTIONS=--inspect` and **Attach to Node** (port 9229, `restart: true`) so the server restarts on change and the debugger reconnects. See [SSR guide → Watch and debug](https://calm-meadow-5cf6.github-8c8.workers.dev/guide/ssr.html#_4-watch-and-debug-optional).
109
- - **Build SSR:** `vite build && vite build --ssr src/entry-server.tsx --outDir dist`
110
- - **Preview SSR:** `npm run build:ssr && NODE_ENV=production tsx src/ssr/server.ts`
111
-
112
- ## API
113
-
114
- - **Types:** `RouteConfig`, `ComponentWithGetServerData`
115
- - **Registry:** `registerRoutes(routes)`, `getRoutes()`
116
- - **Router:** `RouterService.matchRoute(pathname)`, `RouterService.routeParams(path, pathname)`, `RouterService.isSsrRoute(pathname)`
117
- - **Data:** `RouteDataProvider`, `useRouteData()`, `buildRouteKey(path, params)`, `RouteDataState`, `InitialRouteShape`
118
- - **UI:** `AppRoutes` (no props), `BrowserRouteDataProvider` (wraps children with `RouteDataProvider` using `window.__PRELOADED_DATA__` and current pathname; use inside `BrowserRouter`)
119
- - **SSR:** `render(url, options?)` with `options.wrap = (children) => ReactNode`
120
- - **Server:** `createServer(config)` and `runServer(config?)` from `lovable-ssr/server`
121
-
122
- ## Pages with `getServerData`
123
-
124
- Attach a `getServerData` function to the route component; it runs on the server for SSR and on the client when navigating to that route if data is not already cached (keyed by path + params).
125
-
126
- ```ts
127
- async function getServerData(params?: Record<string, string>) {
128
- const id = params?.id;
129
- const data = await fetch(`/api/videos/${id}`).then((r) => r.json());
130
- return { video: data };
131
- }
132
- VideoPage.getServerData = getServerData;
133
- ```
21
+ Details, examples, and API: see the [documentation](https://calm-meadow-5cf6.github-8c8.workers.dev/).
134
22
 
135
23
  ## License
136
24
 
@@ -17,7 +17,7 @@ export function AppRoutes() {
17
17
  const { data, setData } = useRouteData();
18
18
  const currentData = routeKey ? data[routeKey] : undefined;
19
19
  const getServerData = matchedRoute?.Component?.getServerData;
20
- params.searchParams = RouterService.searchParams(pathname);
20
+ params.searchParams = RouterService.searchParams(location.search ?? '');
21
21
  useEffect(() => {
22
22
  if (!routeKey || !getServerData || currentData !== undefined)
23
23
  return;
@@ -3,7 +3,8 @@ declare function matchPath(pathPattern: string, pathname: string): boolean;
3
3
  declare function isSsrRoute(pathname: string): boolean;
4
4
  declare function matchRoute(pathname: string): RouteConfig | undefined;
5
5
  declare function routeParams(routePath: string, pathname?: string): Record<'routeParams' | 'searchParams', Record<string, string>>;
6
- declare function searchParams(pathname: string): Record<string, string>;
6
+ /** Parses the URL query string (e.g. "?filter=FPS" or "filter=FPS") into key/value pairs. */
7
+ declare function searchParams(queryString: string): Record<string, string>;
7
8
  declare const RouterService: {
8
9
  isSsrRoute: typeof isSsrRoute;
9
10
  matchPath: typeof matchPath;
@@ -1 +1 @@
1
- {"version":3,"file":"RouterService.d.ts","sourceRoot":"","sources":["../../src/router/RouterService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAM7D;AAED,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,GAAG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAazH;AACD,iBAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG9D;AAED,QAAA,MAAM,aAAa;;;;;;CAMlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"RouterService.d.ts","sourceRoot":"","sources":["../../src/router/RouterService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAM7D;AAED,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,GAAG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAazH;AACD,6FAA6F;AAC7F,iBAAS,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGjE;AAED,QAAA,MAAM,aAAa;;;;;;CAMlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -36,9 +36,10 @@ function routeParams(routePath, pathname) {
36
36
  });
37
37
  return params;
38
38
  }
39
- function searchParams(pathname) {
40
- const searchParams = new URLSearchParams(pathname);
41
- return Object.fromEntries(searchParams.entries());
39
+ /** Parses the URL query string (e.g. "?filter=FPS" or "filter=FPS") into key/value pairs. */
40
+ function searchParams(queryString) {
41
+ const parsed = new URLSearchParams(queryString);
42
+ return Object.fromEntries(parsed.entries());
42
43
  }
43
44
  const RouterService = {
44
45
  isSsrRoute,
@@ -12,7 +12,7 @@ export async function render(url, options) {
12
12
  routeParams: {},
13
13
  searchParams: {},
14
14
  };
15
- const searchParams = matchedRoute ? RouterService.searchParams(pathname) : {};
15
+ const searchParams = matchedRoute ? RouterService.searchParams(fullUrl.search) : {};
16
16
  params.searchParams = searchParams;
17
17
  let preloadedData = { is_success: true };
18
18
  const getServerData = matchedRoute?.Component?.getServerData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lovable-ssr",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "SSR and route data engine for Lovable projects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",