@kasperpeulen/vitest-plugin-nextjs 0.0.0-stage → 0.1.0-alpha.1
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 +249 -2
- package/dist/_virtual/_rolldown/runtime.js +14 -0
- package/dist/action-ids.d.ts +18 -0
- package/dist/action-ids.js +44 -0
- package/dist/adapter.d.ts +8 -0
- package/dist/adapter.js +12 -0
- package/dist/app-requests.d.ts +63 -0
- package/dist/app-requests.js +253 -0
- package/dist/browser-context.d.ts +24 -0
- package/dist/browser-context.js +157 -0
- package/dist/build-output.d.ts +68 -0
- package/dist/build-output.js +216 -0
- package/dist/build.d.ts +26 -0
- package/dist/build.js +39 -0
- package/dist/built-files.d.ts +9 -0
- package/dist/built-files.js +22 -0
- package/dist/cache.d.ts +44 -0
- package/dist/cache.js +99 -0
- package/dist/client-components.d.ts +3 -0
- package/dist/client-components.js +4 -0
- package/dist/client-file-key.d.ts +5 -0
- package/dist/client-file-key.js +5 -0
- package/dist/client-files.d.ts +83 -0
- package/dist/client-files.js +186 -0
- package/dist/client-graph.d.ts +94 -0
- package/dist/client-graph.js +266 -0
- package/dist/client-modules.d.ts +10 -0
- package/dist/client-modules.js +43 -0
- package/dist/client.d.ts +42 -0
- package/dist/client.js +263 -0
- package/dist/commonjs.d.ts +44 -0
- package/dist/commonjs.js +111 -0
- package/dist/compat/browser/client.d.ts +19 -0
- package/dist/compat/browser/client.js +13 -0
- package/dist/compat/build.d.ts +145 -0
- package/dist/compat/build.js +149 -0
- package/dist/compat/checks.d.ts +16 -0
- package/dist/compat/checks.js +148 -0
- package/dist/compat/patches.d.ts +13 -0
- package/dist/compat/patches.js +30 -0
- package/dist/compat/paths.d.ts +74 -0
- package/dist/compat/paths.js +160 -0
- package/dist/compat/rsc/handlers.d.ts +2 -0
- package/dist/compat/rsc/handlers.js +2 -0
- package/dist/compat/rsc/prerender.d.ts +10 -0
- package/dist/compat/rsc/prerender.js +9 -0
- package/dist/compat/server/headers.d.ts +3 -0
- package/dist/compat/server/headers.js +3 -0
- package/dist/compat/ssr/build-output.d.ts +4 -0
- package/dist/compat/ssr/build-output.js +3 -0
- package/dist/compat/ssr/cache.d.ts +6 -0
- package/dist/compat/ssr/cache.js +6 -0
- package/dist/compat/ssr/handler.d.ts +8 -0
- package/dist/compat/ssr/handler.js +8 -0
- package/dist/compat/ssr/node-server.d.ts +5 -0
- package/dist/compat/ssr/node-server.js +4 -0
- package/dist/compat/ssr/render-errors.d.ts +4 -0
- package/dist/compat/ssr/render-errors.js +4 -0
- package/dist/compat/ssr/senders.d.ts +6 -0
- package/dist/compat/ssr/senders.js +6 -0
- package/dist/compat/types.d.ts +8 -0
- package/dist/compat/types.js +1 -0
- package/dist/compile.d.ts +39 -0
- package/dist/compile.js +175 -0
- package/dist/disk.d.ts +16 -0
- package/dist/disk.js +13 -0
- package/dist/document-loads.d.ts +15 -0
- package/dist/document-loads.js +53 -0
- package/dist/document.d.ts +79 -0
- package/dist/document.js +330 -0
- package/dist/downloads.d.ts +15 -0
- package/dist/downloads.js +71 -0
- package/dist/esm-require.d.ts +11 -0
- package/dist/esm-require.js +32 -0
- package/dist/fast-crypto/index.js +1093 -0
- package/dist/fast-crypto.d.ts +20 -0
- package/dist/fast-crypto.js +151 -0
- package/dist/flight.d.ts +19 -0
- package/dist/flight.js +42 -0
- package/dist/form-submission.d.ts +15 -0
- package/dist/form-submission.js +27 -0
- package/dist/from-page.d.ts +5 -0
- package/dist/from-page.js +44 -0
- package/dist/globals.d.ts +1 -0
- package/dist/globals.js +197 -0
- package/dist/host-document.d.ts +26 -0
- package/dist/host-document.js +34 -0
- package/dist/host-return-shared.d.ts +5 -0
- package/dist/host-return-shared.js +5 -0
- package/dist/host-return.d.ts +5 -0
- package/dist/host-return.js +16 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +281 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +3 -0
- package/dist/leftovers.d.ts +11 -0
- package/dist/leftovers.js +55 -0
- package/dist/mode.d.ts +169 -0
- package/dist/mode.js +31 -0
- package/dist/node/LICENSES.md +3540 -0
- package/dist/node/assert/strict.js +29 -0
- package/dist/node/assert.js +3044 -0
- package/dist/node/buffer.js +1499 -0
- package/dist/node/child_process.js +45 -0
- package/dist/node/cluster.js +85 -0
- package/dist/node/console.js +96 -0
- package/dist/node/constants.js +495 -0
- package/dist/node/crypto.js +1388 -0
- package/dist/node/dgram.js +78 -0
- package/dist/node/diagnostics_channel.js +122 -0
- package/dist/node/dns/promises.js +129 -0
- package/dist/node/dns.js +137 -0
- package/dist/node/domain.js +43 -0
- package/dist/node/events.js +1155 -0
- package/dist/node/fs/promises.js +40 -0
- package/dist/node/fs.js +10375 -0
- package/dist/node/http.js +326 -0
- package/dist/node/http2.js +305 -0
- package/dist/node/https.js +57 -0
- package/dist/node/inspector/promises.js +66 -0
- package/dist/node/inspector.js +76 -0
- package/dist/node/module.js +205 -0
- package/dist/node/net.js +392 -0
- package/dist/node/os.js +259 -0
- package/dist/node/path/posix.js +25 -0
- package/dist/node/path/win32.js +25 -0
- package/dist/node/path.js +384 -0
- package/dist/node/perf_hooks.js +313 -0
- package/dist/node/process.js +452 -0
- package/dist/node/punycode.js +178 -0
- package/dist/node/querystring.js +934 -0
- package/dist/node/readline/promises.js +71 -0
- package/dist/node/readline.js +56 -0
- package/dist/node/repl.js +44 -0
- package/dist/node/sqlite.js +22 -0
- package/dist/node/stream/consumers.js +47 -0
- package/dist/node/stream/promises.js +6 -0
- package/dist/node/stream/web.js +52 -0
- package/dist/node/stream.js +4323 -0
- package/dist/node/string_decoder.js +276 -0
- package/dist/node/sys.js +6 -0
- package/dist/node/timers/promises.js +71 -0
- package/dist/node/timers.js +133 -0
- package/dist/node/tls.js +122 -0
- package/dist/node/trace_events.js +23 -0
- package/dist/node/tty.js +69 -0
- package/dist/node/url.js +1426 -0
- package/dist/node/util/types.js +1456 -0
- package/dist/node/util.js +4807 -0
- package/dist/node/v8.js +160 -0
- package/dist/node/vm.js +88 -0
- package/dist/node/wasi.js +16 -0
- package/dist/node/worker_threads.js +123 -0
- package/dist/node/zlib.js +1396 -0
- package/dist/node-builtins.d.ts +40 -0
- package/dist/node-builtins.js +74 -0
- package/dist/node-on-page.d.ts +46 -0
- package/dist/node-on-page.js +101 -0
- package/dist/node-platform.d.ts +36 -0
- package/dist/node-platform.js +152 -0
- package/dist/node-rendered.d.ts +21 -0
- package/dist/node-rendered.js +26 -0
- package/dist/node-server.d.ts +46 -0
- package/dist/node-server.js +193 -0
- package/dist/not-found-page.d.ts +19 -0
- package/dist/not-found-page.js +74 -0
- package/dist/optimizer-cache-key.d.ts +26 -0
- package/dist/optimizer-cache-key.js +73 -0
- package/dist/own-package.d.ts +1 -0
- package/dist/own-package.js +6 -0
- package/dist/page-store.d.ts +33 -0
- package/dist/page-store.js +80 -0
- package/dist/page-timers.d.ts +22 -0
- package/dist/page-timers.js +47 -0
- package/dist/page.d.ts +112 -0
- package/dist/page.js +549 -0
- package/dist/paths.d.ts +13 -0
- package/dist/paths.js +47 -0
- package/dist/plugin.d.ts +23 -0
- package/dist/plugin.js +1006 -0
- package/dist/prerender-verdict.d.ts +8 -0
- package/dist/prerender-verdict.js +67 -0
- package/dist/project/context.d.ts +546 -0
- package/dist/project/context.js +111 -0
- package/dist/project/entries.d.ts +42 -0
- package/dist/project/entries.js +189 -0
- package/dist/project/env-worker.d.ts +1 -0
- package/dist/project/env-worker.js +9 -0
- package/dist/project/env.d.ts +36 -0
- package/dist/project/env.js +82 -0
- package/dist/project/layers.d.ts +31 -0
- package/dist/project/layers.js +150 -0
- package/dist/project/loaders.d.ts +71 -0
- package/dist/project/loaders.js +300 -0
- package/dist/project/metadata.d.ts +8 -0
- package/dist/project/metadata.js +34 -0
- package/dist/project/module-exports.d.ts +29 -0
- package/dist/project/module-exports.js +77 -0
- package/dist/project/routes.d.ts +19 -0
- package/dist/project/routes.js +92 -0
- package/dist/project/routing.d.ts +46 -0
- package/dist/project/routing.js +280 -0
- package/dist/project/transform.d.ts +8 -0
- package/dist/project/transform.js +110 -0
- package/dist/project/webpack.d.ts +65 -0
- package/dist/project/webpack.js +191 -0
- package/dist/project.d.ts +269 -0
- package/dist/project.js +58 -0
- package/dist/registry-global.d.ts +5 -0
- package/dist/registry-global.js +5 -0
- package/dist/registry.d.ts +241 -0
- package/dist/registry.js +30 -0
- package/dist/route-chunks.d.ts +13 -0
- package/dist/route-chunks.js +32 -0
- package/dist/route-lookup.d.ts +34 -0
- package/dist/route-lookup.js +207 -0
- package/dist/routes-ahead-shared.d.ts +17 -0
- package/dist/routes-ahead-shared.js +9 -0
- package/dist/routes-ahead.d.ts +10 -0
- package/dist/routes-ahead.js +54 -0
- package/dist/rsc.d.ts +19 -0
- package/dist/rsc.js +171 -0
- package/dist/server-action.d.ts +9 -0
- package/dist/server-action.js +15 -0
- package/dist/server-code.d.ts +176 -0
- package/dist/server-code.js +357 -0
- package/dist/server-in-front.d.ts +37 -0
- package/dist/server-in-front.js +90 -0
- package/dist/server-layers.d.ts +22 -0
- package/dist/server-layers.js +33 -0
- package/dist/setup-browser.d.ts +1 -0
- package/dist/setup-browser.js +8 -0
- package/dist/setup-node.d.ts +1 -0
- package/dist/setup-node.js +6 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +62 -0
- package/dist/speed-features.d.ts +13 -0
- package/dist/speed-features.js +95 -0
- package/dist/ssr.d.ts +70 -0
- package/dist/ssr.js +480 -0
- package/dist/static-build.d.ts +36 -0
- package/dist/static-build.js +92 -0
- package/dist/storage.d.ts +31 -0
- package/dist/storage.js +94 -0
- package/dist/styles-shared.d.ts +137 -0
- package/dist/styles-shared.js +124 -0
- package/dist/styles.d.ts +102 -0
- package/dist/styles.js +730 -0
- package/dist/stylesheet-versions.d.ts +17 -0
- package/dist/stylesheet-versions.js +43 -0
- package/dist/tester.html +10 -0
- package/dist/use-cache.d.ts +15 -0
- package/dist/use-cache.js +93 -0
- package/dist/vitest-version.d.ts +6 -0
- package/dist/vitest-version.js +12 -0
- package/package.json +125 -3
package/dist/page.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Opened } from "./registry.js";
|
|
2
|
+
import { PageStore } from "./page-store.js";
|
|
3
|
+
import { NodeOnPage } from "./node-on-page.js";
|
|
4
|
+
import { HistoryEntry } from "./document.js";
|
|
5
|
+
import { ShellErrorAnswer } from "./ssr.js";
|
|
6
|
+
|
|
7
|
+
//#region src/page.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* A navigation without a page of the app, which has the router, under
|
|
10
|
+
* Vitest: a component that a test file with `"use client"` rendered has none.
|
|
11
|
+
*/
|
|
12
|
+
declare function noRouter(url: URL): Error;
|
|
13
|
+
/**
|
|
14
|
+
* A page of the app, from the first line of its load until it is left, with
|
|
15
|
+
* the node of `renderServer()` on it, and where it reports its errors. Its
|
|
16
|
+
* load makes it part by part, and each part that would outlive the page says
|
|
17
|
+
* how it is released. Leaving stops the load at once, and releases the
|
|
18
|
+
* parts, the last first.
|
|
19
|
+
*/
|
|
20
|
+
declare class Page {
|
|
21
|
+
#private;
|
|
22
|
+
readonly errors: unknown[];
|
|
23
|
+
readonly nodeOnPage?: NodeOnPage | undefined;
|
|
24
|
+
/** Whether the page renders in the app kept from the page before: see `kept`. */
|
|
25
|
+
readonly inKeptApp: boolean;
|
|
26
|
+
/** Aborts as the page is left: what the load does takes it. */
|
|
27
|
+
readonly signal: AbortSignal;
|
|
28
|
+
constructor(errors: unknown[], nodeOnPage?: NodeOnPage | undefined, /** Whether the page renders in the app kept from the page before: see `kept`. */
|
|
29
|
+
|
|
30
|
+
inKeptApp?: boolean);
|
|
31
|
+
/** Whether the page is open: from when its client starts until it is left. */
|
|
32
|
+
get open(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Runs `release` as the page is left: before what the load made before
|
|
35
|
+
* it. As `defer()` of a `DisposableStack`.
|
|
36
|
+
*/
|
|
37
|
+
defer(release: () => void): void;
|
|
38
|
+
/**
|
|
39
|
+
* Opens the page: its client starts. Leaving waits for `started` to
|
|
40
|
+
* settle, at most `startTimeout`, before it releases what the page holds.
|
|
41
|
+
* `stopped` runs once the start is over for the page: once `started` has
|
|
42
|
+
* settled, or once leaving stops waiting for it.
|
|
43
|
+
*/
|
|
44
|
+
starts(started: Promise<unknown>, stopped: () => void): void;
|
|
45
|
+
/**
|
|
46
|
+
* Leaves the page: `by` is what leaves it, for its node. Stops its load at
|
|
47
|
+
* once. Resolves once what the page holds is released, or rejects with the
|
|
48
|
+
* first error of a release, after every release has run. A page still
|
|
49
|
+
* starting after `startTimeout` is released once it has stopped, and
|
|
50
|
+
* `reportError()` gets that error: leaving resolves without it. Once:
|
|
51
|
+
* leaving it again gets the same promise.
|
|
52
|
+
*/
|
|
53
|
+
leave(by: string): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Leaves the page of the kept app that is open, as `leavePage()` does with
|
|
57
|
+
* `by`, and then the kept app: a test file ends with them. The app goes also
|
|
58
|
+
* after a page that could not be left, and the first error rejects.
|
|
59
|
+
*/
|
|
60
|
+
declare function leaveKeptAppWithPage(by: string): Promise<void>;
|
|
61
|
+
/** The page that is open: see `Page.open`. */
|
|
62
|
+
declare function currentPage(): Page | undefined;
|
|
63
|
+
type Opening = {
|
|
64
|
+
container?: Element;
|
|
65
|
+
opened: Opened;
|
|
66
|
+
headers: Headers;
|
|
67
|
+
cookie: string | null; /** For a node: what `rerender()` waits for. */
|
|
68
|
+
nodeOnPage?: NodeOnPage; /** What stops the load: see `RequestOptions.signal` in index.ts. */
|
|
69
|
+
signal?: AbortSignal; /** Where the page, and the pages the app loads from it, report their errors. */
|
|
70
|
+
errors: unknown[];
|
|
71
|
+
/**
|
|
72
|
+
* Renders the page in the app of the page before, if it is kept, and else
|
|
73
|
+
* keeps the app it starts for the pages after it: see `kept`. A page of
|
|
74
|
+
* `keepApp` without HTML of the server.
|
|
75
|
+
*/
|
|
76
|
+
inKeptApp?: boolean;
|
|
77
|
+
};
|
|
78
|
+
type PageDocument = {
|
|
79
|
+
response: Response;
|
|
80
|
+
html: string;
|
|
81
|
+
after?: HistoryEntry;
|
|
82
|
+
payload?: Uint8Array<ArrayBuffer>;
|
|
83
|
+
};
|
|
84
|
+
declare function loadPage(url: URL, request: RequestInit | PageDocument, from: PageStore, opening?: Opening): Promise<Response>;
|
|
85
|
+
/** The Flight payload in the scripts of a document of Next. */
|
|
86
|
+
declare function payloadInDocument(html: string): Uint8Array;
|
|
87
|
+
/**
|
|
88
|
+
* How the server would have answered the document of the page of a Flight
|
|
89
|
+
* payload, had it rendered its HTML, by the errors in the payload (see
|
|
90
|
+
* `shellErrorAnswer()` in ssr.ts): the first `redirect()`, wherever it is in
|
|
91
|
+
* the tree, as Next's router follows it (its RedirectBoundary), else the
|
|
92
|
+
* status of the first other error. Undefined for a payload without an error.
|
|
93
|
+
* A path of the app's has the app's `basePath` before it, which `redirect()`
|
|
94
|
+
* takes without it, as Next's server puts it in the `Location` of the 307 of
|
|
95
|
+
* a document.
|
|
96
|
+
*/
|
|
97
|
+
declare function renderFailureOf(payload: Uint8Array, answerOf: (digest: string) => ShellErrorAnswer): ShellErrorAnswer | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* A navigation of the app without its router, as a browser makes it: a
|
|
100
|
+
* `<form>`, an `<a>` that React does not handle, `location.assign()`. The
|
|
101
|
+
* page that is open stays until the answer is there, and for good when the
|
|
102
|
+
* answer is a download (downloads.ts). Else the answer is the next page,
|
|
103
|
+
* which needs a page of the app to navigate in. `downloadRequest` is the
|
|
104
|
+
* navigation's, and `from` the store of the browser context it is in. With
|
|
105
|
+
* `push` the next page gets an entry of the history of its own, as after a
|
|
106
|
+
* link or a form in a browser. Else it takes the entry of the page, as
|
|
107
|
+
* after a reload or `location.replace()`.
|
|
108
|
+
*/
|
|
109
|
+
declare function navigate(url: URL, init: RequestInit, downloadRequest: string | null, from: PageStore, push: boolean): Promise<void>;
|
|
110
|
+
declare function leavePage(by: string): Promise<void>;
|
|
111
|
+
//#endregion
|
|
112
|
+
export { Page, currentPage, leaveKeptAppWithPage, leavePage, loadPage, navigate, noRouter, payloadInDocument, renderFailureOf };
|
package/dist/page.js
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import { loadsNothing, registry } from "./registry.js";
|
|
2
|
+
import { speedFeature } from "./speed-features.js";
|
|
3
|
+
import { loadedSsrLayer, ssrLayer } from "./server-layers.js";
|
|
4
|
+
import { leftTheApp, sendRequest, setOpenPage, setRouterOutlivesPage, unlessAborted } from "./app-requests.js";
|
|
5
|
+
import { keepProperties, loadDocument, unloadDocument, unloadKeptApp } from "./document.js";
|
|
6
|
+
import { callServerWithoutPage } from "./client-graph.js";
|
|
7
|
+
import { download, downloadName } from "./downloads.js";
|
|
8
|
+
import { recordListeners, recordMessageChannels } from "./leftovers.js";
|
|
9
|
+
import { resetAsyncLocalStorage } from "@kasperpeulen/vitest-plugin-rsc/internal/async-local-storage";
|
|
10
|
+
import { routing } from "virtual:vitest-plugin-rsc/next-manifest";
|
|
11
|
+
import { checkFetchedModules, createEnvironmentRunner, environmentModule, forgetHeardVersions, versionOfModules } from "@kasperpeulen/vitest-plugin-rsc/internal/utils";
|
|
12
|
+
//#region src/page.ts
|
|
13
|
+
const startTimeout = 2e4;
|
|
14
|
+
const setTimeout = globalThis.setTimeout;
|
|
15
|
+
const clearTimeout = globalThis.clearTimeout;
|
|
16
|
+
/**
|
|
17
|
+
* A navigation without a page of the app, which has the router, under
|
|
18
|
+
* Vitest: a component that a test file with `"use client"` rendered has none.
|
|
19
|
+
*/
|
|
20
|
+
function noRouter(url) {
|
|
21
|
+
return /* @__PURE__ */ new Error(`vitest-plugin-rsc: a link or a form navigated to ${url.href}, and there is no page of the app to navigate in: no page of renderServer() is open, and a test file with "use client" has none. Test navigation in a page of renderServer(), in a test file without "use client".`);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A page of the app, from the first line of its load until it is left, with
|
|
25
|
+
* the node of `renderServer()` on it, and where it reports its errors. Its
|
|
26
|
+
* load makes it part by part, and each part that would outlive the page says
|
|
27
|
+
* how it is released. Leaving stops the load at once, and releases the
|
|
28
|
+
* parts, the last first.
|
|
29
|
+
*/
|
|
30
|
+
var Page = class {
|
|
31
|
+
errors;
|
|
32
|
+
nodeOnPage;
|
|
33
|
+
inKeptApp;
|
|
34
|
+
#load = new AbortController();
|
|
35
|
+
/** Aborts as the page is left: what the load does takes it. */
|
|
36
|
+
signal = this.#load.signal;
|
|
37
|
+
#releases = [];
|
|
38
|
+
#started;
|
|
39
|
+
#stopped = () => {};
|
|
40
|
+
#left;
|
|
41
|
+
constructor(errors, nodeOnPage, inKeptApp = false) {
|
|
42
|
+
this.errors = errors;
|
|
43
|
+
this.nodeOnPage = nodeOnPage;
|
|
44
|
+
this.inKeptApp = inKeptApp;
|
|
45
|
+
}
|
|
46
|
+
/** Whether the page is open: from when its client starts until it is left. */
|
|
47
|
+
get open() {
|
|
48
|
+
return this.#started !== void 0 && !this.signal.aborted;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Runs `release` as the page is left: before what the load made before
|
|
52
|
+
* it. As `defer()` of a `DisposableStack`.
|
|
53
|
+
*/
|
|
54
|
+
defer(release) {
|
|
55
|
+
this.#releases.push(release);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Opens the page: its client starts. Leaving waits for `started` to
|
|
59
|
+
* settle, at most `startTimeout`, before it releases what the page holds.
|
|
60
|
+
* `stopped` runs once the start is over for the page: once `started` has
|
|
61
|
+
* settled, or once leaving stops waiting for it.
|
|
62
|
+
*/
|
|
63
|
+
starts(started, stopped) {
|
|
64
|
+
this.#started = started.then(stopped, stopped);
|
|
65
|
+
this.#stopped = stopped;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Leaves the page: `by` is what leaves it, for its node. Stops its load at
|
|
69
|
+
* once. Resolves once what the page holds is released, or rejects with the
|
|
70
|
+
* first error of a release, after every release has run. A page still
|
|
71
|
+
* starting after `startTimeout` is released once it has stopped, and
|
|
72
|
+
* `reportError()` gets that error: leaving resolves without it. Once:
|
|
73
|
+
* leaving it again gets the same promise.
|
|
74
|
+
*/
|
|
75
|
+
leave(by) {
|
|
76
|
+
return this.#left ??= this.#leave(by);
|
|
77
|
+
}
|
|
78
|
+
async #leave(by) {
|
|
79
|
+
if (this.inKeptApp && this.open) dispatchPageTransition("pagehide");
|
|
80
|
+
this.#load.abort(new DOMException("The page was left before it had loaded.", "AbortError"));
|
|
81
|
+
this.nodeOnPage?.left(by);
|
|
82
|
+
const released = (this.#started ?? Promise.resolve()).then(() => this.#release());
|
|
83
|
+
let timeout;
|
|
84
|
+
const stopped = await Promise.race([released.then(() => true, () => true), new Promise((resolve) => timeout = setTimeout(resolve, startTimeout, false))]);
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
if (stopped) return released;
|
|
87
|
+
console.warn(`vitest-plugin-rsc: the page that was left was still loading its client code ${startTimeout / 1e3}s later. The next page loads anyway.`);
|
|
88
|
+
this.#stopped();
|
|
89
|
+
released.catch(reportError);
|
|
90
|
+
}
|
|
91
|
+
#release() {
|
|
92
|
+
let failed;
|
|
93
|
+
for (const release of this.#releases.splice(0).reverse()) try {
|
|
94
|
+
release();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
failed ??= { error };
|
|
97
|
+
}
|
|
98
|
+
if (failed) throw failed.error;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
let current;
|
|
102
|
+
let kept;
|
|
103
|
+
/**
|
|
104
|
+
* Leaves the kept app, if any: its root, its module graph, what its modules
|
|
105
|
+
* left behind as they loaded, and what its code added to the `<head>` go.
|
|
106
|
+
* Once its page is left: see `leavePage()`, and `leaveKeptAppWithPage()`.
|
|
107
|
+
* Not `withGlobals` once the app after it has started, whose they are then.
|
|
108
|
+
*/
|
|
109
|
+
function leaveKeptApp(withGlobals = true) {
|
|
110
|
+
const app = kept;
|
|
111
|
+
kept = void 0;
|
|
112
|
+
setRouterOutlivesPage(false);
|
|
113
|
+
try {
|
|
114
|
+
app?.leave();
|
|
115
|
+
} finally {
|
|
116
|
+
unloadKeptApp(withGlobals);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Leaves the page of the kept app that is open, as `leavePage()` does with
|
|
121
|
+
* `by`, and then the kept app: a test file ends with them. The app goes also
|
|
122
|
+
* after a page that could not be left, and the first error rejects.
|
|
123
|
+
*/
|
|
124
|
+
async function leaveKeptAppWithPage(by) {
|
|
125
|
+
let failed;
|
|
126
|
+
try {
|
|
127
|
+
if (current?.inKeptApp) await leavePage(by);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
failed = { error };
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
leaveKeptApp();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
failed ??= { error };
|
|
135
|
+
}
|
|
136
|
+
if (failed) throw failed.error;
|
|
137
|
+
}
|
|
138
|
+
function releaseAll(releases) {
|
|
139
|
+
let failed;
|
|
140
|
+
for (const release of releases.splice(0).reverse()) try {
|
|
141
|
+
release();
|
|
142
|
+
} catch (error) {
|
|
143
|
+
failed ??= { error };
|
|
144
|
+
}
|
|
145
|
+
if (failed) throw failed.error;
|
|
146
|
+
}
|
|
147
|
+
/** The page that is open: see `Page.open`. */
|
|
148
|
+
function currentPage() {
|
|
149
|
+
return current?.open ? current : void 0;
|
|
150
|
+
}
|
|
151
|
+
async function loadPage(url, request, from, opening) {
|
|
152
|
+
const errors = opening?.errors ?? currentPage()?.errors ?? [];
|
|
153
|
+
const inKeptApp = opening ? opening.inKeptApp === true : currentPage()?.inKeptApp === true;
|
|
154
|
+
const leaving = leavePage(opening ? "another renderServer()" : "a page load of the app");
|
|
155
|
+
const page = current = new Page(errors, opening?.nodeOnPage, inKeptApp);
|
|
156
|
+
const loading = (async () => {
|
|
157
|
+
await leaving;
|
|
158
|
+
page.signal.throwIfAborted();
|
|
159
|
+
if (!speedFeature("cacheDevServer")) forgetHeardVersions();
|
|
160
|
+
const changed = opening && inKeptApp && kept && kept.modules !== await versionOfModules("react_client");
|
|
161
|
+
page.signal.throwIfAborted();
|
|
162
|
+
if (!inKeptApp || !kept || changed) leaveKeptApp();
|
|
163
|
+
if (opening?.container?.hasChildNodes()) throw new Error("vitest-plugin-rsc: the container of a node has to be empty. The node is hydrated in it, and leaving the node empties it.");
|
|
164
|
+
const opened = registry.opened = opening?.opened;
|
|
165
|
+
if (opening) {
|
|
166
|
+
from.headers = opening.headers;
|
|
167
|
+
if (opening.cookie !== null) from.seed(opening.cookie);
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
return await openPage(page, url, request, from, opening);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (opened && registry.opened === opened && !page.signal.aborted) registry.opened = void 0;
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
})();
|
|
176
|
+
if (!opening?.signal) return loading;
|
|
177
|
+
return unlessAborted(loading, opening.signal, () => {
|
|
178
|
+
if (current === page) leavePage("the signal of renderServer()").catch(reportError);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
async function openPage(page, url, request, from, opening) {
|
|
182
|
+
const { signal } = page;
|
|
183
|
+
const superseded = () => signal.throwIfAborted();
|
|
184
|
+
const send = (target, init) => sendPageLoad(target, init, {
|
|
185
|
+
from,
|
|
186
|
+
navigation: "test"
|
|
187
|
+
}, signal);
|
|
188
|
+
const requestDocument = async (init) => {
|
|
189
|
+
try {
|
|
190
|
+
const response = await send(url, init);
|
|
191
|
+
superseded();
|
|
192
|
+
return await readDocument(response, (what) => `${what}, which is not a page to open. Use handleRequest() to assert on the response itself.`);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
superseded();
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const byFlight = page.inKeptApp && kept !== void 0 && speedFeature("flightLoads");
|
|
199
|
+
const withoutHtml = !("html" in request) && (byFlight || opening?.opened.ssr === false) ? await requestWithoutHtml(url, request, byFlight, send, signal) : void 0;
|
|
200
|
+
superseded();
|
|
201
|
+
const { response, html, after, payload } = withoutHtml ?? ("html" in request ? request : await requestDocument(request));
|
|
202
|
+
superseded();
|
|
203
|
+
let container = opening?.container;
|
|
204
|
+
if (opening && new URL(response.url).pathname !== opening.opened.pathname) {
|
|
205
|
+
registry.opened = void 0;
|
|
206
|
+
container = void 0;
|
|
207
|
+
opening.nodeOnPage?.replaced();
|
|
208
|
+
}
|
|
209
|
+
if (/^\s*<!doctype/i.test(html)) {
|
|
210
|
+
container = void 0;
|
|
211
|
+
if (!opening?.opened.node?.layouts) opening?.nodeOnPage?.replaced();
|
|
212
|
+
}
|
|
213
|
+
const startsApp = !(page.inKeptApp && kept);
|
|
214
|
+
const modules = startsApp && page.inKeptApp ? await versionOfModules("react_client") : 0;
|
|
215
|
+
if (startsApp) await checkFetchedModules();
|
|
216
|
+
superseded();
|
|
217
|
+
const forgetReact = await loadDocument(html, response.url, container, after, page.inKeptApp);
|
|
218
|
+
superseded();
|
|
219
|
+
const { inKeptApp } = page;
|
|
220
|
+
if (inKeptApp && kept) {
|
|
221
|
+
await startInKeptApp(page, kept.client, payload);
|
|
222
|
+
superseded();
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
const appReleases = [];
|
|
226
|
+
const deferForApp = (release) => inKeptApp ? appReleases.push(release) : page.defer(release);
|
|
227
|
+
const runner = createEnvironmentRunner("react_client");
|
|
228
|
+
deferForApp(() => void runner.close());
|
|
229
|
+
const browserRequire = (id) => registry.loadBrowserModule?.(id) ?? loadsNothing();
|
|
230
|
+
registry.browserRequire = browserRequire;
|
|
231
|
+
deferForApp(() => {
|
|
232
|
+
if (registry.browserRequire === browserRequire) registry.browserRequire = loadsNothing;
|
|
233
|
+
});
|
|
234
|
+
const leftovers = [recordListeners(window), recordMessageChannels()];
|
|
235
|
+
for (const leftover of leftovers) deferForApp(() => leftover.remove());
|
|
236
|
+
const loaded = () => leftovers.forEach((leftover) => leftover.stop());
|
|
237
|
+
const started = (async () => {
|
|
238
|
+
try {
|
|
239
|
+
const client = await runner.import(environmentModule("react_client", "@kasperpeulen/vitest-plugin-nextjs/client"));
|
|
240
|
+
superseded();
|
|
241
|
+
const { unmount, leaveApp } = await client.start(loaded, container, signal, opening?.nodeOnPage, page.errors, inKeptApp);
|
|
242
|
+
page.defer(() => {
|
|
243
|
+
try {
|
|
244
|
+
forgetReact();
|
|
245
|
+
} finally {
|
|
246
|
+
unmount();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
if (!inKeptApp) return;
|
|
250
|
+
appReleases.push(leaveApp);
|
|
251
|
+
const marks = Reflect.ownKeys(document).filter((key) => typeof key === "string" && (key.startsWith("__reactContainer$") || key.startsWith("_reactListening")));
|
|
252
|
+
keepProperties(document, marks);
|
|
253
|
+
leaveKeptApp(false);
|
|
254
|
+
kept = {
|
|
255
|
+
client,
|
|
256
|
+
modules,
|
|
257
|
+
leave() {
|
|
258
|
+
keepProperties(document);
|
|
259
|
+
try {
|
|
260
|
+
releaseAll(appReleases);
|
|
261
|
+
} finally {
|
|
262
|
+
for (const key of marks) Reflect.deleteProperty(document, key);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
setRouterOutlivesPage(true);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
releaseAll(appReleases);
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
})();
|
|
272
|
+
page.starts(started, loaded);
|
|
273
|
+
setOpenPage(signal);
|
|
274
|
+
try {
|
|
275
|
+
await started;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
superseded();
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
superseded();
|
|
281
|
+
return response;
|
|
282
|
+
}
|
|
283
|
+
function dispatchPageTransition(type) {
|
|
284
|
+
window.dispatchEvent(new PageTransitionEvent(type, { persisted: false }));
|
|
285
|
+
}
|
|
286
|
+
async function startInKeptApp(page, client, payload) {
|
|
287
|
+
const { signal } = page;
|
|
288
|
+
dispatchPageTransition("pageshow");
|
|
289
|
+
const started = client.renderAgain(signal, page.errors, payload).then(({ unmount }) => page.defer(unmount));
|
|
290
|
+
page.starts(started, () => {});
|
|
291
|
+
setOpenPage(signal);
|
|
292
|
+
try {
|
|
293
|
+
await started;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
signal.throwIfAborted();
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function sendPageLoad(url, init, sending, signal) {
|
|
300
|
+
checkFetchedModules().catch(() => {});
|
|
301
|
+
signal.throwIfAborted();
|
|
302
|
+
return sendRequest(new registry.Request(url, {
|
|
303
|
+
...init,
|
|
304
|
+
signal
|
|
305
|
+
}), sending);
|
|
306
|
+
}
|
|
307
|
+
const flightDocument = "<!DOCTYPE html><html id=\"__next_error__\"><head></head><body></body></html>";
|
|
308
|
+
async function requestWithoutHtml(url, init, byFlight, send, signal) {
|
|
309
|
+
const headers = new Headers(init.headers);
|
|
310
|
+
if (byFlight) headers.set("rsc", "1");
|
|
311
|
+
const request = {
|
|
312
|
+
...init,
|
|
313
|
+
headers
|
|
314
|
+
};
|
|
315
|
+
try {
|
|
316
|
+
const loaded = await followPage(await send(url, request), (target) => send(target, request), request, byFlight ? readFlight : readDocumentWithoutHtml);
|
|
317
|
+
signal.throwIfAborted();
|
|
318
|
+
if ("html" in loaded) return loaded;
|
|
319
|
+
return await readDocument(loaded.response, (what) => `${what}, which is not a page to open. Use handleRequest() to assert on the response itself.`);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
signal.throwIfAborted();
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const isFlight = (response) => /^text\/x-component\b/i.test(response.headers.get("content-type") ?? "");
|
|
326
|
+
async function readFlight(response) {
|
|
327
|
+
if (!isFlight(response)) return void 0;
|
|
328
|
+
const payload = new Uint8Array(await response.arrayBuffer());
|
|
329
|
+
return {
|
|
330
|
+
document: {
|
|
331
|
+
response,
|
|
332
|
+
html: flightDocument,
|
|
333
|
+
payload
|
|
334
|
+
},
|
|
335
|
+
payload
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async function readDocumentWithoutHtml(response) {
|
|
339
|
+
if (!/^text\/html\b/i.test(response.headers.get("content-type") ?? "")) return void 0;
|
|
340
|
+
const html = await response.text();
|
|
341
|
+
return {
|
|
342
|
+
document: {
|
|
343
|
+
response,
|
|
344
|
+
html
|
|
345
|
+
},
|
|
346
|
+
payload: payloadInDocument(html)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const documentScripts = /<script\b[^>]*>([^<]*)<\/script>/g;
|
|
350
|
+
const pushOfPayload = "self.__next_f.push(";
|
|
351
|
+
/** The Flight payload in the scripts of a document of Next. */
|
|
352
|
+
function payloadInDocument(html) {
|
|
353
|
+
const encoder = new TextEncoder();
|
|
354
|
+
const chunks = [];
|
|
355
|
+
for (const [, script = ""] of html.matchAll(documentScripts)) {
|
|
356
|
+
if (!script.startsWith(pushOfPayload)) continue;
|
|
357
|
+
const [kind, data = ""] = JSON.parse(script.slice(19, -1));
|
|
358
|
+
if (kind === 1) chunks.push(encoder.encode(data));
|
|
359
|
+
else if (kind === 3) chunks.push(Uint8Array.from(atob(data), (c) => c.charCodeAt(0)));
|
|
360
|
+
}
|
|
361
|
+
const payload = new Uint8Array(chunks.reduce((length, chunk) => length + chunk.length, 0));
|
|
362
|
+
chunks.reduce((offset, chunk) => (payload.set(chunk, offset), offset + chunk.length), 0);
|
|
363
|
+
return payload;
|
|
364
|
+
}
|
|
365
|
+
async function followPage(response, send, init, read) {
|
|
366
|
+
let redirected = response.redirected;
|
|
367
|
+
for (let redirects = 0;;) {
|
|
368
|
+
const page = await read(response);
|
|
369
|
+
if (!page) return { response: pageResponse(response, response.body, redirected) };
|
|
370
|
+
const { shellErrorAnswer } = await ssrLayer();
|
|
371
|
+
const failure = renderFailureOf(page.payload, shellErrorAnswer);
|
|
372
|
+
if (failure?.location === void 0) {
|
|
373
|
+
const { document } = page;
|
|
374
|
+
const status = response.status === 200 ? failure?.status : void 0;
|
|
375
|
+
const body = document.payload ?? document.html;
|
|
376
|
+
return {
|
|
377
|
+
...document,
|
|
378
|
+
response: pageResponse(response, body, redirected, status)
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
const target = new URL(failure.location, response.url);
|
|
382
|
+
if (target.origin !== window.location.origin) throw leftTheApp(target);
|
|
383
|
+
if (++redirects > 20) throw new Error(`vitest-plugin-rsc: too many redirects for ${response.url}. The last one was to ${target.href}.`);
|
|
384
|
+
if (init.body instanceof ReadableStream) throw new TypeError("fetch failed", { cause: /* @__PURE__ */ new Error(`vitest-plugin-rsc: ${response.url} redirected to ${target.href}, and the body of its request is a stream, which cannot be sent again.`) });
|
|
385
|
+
redirected = true;
|
|
386
|
+
response = await send(target);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function pageResponse(response, body, redirected, status = response.status) {
|
|
390
|
+
if ((response.redirected || !redirected) && status === response.status) return response;
|
|
391
|
+
const { statusText, headers, url } = response;
|
|
392
|
+
return Object.defineProperties(new registry.Response(body, {
|
|
393
|
+
status,
|
|
394
|
+
statusText,
|
|
395
|
+
headers
|
|
396
|
+
}), {
|
|
397
|
+
url: { value: url },
|
|
398
|
+
redirected: { value: redirected || response.redirected }
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
const errorRows = /(?:^|\n)[0-9a-f]+:E(\{.*)/g;
|
|
402
|
+
/**
|
|
403
|
+
* How the server would have answered the document of the page of a Flight
|
|
404
|
+
* payload, had it rendered its HTML, by the errors in the payload (see
|
|
405
|
+
* `shellErrorAnswer()` in ssr.ts): the first `redirect()`, wherever it is in
|
|
406
|
+
* the tree, as Next's router follows it (its RedirectBoundary), else the
|
|
407
|
+
* status of the first other error. Undefined for a payload without an error.
|
|
408
|
+
* A path of the app's has the app's `basePath` before it, which `redirect()`
|
|
409
|
+
* takes without it, as Next's server puts it in the `Location` of the 307 of
|
|
410
|
+
* a document.
|
|
411
|
+
*/
|
|
412
|
+
function renderFailureOf(payload, answerOf) {
|
|
413
|
+
let failure;
|
|
414
|
+
for (const [, row] of new TextDecoder().decode(payload).matchAll(errorRows)) {
|
|
415
|
+
let digest;
|
|
416
|
+
try {
|
|
417
|
+
digest = JSON.parse(row).digest;
|
|
418
|
+
} catch {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (typeof digest !== "string") continue;
|
|
422
|
+
const answer = answerOf(digest);
|
|
423
|
+
const { location } = answer;
|
|
424
|
+
if (location !== void 0) return {
|
|
425
|
+
...answer,
|
|
426
|
+
location: location.startsWith("/") ? routing.basePath + location : location
|
|
427
|
+
};
|
|
428
|
+
failure ??= answer;
|
|
429
|
+
}
|
|
430
|
+
return failure;
|
|
431
|
+
}
|
|
432
|
+
async function readDocument(response, notAPage) {
|
|
433
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
434
|
+
if (!/^text\/html\b/i.test(contentType)) {
|
|
435
|
+
await response.body?.cancel();
|
|
436
|
+
const what = `${response.url} responded with ${contentType || "no content type"}`;
|
|
437
|
+
throw new Error(`vitest-plugin-rsc: ${notAPage(what)}`);
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
response,
|
|
441
|
+
html: await response.text()
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
let navigating = new AbortController();
|
|
445
|
+
let withoutPage = new AbortController();
|
|
446
|
+
/**
|
|
447
|
+
* A navigation of the app without its router, as a browser makes it: a
|
|
448
|
+
* `<form>`, an `<a>` that React does not handle, `location.assign()`. The
|
|
449
|
+
* page that is open stays until the answer is there, and for good when the
|
|
450
|
+
* answer is a download (downloads.ts). Else the answer is the next page,
|
|
451
|
+
* which needs a page of the app to navigate in. `downloadRequest` is the
|
|
452
|
+
* navigation's, and `from` the store of the browser context it is in. With
|
|
453
|
+
* `push` the next page gets an entry of the history of its own, as after a
|
|
454
|
+
* link or a form in a browser. Else it takes the entry of the page, as
|
|
455
|
+
* after a reload or `location.replace()`.
|
|
456
|
+
*/
|
|
457
|
+
async function navigate(url, init, downloadRequest, from, push) {
|
|
458
|
+
const page = currentPage();
|
|
459
|
+
const signals = [page?.signal ?? withoutPage.signal];
|
|
460
|
+
if (downloadRequest === null) {
|
|
461
|
+
navigating.abort(new DOMException("Another navigation took the place of this one.", "AbortError"));
|
|
462
|
+
navigating = new AbortController();
|
|
463
|
+
signals.push(navigating.signal);
|
|
464
|
+
}
|
|
465
|
+
const signal = AbortSignal.any(signals);
|
|
466
|
+
const byFlight = downloadRequest === null && page?.inKeptApp === true && kept !== void 0 && speedFeature("flightLoads") && (init.method ?? "GET").toUpperCase() === "GET";
|
|
467
|
+
const send = (target, flight) => {
|
|
468
|
+
const headers = new Headers(init.headers);
|
|
469
|
+
if (flight) headers.set("rsc", "1");
|
|
470
|
+
return sendPageLoad(target, {
|
|
471
|
+
...init,
|
|
472
|
+
headers
|
|
473
|
+
}, {
|
|
474
|
+
from,
|
|
475
|
+
navigation: "app",
|
|
476
|
+
opened: page?.inKeptApp ? {
|
|
477
|
+
pathname: target.pathname,
|
|
478
|
+
proxy: true,
|
|
479
|
+
ssr: false
|
|
480
|
+
} : void 0,
|
|
481
|
+
network: downloadRequest === null ? void 0 : () => registry.network(url, {
|
|
482
|
+
...init,
|
|
483
|
+
signal
|
|
484
|
+
})
|
|
485
|
+
}, signal);
|
|
486
|
+
};
|
|
487
|
+
let loaded;
|
|
488
|
+
try {
|
|
489
|
+
let response = await send(url, byFlight);
|
|
490
|
+
signal.throwIfAborted();
|
|
491
|
+
if (page?.inKeptApp && downloadRequest === null) {
|
|
492
|
+
const followed = await followPage(response, (target) => send(target, byFlight), init, byFlight ? readFlight : readDocumentWithoutHtml);
|
|
493
|
+
signal.throwIfAborted();
|
|
494
|
+
if ("html" in followed) loaded = followed;
|
|
495
|
+
else response = followed.response;
|
|
496
|
+
}
|
|
497
|
+
if (!loaded) {
|
|
498
|
+
const name = downloadName(response, downloadRequest);
|
|
499
|
+
if (name !== void 0) return await download(response, name, signal);
|
|
500
|
+
if (!page) {
|
|
501
|
+
await response.body?.cancel();
|
|
502
|
+
throw noRouter(url);
|
|
503
|
+
}
|
|
504
|
+
loaded = await readDocument(response, (what) => `the app navigated to a URL that is not a page: ${what}. A browser would show it in place of the app, which a test cannot do. A route handler that answers with a file to save says so with Content-Disposition: attachment, or a link with download.`);
|
|
505
|
+
signal.throwIfAborted();
|
|
506
|
+
}
|
|
507
|
+
} catch (error) {
|
|
508
|
+
signal.throwIfAborted();
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
await loadPage(url, {
|
|
512
|
+
...loaded,
|
|
513
|
+
after: entryOf(push)
|
|
514
|
+
}, from);
|
|
515
|
+
}
|
|
516
|
+
const entryOf = (push) => push ? {
|
|
517
|
+
url: window.location.href,
|
|
518
|
+
state: window.history.state
|
|
519
|
+
} : void 0;
|
|
520
|
+
let leaving = Promise.resolve();
|
|
521
|
+
function leavePage(by) {
|
|
522
|
+
const left = current;
|
|
523
|
+
current = void 0;
|
|
524
|
+
withoutPage.abort(new DOMException("The test moved on before it was done.", "AbortError"));
|
|
525
|
+
withoutPage = new AbortController();
|
|
526
|
+
const pageLeft = left?.leave(by);
|
|
527
|
+
const gone = leaving.catch(() => {}).then(async () => {
|
|
528
|
+
try {
|
|
529
|
+
await pageLeft;
|
|
530
|
+
} finally {
|
|
531
|
+
try {
|
|
532
|
+
unloadDocument();
|
|
533
|
+
} finally {
|
|
534
|
+
try {
|
|
535
|
+
await loadedSsrLayer()?.settleRequests();
|
|
536
|
+
} finally {
|
|
537
|
+
registry.opened = void 0;
|
|
538
|
+
setOpenPage(void 0);
|
|
539
|
+
globalThis.__viteRscCallServer = callServerWithoutPage;
|
|
540
|
+
resetAsyncLocalStorage();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
leaving = gone;
|
|
546
|
+
return gone;
|
|
547
|
+
}
|
|
548
|
+
//#endregion
|
|
549
|
+
export { Page, currentPage, leaveKeptAppWithPage, leavePage, loadPage, navigate, noRouter, payloadInDocument, renderFailureOf };
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NextProject } from "./project.js";
|
|
2
|
+
import { Plugin } from "vite";
|
|
3
|
+
|
|
4
|
+
//#region src/paths.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The files that the `paths` have for a request, in order: TypeScript takes
|
|
7
|
+
* the pattern with the longest prefix. Not a declaration file, which is for
|
|
8
|
+
* the types only.
|
|
9
|
+
*/
|
|
10
|
+
declare function candidatesOf(paths: NonNullable<NextProject["paths"]>, request: string): string[];
|
|
11
|
+
declare function createPathsPlugin(getProject: () => NextProject): Plugin;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { candidatesOf, createPathsPlugin };
|