@kasperpeulen/vitest-plugin-nextjs 0.0.0-stage → 0.1.0-alpha.0
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 +31 -0
- package/dist/compile.js +140 -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 +328 -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/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/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 +22 -0
- package/dist/plugin.js +995 -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 +46 -0
- package/dist/project/webpack.js +160 -0
- package/dist/project.d.ts +264 -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/index.d.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { JSXElementConstructor, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Sends a request to the Next.js server of the app, like `fetch` from a page
|
|
6
|
+
* of the app would. Use it to assert on a response itself: its status, its
|
|
7
|
+
* headers, its HTML or Flight body.
|
|
8
|
+
*
|
|
9
|
+
* The request carries the app's cookies and the `headers` of what
|
|
10
|
+
* `renderServer()` opened, under its own. A `cookie` header is sent instead
|
|
11
|
+
* of the app's cookies, with this request and the redirects it follows. The
|
|
12
|
+
* cookies the server sets are the app's from then on:
|
|
13
|
+
* `response.headers.getSetCookie()` has them, where the `fetch` of the page
|
|
14
|
+
* does not show them.
|
|
15
|
+
* One to the pathname of what `renderServer()` opened is that page's, as a
|
|
16
|
+
* `fetch` of the page is: it gets the route of the node, and skips the proxy
|
|
17
|
+
* if the page does.
|
|
18
|
+
*
|
|
19
|
+
* Ask the server yourself with this, not with `fetch`: a `fetch` with Next's
|
|
20
|
+
* `rsc` or `next-action` header is Next's. It is the page's that is open as
|
|
21
|
+
* it is sent, and it never settles once that page is left, or without one.
|
|
22
|
+
*/
|
|
23
|
+
declare function handleRequest(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
24
|
+
type RunInServerActionOptions = {
|
|
25
|
+
/**
|
|
26
|
+
* The URL of the page the action is of. Defaults to `/`. For this request
|
|
27
|
+
* its pathname gets a route of its own that renders nothing, with the params
|
|
28
|
+
* the app's route for it has.
|
|
29
|
+
*/
|
|
30
|
+
url?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the server in front of the app takes the request: `proxy.ts`, and
|
|
33
|
+
* the `redirects`, `rewrites` and `headers` of `next.config`. Defaults to
|
|
34
|
+
* `false`, as for a node. A redirect there rejects: the action did not run.
|
|
35
|
+
*/
|
|
36
|
+
proxy?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Headers for the request of the action, next to the ones a browser sends.
|
|
39
|
+
* The ones that make it a Server Action are the plugin's: `accept`,
|
|
40
|
+
* `content-type` and `next-action`. A `cookie` header is sent instead of
|
|
41
|
+
* the app's cookies.
|
|
42
|
+
*/
|
|
43
|
+
headers?: HeadersInit;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Runs `action` as a Server Action of the page at `url`, the way Next runs
|
|
47
|
+
* one: in the request of the action, where `cookies()` can be set and
|
|
48
|
+
* `redirect()`, `refresh()` and `after()` work. Resolves with what it
|
|
49
|
+
* returns, and rejects with what it throws, also the error of a `redirect()`.
|
|
50
|
+
* Its arguments and its result are the test's own, not sent through Flight.
|
|
51
|
+
*
|
|
52
|
+
* It sends a Server Action request like the one of Next's router, to a route
|
|
53
|
+
* at `url` that renders nothing, and opens no page: there is no app to start.
|
|
54
|
+
* After a `redirect()` no page renders: open the page the action redirects
|
|
55
|
+
* to in the test. A page that is open stays open. The request carries the app's cookies, the `headers` of
|
|
56
|
+
* what `renderServer()` opened and the ones passed here, and the cookies the
|
|
57
|
+
* action sets are the app's from then on. For code that a Server Action
|
|
58
|
+
* calls; test a form of a page through the page.
|
|
59
|
+
*
|
|
60
|
+
* The function runs in the request, so it cannot send a request to the app
|
|
61
|
+
* itself, which would wait for this one: call the app's code instead.
|
|
62
|
+
*/
|
|
63
|
+
declare function runInServerAction<T>(action: () => T, options?: RunInServerActionOptions): Promise<Awaited<T>>;
|
|
64
|
+
type RequestOptions = {
|
|
65
|
+
/** The URL to open. Defaults to `/`. */url?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Headers for the requests of the browser to the app, next to the ones a
|
|
68
|
+
* browser sends: the request of the document, and every one after it, like
|
|
69
|
+
* a Server Action, a `router.refresh()`, a navigation or a `fetch`. Until
|
|
70
|
+
* the test opens something else, or ends. A request keeps the headers it
|
|
71
|
+
* sets itself. An `accept` header is for the document alone. A `cookie`
|
|
72
|
+
* header sets those cookies for the app, as a `Set-Cookie` would: they go
|
|
73
|
+
* with the document and the requests after it, until the server or a
|
|
74
|
+
* script of the page changes them, or the test ends.
|
|
75
|
+
*/
|
|
76
|
+
headers?: HeadersInit;
|
|
77
|
+
/**
|
|
78
|
+
* Stops the render when it aborts, as it stops a `fetch`: the page load
|
|
79
|
+
* ends, the page is left, and `renderServer()` rejects at once with the
|
|
80
|
+
* signal's reason, an `AbortError` unless it has another. Leaving goes on
|
|
81
|
+
* after that, and the next `renderServer()` or `cleanup()` waits for it.
|
|
82
|
+
* Once `renderServer()` has resolved, the page stays: leave it with
|
|
83
|
+
* `unmount()` or `cleanup()`. An aborted signal opens nothing, and leaves
|
|
84
|
+
* nothing either.
|
|
85
|
+
*/
|
|
86
|
+
signal?: AbortSignal;
|
|
87
|
+
};
|
|
88
|
+
type RenderServerOptions = RequestOptions & {
|
|
89
|
+
/**
|
|
90
|
+
* Whether the server in front of the app takes the request: `proxy.ts`, and
|
|
91
|
+
* the `redirects`, `rewrites` and `headers` of `next.config`. Defaults to
|
|
92
|
+
* `true`. With `false` the URL goes straight to the app's route for its
|
|
93
|
+
* pathname, and so do the requests the page sends there while it is open:
|
|
94
|
+
* Server Actions and `router.refresh()`. A navigation to another route goes
|
|
95
|
+
* through it, as in the app.
|
|
96
|
+
*/
|
|
97
|
+
proxy?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Whether the layouts of the app render around the page. Defaults to
|
|
100
|
+
* `true`, and `false` is not supported yet: to render a page on its own,
|
|
101
|
+
* pass it as a node, `renderServer(<Page />, { url })`.
|
|
102
|
+
*/
|
|
103
|
+
layouts?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Whether the server renders the HTML of the page, which the browser then
|
|
106
|
+
* hydrates. With `false` the browser renders the app from the Flight
|
|
107
|
+
* payload alone, with `createRoot()`, as Next does for an error page: a
|
|
108
|
+
* test no longer sees the HTML of the server, or a mismatch between it and
|
|
109
|
+
* the browser.
|
|
110
|
+
*
|
|
111
|
+
* Defaults to `true`, and to `false` with the speed feature `keepApp` of
|
|
112
|
+
* `vitestPluginNext()`, on in fast mode, where a page without HTML of the
|
|
113
|
+
* server renders in the app of the page before it in the test file: see
|
|
114
|
+
* `SpeedOptions.keepApp`. A test that asserts on the HTML of the server
|
|
115
|
+
* passes `ssr: true`, and gets a page load of its own.
|
|
116
|
+
*/
|
|
117
|
+
ssr?: boolean;
|
|
118
|
+
};
|
|
119
|
+
type RenderServerResult = {
|
|
120
|
+
/**
|
|
121
|
+
* The server's response to the request of the document. With the speed
|
|
122
|
+
* feature `flightLoads`, for a page of the kept app, the response to the
|
|
123
|
+
* request of its Flight payload, `text/x-component`, which loads it
|
|
124
|
+
* without a document: for a page whose render calls `redirect()`, the one
|
|
125
|
+
* of the page it redirects to, with `redirected` and its URL.
|
|
126
|
+
*/
|
|
127
|
+
response: Response;
|
|
128
|
+
/**
|
|
129
|
+
* The errors that React reported on the page, in order: what threw as the
|
|
130
|
+
* server or the browser rendered, what an error boundary caught, what a
|
|
131
|
+
* Server Action threw into a form or a transition. Not Next's own, like a
|
|
132
|
+
* `redirect()` or a `notFound()`, which Next does not report either. Empty
|
|
133
|
+
* when nothing failed. A render that fails still opens its page, with
|
|
134
|
+
* Next's error page or the app's `error.tsx`, so `renderServer()` resolves
|
|
135
|
+
* with the error here; it rejects when there is no page to open. The array
|
|
136
|
+
* grows while the page is open, also with the pages the app loads from it,
|
|
137
|
+
* until `unmount()`, `cleanup()` or another `renderServer()` leaves it.
|
|
138
|
+
*/
|
|
139
|
+
errors: unknown[]; /** Leaves the page. The cookies and the `headers` stay until the test ends. */
|
|
140
|
+
unmount(): Promise<void>;
|
|
141
|
+
};
|
|
142
|
+
type RenderComponentOptions = RequestOptions & {
|
|
143
|
+
/**
|
|
144
|
+
* Where the node renders. Defaults to a `<div>` appended to `baseElement`,
|
|
145
|
+
* which `cleanup()` removes. A container of the test's is only emptied.
|
|
146
|
+
*/
|
|
147
|
+
container?: HTMLElement; /** Defaults to the `container` of the test, or else to `document.body`. */
|
|
148
|
+
baseElement?: HTMLElement; /** Wraps the node on the server. It can be a Server Component. */
|
|
149
|
+
wrapper?: JSXElementConstructor<{
|
|
150
|
+
children: ReactNode;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* For another host than Vitest: the files of the host that render the
|
|
154
|
+
* node, for their CSS. A Vitest test never passes it: the plugin's setup
|
|
155
|
+
* file fills in the setup files and the test file. Another host passes
|
|
156
|
+
* the files of its own that render the node: absolute, or from the project
|
|
157
|
+
* root without a leading slash, like `host/button.demo.tsx`. The node has
|
|
158
|
+
* the stylesheets that these files import, as a page has those of its
|
|
159
|
+
* route. What is passed replaces the default, it adds nothing to it. A
|
|
160
|
+
* file that is not the host's, like one of the app, is left out, with a
|
|
161
|
+
* warning. `host.files` of `vitestPluginNext()` says which files are the
|
|
162
|
+
* host's at all, and in a static build only those have stylesheets.
|
|
163
|
+
*/
|
|
164
|
+
hostFiles?: string[];
|
|
165
|
+
/**
|
|
166
|
+
* Whether the server in front of the app takes the request: `proxy.ts`, and
|
|
167
|
+
* the `redirects`, `rewrites` and `headers` of `next.config`. Defaults to
|
|
168
|
+
* `false`: the node renders at `url` as it is given, and so do its Server
|
|
169
|
+
* Actions and `router.refresh()`. A navigation to another route goes
|
|
170
|
+
* through it, as in the app.
|
|
171
|
+
*/
|
|
172
|
+
proxy?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Renders the node in place of the page at `url`, inside the layouts of the
|
|
175
|
+
* app, with the `loading`, `error` and `not-found` of that route. The root
|
|
176
|
+
* layout renders the document, so `container` and `baseElement` cannot be
|
|
177
|
+
* passed, and the `container` in the result is the `<body>`. Defaults to
|
|
178
|
+
* `false`.
|
|
179
|
+
*/
|
|
180
|
+
layouts?: boolean;
|
|
181
|
+
};
|
|
182
|
+
type RenderComponentResult = RenderServerResult & {
|
|
183
|
+
container: HTMLElement;
|
|
184
|
+
baseElement: HTMLElement;
|
|
185
|
+
/**
|
|
186
|
+
* What the container holds now, as a fragment of its own, for a snapshot.
|
|
187
|
+
* Without the scripts that run, which are Next's and React's.
|
|
188
|
+
*/
|
|
189
|
+
asFragment(): DocumentFragment;
|
|
190
|
+
/**
|
|
191
|
+
* Renders `ui` in place of the node, like Testing Library's `rerender`:
|
|
192
|
+
* on the server, in one request of Next's router, as `router.refresh()`
|
|
193
|
+
* sends, and without a page load, so the state of the Client Components in
|
|
194
|
+
* the node stays. `ui` gets the same `wrapper`, and the page keeps its
|
|
195
|
+
* `url`, `headers` and the other options: for others, call
|
|
196
|
+
* `renderServer()`. Resolves once React has committed the new node, or a
|
|
197
|
+
* later one, and run its effects. A Suspense boundary that is new in it
|
|
198
|
+
* may still show its fallback.
|
|
199
|
+
*
|
|
200
|
+
* Every call is a request of its own, and React commits once the last has
|
|
201
|
+
* answered: a host that renders often, as a user types, waits for one call
|
|
202
|
+
* before it makes the next. When the app changes the history without a
|
|
203
|
+
* request while it waits, like a hash link or `history.back()`, Next drops
|
|
204
|
+
* the refresh, and the call waits for the next one.
|
|
205
|
+
*
|
|
206
|
+
* Rejects with a `NodeReplacedError` when something else is in the node's
|
|
207
|
+
* place: an error page, a not-found page, a page the node redirected to,
|
|
208
|
+
* or a route the app navigated to. Its `cause` is the error React reported
|
|
209
|
+
* since the call, if any, which for `notFound()` and `redirect()` is
|
|
210
|
+
* Next's own. In the layouts of a route, an error of a layout or of
|
|
211
|
+
* another slot before the node has hydrated counts as one too. Rejects
|
|
212
|
+
* when the page was left, and with an `AbortError` when it is left while
|
|
213
|
+
* the node renders again.
|
|
214
|
+
*/
|
|
215
|
+
rerender(ui: ReactNode): Promise<void>;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Opens a route of the Next.js app, as a browser does: it requests
|
|
219
|
+
* the document from the server, shows the HTML it gets back, and starts the
|
|
220
|
+
* app's client code, which hydrates it. From there Next's own router is in
|
|
221
|
+
* charge, so links, forms and Server Actions work as they do in the app.
|
|
222
|
+
*
|
|
223
|
+
* With a node, it renders that node in a container, like Testing Library
|
|
224
|
+
* does, on a route of its own: one without the app's layouts, at the URL of
|
|
225
|
+
* `url` with the params the app's route for it has. The request, the cookies,
|
|
226
|
+
* the Server Actions and the router are Next's, as for a page. A navigation
|
|
227
|
+
* to a route of the app loads that page.
|
|
228
|
+
*
|
|
229
|
+
* Two options say how much of the app is around it. `proxy` runs the server
|
|
230
|
+
* in front of the app, `proxy.ts` and the routing of `next.config`, and
|
|
231
|
+
* `layouts` renders the app's layouts. A route has both by default, and a
|
|
232
|
+
* node neither.
|
|
233
|
+
*
|
|
234
|
+
* Resolves once the page has hydrated.
|
|
235
|
+
*/
|
|
236
|
+
declare function renderServer(options: RenderServerOptions): Promise<RenderServerResult>;
|
|
237
|
+
declare function renderServer(ui: ReactNode, options?: RenderComponentOptions): Promise<RenderComponentResult>;
|
|
238
|
+
type CleanupOptions = {
|
|
239
|
+
/**
|
|
240
|
+
* The URL of the document once the page is left: the host's own, like one
|
|
241
|
+
* that says what it shows next. Leaving puts back the URL that the
|
|
242
|
+
* document had as the page opened; this one replaces it, in the same entry
|
|
243
|
+
* of the history, as leaving does. Relative to that URL, and of its
|
|
244
|
+
* origin. Defaults to the URL that leaving puts back.
|
|
245
|
+
*/
|
|
246
|
+
url?: string;
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Leaves the page that `renderServer()` opened, removes the containers it made
|
|
250
|
+
* and closes the browser context of the app: its `headers`, the app's
|
|
251
|
+
* cookies and what the app put in its storage go, and the document has the
|
|
252
|
+
* browser's own APIs again. What still runs of the page, like a timer, uses
|
|
253
|
+
* those. The server forgets what it has cached, and what was written to its
|
|
254
|
+
* disk, the `fs` of the server code and the tests.
|
|
255
|
+
* Unlinks the stylesheets of the files of the host: the host links a file's
|
|
256
|
+
* again as it uses it, with `linkStylesheetsOf()`. Runs before and after
|
|
257
|
+
* every test, after which the plugin's setup file links those of the setup
|
|
258
|
+
* files and the test file again.
|
|
259
|
+
*
|
|
260
|
+
* What was set on the document, `<html>`, `<head>` and the container of a
|
|
261
|
+
* node while the page was open goes with the page; what the host set there
|
|
262
|
+
* while no page was open stays. So a library that marks the document once,
|
|
263
|
+
* on its first use, is prepared before the first page.
|
|
264
|
+
*/
|
|
265
|
+
declare function cleanup({
|
|
266
|
+
url
|
|
267
|
+
}?: CleanupOptions): Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Leaves the page, as `cleanup()` does, and opens a new browser context of
|
|
270
|
+
* the app: until `cleanup()`, the document has the app's cookies, storage,
|
|
271
|
+
* requests and navigations, empty, as a page of the app has them. Outside
|
|
272
|
+
* one it has the browser's own. `renderServer()`, `handleRequest()` and
|
|
273
|
+
* `runInServerAction()` open one when none is open, so call this before
|
|
274
|
+
* what seeds the page before it opens, like an item of `localStorage`. The
|
|
275
|
+
* plugin's setup file calls it before every test of a file without
|
|
276
|
+
* `"use client"`.
|
|
277
|
+
*/
|
|
278
|
+
declare function newBrowserContext(): Promise<void>;
|
|
279
|
+
//#endregion
|
|
280
|
+
export { CleanupOptions, RenderComponentOptions, RenderComponentResult, RenderServerOptions, RenderServerResult, RunInServerActionOptions, cleanup, handleRequest, newBrowserContext, renderServer, runInServerAction };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { registry } from "./registry.js";
|
|
2
|
+
import "./globals.js";
|
|
3
|
+
import { speedFeature } from "./speed-features.js";
|
|
4
|
+
import { loadedSsrLayer, rscLayer, ssrLayer } from "./server-layers.js";
|
|
5
|
+
import { sendRequest, unlessAborted } from "./app-requests.js";
|
|
6
|
+
import { unlinkStylesheets } from "./client-graph.js";
|
|
7
|
+
import { leavePage, loadPage } from "./page.js";
|
|
8
|
+
import "./document-loads.js";
|
|
9
|
+
import { closeContext, openContext } from "./browser-context.js";
|
|
10
|
+
import { resetDisk } from "./disk.js";
|
|
11
|
+
import { NodeOnPage } from "./node-on-page.js";
|
|
12
|
+
import { createElement } from "react";
|
|
13
|
+
//#region src/index.ts
|
|
14
|
+
ssrLayer();
|
|
15
|
+
await rscLayer();
|
|
16
|
+
/**
|
|
17
|
+
* Sends a request to the Next.js server of the app, like `fetch` from a page
|
|
18
|
+
* of the app would. Use it to assert on a response itself: its status, its
|
|
19
|
+
* headers, its HTML or Flight body.
|
|
20
|
+
*
|
|
21
|
+
* The request carries the app's cookies and the `headers` of what
|
|
22
|
+
* `renderServer()` opened, under its own. A `cookie` header is sent instead
|
|
23
|
+
* of the app's cookies, with this request and the redirects it follows. The
|
|
24
|
+
* cookies the server sets are the app's from then on:
|
|
25
|
+
* `response.headers.getSetCookie()` has them, where the `fetch` of the page
|
|
26
|
+
* does not show them.
|
|
27
|
+
* One to the pathname of what `renderServer()` opened is that page's, as a
|
|
28
|
+
* `fetch` of the page is: it gets the route of the node, and skips the proxy
|
|
29
|
+
* if the page does.
|
|
30
|
+
*
|
|
31
|
+
* Ask the server yourself with this, not with `fetch`: a `fetch` with Next's
|
|
32
|
+
* `rsc` or `next-action` header is Next's. It is the page's that is open as
|
|
33
|
+
* it is sent, and it never settles once that page is left, or without one.
|
|
34
|
+
*/
|
|
35
|
+
function handleRequest(input, init) {
|
|
36
|
+
const request = new registry.Request(typeof input === "string" ? new URL(input, window.location.href) : input, init);
|
|
37
|
+
return sendRequest(request, {
|
|
38
|
+
from: openContext().store,
|
|
39
|
+
credentials: request.credentials !== "omit"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
let serverActionKeys = 0;
|
|
43
|
+
/**
|
|
44
|
+
* Runs `action` as a Server Action of the page at `url`, the way Next runs
|
|
45
|
+
* one: in the request of the action, where `cookies()` can be set and
|
|
46
|
+
* `redirect()`, `refresh()` and `after()` work. Resolves with what it
|
|
47
|
+
* returns, and rejects with what it throws, also the error of a `redirect()`.
|
|
48
|
+
* Its arguments and its result are the test's own, not sent through Flight.
|
|
49
|
+
*
|
|
50
|
+
* It sends a Server Action request like the one of Next's router, to a route
|
|
51
|
+
* at `url` that renders nothing, and opens no page: there is no app to start.
|
|
52
|
+
* After a `redirect()` no page renders: open the page the action redirects
|
|
53
|
+
* to in the test. A page that is open stays open. The request carries the app's cookies, the `headers` of
|
|
54
|
+
* what `renderServer()` opened and the ones passed here, and the cookies the
|
|
55
|
+
* action sets are the app's from then on. For code that a Server Action
|
|
56
|
+
* calls; test a form of a page through the page.
|
|
57
|
+
*
|
|
58
|
+
* The function runs in the request, so it cannot send a request to the app
|
|
59
|
+
* itself, which would wait for this one: call the app's code instead.
|
|
60
|
+
*/
|
|
61
|
+
async function runInServerAction(action, options = {}) {
|
|
62
|
+
const from = openContext().store;
|
|
63
|
+
const url = new URL(options.url ?? "/", window.location.origin);
|
|
64
|
+
if (url.origin !== window.location.origin) throw new Error(`vitest-plugin-rsc: a Server Action is of a page of the app, not of ${url.href}`);
|
|
65
|
+
const { runInServerActionOfTest } = await import("./server-action.js");
|
|
66
|
+
const key = serverActionKeys++;
|
|
67
|
+
const ran = {};
|
|
68
|
+
registry.serverActions.set(key, async () => {
|
|
69
|
+
try {
|
|
70
|
+
ran.outcome = { value: await action() };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
ran.outcome = { error };
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const headers = new Headers(options.headers);
|
|
78
|
+
headers.set("accept", "text/x-component");
|
|
79
|
+
headers.set("content-type", "text/plain;charset=UTF-8");
|
|
80
|
+
headers.set("next-action", serverActionId(runInServerActionOfTest));
|
|
81
|
+
const request = new registry.Request(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers,
|
|
84
|
+
body: JSON.stringify([key]),
|
|
85
|
+
redirect: "manual"
|
|
86
|
+
});
|
|
87
|
+
const response = await sendRequest(request, {
|
|
88
|
+
from,
|
|
89
|
+
opened: {
|
|
90
|
+
pathname: url.pathname,
|
|
91
|
+
proxy: options.proxy ?? false,
|
|
92
|
+
node: {
|
|
93
|
+
ui: null,
|
|
94
|
+
layouts: false,
|
|
95
|
+
files: []
|
|
96
|
+
},
|
|
97
|
+
rendersRedirect: false
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (ran.outcome) await response.body?.pipeTo(new WritableStream()).catch(() => {});
|
|
101
|
+
else {
|
|
102
|
+
await response.body?.cancel();
|
|
103
|
+
const location = response.headers.get("location");
|
|
104
|
+
throw new Error(`vitest-plugin-rsc: the Server Action did not run: ${response.url} ` + (location ? `redirected to ${location}.` : `responded with ${response.status}.`));
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
registry.serverActions.delete(key);
|
|
108
|
+
}
|
|
109
|
+
if ("error" in ran.outcome) throw ran.outcome.error;
|
|
110
|
+
return ran.outcome.value;
|
|
111
|
+
}
|
|
112
|
+
function serverActionId(action) {
|
|
113
|
+
const id = Reflect.get(Object(action), "$$id");
|
|
114
|
+
if (typeof id !== "string") throw new Error("vitest-plugin-rsc: Vite RSC did not compile the plugin's own Server Action. Are `@kasperpeulen/vitest-plugin-rsc` and `@kasperpeulen/vitest-plugin-nextjs` excluded from `optimizeDeps`?");
|
|
115
|
+
return id;
|
|
116
|
+
}
|
|
117
|
+
const unmount = () => leavePage("unmount()");
|
|
118
|
+
async function renderServer(...args) {
|
|
119
|
+
const [first, second] = args;
|
|
120
|
+
const options = (isOptions(first) ? first : second) ?? {};
|
|
121
|
+
const { signal } = options;
|
|
122
|
+
signal?.throwIfAborted();
|
|
123
|
+
const { store } = openContext();
|
|
124
|
+
const errors = [];
|
|
125
|
+
const url = new URL(options.url ?? "/", window.location.origin);
|
|
126
|
+
const headers = new Headers(options.headers);
|
|
127
|
+
const cookie = headers.get("cookie");
|
|
128
|
+
headers.delete("cookie");
|
|
129
|
+
const sticky = new Headers(headers);
|
|
130
|
+
sticky.delete("accept");
|
|
131
|
+
if (!headers.has("accept")) headers.set("accept", "text/html");
|
|
132
|
+
const { pathname } = url;
|
|
133
|
+
if (isOptions(first)) {
|
|
134
|
+
if (first.layouts === false) throw new Error("vitest-plugin-rsc: `renderServer({ url, layouts: false })` is not supported yet. To render a page without the layouts of the app, render it as a node: `renderServer(<Page />, { url })`.");
|
|
135
|
+
const ssr = first.ssr ?? !speedFeature("keepApp");
|
|
136
|
+
const opened = {
|
|
137
|
+
pathname,
|
|
138
|
+
proxy: first.proxy ?? true,
|
|
139
|
+
ssr
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
response: await loadPage(url, { headers }, store, {
|
|
143
|
+
opened,
|
|
144
|
+
headers: sticky,
|
|
145
|
+
cookie,
|
|
146
|
+
signal,
|
|
147
|
+
errors,
|
|
148
|
+
inKeptApp: speedFeature("keepApp") && !ssr
|
|
149
|
+
}),
|
|
150
|
+
errors,
|
|
151
|
+
unmount
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const { wrapper, proxy = false } = options;
|
|
155
|
+
const wrap = (node) => wrapper ? createElement(wrapper, null, node) : node;
|
|
156
|
+
const files = options.hostFiles ?? registry.nodeFiles?.() ?? [];
|
|
157
|
+
const layouts = options.layouts ?? false;
|
|
158
|
+
const served = {
|
|
159
|
+
ui: wrap(first),
|
|
160
|
+
layouts,
|
|
161
|
+
files,
|
|
162
|
+
version: 0
|
|
163
|
+
};
|
|
164
|
+
const opened = {
|
|
165
|
+
pathname,
|
|
166
|
+
proxy,
|
|
167
|
+
node: served
|
|
168
|
+
};
|
|
169
|
+
const nodeOnPage = new NodeOnPage(pathname);
|
|
170
|
+
const rerender = (next) => {
|
|
171
|
+
nodeOnPage.at(window.location.pathname);
|
|
172
|
+
return nodeOnPage.rerender((version) => Object.assign(served, {
|
|
173
|
+
ui: wrap(next),
|
|
174
|
+
version
|
|
175
|
+
}));
|
|
176
|
+
};
|
|
177
|
+
if (layouts) {
|
|
178
|
+
if (options.container || options.baseElement) throw new Error("vitest-plugin-rsc: with `layouts` a node renders in the document of its route, whose root layout has the <html> and the <body>. There is no `container` or `baseElement` to pass.");
|
|
179
|
+
return {
|
|
180
|
+
response: await loadPage(url, { headers }, store, {
|
|
181
|
+
opened,
|
|
182
|
+
headers: sticky,
|
|
183
|
+
cookie,
|
|
184
|
+
nodeOnPage,
|
|
185
|
+
signal,
|
|
186
|
+
errors
|
|
187
|
+
}),
|
|
188
|
+
errors,
|
|
189
|
+
get container() {
|
|
190
|
+
return document.body;
|
|
191
|
+
},
|
|
192
|
+
get baseElement() {
|
|
193
|
+
return document.body;
|
|
194
|
+
},
|
|
195
|
+
asFragment: () => fragmentOf(document.body),
|
|
196
|
+
unmount,
|
|
197
|
+
rerender
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (options.container === document.body || options.container === document.documentElement) throw new Error("vitest-plugin-rsc: the container of a node cannot be the <body> or the <html> of the document, which hold the test's own elements. Pass an element in it, or no container.");
|
|
201
|
+
const left = leavePage("another renderServer()");
|
|
202
|
+
await (signal ? unlessAborted(left, signal, () => {}) : left);
|
|
203
|
+
const base = options.baseElement instanceof HTMLBodyElement ? void 0 : options.baseElement;
|
|
204
|
+
const defaultsToContainer = !options.baseElement && options.container;
|
|
205
|
+
if (options.container && !options.container.isConnected) throw new Error("vitest-plugin-rsc: the container of a node has to be in the document. What is added to the document while a page is open is removed when that page is left, so add the container after it.");
|
|
206
|
+
const container = options.container ?? (base ?? document.body).appendChild(document.createElement("div"));
|
|
207
|
+
if (!options.container) containers.add(container);
|
|
208
|
+
return {
|
|
209
|
+
response: await loadPage(url, { headers }, store, {
|
|
210
|
+
container,
|
|
211
|
+
opened,
|
|
212
|
+
headers: sticky,
|
|
213
|
+
cookie,
|
|
214
|
+
nodeOnPage,
|
|
215
|
+
signal,
|
|
216
|
+
errors
|
|
217
|
+
}),
|
|
218
|
+
errors,
|
|
219
|
+
container,
|
|
220
|
+
get baseElement() {
|
|
221
|
+
return base ?? (defaultsToContainer || document.body);
|
|
222
|
+
},
|
|
223
|
+
asFragment: () => fragmentOf(container),
|
|
224
|
+
unmount,
|
|
225
|
+
rerender
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function fragmentOf(container) {
|
|
229
|
+
const fragment = document.createRange().createContextualFragment(container.innerHTML);
|
|
230
|
+
for (const script of fragment.querySelectorAll("script")) if (!script.type || /^(text\/javascript|module)$/i.test(script.type)) script.remove();
|
|
231
|
+
return fragment;
|
|
232
|
+
}
|
|
233
|
+
const containers = /* @__PURE__ */ new Set();
|
|
234
|
+
function isOptions(value) {
|
|
235
|
+
if (typeof value !== "object" || value === null || "$$typeof" in value) return false;
|
|
236
|
+
const prototype = Object.getPrototypeOf(value);
|
|
237
|
+
return prototype === Object.prototype || prototype === null;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Leaves the page that `renderServer()` opened, removes the containers it made
|
|
241
|
+
* and closes the browser context of the app: its `headers`, the app's
|
|
242
|
+
* cookies and what the app put in its storage go, and the document has the
|
|
243
|
+
* browser's own APIs again. What still runs of the page, like a timer, uses
|
|
244
|
+
* those. The server forgets what it has cached, and what was written to its
|
|
245
|
+
* disk, the `fs` of the server code and the tests.
|
|
246
|
+
* Unlinks the stylesheets of the files of the host: the host links a file's
|
|
247
|
+
* again as it uses it, with `linkStylesheetsOf()`. Runs before and after
|
|
248
|
+
* every test, after which the plugin's setup file links those of the setup
|
|
249
|
+
* files and the test file again.
|
|
250
|
+
*
|
|
251
|
+
* What was set on the document, `<html>`, `<head>` and the container of a
|
|
252
|
+
* node while the page was open goes with the page; what the host set there
|
|
253
|
+
* while no page was open stays. So a library that marks the document once,
|
|
254
|
+
* on its first use, is prepared before the first page.
|
|
255
|
+
*/
|
|
256
|
+
async function cleanup({ url } = {}) {
|
|
257
|
+
await leavePage("cleanup()");
|
|
258
|
+
for (const container of containers) container.remove();
|
|
259
|
+
containers.clear();
|
|
260
|
+
unlinkStylesheets();
|
|
261
|
+
closeContext();
|
|
262
|
+
await loadedSsrLayer()?.resetCaches();
|
|
263
|
+
resetDisk();
|
|
264
|
+
if (url !== void 0) window.history.replaceState(window.history.state, "", url);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Leaves the page, as `cleanup()` does, and opens a new browser context of
|
|
268
|
+
* the app: until `cleanup()`, the document has the app's cookies, storage,
|
|
269
|
+
* requests and navigations, empty, as a page of the app has them. Outside
|
|
270
|
+
* one it has the browser's own. `renderServer()`, `handleRequest()` and
|
|
271
|
+
* `runInServerAction()` open one when none is open, so call this before
|
|
272
|
+
* what seeds the page before it opens, like an item of `localStorage`. The
|
|
273
|
+
* plugin's setup file calls it before every test of a file without
|
|
274
|
+
* `"use client"`.
|
|
275
|
+
*/
|
|
276
|
+
async function newBrowserContext() {
|
|
277
|
+
await cleanup();
|
|
278
|
+
openContext();
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
export { cleanup, handleRequest, newBrowserContext, renderServer, runInServerAction };
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/leftovers.d.ts
|
|
2
|
+
type Leftovers = {
|
|
3
|
+
/** Stops recording. */stop(): void; /** Stops recording, and removes what was recorded. */
|
|
4
|
+
remove(): void;
|
|
5
|
+
};
|
|
6
|
+
/** Records the listeners that are added to `target`, until `stop()`. */
|
|
7
|
+
declare function recordListeners(target: EventTarget): Leftovers;
|
|
8
|
+
/** Records the message channels that the page opens, until `stop()`. */
|
|
9
|
+
declare function recordMessageChannels(): Leftovers;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { Leftovers, recordListeners, recordMessageChannels };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/leftovers.ts
|
|
2
|
+
/** Records the listeners that are added to `target`, until `stop()`. */
|
|
3
|
+
function recordListeners(target) {
|
|
4
|
+
const own = Object.getOwnPropertyDescriptor(target, "addEventListener");
|
|
5
|
+
const add = target.addEventListener;
|
|
6
|
+
const added = [];
|
|
7
|
+
let recording = true;
|
|
8
|
+
const record = function(...args) {
|
|
9
|
+
if (recording) added.push(args);
|
|
10
|
+
add.apply(this ?? target, args);
|
|
11
|
+
};
|
|
12
|
+
target.addEventListener = record;
|
|
13
|
+
const stop = () => {
|
|
14
|
+
recording = false;
|
|
15
|
+
if (target.addEventListener !== record) return;
|
|
16
|
+
if (own) target.addEventListener = add;
|
|
17
|
+
else delete target.addEventListener;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
stop,
|
|
21
|
+
remove() {
|
|
22
|
+
stop();
|
|
23
|
+
for (const args of added.splice(0)) target.removeEventListener(...args);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Records the message channels that the page opens, until `stop()`. */
|
|
28
|
+
function recordMessageChannels() {
|
|
29
|
+
const Native = window.MessageChannel;
|
|
30
|
+
const opened = [];
|
|
31
|
+
let recording = true;
|
|
32
|
+
const Recorded = class MessageChannel extends Native {
|
|
33
|
+
constructor() {
|
|
34
|
+
super();
|
|
35
|
+
if (recording) opened.push(this);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
window.MessageChannel = Recorded;
|
|
39
|
+
const stop = () => {
|
|
40
|
+
recording = false;
|
|
41
|
+
if (window.MessageChannel === Recorded) window.MessageChannel = Native;
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
stop,
|
|
45
|
+
remove() {
|
|
46
|
+
stop();
|
|
47
|
+
for (const channel of opened.splice(0)) {
|
|
48
|
+
channel.port1.close();
|
|
49
|
+
channel.port2.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { recordListeners, recordMessageChannels };
|