@rsc-kit/core 0.19.0 → 0.20.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/dist/apiPrerender.js +1 -1
- package/dist/apiPrerender.js.map +1 -1
- package/dist/js/Form.d.ts +2 -2
- package/dist/js/Form.js.map +1 -1
- package/dist/js/Link.d.ts +5 -5
- package/dist/js/Link.js.map +1 -1
- package/dist/js/RedirectBoundary.d.ts +11 -1
- package/dist/js/RedirectBoundary.js +10 -1
- package/dist/js/RedirectBoundary.js.map +1 -1
- package/dist/js/RouteErrorBoundary.d.ts +6 -2
- package/dist/js/RouteErrorBoundary.js +10 -0
- package/dist/js/RouteErrorBoundary.js.map +1 -1
- package/dist/js/SegmentBoundary.js +11 -4
- package/dist/js/SegmentBoundary.js.map +1 -1
- package/dist/js/SlotBoundary.d.ts +7 -1
- package/dist/js/SlotBoundary.js +9 -1
- package/dist/js/SlotBoundary.js.map +1 -1
- package/dist/js/createViteRscApp.js +20 -0
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/navigate.d.ts +3 -3
- package/dist/js/navigate.js +1 -1
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/nuqs.js.map +1 -1
- package/dist/js/router.d.ts +3 -3
- package/dist/js/router.js.map +1 -1
- package/dist/manifest.d.ts +6 -0
- package/dist/manifest.js.map +1 -1
- package/dist/redirect.d.ts +3 -3
- package/dist/redirect.js.map +1 -1
- package/dist/routes.d.ts +6 -9
- package/dist/routes.js.map +1 -1
- package/dist/useSsr.d.ts +11 -0
- package/dist/useSsr.js +15 -0
- package/dist/useSsr.js.map +1 -1
- package/dist/vite.js +49 -8
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/dist/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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","sourcesContent":["// Typed routes: the urls this app can actually answer, as a type.\n//\n// Laravel needs route() because the url lives in PHP and can move\n// independently of the name it is called by. Here the url *is* the file path,\n// so a name would be indirection that buys nothing. What is worth having is\n// the other half — a link to a page that does not exist should fail at build\n// time rather than in the browser.\n//\n// There is no route() builder to go with this, deliberately. A template\n// literal is checked the same way — `/posts/${slug}` compiles and\n// `/postz/${slug}` does not — so a builder would only wrap what the language\n// already does. Encoding a value that is not url-safe is `encodeURIComponent`\n// in the template, the same as anywhere else.\n//\n// The build already walks app/ and knows every route's segments, so it writes\n// one line into the app's source dir:\n//\n// declare module '@rsc-kit/core/routes' {\n// interface Register { routes: '/' | '/posts/[slug]' }\n// }\n//\n// Everything below is derived from that union. An app that never runs the\n// generator — a generic host, a Laravel app that has not rebuilt — registers\n// nothing, `RoutePattern` stays `string`, and every url-taking API is exactly\n// as permissive as it was before. That fallback is the reason this can ship\n// without a flag.\n\n/**\n * Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.\n *\n * Declaration merging rather than a generic parameter, because the routes are\n * a property of the project, not of each call site — threading them through\n * every component that renders a Link is not a thing anyone would do twice.\n */\nexport interface Register {}\n\n/** The route patterns this app declared: `'/posts/[slug]'`. */\nexport type RoutePattern = Register extends { routes: infer R extends string }\n ? R\n : string;\n\n/** Whether anything was registered. `string` means the generator never ran. */\ntype Unregistered = string extends RoutePattern ? true : false;\n\n/**\n * A pattern with its dynamic segments opened up: `/posts/[slug]` accepts\n * `/posts/anything`.\n *\n * Catch-all and single params both become `${string}`, which for a catch-all\n * also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.\n */\ntype Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P extends `${infer A}[${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P;\n\n/**\n * Not a route, but a legitimate href: another site, a mail client, a phone\n * number, an anchor on this page, a bare query string.\n */\ntype OffRoute =\n | `${string}://${string}`\n | `mailto:${string}`\n | `tel:${string}`\n | `#${string}`\n | `?${string}`;\n\n/**\n * A url this app can answer, or one that deliberately leaves it.\n *\n * Cast when the destination is computed rather than written:\n * `href={path as Href}`.\n */\n/**\n * A url this app answers - a page or a route.ts - or one that deliberately\n * leaves it. One type, as Next's `Route` is one type: what `<Link href>`,\n * `visit()` and `redirect()` take. A route.ts is a full navigation rather\n * than a payload fetch, and never prefetched - the client knows which urls\n * are routes and treats a link to one as the anchor it is - so the type\n * does not have to keep them apart to keep a hover from running one.\n *\n * `Route` is the same type under the name Next uses, so a port keeps the\n * word it already has.\n */\nexport type Href = Unregistered extends true\n ? string\n : | Filled<RoutePattern>\n | `${Filled<RoutePattern>}?${string}`\n | `${Filled<RoutePattern>}#${string}`\n | Filled<ApiPattern>\n | `${Filled<ApiPattern>}?${string}`\n | OffRoute;\n\nexport type Route = Href;\n\n// ── Api routes ───────────────────────────────────────────────────────────────\n//\n// Their own union rather than part of Href, because they are not pages and a\n// link to one is almost always a mistake — an <a href=\"/api/orders\"> navigates\n// the browser away to a json document. Keeping them apart means `Link` refuses\n// an api url and `apiUrl()` refuses a page, which is the pair of mistakes worth\n// catching.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterApi {}\n\n// ── Regions a page can re-render on its own ──────────────────────────────────\n//\n// Every `section('name', …)` the build found, and every `@slot` directory.\n// `revalidate('orders')` is checked against them, so a name that matches no\n// section - a typo, or a section renamed since - stops compiling instead of\n// being refused by the renderer at runtime.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterRegions {}\n\n/** The region names the build found: section names and slot names. */\nexport type RegionName = RegisterRegions extends {\n regions: infer R extends string;\n}\n ? R\n : string;\n\ntype NoRegions = string extends RegionName ? true : false;\n\n/**\n * What `revalidate()` takes: the page, the whole document, or a region by\n * name. `string` until the build has written the types.\n */\nexport type RevalidateTarget = NoRegions extends true\n ? string\n : \"page\" | \"all\" | RegionName;\n\n/** The api route patterns this app declared: `'/api/orders/[id]'`. */\nexport type ApiPattern = RegisterApi extends { apis: infer R extends string }\n ? R\n : string;\n\ntype NoApis = string extends ApiPattern ? true : false;\n\n/**\n * A url an api route in this app answers.\n *\n * `/api/orders/[id]` accepts `/api/orders/42`, and a query string is allowed\n * because that is how a GET is parameterised.\n */\nexport type ApiHref = NoApis extends true\n ? string\n : Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}`;\n\n/** A url a route.ts answers, alone: what `apiUrl()` narrows a fetch to. */\nexport type ApiRoute = ApiHref;\n\n/**\n * An api url, checked against the routes the build found.\n *\n * await fetch(apiUrl(`/api/orders/${id}`))\n *\n * A function rather than a bare type so it can be used inline at a call site\n * that is typed `string` — `fetch` takes any string, so nothing would check the\n * argument without somewhere to put the type. It returns what it was given.\n *\n * Wrong path, and it stops compiling. Renamed the directory, and every call\n * site says so rather than one of them 404ing in production.\n */\nexport function apiUrl(href: ApiHref): string {\n return href;\n}\n\n// ── Search params, typed per route ───────────────────────────────────────────\n//\n// A page that exports a `searchParams` schema has said what its query string\n// means. The generated file records that schema per pattern:\n//\n// interface Register {\n// search: { '/search': SearchExportOf<typeof import('../src/app/search/page')> }\n// }\n//\n// and from there a link to `/search` is checked against the same schema the\n// page parses with — a `page` that must be a number is a number on the link,\n// a `q` the page requires is required to write the link, and a key the page\n// never reads does not compile. One schema, both ends. A route that exports\n// none takes anything; an href that is not a single route (computed, cast, or\n// off-site) takes anything too, because there is nothing to check it against.\n\n/** What a page module contributes: its `searchParams` export, or nothing. For the generated file. */\nexport type SearchExportOf<M> = M extends { searchParams: infer S }\n ? S\n : undefined;\n\ntype SearchMap = Register extends { search: infer M } ? M : {};\n\ntype StripQuery<H extends string> = H extends `${infer P}?${string}`\n ? P\n : H extends `${infer P}#${string}`\n ? P\n : H;\n\n/** The pattern a written href belongs to: `/posts/hello` is `/posts/[slug]`. */\ntype PatternOf<H extends string> = RoutePattern extends infer P\n ? P extends string\n ? StripQuery<H> extends Filled<P>\n ? P\n : never\n : never\n : never;\n\ntype IsUnion<T, U = T> = T extends unknown\n ? [U] extends [T]\n ? false\n : true\n : never;\n\ntype SchemaFor<P> = P extends keyof SearchMap ? SearchMap[P] : undefined;\n\ntype InputOf<S> = S extends { \"~standard\": { types?: { input: infer I } } }\n ? I\n : never;\ntype OutputOf<S> = S extends { \"~standard\": { types?: { output: infer O } } }\n ? O\n : never;\n\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\ntype RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * What a link may write for a schema: the keys the schema's input requires\n * are required, the rest optional, and every value is the schema's OUTPUT\n * type. Output rather than input because `z.coerce.number()` takes `unknown`\n * in - that is what coercion means - and a link typed by it would accept\n * `page: 'two'`. The output is the number the page will actually see.\n */\ntype LinkInputOf<S> = Simplify<\n { [K in RequiredKeys<InputOf<S>> & keyof OutputOf<S>]: OutputOf<S>[K] } & {\n [K in OptionalKeys<InputOf<S>> & keyof OutputOf<S>]?: OutputOf<S>[K];\n }\n>;\n\ntype Scalar = string | number | boolean | null | undefined;\n\n/** What a link may carry when nothing declares otherwise. */\nexport type LooseSearch = Record<string, Scalar | readonly (string | number)[]>;\n\n/**\n * The search params a link to `H` may carry.\n *\n * The page's schema input when `H` is one declared route with a schema;\n * otherwise anything. \"One route\" matters: `path as Href` is every route at\n * once, and a link that could go anywhere cannot be held to one page's schema.\n */\nexport type SearchFor<H extends string> = Unregistered extends true\n ? LooseSearch\n : IsUnion<H> extends true\n ? LooseSearch\n : [PatternOf<H>] extends [never]\n ? LooseSearch\n : SchemaFor<PatternOf<H>> extends undefined\n ? LooseSearch\n : LinkInputOf<SchemaFor<PatternOf<H>>>;\n\n/**\n * The `search` prop, required exactly when the page's schema has a required\n * key. A page that needs `q` is not reachable without one, so the link that\n * omits it is the bug — caught here rather than on the page's error boundary.\n */\nexport type SearchProp<H extends string> =\n {} extends SearchFor<H>\n ? { search?: SearchFor<H> }\n : { search: SearchFor<H> };\n\n/** A query string from an object: scalars stringified, arrays repeated, null and undefined dropped. */\nexport function searchString(search: object): string {\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(search)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n } else {\n params.set(key, String(value));\n }\n }\n\n return params.toString();\n}\n\n/** `path` with `search` appended, keeping any query and hash already on it. */\nexport function withSearch(path: string, search: object | undefined): string {\n if (!search) return path;\n\n const hashAt = path.indexOf(\"#\");\n const hash = hashAt === -1 ? \"\" : path.slice(hashAt);\n const before = hashAt === -1 ? path : path.slice(0, hashAt);\n const queryAt = before.indexOf(\"?\");\n const base = queryAt === -1 ? before : before.slice(0, queryAt);\n const existing = queryAt === -1 ? \"\" : before.slice(queryAt + 1);\n const added = searchString(search);\n const query = [existing, added].filter(Boolean).join(\"&\");\n\n return query ? `${base}?${query}${hash}` : `${base}${hash}`;\n}\n\n/**\n * A typed url with its search params, for the places that take a string.\n *\n * visit(href('/search', { q: 'shoes', page: 2 }))\n *\n * `Link` has the same check on its own `search` prop. This is for `visit`,\n * `prefetch`, `redirect` and anything else that wants the finished string.\n */\nexport function href<H extends Href>(\n path: H,\n ...rest: {} extends SearchFor<H>\n ? [search?: SearchFor<H>]\n : [search: SearchFor<H>]\n): Href {\n return withSearch(path, rest[0] as object | undefined) as Href;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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","sourcesContent":["// Typed routes: the urls this app can actually answer, as a type.\n//\n// Laravel needs route() because the url lives in PHP and can move\n// independently of the name it is called by. Here the url *is* the file path,\n// so a name would be indirection that buys nothing. What is worth having is\n// the other half — a link to a page that does not exist should fail at build\n// time rather than in the browser.\n//\n// There is no route() builder to go with this, deliberately. A template\n// literal is checked the same way — `/posts/${slug}` compiles and\n// `/postz/${slug}` does not — so a builder would only wrap what the language\n// already does. Encoding a value that is not url-safe is `encodeURIComponent`\n// in the template, the same as anywhere else.\n//\n// The build already walks app/ and knows every route's segments, so it writes\n// one line into the app's source dir:\n//\n// declare module '@rsc-kit/core/routes' {\n// interface Register { routes: '/' | '/posts/[slug]' }\n// }\n//\n// Everything below is derived from that union. An app that never runs the\n// generator — a generic host, a Laravel app that has not rebuilt — registers\n// nothing, `RoutePattern` stays `string`, and every url-taking API is exactly\n// as permissive as it was before. That fallback is the reason this can ship\n// without a flag.\n\n/**\n * Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.\n *\n * Declaration merging rather than a generic parameter, because the routes are\n * a property of the project, not of each call site — threading them through\n * every component that renders a Link is not a thing anyone would do twice.\n */\nexport interface Register {}\n\n/** The route patterns this app declared: `'/posts/[slug]'`. */\nexport type RoutePattern = Register extends { routes: infer R extends string }\n ? R\n : string;\n\n/** Whether anything was registered. `string` means the generator never ran. */\ntype Unregistered = string extends RoutePattern ? true : false;\n\n/**\n * A pattern with its dynamic segments opened up: `/posts/[slug]` accepts\n * `/posts/anything`.\n *\n * Catch-all and single params both become `${string}`, which for a catch-all\n * also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.\n */\ntype Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P extends `${infer A}[${string}]${infer B}`\n ? `${A}${string}${Filled<B>}`\n : P;\n\n/**\n * Not a route, but a legitimate href: another site, a mail client, a phone\n * number, an anchor on this page, a bare query string.\n */\ntype OffRoute =\n | `${string}://${string}`\n | `mailto:${string}`\n | `tel:${string}`\n | `#${string}`\n | `?${string}`;\n\n/**\n * A url this app can answer, or one that deliberately leaves it.\n *\n * Cast when the destination is computed rather than written:\n * `href={path as Route}`.\n */\n/**\n * A url this app answers - a page or a route.ts - or one that deliberately\n * leaves it. One type, as Next's `Route` is one type: what `<Link href>`,\n * `visit()` and `redirect()` take. A route.ts is a full navigation rather\n * than a payload fetch, and never prefetched - the client knows which urls\n * are routes and treats a link to one as the anchor it is - so the type\n * does not have to keep them apart to keep a hover from running one.\n *\n * `Route` is the same type under the name Next uses, so a port keeps the\n * word it already has.\n */\nexport type Route = Unregistered extends true\n ? string\n : | Filled<RoutePattern>\n | `${Filled<RoutePattern>}?${string}`\n | `${Filled<RoutePattern>}#${string}`\n | Filled<ApiPattern>\n | `${Filled<ApiPattern>}?${string}`\n | OffRoute;\n\n// ── Api routes ───────────────────────────────────────────────────────────────\n//\n// Their own union rather than part of Route, because they are not pages and a\n// link to one is almost always a mistake — an <a href=\"/api/orders\"> navigates\n// the browser away to a json document. Keeping them apart means `Link` refuses\n// an api url and `apiUrl()` refuses a page, which is the pair of mistakes worth\n// catching.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterApi {}\n\n// ── Regions a page can re-render on its own ──────────────────────────────────\n//\n// Every `section('name', …)` the build found, and every `@slot` directory.\n// `revalidate('orders')` is checked against them, so a name that matches no\n// section - a typo, or a section renamed since - stops compiling instead of\n// being refused by the renderer at runtime.\n\n/** Augmented by the generated `rsc-routes.d.ts`, like `Register`. */\nexport interface RegisterRegions {}\n\n/** The region names the build found: section names and slot names. */\nexport type RegionName = RegisterRegions extends {\n regions: infer R extends string;\n}\n ? R\n : string;\n\ntype NoRegions = string extends RegionName ? true : false;\n\n/**\n * What `revalidate()` takes: the page, the whole document, or a region by\n * name. `string` until the build has written the types.\n */\nexport type RevalidateTarget = NoRegions extends true\n ? string\n : \"page\" | \"all\" | RegionName;\n\n/** The api route patterns this app declared: `'/api/orders/[id]'`. */\nexport type ApiPattern = RegisterApi extends { apis: infer R extends string }\n ? R\n : string;\n\ntype NoApis = string extends ApiPattern ? true : false;\n\n/**\n * A url an api route in this app answers.\n *\n * `/api/orders/[id]` accepts `/api/orders/42`, and a query string is allowed\n * because that is how a GET is parameterised.\n */\nexport type ApiRoute = NoApis extends true\n ? string\n : Filled<ApiPattern> | `${Filled<ApiPattern>}?${string}`;\n\n/**\n * An api url, checked against the routes the build found.\n *\n * await fetch(apiUrl(`/api/orders/${id}`))\n *\n * A function rather than a bare type so it can be used inline at a call site\n * that is typed `string` — `fetch` takes any string, so nothing would check the\n * argument without somewhere to put the type. It returns what it was given.\n *\n * Wrong path, and it stops compiling. Renamed the directory, and every call\n * site says so rather than one of them 404ing in production.\n */\nexport function apiUrl(href: ApiRoute): string {\n return href;\n}\n\n// ── Search params, typed per route ───────────────────────────────────────────\n//\n// A page that exports a `searchParams` schema has said what its query string\n// means. The generated file records that schema per pattern:\n//\n// interface Register {\n// search: { '/search': SearchExportOf<typeof import('../src/app/search/page')> }\n// }\n//\n// and from there a link to `/search` is checked against the same schema the\n// page parses with — a `page` that must be a number is a number on the link,\n// a `q` the page requires is required to write the link, and a key the page\n// never reads does not compile. One schema, both ends. A route that exports\n// none takes anything; an href that is not a single route (computed, cast, or\n// off-site) takes anything too, because there is nothing to check it against.\n\n/** What a page module contributes: its `searchParams` export, or nothing. For the generated file. */\nexport type SearchExportOf<M> = M extends { searchParams: infer S }\n ? S\n : undefined;\n\ntype SearchMap = Register extends { search: infer M } ? M : {};\n\ntype StripQuery<H extends string> = H extends `${infer P}?${string}`\n ? P\n : H extends `${infer P}#${string}`\n ? P\n : H;\n\n/** The pattern a written href belongs to: `/posts/hello` is `/posts/[slug]`. */\ntype PatternOf<H extends string> = RoutePattern extends infer P\n ? P extends string\n ? StripQuery<H> extends Filled<P>\n ? P\n : never\n : never\n : never;\n\ntype IsUnion<T, U = T> = T extends unknown\n ? [U] extends [T]\n ? false\n : true\n : never;\n\ntype SchemaFor<P> = P extends keyof SearchMap ? SearchMap[P] : undefined;\n\ntype InputOf<S> = S extends { \"~standard\": { types?: { input: infer I } } }\n ? I\n : never;\ntype OutputOf<S> = S extends { \"~standard\": { types?: { output: infer O } } }\n ? O\n : never;\n\ntype OptionalKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? K : never;\n}[keyof T];\ntype RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n/**\n * What a link may write for a schema: the keys the schema's input requires\n * are required, the rest optional, and every value is the schema's OUTPUT\n * type. Output rather than input because `z.coerce.number()` takes `unknown`\n * in - that is what coercion means - and a link typed by it would accept\n * `page: 'two'`. The output is the number the page will actually see.\n */\ntype LinkInputOf<S> = Simplify<\n { [K in RequiredKeys<InputOf<S>> & keyof OutputOf<S>]: OutputOf<S>[K] } & {\n [K in OptionalKeys<InputOf<S>> & keyof OutputOf<S>]?: OutputOf<S>[K];\n }\n>;\n\ntype Scalar = string | number | boolean | null | undefined;\n\n/** What a link may carry when nothing declares otherwise. */\nexport type LooseSearch = Record<string, Scalar | readonly (string | number)[]>;\n\n/**\n * The search params a link to `H` may carry.\n *\n * The page's schema input when `H` is one declared route with a schema;\n * otherwise anything. \"One route\" matters: `path as Route` is every route at\n * once, and a link that could go anywhere cannot be held to one page's schema.\n */\nexport type SearchFor<H extends string> = Unregistered extends true\n ? LooseSearch\n : IsUnion<H> extends true\n ? LooseSearch\n : [PatternOf<H>] extends [never]\n ? LooseSearch\n : SchemaFor<PatternOf<H>> extends undefined\n ? LooseSearch\n : LinkInputOf<SchemaFor<PatternOf<H>>>;\n\n/**\n * The `search` prop, required exactly when the page's schema has a required\n * key. A page that needs `q` is not reachable without one, so the link that\n * omits it is the bug — caught here rather than on the page's error boundary.\n */\nexport type SearchProp<H extends string> =\n {} extends SearchFor<H>\n ? { search?: SearchFor<H> }\n : { search: SearchFor<H> };\n\n/** A query string from an object: scalars stringified, arrays repeated, null and undefined dropped. */\nexport function searchString(search: object): string {\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(search)) {\n if (value === null || value === undefined) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n } else {\n params.set(key, String(value));\n }\n }\n\n return params.toString();\n}\n\n/** `path` with `search` appended, keeping any query and hash already on it. */\nexport function withSearch(path: string, search: object | undefined): string {\n if (!search) return path;\n\n const hashAt = path.indexOf(\"#\");\n const hash = hashAt === -1 ? \"\" : path.slice(hashAt);\n const before = hashAt === -1 ? path : path.slice(0, hashAt);\n const queryAt = before.indexOf(\"?\");\n const base = queryAt === -1 ? before : before.slice(0, queryAt);\n const existing = queryAt === -1 ? \"\" : before.slice(queryAt + 1);\n const added = searchString(search);\n const query = [existing, added].filter(Boolean).join(\"&\");\n\n return query ? `${base}?${query}${hash}` : `${base}${hash}`;\n}\n\n/**\n * A typed url with its search params, for the places that take a string.\n *\n * visit(href('/search', { q: 'shoes', page: 2 }))\n *\n * `Link` has the same check on its own `search` prop. This is for `visit`,\n * `prefetch`, `redirect` and anything else that wants the finished string.\n */\nexport function href<H extends Route>(\n path: H,\n ...rest: {} extends SearchFor<H>\n ? [search?: SearchFor<H>]\n : [search: SearchFor<H>]\n): Route {\n return withSearch(path, rest[0] as object | undefined) as Route;\n}\n"]}
|
package/dist/useSsr.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The module with its directive taken out, for the environment it runs in.
|
|
3
|
+
*
|
|
4
|
+
* Where server components render the directive turns the module into
|
|
5
|
+
* proxies; in the ssr environment the module is the real thing and the
|
|
6
|
+
* directive is a string with no meaning left - which the bundler says so
|
|
7
|
+
* about, once per build, as MODULE_LEVEL_DIRECTIVE "may not be preserved".
|
|
8
|
+
* Nothing needed preserving. Removed, and the warning with it. Null when the
|
|
9
|
+
* code has no directive.
|
|
10
|
+
*/
|
|
11
|
+
export declare function withoutSsrDirective(code: string): string | null;
|
|
1
12
|
export declare const SSR_GUIDE = "https://docs.rsc-kit.dev/guides/emails";
|
|
2
13
|
export declare function hasUseSsr(code: string): boolean;
|
|
3
14
|
export declare class UseSsrError extends Error {
|
package/dist/useSsr.js
CHANGED
|
@@ -21,6 +21,21 @@ import { basename, relative } from "node:path";
|
|
|
21
21
|
* export named, at build and in dev, rather than proxied into nonsense.
|
|
22
22
|
*/
|
|
23
23
|
const DIRECTIVE = /^\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*["']use ssr["']/;
|
|
24
|
+
/**
|
|
25
|
+
* The module with its directive taken out, for the environment it runs in.
|
|
26
|
+
*
|
|
27
|
+
* Where server components render the directive turns the module into
|
|
28
|
+
* proxies; in the ssr environment the module is the real thing and the
|
|
29
|
+
* directive is a string with no meaning left - which the bundler says so
|
|
30
|
+
* about, once per build, as MODULE_LEVEL_DIRECTIVE "may not be preserved".
|
|
31
|
+
* Nothing needed preserving. Removed, and the warning with it. Null when the
|
|
32
|
+
* code has no directive.
|
|
33
|
+
*/
|
|
34
|
+
export function withoutSsrDirective(code) {
|
|
35
|
+
if (!DIRECTIVE.test(code))
|
|
36
|
+
return null;
|
|
37
|
+
return code.replace(/(^\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*)["']use ssr["'];?[ \t]*\n?/, "$1");
|
|
38
|
+
}
|
|
24
39
|
export const SSR_GUIDE = "https://docs.rsc-kit.dev/guides/emails";
|
|
25
40
|
export function hasUseSsr(code) {
|
|
26
41
|
return DIRECTIVE.test(code.slice(0, 2048));
|
package/dist/useSsr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSsr.js","sourceRoot":"","sources":["../src/useSsr.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, relative } from \"node:path\";\n\n/**\n * \"use ssr\": a module that runs in the ssr environment and is called from\n * where server components render.\n *\n * react-dom/server cannot run where server components render. React's\n * server build refuses to load it, and the refusal is right: the renderer\n * needs the client build's internals, and the components it would render\n * import that same server `react` - no useState, no useContext. A template\n * and the call that renders it have to load together somewhere React DOM's\n * server renderer can: the ssr environment, which every app here already\n * has. plugin-rsc exposes that as a call to write by hand,\n * `import.meta.viteRsc.import('./render', { environment: 'ssr' })`, and\n * nobody should have to. So a directive names the module instead, the way\n * \"use client\" names one, and this rewrites it for the server-components\n * environment into proxies of its exports that call across. Everything\n * else imports it normally.\n *\n * The exports are functions and the calls are async, because the answer\n * crosses environments as a promise. Anything else is refused with the\n * export named, at build and in dev, rather than proxied into nonsense.\n */\nconst DIRECTIVE = /^\\s*(?:\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/\\s*)*[\"']use ssr[\"']/;\n\nexport const SSR_GUIDE = \"https://docs.rsc-kit.dev/guides/emails\";\n\nexport function hasUseSsr(code: string): boolean {\n return DIRECTIVE.test(code.slice(0, 2048));\n}\n\nexport class UseSsrError extends Error {}\n\n/** Comments blanked, so an `export` inside one is not read as one. */\nfunction withoutComments(code: string): string {\n return code\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => m.replace(/[^\\n]/g, \" \"))\n .replace(\n /(^|[^:\\\\])\\/\\/[^\\n]*/g,\n (m, lead: string) => lead + \" \".repeat(m.length - lead.length),\n );\n}\n\nconst NAME = \"[A-Za-z_$][\\\\w$]*\";\nconst NON_FUNCTION = /^\\s*(?:new\\b|[\"'`\\d[{]|true\\b|false\\b|null\\b)/;\n\n/** The runtime exports a \"use ssr\" module can offer: named functions, and a default. */\nexport function ssrExports(\n code: string,\n file: string,\n): { named: string[]; hasDefault: boolean } {\n const src = withoutComments(code);\n const named = new Set<string>();\n let hasDefault = false;\n\n for (const m of src.matchAll(\n new RegExp(\n `^[ \\\\t]*export\\\\s+(async\\\\s+)?function\\\\s*\\\\*?\\\\s*(${NAME})`,\n \"gm\",\n ),\n )) {\n if (!m[1]) {\n throw new UseSsrError(\n `\"use ssr\": \\`${m[2]}\\` in ${file} is called from another environment, and the answer crosses as a promise. Make it an async function.`,\n );\n }\n\n named.add(m[2]);\n }\n\n for (const m of src.matchAll(\n new RegExp(\n `^[ \\\\t]*export\\\\s+(?:const|let|var)\\\\s+(${NAME})\\\\s*(?::[^=\\\\n]*)?=([^\\\\n]*)`,\n \"gm\",\n ),\n )) {\n if (NON_FUNCTION.test(m[2])) {\n throw new UseSsrError(\n `\"use ssr\": a module with the directive exports functions only; \\`${m[1]}\\` in ${file} is a value. Put it in a module without the directive.`,\n );\n }\n\n named.add(m[1]);\n }\n\n if (/^[ \\t]*export\\s+(?:const|let|var)\\s*[[{]/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": ${file} destructures an export. Export each function by its own name; that is what gets called across.`,\n );\n }\n\n if (/^[ \\t]*export\\s+(?:abstract\\s+)?(?:class|enum)\\b/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": a module with the directive exports functions only; ${file} exports a class or enum.`,\n );\n }\n\n if (/^[ \\t]*export\\s*(?:\\{|\\*)/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": ${file} re-exports. Export the functions where they are declared; \\`export { … }\\` and \\`export *\\` cannot be called across.`,\n );\n }\n\n const dflt = /^[ \\t]*export\\s+default\\s+(async\\s+)?(function\\b)?/m.exec(src);\n\n if (dflt) {\n if (dflt[2] && !dflt[1]) {\n throw new UseSsrError(\n `\"use ssr\": the default export of ${file} is called from another environment. Make it an async function.`,\n );\n }\n\n hasDefault = true;\n }\n\n return { named: [...named], hasDefault };\n}\n\n/**\n * The module the server-components environment gets in place of a \"use ssr\"\n * one: its exports, as async proxies that call the real module where it\n * runs. Null when the module carries no directive.\n */\nexport function ssrProxyModule(\n code: string,\n id: string,\n root: string = process.cwd(),\n): string | null {\n if (!hasUseSsr(code)) return null;\n\n const path = id.split(\"?\")[0];\n const file = relative(root, path);\n const { named, hasDefault } = ssrExports(code, file);\n\n return (\n [\n `// \"use ssr\": ${file} runs in the ssr environment, where React DOM's server renderer can. These call across.`,\n `const __rsc_kit_ssr = import.meta.viteRsc.import(${JSON.stringify(\"./\" + basename(path))}, { environment: 'ssr' });`,\n ...named.map(\n (name) =>\n `export const ${name} = async (...args) => (await __rsc_kit_ssr).${name}(...args);`,\n ),\n ...(hasDefault\n ? [\n \"export default async (...args) => (await __rsc_kit_ssr).default(...args);\",\n ]\n : []),\n ].join(\"\\n\") + \"\\n\"\n );\n}\n\n/** Any of react-dom's server renderer entries. */\nexport const SERVER_RENDERER = /^react-dom\\/server(?:\\.[a-z]+)?$/;\n\n/** What a module gets in place of react-dom/server where server components render. */\nexport function serverRendererMessage(importer: string | null): string {\n return (\n \"react-dom/server cannot run where server components render: React's server build has no client internals for it, and the components it would render import that same React.\" +\n (importer ? ` Imported by ${importer}.` : \"\") +\n ' Put the rendering - the template and the call - in a module that starts with \"use ssr\": it runs in the ssr environment, and its exports are called from here as async functions. ' +\n SSR_GUIDE\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useSsr.js","sourceRoot":"","sources":["../src/useSsr.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, relative } from \"node:path\";\n\n/**\n * \"use ssr\": a module that runs in the ssr environment and is called from\n * where server components render.\n *\n * react-dom/server cannot run where server components render. React's\n * server build refuses to load it, and the refusal is right: the renderer\n * needs the client build's internals, and the components it would render\n * import that same server `react` - no useState, no useContext. A template\n * and the call that renders it have to load together somewhere React DOM's\n * server renderer can: the ssr environment, which every app here already\n * has. plugin-rsc exposes that as a call to write by hand,\n * `import.meta.viteRsc.import('./render', { environment: 'ssr' })`, and\n * nobody should have to. So a directive names the module instead, the way\n * \"use client\" names one, and this rewrites it for the server-components\n * environment into proxies of its exports that call across. Everything\n * else imports it normally.\n *\n * The exports are functions and the calls are async, because the answer\n * crosses environments as a promise. Anything else is refused with the\n * export named, at build and in dev, rather than proxied into nonsense.\n */\nconst DIRECTIVE = /^\\s*(?:\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/\\s*)*[\"']use ssr[\"']/;\n\n/**\n * The module with its directive taken out, for the environment it runs in.\n *\n * Where server components render the directive turns the module into\n * proxies; in the ssr environment the module is the real thing and the\n * directive is a string with no meaning left - which the bundler says so\n * about, once per build, as MODULE_LEVEL_DIRECTIVE \"may not be preserved\".\n * Nothing needed preserving. Removed, and the warning with it. Null when the\n * code has no directive.\n */\nexport function withoutSsrDirective(code: string): string | null {\n if (!DIRECTIVE.test(code)) return null;\n\n return code.replace(/(^\\s*(?:\\/\\/[^\\n]*\\n|\\/\\*[\\s\\S]*?\\*\\/\\s*)*)[\"']use ssr[\"'];?[ \\t]*\\n?/, \"$1\");\n}\n\nexport const SSR_GUIDE = \"https://docs.rsc-kit.dev/guides/emails\";\n\nexport function hasUseSsr(code: string): boolean {\n return DIRECTIVE.test(code.slice(0, 2048));\n}\n\nexport class UseSsrError extends Error {}\n\n/** Comments blanked, so an `export` inside one is not read as one. */\nfunction withoutComments(code: string): string {\n return code\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => m.replace(/[^\\n]/g, \" \"))\n .replace(\n /(^|[^:\\\\])\\/\\/[^\\n]*/g,\n (m, lead: string) => lead + \" \".repeat(m.length - lead.length),\n );\n}\n\nconst NAME = \"[A-Za-z_$][\\\\w$]*\";\nconst NON_FUNCTION = /^\\s*(?:new\\b|[\"'`\\d[{]|true\\b|false\\b|null\\b)/;\n\n/** The runtime exports a \"use ssr\" module can offer: named functions, and a default. */\nexport function ssrExports(\n code: string,\n file: string,\n): { named: string[]; hasDefault: boolean } {\n const src = withoutComments(code);\n const named = new Set<string>();\n let hasDefault = false;\n\n for (const m of src.matchAll(\n new RegExp(\n `^[ \\\\t]*export\\\\s+(async\\\\s+)?function\\\\s*\\\\*?\\\\s*(${NAME})`,\n \"gm\",\n ),\n )) {\n if (!m[1]) {\n throw new UseSsrError(\n `\"use ssr\": \\`${m[2]}\\` in ${file} is called from another environment, and the answer crosses as a promise. Make it an async function.`,\n );\n }\n\n named.add(m[2]);\n }\n\n for (const m of src.matchAll(\n new RegExp(\n `^[ \\\\t]*export\\\\s+(?:const|let|var)\\\\s+(${NAME})\\\\s*(?::[^=\\\\n]*)?=([^\\\\n]*)`,\n \"gm\",\n ),\n )) {\n if (NON_FUNCTION.test(m[2])) {\n throw new UseSsrError(\n `\"use ssr\": a module with the directive exports functions only; \\`${m[1]}\\` in ${file} is a value. Put it in a module without the directive.`,\n );\n }\n\n named.add(m[1]);\n }\n\n if (/^[ \\t]*export\\s+(?:const|let|var)\\s*[[{]/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": ${file} destructures an export. Export each function by its own name; that is what gets called across.`,\n );\n }\n\n if (/^[ \\t]*export\\s+(?:abstract\\s+)?(?:class|enum)\\b/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": a module with the directive exports functions only; ${file} exports a class or enum.`,\n );\n }\n\n if (/^[ \\t]*export\\s*(?:\\{|\\*)/m.test(src)) {\n throw new UseSsrError(\n `\"use ssr\": ${file} re-exports. Export the functions where they are declared; \\`export { … }\\` and \\`export *\\` cannot be called across.`,\n );\n }\n\n const dflt = /^[ \\t]*export\\s+default\\s+(async\\s+)?(function\\b)?/m.exec(src);\n\n if (dflt) {\n if (dflt[2] && !dflt[1]) {\n throw new UseSsrError(\n `\"use ssr\": the default export of ${file} is called from another environment. Make it an async function.`,\n );\n }\n\n hasDefault = true;\n }\n\n return { named: [...named], hasDefault };\n}\n\n/**\n * The module the server-components environment gets in place of a \"use ssr\"\n * one: its exports, as async proxies that call the real module where it\n * runs. Null when the module carries no directive.\n */\nexport function ssrProxyModule(\n code: string,\n id: string,\n root: string = process.cwd(),\n): string | null {\n if (!hasUseSsr(code)) return null;\n\n const path = id.split(\"?\")[0];\n const file = relative(root, path);\n const { named, hasDefault } = ssrExports(code, file);\n\n return (\n [\n `// \"use ssr\": ${file} runs in the ssr environment, where React DOM's server renderer can. These call across.`,\n `const __rsc_kit_ssr = import.meta.viteRsc.import(${JSON.stringify(\"./\" + basename(path))}, { environment: 'ssr' });`,\n ...named.map(\n (name) =>\n `export const ${name} = async (...args) => (await __rsc_kit_ssr).${name}(...args);`,\n ),\n ...(hasDefault\n ? [\n \"export default async (...args) => (await __rsc_kit_ssr).default(...args);\",\n ]\n : []),\n ].join(\"\\n\") + \"\\n\"\n );\n}\n\n/** Any of react-dom's server renderer entries. */\nexport const SERVER_RENDERER = /^react-dom\\/server(?:\\.[a-z]+)?$/;\n\n/** What a module gets in place of react-dom/server where server components render. */\nexport function serverRendererMessage(importer: string | null): string {\n return (\n \"react-dom/server cannot run where server components render: React's server build has no client internals for it, and the components it would render import that same React.\" +\n (importer ? ` Imported by ${importer}.` : \"\") +\n ' Put the rendering - the template and the call - in a module that starts with \"use ssr\": it runs in the ssr environment, and its exports are called from here as async functions. ' +\n SSR_GUIDE\n );\n}\n"]}
|
package/dist/vite.js
CHANGED
|
@@ -30,7 +30,7 @@ import { serverImportsOfClientPackages } from "./clientImports.js";
|
|
|
30
30
|
import { automaticSitemap, METADATA_ROUTES, ROOT_FILES, rootFileType, } from "./metadataRoutes.js";
|
|
31
31
|
import { ownHosts } from "./hostRouting.js";
|
|
32
32
|
import { unrollBarrelImports } from "./barrelImports.js";
|
|
33
|
-
import { serverRendererMessage, SERVER_RENDERER, ssrProxyModule, UseSsrError, } from "./useSsr.js";
|
|
33
|
+
import { serverRendererMessage, SERVER_RENDERER, ssrProxyModule, UseSsrError, withoutSsrDirective } from "./useSsr.js";
|
|
34
34
|
import { httpHostCalls } from "./hostCalls.js";
|
|
35
35
|
import { clientPackages, importsServerRenderer, packageDir as installedPackageDir, packageEntryInGraph } from "./clientPackages.js";
|
|
36
36
|
// Resolved once per rscKit() call. One build runs in one process, so these are
|
|
@@ -585,10 +585,16 @@ function routeManifest() {
|
|
|
585
585
|
},
|
|
586
586
|
routes,
|
|
587
587
|
intercepts,
|
|
588
|
-
apis: [...apiRoutes.values()].map(({ name, methods, generated }) => ({
|
|
588
|
+
apis: [...apiRoutes.values()].map(({ name, methods, generated, absPath }) => ({
|
|
589
589
|
name,
|
|
590
590
|
segments: urlSegments(name),
|
|
591
591
|
methods,
|
|
592
|
+
// The OpenAPI document is synthesised from nothing on disk.
|
|
593
|
+
source: generated
|
|
594
|
+
? generated.file
|
|
595
|
+
? relative(sourceDir, generated.file).replace(/\\/g, "/")
|
|
596
|
+
: undefined
|
|
597
|
+
: relative(sourceDir, absPath).replace(/\\/g, "/"),
|
|
592
598
|
// A synthesised robots.txt or sitemap.xml runs no guard: it exists to be
|
|
593
599
|
// read by anyone, and a guard on the root would 401 the crawler.
|
|
594
600
|
middleware: generated ? [] : ancestors(name, "middleware"),
|
|
@@ -1735,7 +1741,7 @@ function renderRouteTypes(manifest) {
|
|
|
1735
1741
|
"",
|
|
1736
1742
|
"// `export {}` is load-bearing: in a file with no import or export,",
|
|
1737
1743
|
"// `declare module` *replaces* the real module rather than augmenting it,",
|
|
1738
|
-
"// and
|
|
1744
|
+
"// and Route and route() vanish from it with no error to explain why.",
|
|
1739
1745
|
"export {}",
|
|
1740
1746
|
"",
|
|
1741
1747
|
"declare module '@rsc-kit/core/routes' {",
|
|
@@ -3403,8 +3409,18 @@ async function runMiddleware(component: string, props: Record<string, unknown> =
|
|
|
3403
3409
|
}
|
|
3404
3410
|
|
|
3405
3411
|
// Sequential and awaited, outermost first: an outer guard refusing means
|
|
3406
|
-
// the inner one should never have been asked.
|
|
3407
|
-
|
|
3412
|
+
// the inner one should never have been asked. A directory may declare
|
|
3413
|
+
// several as a list - reused checks imported from one place - and they
|
|
3414
|
+
// run in the order written, stopping at the first refusal.
|
|
3415
|
+
for (const check of Array.isArray(guard) ? guard : [guard]) {
|
|
3416
|
+
if (typeof check !== 'function') {
|
|
3417
|
+
throw new Error(
|
|
3418
|
+
'Route middleware ' + name + ' exports something that is not a function or a list of them.',
|
|
3419
|
+
)
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
await check(props)
|
|
3423
|
+
}
|
|
3408
3424
|
}
|
|
3409
3425
|
}
|
|
3410
3426
|
|
|
@@ -4926,10 +4942,16 @@ function useSsrModules() {
|
|
|
4926
4942
|
return {
|
|
4927
4943
|
name: "rsc-kit:use-ssr",
|
|
4928
4944
|
enforce: "pre",
|
|
4929
|
-
applyToEnvironment: (environment) => environment.name === "rsc",
|
|
4945
|
+
applyToEnvironment: (environment) => environment.name === "rsc" || environment.name === "ssr",
|
|
4930
4946
|
transform(code, id) {
|
|
4931
4947
|
if (!code.includes("use ssr"))
|
|
4932
4948
|
return;
|
|
4949
|
+
// In the environment the module runs in, the directive has done its
|
|
4950
|
+
// work and is a string the bundler warns about. Out it goes.
|
|
4951
|
+
if (this.environment.name === "ssr") {
|
|
4952
|
+
const stripped = withoutSsrDirective(code);
|
|
4953
|
+
return stripped === null ? undefined : { code: stripped, map: null };
|
|
4954
|
+
}
|
|
4933
4955
|
try {
|
|
4934
4956
|
const proxy = ssrProxyModule(code, id);
|
|
4935
4957
|
return proxy === null ? undefined : { code: proxy, map: null };
|
|
@@ -5281,10 +5303,12 @@ export function rscKit(options = {}) {
|
|
|
5281
5303
|
// reflect-metadata runs it after the chunk that checks for it.
|
|
5282
5304
|
// Traced, each is copied into .output/server/node_modules and
|
|
5283
5305
|
// imported by the built server the way its author expected.
|
|
5306
|
+
// Only the ones the project has. Nitro's tracer says so, once per
|
|
5307
|
+
// name, for every entry it cannot find - and a list of every native
|
|
5308
|
+
// package anyone might install is mostly ones this app does not.
|
|
5284
5309
|
nitro.options.traceDeps = [
|
|
5285
5310
|
...(nitro.options.traceDeps ?? []),
|
|
5286
|
-
...DEFAULT_SERVER_EXTERNALS,
|
|
5287
|
-
...(options.serverExternalPackages ?? []),
|
|
5311
|
+
...[...DEFAULT_SERVER_EXTERNALS, ...(options.serverExternalPackages ?? [])].filter((name) => installedPackageDir(name, projectRoot) !== null || packageEntryInGraph(projectRoot, name) !== null),
|
|
5288
5312
|
];
|
|
5289
5313
|
// The assets, precompressed at build and served with their encoding
|
|
5290
5314
|
// by Nitro's own static handler; the host gzips the rest as it
|
|
@@ -5701,6 +5725,23 @@ export function rscKit(options = {}) {
|
|
|
5701
5725
|
server.watcher.add(sourceDir);
|
|
5702
5726
|
server.watcher.on("add", restart);
|
|
5703
5727
|
server.watcher.on("unlink", restart);
|
|
5728
|
+
// The host actions are read once, in config(), from the file the
|
|
5729
|
+
// backend writes; the "use server" stubs are generated from it. A
|
|
5730
|
+
// backend that writes a new action - `make:rsc-action` under a running
|
|
5731
|
+
// dev server - rewrites the file, and until the server started again
|
|
5732
|
+
// the stub was not there to import. Any event: the file appears on a
|
|
5733
|
+
// first action, changes on the next, and goes when the last is removed.
|
|
5734
|
+
const hostActionsPath = join(projectRoot, HOST_ACTIONS_FILE);
|
|
5735
|
+
const hostActionsChanged = (file) => {
|
|
5736
|
+
if (file !== hostActionsPath)
|
|
5737
|
+
return;
|
|
5738
|
+
server.config.logger.info(`[rsc-kit] ${HOST_ACTIONS_FILE} changed — restarting`);
|
|
5739
|
+
void server.restart();
|
|
5740
|
+
};
|
|
5741
|
+
server.watcher.add(hostActionsPath);
|
|
5742
|
+
server.watcher.on("add", hostActionsChanged);
|
|
5743
|
+
server.watcher.on("change", hostActionsChanged);
|
|
5744
|
+
server.watcher.on("unlink", hostActionsChanged);
|
|
5704
5745
|
},
|
|
5705
5746
|
/**
|
|
5706
5747
|
* Freeze what can be frozen, once every bundle exists.
|