@octanejs/vite-plugin 0.1.5 → 0.1.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.
package/CHANGELOG.md DELETED
@@ -1,254 +0,0 @@
1
- # @octanejs/vite-plugin
2
-
3
- ## 0.1.5
4
-
5
- ### Patch Changes
6
-
7
- - Updated dependencies [940ae5a]
8
- - Updated dependencies [6fceaf3]
9
- - Updated dependencies [62da8cc]
10
- - Updated dependencies [e737057]
11
- - octane@0.1.5
12
-
13
- ## 0.1.4
14
-
15
- ### Patch Changes
16
-
17
- - 6d332ad: octane.config `adapter` is now the full deploy contract `{ name?, adapt?, serve?, runtime? }`: after the production server build, closeBundle runs `adapter.adapt({ root, outDir, clientDir, serverDir, log })` so an adapter package (e.g. `@octanejs/adapter-vercel`) can restructure the output for its platform (SvelteKit-style). All parts are optional and independent — `serve`/`runtime` keep their existing meanings. `@octanejs/adapter-vercel` is registered as a server-only package: client-side imports of it resolve to the browser stub (which now also covers the octane adapter surface, `vercel`/`adapt`) instead of dragging node builtins into the client graph.
18
- - 8fc8554: Production SSR builds — octane apps now deploy server-rendered instead of SPA-only:
19
-
20
- - `vite build` produces BOTH bundles: hashed client assets in `{outDir}/client` (with the generated hydrate entry bundled into index.html via a static, Rollup-analyzable import map over the routes' entry/layout/preHydrate modules) and a self-contained SSR server at `{outDir}/server/entry.js` (app + octane bundled, only node builtins external; octane.config.ts is compiled in through a config-surface facade so neither the compiler nor vite ride along). The built index.html moves to `dist/server` — it is the SSR template, and leaving it in the static dir would shadow the handler at `/` on filesystem-first hosts.
21
- - `createHandler` (`@octanejs/vite-plugin/production`) is implemented: it matches RenderRoutes/ServerRoutes, runs middleware chains, and streams via the same `renderToReadableStream` engine dev SSR uses — the rendered body region and the `#__octane_data` payload are byte-identical to dev, so `hydrateRoot()` adopts production responses unchanged (covered by an end-to-end fixture test). Per-route `<link rel="stylesheet">`/`modulepreload` tags come from the client manifest. `server.render: 'buffered'` in octane.config.ts switches to the await-everything `prerender` for hosts that break streamed responses.
22
- - The server entry exports `handler` (Web fetch) and `nodeHandler` (Node `(req, res)`, for serverless wrappers such as a Vercel Node function), and auto-boots under `node dist/server/entry.js` — with the adapter's `serve()` when configured, else the new built-in Node server (`@octanejs/vite-plugin/node`) serving the client assets with immutable caching for `/assets/*`.
23
- - `octane-preview` now runs that entry for real (pre-deploy verification) and accepts `--port`.
24
-
25
- - 3c56d95: Close the four metaframework gaps the octane website surfaced:
26
-
27
- - `octane()` now accepts `exclude` and forwards it to the bundled compiler, so monorepo / aliased-to-source setups can skip the hook-slotting pass for hand-slot-forwarding binding sources (pnpm symlinks resolve `@octanejs/*` to `packages/*/src`, which the automatic node_modules skip can't see).
28
- - The dev SSR middleware skips Vite-owned requests (`/@` namespaces, `/__` internals, node_modules, `?import`-style transform queries, and extension-bearing paths that name a real file under the Vite root/publicDir — dotted page URLs like `/docs/v2.0` still SSR) before route matching, so a catch-all `'/*splat'` RenderRoute can SSR a real not-found page without swallowing `/@vite/client` or `/src/*.ts`. `RenderRoute` also takes a `status` (e.g. 404 for the catch-all) applied to the rendered response.
29
- - `appType: 'custom'` is now only a default: an explicit user `appType` wins, and `vite preview` is left on Vite's own SPA fallback so it can serve the client build (production SSR serving is still Phase 2).
30
- - RenderRoute components (and layouts) receive the request `url` (pathname + search) alongside `params`, on the server and on the hydrating client, and the new `router.preHydrate` config names a module whose default export the client entry awaits before `hydrateRoot` — the hook an app-level client router uses to commit its match tree so hydration adopts the server DOM. The generated client entry also hides its dynamic imports from Vite's `?import` query injection so the page/hook share module singletons with statically-imported copies.
31
-
32
- Dev SSR now streams: RenderRoutes render through `renderToReadableStream` (shell first, suspense boundaries flush out of order behind it) instead of the buffered `prerender`.
33
-
34
- - Updated dependencies [05fdef8]
35
- - Updated dependencies [e9ebfbf]
36
- - Updated dependencies [4ac4c98]
37
- - Updated dependencies [c2129eb]
38
- - Updated dependencies [4ac4c98]
39
- - Updated dependencies [8a44bb5]
40
- - Updated dependencies [6b0c244]
41
- - Updated dependencies [d3cf678]
42
- - Updated dependencies [05fdef8]
43
- - Updated dependencies [d19d4f3]
44
- - Updated dependencies [7e84258]
45
- - Updated dependencies [2f8c6ed]
46
- - Updated dependencies [8de4584]
47
- - Updated dependencies [9be6ba5]
48
- - Updated dependencies [db409de]
49
- - Updated dependencies [4f3c6c8]
50
- - Updated dependencies [62c3c4e]
51
- - Updated dependencies [3c56d95]
52
- - Updated dependencies [4c5b1d0]
53
- - Updated dependencies [b732399]
54
- - Updated dependencies [6d27cb0]
55
- - Updated dependencies [a3784b1]
56
- - Updated dependencies [fa77edf]
57
- - Updated dependencies [f5c9dba]
58
- - Updated dependencies [12d5410]
59
- - Updated dependencies [d71f1fc]
60
- - Updated dependencies [2f8c6ed]
61
- - Updated dependencies [63e51e8]
62
- - Updated dependencies [6d3b269]
63
- - Updated dependencies [b171c6d]
64
- - Updated dependencies [7f3d9c9]
65
- - Updated dependencies [820baaf]
66
- - Updated dependencies [c36cb32]
67
- - Updated dependencies [c33f409]
68
- - Updated dependencies [63e51e8]
69
- - Updated dependencies [8fc8554]
70
- - Updated dependencies [569daad]
71
- - Updated dependencies [6b7b727]
72
- - Updated dependencies [2ce7bc5]
73
- - Updated dependencies [c6a23f5]
74
- - Updated dependencies [c93aad5]
75
- - Updated dependencies [2942afb]
76
- - Updated dependencies [388b23c]
77
- - Updated dependencies [352cff1]
78
- - Updated dependencies [c7989eb]
79
- - Updated dependencies [dda2854]
80
- - Updated dependencies [dda2854]
81
- - Updated dependencies [3a9d855]
82
- - Updated dependencies [1f85217]
83
- - octane@0.1.4
84
-
85
- ## 0.1.3
86
-
87
- ### Patch Changes
88
-
89
- - 3431ec3: SSR: the buffered renderers (`renderToString`/`renderToStaticMarkup` in
90
- `octane/server`, `prerender` in `octane/static`) gain a `RenderOptions` argument:
91
- `nonce` (CSP nonce stamped on the emitted inline `<style>` tags and the suspense seed
92
- script — all renderers), plus `signal` (AbortSignal that rejects a suspended render
93
- when the request dies) and `timeoutMs` (per-render override of the suspense settle
94
- deadline) on the async `prerender`. `octane/server` now documents which exports are
95
- the compiler's private ABI and exports the `executeServerFunction` RPC executor the
96
- vite plugin's dev RPC handler loads via `ssrLoadModule('octane/server')` (previously a
97
- missing export, so any `module server` call crashed). Wire format is devalue, matching
98
- `@ripple-ts/adapter`'s client stub: devalue-encoded argument array in, devalue-encoded
99
- `{ value }` envelope out. See the new `docs/ssr.md` for the full SSR guide and the
100
- current gaps (streaming, selective hydration, production server build).
101
- - Updated dependencies [71b5167]
102
- - Updated dependencies [7b2acbd]
103
- - Updated dependencies [a000fa2]
104
- - Updated dependencies [71b5167]
105
- - Updated dependencies [735f5ca]
106
- - Updated dependencies [634c4b4]
107
- - Updated dependencies [1987d47]
108
- - Updated dependencies [fda2200]
109
- - Updated dependencies [71b5167]
110
- - Updated dependencies [fda2200]
111
- - Updated dependencies [3431ec3]
112
- - Updated dependencies [3afe217]
113
- - Updated dependencies [1a1f1db]
114
- - Updated dependencies [3431ec3]
115
- - Updated dependencies [5e3858f]
116
- - Updated dependencies [d2afbbb]
117
- - Updated dependencies [1987d47]
118
- - Updated dependencies [eb48930]
119
- - Updated dependencies [3431ec3]
120
- - Updated dependencies [87c5bc3]
121
- - octane@0.1.3
122
-
123
- ## 0.1.2
124
-
125
- ### Patch Changes
126
-
127
- - b3a9191: Rename `hydrate` → `hydrateRoot` and adopt React 18's shape. The hydration entry is now `hydrateRoot(container, <App/>)` — container first — and returns a full `Root` (with `.render()` and `.unmount()`), symmetric with `createRoot`. Previously `hydrate(Component, container, props)` put the component first and returned only `{ unmount }`. After hydration the returned root's `.render()` performs a normal client update against the adopted DOM (no re-hydration). The vite-plugin's generated client entry now imports and calls `hydrateRoot`.
128
- - 43d940d: Exclude `@octanejs/tanstack-query` from esbuild pre-bundling (optimizeDeps.exclude + ssr.noExternal). It ships a `.tsrx` provider component, so — like `octane` itself — its source must flow through the octane `.tsrx` transform rather than being pre-bundled by esbuild.
129
- - cb9ad82: Rename the project from `vyre` to `octane`. The runtime now publishes as `octane` and the Vite metaframework plugin as `@octanejs/vite-plugin`. Identifiers inherited from the Ripple fork were also renamed to Octane (e.g. `setIsRippleActEnvironment` → `setIsOctaneActEnvironment`, the metaframework `ripple()` plugin → `octane()`, and the `ripple.config.ts` convention → `octane.config.ts`). References to the upstream Ripple framework and its `@ripple-ts`/`@tsrx` packages are unchanged.
130
- - fcac573: Unify the server-rendering ABI to props-first, matching the client. A component body is now invoked as `(props, scope, extra)` on the server (it used to be `(scope, props, extra)`). This makes a plain `function Foo(props)` used at a `<Foo/>` site work the same on the server as on the client — including components that return a non-JSX value (a primitive coerced to text, an early return, `null`). SSR markup is unchanged (only the invocation order flipped), so hydration is unaffected. The server layout/page wrappers in the vite-plugin were updated to match.
131
- - 634fd52: Align the SSR API with React and reshape the render result to `{ html, css }`.
132
-
133
- The octane-invented `render(Component, props) → { head, body, css }` is replaced by
134
- React-aligned entry points:
135
-
136
- - `octane/server` (mirrors `react-dom/server`):
137
- - `renderToString(element, props?, options?)` — a single synchronous pass; a Suspense
138
- boundary that suspends renders its `@pending` fallback (no awaiting).
139
- - `renderToStaticMarkup(element, props?, options?)` — clean, non-hydratable HTML (no block
140
- or head-adoption markers, no suspense seed script).
141
- - `octane/static` (NEW subpath, mirrors `react-dom/static`):
142
- - `prerender(element, props?, options?)` — the await-everything behaviour of the old
143
- `render()`: all Suspense data resolves and success arms render, returning complete HTML.
144
-
145
- All three return `{ html, css }`. The separate `head` field is gone — hoisted `<title>`/
146
- `<meta>`/`<link>` fold into `html` (spliced into `<head>` when the render produced a
147
- document, else prepended), matching React 19's resource hoisting. `css` remains a distinct
148
- field (octane has scoped CSS that React core does not). `render` is removed; the vite
149
- plugin's dev SSR now uses `prerender`.
150
-
151
- - b3a9191: Fix the generated client hydration entry for routes with a layout. The layout's `children` ComponentBody was emitted with the old scope-first calling convention (`(s) => Component(s, { params })`), but octane's client runtime invokes a function child props-first as `({}, block, extra)` — so the page received the block as its props and rendered without its route data. The closure now calls `Component({ params }, scope, extra)`.
152
- - Updated dependencies [c19f1aa]
153
- - Updated dependencies [6983478]
154
- - Updated dependencies [6983478]
155
- - Updated dependencies [169c7c6]
156
- - Updated dependencies [86ae0c5]
157
- - Updated dependencies [357f841]
158
- - Updated dependencies [6675ac7]
159
- - Updated dependencies [f414710]
160
- - Updated dependencies [894d51c]
161
- - Updated dependencies [f44fb6b]
162
- - Updated dependencies [056c441]
163
- - Updated dependencies [aa9cc6e]
164
- - Updated dependencies [0f57f20]
165
- - Updated dependencies [f44fb6b]
166
- - Updated dependencies [067efa3]
167
- - Updated dependencies [f0c6c4d]
168
- - Updated dependencies [dd24fd5]
169
- - Updated dependencies [524939e]
170
- - Updated dependencies [e8ee0a8]
171
- - Updated dependencies [b680431]
172
- - Updated dependencies [524939e]
173
- - Updated dependencies [7f8dbc0]
174
- - Updated dependencies [a13acd1]
175
- - Updated dependencies [067efa3]
176
- - Updated dependencies [524939e]
177
- - Updated dependencies [894d51c]
178
- - Updated dependencies [894d51c]
179
- - Updated dependencies [1960647]
180
- - Updated dependencies [e8ee0a8]
181
- - Updated dependencies [93e2733]
182
- - Updated dependencies [149800c]
183
- - Updated dependencies [6983478]
184
- - Updated dependencies [6983478]
185
- - Updated dependencies [6983478]
186
- - Updated dependencies [169c7c6]
187
- - Updated dependencies [bbc3275]
188
- - Updated dependencies [ed6afad]
189
- - Updated dependencies [40bcb16]
190
- - Updated dependencies [c842fb7]
191
- - Updated dependencies [c62efa7]
192
- - Updated dependencies [524939e]
193
- - Updated dependencies [b3a9191]
194
- - Updated dependencies [ffe32c4]
195
- - Updated dependencies [e1f996b]
196
- - Updated dependencies [6983478]
197
- - Updated dependencies [fc36e15]
198
- - Updated dependencies [524939e]
199
- - Updated dependencies [405f06e]
200
- - Updated dependencies [f50c829]
201
- - Updated dependencies [b3a9191]
202
- - Updated dependencies [dd24fd5]
203
- - Updated dependencies [7042056]
204
- - Updated dependencies [6983478]
205
- - Updated dependencies [e031a7d]
206
- - Updated dependencies [86ae0c5]
207
- - Updated dependencies [a33cdd6]
208
- - Updated dependencies [067efa3]
209
- - Updated dependencies [fab1cb0]
210
- - Updated dependencies [6983478]
211
- - Updated dependencies [dd24fd5]
212
- - Updated dependencies [149800c]
213
- - Updated dependencies [6983478]
214
- - Updated dependencies [cb9ad82]
215
- - Updated dependencies [ea6352e]
216
- - Updated dependencies [1987bd7]
217
- - Updated dependencies [0c4d5a1]
218
- - Updated dependencies [dd24fd5]
219
- - Updated dependencies [fcac573]
220
- - Updated dependencies [41aa22a]
221
- - Updated dependencies [c842fb7]
222
- - Updated dependencies [6983478]
223
- - Updated dependencies [6983478]
224
- - Updated dependencies [634fd52]
225
- - Updated dependencies [149800c]
226
- - Updated dependencies [aafaaa9]
227
- - Updated dependencies [1987bd7]
228
- - Updated dependencies [74cbff9]
229
- - Updated dependencies [894d51c]
230
- - Updated dependencies [0040cad]
231
- - Updated dependencies [a3dce2f]
232
- - Updated dependencies [3656e32]
233
- - Updated dependencies [43d940d]
234
- - Updated dependencies [a032c5c]
235
- - Updated dependencies [7f8dbc0]
236
- - Updated dependencies [c71d4f3]
237
- - Updated dependencies [a3dce2f]
238
- - Updated dependencies [c2f3f69]
239
- - Updated dependencies [3656e32]
240
- - Updated dependencies [1987bd7]
241
- - Updated dependencies [f42e5b7]
242
- - Updated dependencies [cc2bca1]
243
- - Updated dependencies [6983478]
244
- - Updated dependencies [1987bd7]
245
- - octane@0.1.2
246
-
247
- ## 0.1.1
248
-
249
- ### Patch Changes
250
-
251
- - [#1](https://github.com/octanejs/octane/pull/1) [`dcdf237`](https://github.com/octanejs/octane/commit/dcdf2375ce3a8a2e00b1e1de04f65c2529fd287e) Thanks [@trueadm](https://github.com/trueadm)! - Rename the project from `vyre` to `octane`. The runtime now publishes as `octane` and the Vite metaframework plugin as `@octanejs/vite-plugin`. Identifiers inherited from the Ripple fork were also renamed to Octane (e.g. `setIsRippleActEnvironment` → `setIsOctaneActEnvironment`, the metaframework `ripple()` plugin → `octane()`, and the `ripple.config.ts` convention → `octane.config.ts`). References to the upstream Ripple framework and its `@ripple-ts`/`@tsrx` packages are unchanged.
252
-
253
- - Updated dependencies [[`dcdf237`](https://github.com/octanejs/octane/commit/dcdf2375ce3a8a2e00b1e1de04f65c2529fd287e)]:
254
- - octane@0.1.1
@@ -1,11 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>Fixture</title>
6
- <!--ssr-head-->
7
- </head>
8
- <body>
9
- <div id="root"><!--ssr-body--></div>
10
- </body>
11
- </html>
@@ -1,14 +0,0 @@
1
- import { defineConfig, RenderRoute } from '@octanejs/vite-plugin';
2
-
3
- export default defineConfig({
4
- router: {
5
- routes: [
6
- new RenderRoute({ path: '/', entry: ['Page', '/src/Page.tsrx'], layout: '/src/Layout.tsrx' }),
7
- new RenderRoute({
8
- path: '/pages/:slug',
9
- entry: ['Page', '/src/Page.tsrx'],
10
- layout: '/src/Layout.tsrx',
11
- }),
12
- ],
13
- },
14
- });
@@ -1,7 +0,0 @@
1
- {
2
- "name": "octane-vite-plugin-fixture-app",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "description": "Minimal app the production-build test builds end-to-end (node_modules is symlinked by the test setup)."
7
- }
@@ -1,6 +0,0 @@
1
- export default function Layout(props: { children: unknown }) @{
2
- <div class="shell">
3
- <nav>{'fixture-nav'}</nav>
4
- {props.children}
5
- </div>
6
- }
@@ -1,17 +0,0 @@
1
- import { useState } from 'octane';
2
-
3
- // A page with a dynamic text hole, a param read, and an event handler — enough
4
- // to prove the server bundle renders hydratable markup (markers + scoped
5
- // state) identical to dev SSR.
6
- export function Page(props: { params: Record<string, string>; url: string }) @{
7
- const [count] = useState(1);
8
- <main>
9
- <h1>
10
- {'Fixture page ' + (props.params.slug ?? 'home')}
11
- </h1>
12
- <p class="url">{props.url}</p>
13
- <p class="count">
14
- {'Count: ' + count}
15
- </p>
16
- </main>
17
- }
@@ -1,12 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import { octane } from '@octanejs/vite-plugin';
3
-
4
- // SSR module graphs (dev + server build) must see the server runtime for bare
5
- // 'octane' imports from non-compiled sources (none here, but keeps the fixture
6
- // shaped like a real app).
7
- export default defineConfig({
8
- plugins: [octane()],
9
- ssr: {
10
- noExternal: [/^octane($|\/)/],
11
- },
12
- });
@@ -1,134 +0,0 @@
1
- // createHandler unit tests — the production handler's pure assembly decisions
2
- // (template splitting, hydration payload shape, status, preload tags, the
3
- // buffered fallback, server routes, 404) with stub renderers. The real-render
4
- // byte-compat path is covered end-to-end in production.test.ts.
5
- import { describe, it, expect } from 'vitest';
6
- import { createHandler } from '../src/server/production.js';
7
- import { RenderRoute, ServerRoute } from '../src/routes.js';
8
-
9
- const TEMPLATE = `<!doctype html>
10
- <html><head><!--ssr-head--></head><body><div id="root"><!--ssr-body--></div>
11
- <script type="module" src="/assets/hydrate-abc.js"></script></body></html>`;
12
-
13
- function Page() {
14
- return '<main>page</main>';
15
- }
16
-
17
- function streamOf(text: string): ReadableStream<Uint8Array> {
18
- return new ReadableStream({
19
- start(controller) {
20
- controller.enqueue(new TextEncoder().encode(text));
21
- controller.close();
22
- },
23
- });
24
- }
25
-
26
- const baseDeps = {
27
- renderToReadableStream: async () =>
28
- streamOf('<style data-octane="x">.x{}</style><main>page</main>'),
29
- prerender: async () => ({
30
- html: '<main>page</main>',
31
- css: '<style data-octane="x">.x{}</style>',
32
- }),
33
- htmlTemplate: TEMPLATE,
34
- executeServerFunction: async () => '',
35
- };
36
-
37
- function makeManifest(overrides: Record<string, unknown> = {}) {
38
- const routes = [
39
- new RenderRoute({ path: '/', entry: ['Page', '/src/Page.tsrx'] }),
40
- new RenderRoute({ path: '/*splat', entry: ['Page', '/src/Page.tsrx'], status: 404 }),
41
- new ServerRoute({
42
- path: '/api/ping',
43
- handler: () => new Response('pong', { status: 200 }),
44
- }),
45
- ];
46
- return {
47
- routes,
48
- components: { '/src/Page.tsrx': { Page } },
49
- layouts: {},
50
- middlewares: [],
51
- ...overrides,
52
- };
53
- }
54
-
55
- describe('createHandler', () => {
56
- it('streams template prefix → render stream → suffix with the dev-shaped data script', async () => {
57
- const handler = createHandler(makeManifest() as any, baseDeps as any);
58
- const response = await handler(new Request('http://localhost/?q=1'));
59
- expect(response.status).toBe(200);
60
- const html = await response.text();
61
-
62
- // Assembly: styles + markup inside #root, template suffix intact.
63
- expect(html).toContain(
64
- '<div id="root"><style data-octane="x">.x{}</style><main>page</main></div>',
65
- );
66
- expect(html).toContain('src="/assets/hydrate-abc.js"');
67
-
68
- // The payload matches dev render-route.js: same keys, same order.
69
- const payload = html.match(/__octane_data" type="application\/json">(.*?)<\/script>/s)![1];
70
- expect(payload).toBe(
71
- JSON.stringify({
72
- entry: '/src/Page.tsrx',
73
- exportName: 'Page',
74
- layout: null,
75
- routeIndex: 0,
76
- params: {},
77
- url: '/?q=1',
78
- preHydrate: null,
79
- }),
80
- );
81
- });
82
-
83
- it('uses the RenderRoute status (catch-all 404) and route params', async () => {
84
- const handler = createHandler(makeManifest() as any, baseDeps as any);
85
- const response = await handler(new Request('http://localhost/not/a/page'));
86
- expect(response.status).toBe(404);
87
- const html = await response.text();
88
- expect(html).toContain('"params":{"splat":"not/a/page"}');
89
- });
90
-
91
- it("render: 'buffered' awaits prerender and sends one document (css leads the body)", async () => {
92
- let streamed = false;
93
- const deps = {
94
- ...baseDeps,
95
- renderToReadableStream: async () => {
96
- streamed = true;
97
- return streamOf('');
98
- },
99
- };
100
- const handler = createHandler(makeManifest({ render: 'buffered' }) as any, deps as any);
101
- const html = await (await handler(new Request('http://localhost/'))).text();
102
- expect(streamed).toBe(false);
103
- expect(html).toContain(
104
- '<div id="root"><style data-octane="x">.x{}</style><main>page</main></div>',
105
- );
106
- });
107
-
108
- it('emits stylesheet + modulepreload tags for the matched entry from clientAssets', async () => {
109
- const handler = createHandler(
110
- makeManifest({
111
- clientAssets: {
112
- '/src/Page.tsrx': { js: 'assets/Page-123.js', css: ['assets/Page-123.css'] },
113
- },
114
- }) as any,
115
- baseDeps as any,
116
- );
117
- const html = await (await handler(new Request('http://localhost/'))).text();
118
- expect(html).toContain('<link rel="stylesheet" href="/assets/Page-123.css">');
119
- expect(html).toContain('<link rel="modulepreload" href="/assets/Page-123.js">');
120
- });
121
-
122
- it('serves ServerRoutes and 404s unmatched paths when no catch-all exists', async () => {
123
- const manifest = makeManifest();
124
- (manifest.routes as unknown[]).splice(1, 1); // drop the catch-all
125
- const handler = createHandler(manifest as any, baseDeps as any);
126
-
127
- const api = await handler(new Request('http://localhost/api/ping'));
128
- expect(api.status).toBe(200);
129
- expect(await api.text()).toBe('pong');
130
-
131
- const missing = await handler(new Request('http://localhost/nope'));
132
- expect(missing.status).toBe(404);
133
- });
134
- });
@@ -1,155 +0,0 @@
1
- // @octanejs/vite-plugin unit tests — the pure decision logic the website
2
- // surfaced gaps in: the dev-middleware's Vite-owned URL filter, `octane()`
3
- // option forwarding to the bundled compiler, the appType default, and the
4
- // config resolution of `router.preHydrate` / RenderRoute `status`.
5
- import { fileURLToPath } from 'node:url';
6
- import { dirname } from 'node:path';
7
- import { describe, it, expect } from 'vitest';
8
- import { octane, isViteOwnedUrl, resolveOctaneConfig, RenderRoute } from '../src/index.js';
9
- import { RESOLVED_ADAPTER_BROWSER_STUB_ID } from '../src/project-codegen.js';
10
-
11
- function url(u: string): URL {
12
- return new URL(u, 'http://localhost');
13
- }
14
-
15
- // Use this package as the fake Vite root: '/src/index.js' and '/types/index.d.ts'
16
- // are real files under it, '/docs/v2.0' etc. are not.
17
- const PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
18
-
19
- describe('isViteOwnedUrl', () => {
20
- it('skips Vite-internal namespaces and module/asset requests', () => {
21
- expect(isViteOwnedUrl(url('/@vite/client'))).toBe(true);
22
- expect(isViteOwnedUrl(url('/@id/virtual:octane-hydrate'))).toBe(true);
23
- expect(isViteOwnedUrl(url('/@fs/Users/x/repo/src/main.ts'))).toBe(true);
24
- expect(isViteOwnedUrl(url('/__open-in-editor?file=x'))).toBe(true);
25
- expect(isViteOwnedUrl(url('/node_modules/.vite/deps/chunk.js'))).toBe(true);
26
- expect(isViteOwnedUrl(url('/src/main.ts'))).toBe(true);
27
- expect(isViteOwnedUrl(url('/src/app/App.tsrx?v=abc123'))).toBe(true);
28
- expect(isViteOwnedUrl(url('/favicon.svg'))).toBe(true);
29
- expect(isViteOwnedUrl(url('/src/worker?worker'))).toBe(true);
30
- expect(isViteOwnedUrl(url('/src/logo?url'))).toBe(true);
31
- });
32
-
33
- it('keeps page navigations', () => {
34
- expect(isViteOwnedUrl(url('/'))).toBe(false);
35
- expect(isViteOwnedUrl(url('/docs'))).toBe(false);
36
- expect(isViteOwnedUrl(url('/docs/quick-start'))).toBe(false);
37
- expect(isViteOwnedUrl(url('/definitely/not/a/page'))).toBe(false);
38
- expect(isViteOwnedUrl(url('/search?q=octane'))).toBe(false);
39
- // A bare dotfile segment is not an extension.
40
- expect(isViteOwnedUrl(url('/.well-known'))).toBe(false);
41
- // VALUED params matching a marker name are app query strings — Vite's
42
- // transform markers are always bare (`?url`, `?raw`, `&import`).
43
- expect(isViteOwnedUrl(url('/docs?url=https://example.com'))).toBe(false);
44
- expect(isViteOwnedUrl(url('/page?raw=1'))).toBe(false);
45
- expect(isViteOwnedUrl(url('/jobs?worker=nurse'))).toBe(false);
46
- });
47
-
48
- it('with fileRoots, an extension only counts when a real file backs it', () => {
49
- // Real files under the root → Vite's.
50
- expect(isViteOwnedUrl(url('/src/index.js'), [PKG_ROOT])).toBe(true);
51
- expect(isViteOwnedUrl(url('/types/index.d.ts?v=abc'), [PKG_ROOT])).toBe(true);
52
- // Dotted PAGE urls → SSR them.
53
- expect(isViteOwnedUrl(url('/docs/v2.0'), [PKG_ROOT])).toBe(false);
54
- expect(isViteOwnedUrl(url('/users/jane.doe'), [PKG_ROOT])).toBe(false);
55
- // A missing asset also SSRs (the app's 404 page beats a bare dev 404).
56
- expect(isViteOwnedUrl(url('/missing.png'), [PKG_ROOT])).toBe(false);
57
- // Multiple roots (root + publicDir).
58
- expect(isViteOwnedUrl(url('/src/index.js'), ['/nowhere', PKG_ROOT])).toBe(true);
59
- // Non-file checks are unaffected by fileRoots.
60
- expect(isViteOwnedUrl(url('/@vite/client'), [PKG_ROOT])).toBe(true);
61
- expect(isViteOwnedUrl(url('/src/worker?worker'), [PKG_ROOT])).toBe(true);
62
- });
63
- });
64
-
65
- describe('octane() plugin factory', () => {
66
- it('forwards `exclude` to the bundled compiler (hook-slotting skip)', () => {
67
- const [compiler] = octane({ exclude: ['/packages/tanstack-router/src/'] });
68
- const code =
69
- "import { useState } from 'octane';\nexport function useThing() { return useState(0); }\n";
70
- const transform = compiler.transform as (code: string, id: string) => unknown;
71
-
72
- // A pnpm-symlinked binding source resolves to /packages/*/src — excluded,
73
- // so the hand-slot-forwarding file passes through untouched.
74
- expect(transform.call({}, code, '/repo/packages/tanstack-router/src/useRouter.ts')).toBeNull();
75
- // App code is still slotted.
76
- expect(transform.call({}, code, '/repo/src/useThing.ts')).not.toBeNull();
77
- });
78
-
79
- it("defaults appType to 'custom' for dev, but respects the user and `vite preview`", async () => {
80
- const [, meta] = octane();
81
- const config = meta.config as (
82
- userConfig: object,
83
- env: object,
84
- ) => Promise<{ appType?: string }>;
85
-
86
- expect((await config({}, { command: 'serve', mode: 'development' })).appType).toBe('custom');
87
- // An explicit user appType wins.
88
- expect(
89
- (await config({ appType: 'spa' }, { command: 'serve', mode: 'development' })).appType,
90
- ).toBe(undefined);
91
- // `vite preview` serves static files with Vite's own fallback; the
92
- // production SSR build is previewed with `octane-preview` instead.
93
- expect(
94
- (await config({}, { command: 'serve', mode: 'production', isPreview: true })).appType,
95
- ).toBe(undefined);
96
- });
97
- });
98
-
99
- describe('resolveOctaneConfig', () => {
100
- const entry = ['App', '/src/App.tsrx'] as const;
101
-
102
- it('passes router.preHydrate through and validates its shape', () => {
103
- const resolved = resolveOctaneConfig({
104
- router: {
105
- routes: [new RenderRoute({ path: '/', entry })],
106
- preHydrate: '/src/pre-hydrate.ts',
107
- },
108
- });
109
- expect(resolved.router.preHydrate).toBe('/src/pre-hydrate.ts');
110
- // Absent stays absent.
111
- expect(resolveOctaneConfig({}).router.preHydrate).toBe(undefined);
112
- // Must be a Vite-root path the browser can dynamic-import.
113
- expect(() =>
114
- resolveOctaneConfig({ router: { routes: [], preHydrate: 'src/pre-hydrate.ts' } }),
115
- ).toThrow(/preHydrate/);
116
- });
117
-
118
- it('accepts an integer RenderRoute status and rejects anything else', () => {
119
- const route = new RenderRoute({ path: '/*splat', entry, status: 404 });
120
- expect(route.status).toBe(404);
121
- expect(resolveOctaneConfig({ router: { routes: [route] } }).router.routes[0]).toBe(route);
122
- expect(() =>
123
- resolveOctaneConfig({
124
- router: { routes: [new RenderRoute({ path: '/', entry, status: 4.04 })] },
125
- }),
126
- ).toThrow(/status/);
127
- });
128
- });
129
-
130
- describe('server-only adapter browser stub', () => {
131
- const [, meta] = octane();
132
- const resolveId = meta.resolveId as (
133
- id: string,
134
- importer?: string,
135
- options?: { ssr?: boolean },
136
- ) => Promise<string | null>;
137
- const load = meta.load as (id: string) => Promise<string | undefined>;
138
-
139
- it('client-side imports of adapter packages resolve to the stub, server gets the real one', async () => {
140
- for (const id of ['@octanejs/adapter-vercel', '@ripple-ts/adapter-node']) {
141
- expect(await resolveId(id, undefined, { ssr: false })).toBe(RESOLVED_ADAPTER_BROWSER_STUB_ID);
142
- }
143
- expect(await resolveId('@octanejs/adapter-vercel', undefined, { ssr: true })).toBe(null);
144
- });
145
-
146
- it('the stub covers the octane adapter surface with no node builtins', async () => {
147
- const source = (await load(RESOLVED_ADAPTER_BROWSER_STUB_ID)) as string;
148
- // The union of the listed adapters' public names — a client import of any
149
- // of them must resolve (and only throw on USE).
150
- for (const name of ['vercel', 'adapt', 'serve']) {
151
- expect(source).toContain(`export function ${name}`);
152
- }
153
- expect(source).not.toContain('node:');
154
- });
155
- });