mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 11:59:00 +00:00
start extracting parts of the framework out
This commit is contained in:
18
packages/bun-framework-react/bun.lock
Normal file
18
packages/bun-framework-react/bun.lock
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
}
|
||||
}
|
||||
7
packages/bun-framework-react/package.json
Normal file
7
packages/bun-framework-react/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9"
|
||||
}
|
||||
}
|
||||
3
packages/bun-framework-react/src/components/link.tsx
Normal file
3
packages/bun-framework-react/src/components/link.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export function Link(props: React.ComponentProps<"a">) {
|
||||
return <a {...props} />;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Components integration. It is designed as a minimal base to build RSC
|
||||
// applications on, and to showcase what features that Bake offers.
|
||||
/// <reference lib="dom" />
|
||||
import { onServerSideReload } from "bun:bake/client";
|
||||
import { onServerSideReload } from "bun:app/client";
|
||||
import * as React from "react";
|
||||
import { flushSync } from "react-dom";
|
||||
import { hydrateRoot } from "react-dom/client";
|
||||
@@ -11,32 +11,65 @@ import { createFromReadableStream } from "react-server-dom-bun/client.browser";
|
||||
const te = new TextEncoder();
|
||||
const td = new TextDecoder();
|
||||
|
||||
const windowDebugKey = "$bake";
|
||||
|
||||
interface WindowDebugObject {
|
||||
navigate: (href: string, cacheId?: number) => Promise<void>;
|
||||
onServerSideReload: (cb: () => void | Promise<void>) => Promise<void>;
|
||||
readonly currentCssList: string[] | undefined;
|
||||
}
|
||||
|
||||
type WindowWithBakeDebugObject = { [key in typeof windowDebugKey]: WindowDebugObject };
|
||||
declare global {
|
||||
interface Window extends WindowWithBakeDebugObject {}
|
||||
}
|
||||
|
||||
// It is the framework's responsibility to ensure that client-side navigation
|
||||
// loads CSS files. The implementation here loads all CSS files as <link> tags,
|
||||
// and uses the ".disabled" property to enable/disable them.
|
||||
const cssFiles = new Map<string, { promise: Promise<void> | null; link: HTMLLinkElement }>();
|
||||
let currentCssList: string[] | undefined = undefined;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__bun_f:
|
||||
| Array<string | Uint8Array<ArrayBufferLike>>
|
||||
| {
|
||||
// it's still an array, but we overwrite the push method to not return
|
||||
// a number and instead use the `handleChunk` function
|
||||
push: (chunk: string | Uint8Array<ArrayBufferLike>) => void;
|
||||
forEach: (callback: (chunk: string | Uint8Array<ArrayBufferLike>) => void) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// The initial RSC payload is put into inline <script> tags that follow the pattern
|
||||
// `(self.__bun_f ??= []).push(chunk)`, which is converted into a ReadableStream
|
||||
// here for React hydration. Since inline scripts are executed immediately, and
|
||||
// this file is loaded asynchronously, the `__bun_f` becomes a clever way to
|
||||
// stream the arbitrary data while HTML is loading. In a static build, this is
|
||||
// setup as an array with one string.
|
||||
let rscPayload: any = createFromReadableStream(
|
||||
let rscPayload = createFromReadableStream(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
let handleChunk = chunk =>
|
||||
const handleChunk = (chunk: string | Uint8Array<ArrayBufferLike>) =>
|
||||
typeof chunk === "string" //
|
||||
? controller.enqueue(te.encode(chunk))
|
||||
: controller.enqueue(chunk);
|
||||
|
||||
(self.__bun_f ||= []).forEach((__bun_f.push = handleChunk));
|
||||
const bunF = (self.__bun_f ??= []);
|
||||
|
||||
bunF.push = handleChunk;
|
||||
bunF.forEach(handleChunk);
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
controller.close();
|
||||
});
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
controller.close();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
@@ -49,7 +82,7 @@ let rscPayload: any = createFromReadableStream(
|
||||
// This is the same logic that happens on the server, except there is also a
|
||||
// hook to update the promise when the client navigates. The `Root` component
|
||||
// also updates CSS files when navigating between routes.
|
||||
let setPage;
|
||||
let setPage: React.Dispatch<React.SetStateAction<any>>;
|
||||
let abortOnRender: AbortController | undefined;
|
||||
const Root = () => {
|
||||
setPage = React.useState(rscPayload)[1];
|
||||
@@ -126,11 +159,12 @@ let lastNavigationId = 0;
|
||||
let lastNavigationController: AbortController;
|
||||
|
||||
// Client side navigation is implemented by updating the app's `useState` with a
|
||||
// new RSC payload promise. Callers of `goto` are expected to manage history state.
|
||||
// A navigation id is used
|
||||
async function goto(href: string, cacheId?: number) {
|
||||
// new RSC payload promise. Callers of `navigate` are expected to manage history
|
||||
// state. A navigation id is used
|
||||
async function navigate(href: string, cacheId?: number) {
|
||||
const thisNavigationId = ++lastNavigationId;
|
||||
const olderController = lastNavigationController;
|
||||
|
||||
lastNavigationController = new AbortController();
|
||||
const signal = lastNavigationController.signal;
|
||||
signal.addEventListener("abort", () => {
|
||||
@@ -138,11 +172,12 @@ async function goto(href: string, cacheId?: number) {
|
||||
});
|
||||
|
||||
// If the page is cached, use the cached promise instead of fetching it again.
|
||||
const cached = cacheId && cachedPages.get(cacheId);
|
||||
const cached = (cacheId !== undefined && cachedPages.get(cacheId)) || undefined;
|
||||
if (cached) {
|
||||
currentCssList = cached.css;
|
||||
await ensureCssIsReady(currentCssList);
|
||||
setPage?.((rscPayload = cached.element));
|
||||
rscPayload = cached.element;
|
||||
setPage(rscPayload);
|
||||
if (olderController?.signal.aborted === false) abortOnRender = olderController;
|
||||
return;
|
||||
}
|
||||
@@ -314,7 +349,7 @@ document.addEventListener("click", async (event, element = event.target as HTMLA
|
||||
const href = url.href;
|
||||
const newId = Date.now();
|
||||
history.pushState(newId, "", href);
|
||||
goto(href, newId);
|
||||
navigate(href, newId);
|
||||
|
||||
return event.preventDefault();
|
||||
}
|
||||
@@ -330,7 +365,7 @@ window.addEventListener("popstate", event => {
|
||||
if (typeof state !== "number") {
|
||||
state = undefined;
|
||||
}
|
||||
goto(location.href, state);
|
||||
navigate(location.href, state);
|
||||
});
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
@@ -339,12 +374,12 @@ if (import.meta.env.DEV) {
|
||||
onServerSideReload(async () => {
|
||||
const newId = Date.now();
|
||||
history.replaceState(newId, "", location.href);
|
||||
await goto(location.href, newId);
|
||||
await navigate(location.href, newId);
|
||||
});
|
||||
|
||||
// Expose a global in Development mode
|
||||
(window as any).$bake = {
|
||||
goto,
|
||||
window[windowDebugKey] = {
|
||||
navigate,
|
||||
onServerSideReload,
|
||||
get currentCssList() {
|
||||
return currentCssList;
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Bake } from "bun";
|
||||
import { renderToHtml, renderToStaticHtml } from "bun-framework-react/ssr.tsx" with { bunBakeGraph: "ssr" };
|
||||
import { serverManifest } from "bun:bake/server";
|
||||
import { serverManifest } from "bun:app/server";
|
||||
import type { AsyncLocalStorage } from "node:async_hooks";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { renderToPipeableStream } from "react-server-dom-bun/server.node.unbundled.js";
|
||||
import type { RequestContext } from "../hmr-runtime-server";
|
||||
import type { RequestContext } from "../../../../src/bake/hmr-runtime-server.ts";
|
||||
|
||||
function assertReactComponent(Component: any) {
|
||||
if (typeof Component !== "function") {
|
||||
@@ -37,7 +37,7 @@ function getPage(meta: Bake.RouteMetadata & { request?: Request }, styles: reado
|
||||
);
|
||||
}
|
||||
|
||||
function component(mod: any, params: Record<string, string> | null, request?: Request) {
|
||||
function component(mod: any, params: Record<string, string | string[]> | null, request?: Request) {
|
||||
const Page = mod.default;
|
||||
let props = {};
|
||||
if (import.meta.env.DEV) assertReactComponent(Page);
|
||||
@@ -1,13 +1,13 @@
|
||||
// This file is loaded in the SSR graph, meaning the `react-server` condition is
|
||||
// no longer set. This means we can import client components, using `react-dom`
|
||||
// to perform Server-side rendering (creating HTML) out of the RSC payload.
|
||||
import { ssrManifest } from "bun:bake/server";
|
||||
import { ssrManifest } from "bun:app/server";
|
||||
import { EventEmitter } from "node:events";
|
||||
import type { Readable } from "node:stream";
|
||||
import * as React from "react";
|
||||
import { renderToPipeableStream } from "react-dom/server.node";
|
||||
import { createFromNodeStream, type Manifest } from "react-server-dom-bun/client.node.unbundled.js";
|
||||
import type { MiniAbortSignal } from "./server";
|
||||
import type { MiniAbortSignal } from "./server.tsx";
|
||||
|
||||
// Verify that React 19 is being used.
|
||||
if (!React.use) {
|
||||
11
packages/bun-framework-react/src/util/dom.ts
Normal file
11
packages/bun-framework-react/src/util/dom.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
function isModifiedEvent(event: MouseEvent) {
|
||||
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||
}
|
||||
|
||||
export function shouldProcessLinkClick(event: MouseEvent, target?: string) {
|
||||
return (
|
||||
event.button === 0 && // Ignore everything but left clicks
|
||||
(!target || target === "_self") && // Let browser handle "target=_blank" etc.
|
||||
!isModifiedEvent(event) // Ignore clicks with modifier keys
|
||||
);
|
||||
}
|
||||
24
packages/bun-framework-react/tsconfig.json
Normal file
24
packages/bun-framework-react/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"noImplicitOverride": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"isolatedDeclarations": true,
|
||||
"declaration": true,
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
4
packages/bun-react/package.json
Normal file
4
packages/bun-react/package.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "bun-react",
|
||||
"type": "module"
|
||||
}
|
||||
23
packages/bun-react/tsconfig.json
Normal file
23
packages/bun-react/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"moduleResolution": "NodeNext",
|
||||
"module": "NodeNext",
|
||||
"target": "ESNext",
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"noImplicitOverride": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"isolatedDeclarations": true
|
||||
}
|
||||
}
|
||||
1
packages/bun-types/index.d.ts
vendored
1
packages/bun-types/index.d.ts
vendored
@@ -23,6 +23,7 @@
|
||||
/// <reference path="./experimental.d.ts" />
|
||||
/// <reference path="./sql.d.ts" />
|
||||
/// <reference path="./security.d.ts" />
|
||||
/// <reference path="./rendering.d.ts" />
|
||||
|
||||
/// <reference path="./bun.ns.d.ts" />
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
declare module "bun" {
|
||||
type Awaitable<T> = T | Promise<T>;
|
||||
|
||||
declare namespace Bake {
|
||||
namespace Bake {
|
||||
interface Options {
|
||||
/**
|
||||
* Bun provides built-in support for using React as a framework by passing
|
||||
@@ -19,6 +19,7 @@ declare module "bun" {
|
||||
* ```
|
||||
*/
|
||||
framework: Framework | "react";
|
||||
|
||||
// Note: To contribute to 'bun-framework-react', it can be run from this file:
|
||||
// https://github.com/oven-sh/bun/blob/main/src/bake/bun-framework-react/index.ts
|
||||
/**
|
||||
@@ -28,13 +29,16 @@ declare module "bun" {
|
||||
* @default {}
|
||||
*/
|
||||
bundlerOptions?: BundlerOptions | undefined;
|
||||
|
||||
/**
|
||||
* These plugins are applied after `framework.plugins`
|
||||
*/
|
||||
plugins?: BunPlugin[] | undefined;
|
||||
}
|
||||
|
||||
/** Bake only allows a subset of options from `Bun.build` */
|
||||
/**
|
||||
* Bake only allows a subset of options from `Bun.build`
|
||||
*/
|
||||
type BuildConfigSubset = Pick<
|
||||
BuildConfig,
|
||||
"conditions" | "define" | "loader" | "ignoreDCEAnnotations" | "drop"
|
||||
@@ -69,22 +73,22 @@ declare module "bun" {
|
||||
*/
|
||||
interface Framework {
|
||||
/**
|
||||
* Customize the bundler options. Plugins in this array are merged
|
||||
* with any plugins the user has.
|
||||
* Customize the bundler options. Plugins in this array are merged with
|
||||
* any plugins the user has.
|
||||
* @default {}
|
||||
*/
|
||||
bundlerOptions?: BundlerOptions | undefined;
|
||||
/**
|
||||
* The translation of files to routes is unopinionated and left
|
||||
* to framework authors. This interface allows most flexibility
|
||||
* between the already established conventions while allowing
|
||||
* new ideas to be explored too.
|
||||
* The translation of files to routes is unopinionated and left to
|
||||
* framework authors. This interface allows most flexibility between the
|
||||
* already established conventions while allowing new ideas to be explored
|
||||
* too.
|
||||
* @default []
|
||||
*/
|
||||
fileSystemRouterTypes?: FrameworkFileSystemRouterType[];
|
||||
/**
|
||||
* A list of directories that should be served statically. If the directory
|
||||
* does not exist in the user's project, it is ignored.
|
||||
* A list of directories that should be served statically. If the
|
||||
* directory does not exist in the user's project, it is ignored.
|
||||
*
|
||||
* Example: 'public' or 'static'
|
||||
*
|
||||
@@ -103,8 +107,8 @@ declare module "bun" {
|
||||
*/
|
||||
builtInModules?: BuiltInModule[] | undefined;
|
||||
/**
|
||||
* Bun offers integration for React's Server Components with an
|
||||
* interface that is generic enough to adapt to any framework.
|
||||
* Bun offers integration for React's Server Components with an interface
|
||||
* that is generic enough to adapt to any framework.
|
||||
* @default undefined
|
||||
*/
|
||||
serverComponents?: ServerComponentsOptions | undefined;
|
||||
@@ -115,7 +119,7 @@ declare module "bun" {
|
||||
*/
|
||||
reactFastRefresh?: boolean | ReactFastRefreshOptions | undefined;
|
||||
/** Framework bundler plugins load before the user-provided ones. */
|
||||
plugins?: BunPlugin[];
|
||||
plugins?: BunPlugin[] | undefined;
|
||||
|
||||
// /**
|
||||
// * Called after the list of routes is updated. This can be used to
|
||||
@@ -129,11 +133,11 @@ declare module "bun" {
|
||||
type BuiltInModule = { import: string; code: string } | { import: string; path: string };
|
||||
|
||||
/**
|
||||
* A high-level overview of what server components means exists
|
||||
* in the React Docs: https://react.dev/reference/rsc/server-components
|
||||
* A high-level overview of what server components means exists in the React
|
||||
* Docs: https://react.dev/reference/rsc/server-components
|
||||
*
|
||||
* When enabled, files with "use server" and "use client" directives will get
|
||||
* special processing according to this object, in combination with the
|
||||
* When enabled, files with "use server" and "use client" directives will
|
||||
* get special processing according to this object, in combination with the
|
||||
* framework-specified entry points for server rendering and browser
|
||||
* interactivity.
|
||||
*/
|
||||
@@ -144,15 +148,14 @@ declare module "bun" {
|
||||
*
|
||||
* When set `true`, bundling "use client" components for SSR will be
|
||||
* placed in a separate bundling graph without the `react-server`
|
||||
* condition. All imports that stem from here get re-bundled for
|
||||
* this second graph, regardless if they actually differ via this
|
||||
* condition.
|
||||
* condition. All imports that stem from here get re-bundled for this
|
||||
* second graph, regardless if they actually differ via this condition.
|
||||
*
|
||||
* The built in framework config for React enables this flag so that server
|
||||
* components and client components utilize their own versions of React,
|
||||
* despite running in the same process. This facilitates different aspects
|
||||
* of the server and client react runtimes, such as `async` components only
|
||||
* being available on the server.
|
||||
* The built in framework config for React enables this flag so that
|
||||
* server components and client components utilize their own versions of
|
||||
* React, despite running in the same process. This facilitates different
|
||||
* aspects of the server and client react runtimes, such as `async`
|
||||
* components only being available on the server.
|
||||
*
|
||||
* To cross from the server graph to the SSR graph, use the bun_bake_graph
|
||||
* import attribute:
|
||||
@@ -166,9 +169,9 @@ declare module "bun" {
|
||||
/** Server components runtime for the server */
|
||||
serverRuntimeImportSource: ImportSource;
|
||||
/**
|
||||
* When server code imports client code, a stub module is generated,
|
||||
* where every export calls this export from `serverRuntimeImportSource`.
|
||||
* This is used to implement client components on the server.
|
||||
* When server code imports client code, a stub module is generated, where
|
||||
* every export calls this export from `serverRuntimeImportSource`. This
|
||||
* is used to implement client components on the server.
|
||||
*
|
||||
* When separateSSRGraph is enabled, the call looks like:
|
||||
*
|
||||
@@ -225,12 +228,12 @@ declare module "bun" {
|
||||
/**
|
||||
* This import has four exports, mirroring "react-refresh/runtime":
|
||||
*
|
||||
* `injectIntoGlobalHook(window): void`
|
||||
* Called on first startup, before the user entrypoint.
|
||||
* `injectIntoGlobalHook(window): void` Called on first startup, before
|
||||
* the user entrypoint.
|
||||
*
|
||||
* `register(component, uniqueId: string): void`
|
||||
* Called on every function that starts with an uppercase letter. These
|
||||
* may or may not be components, but they are always functions.
|
||||
* `register(component, uniqueId: string): void` Called on every function
|
||||
* that starts with an uppercase letter. These may or may not be
|
||||
* components, but they are always functions.
|
||||
*
|
||||
* `createSignatureFunctionForTransform(): ReactRefreshSignatureFunction`
|
||||
* TODO: document. A passing no-op for this api is `return () => {}`
|
||||
@@ -256,22 +259,24 @@ declare module "bun" {
|
||||
*/
|
||||
prefix?: string | undefined;
|
||||
/**
|
||||
* This file is the entrypoint of the server application. This module
|
||||
* must `export default` a fetch function, which takes a request and the
|
||||
* This file is the entrypoint of the server application. This module must
|
||||
* `export default` a fetch function, which takes a request and the
|
||||
* bundled route module, and returns a response. See `ServerEntryPoint`
|
||||
*
|
||||
* When `serverComponents` is configured, this can access the component
|
||||
* manifest using the special 'bun:bake/server' import:
|
||||
* manifest using the special 'bun:app/server' import:
|
||||
*
|
||||
* import { serverManifest } from 'bun:bake/server'
|
||||
* import { serverManifest } from 'bun:app/server'
|
||||
*/
|
||||
serverEntryPoint: ImportSource<ServerEntryPoint>;
|
||||
|
||||
/**
|
||||
* This file is the true entrypoint of the client application. If null,
|
||||
* a client will not be bundled, and the route will not receive bundling
|
||||
* for client-side interactivity.
|
||||
* This file is the true entrypoint of the client application. If null, a
|
||||
* client will not be bundled, and the route will not receive bundling for
|
||||
* client-side interactivity.
|
||||
*/
|
||||
clientEntryPoint?: ImportSource<ClientEntryPoint> | undefined;
|
||||
|
||||
/**
|
||||
* Do not traverse into directories and files that start with an `_`. Do
|
||||
* not index pages that start with an `_`. Does not prevent stuff like
|
||||
@@ -279,28 +284,33 @@ declare module "bun" {
|
||||
* @default false
|
||||
*/
|
||||
ignoreUnderscores?: boolean;
|
||||
|
||||
/**
|
||||
* @default ["node_modules", ".git"]
|
||||
*/
|
||||
ignoreDirs?: string[];
|
||||
|
||||
/**
|
||||
* Extensions to match on.
|
||||
* '*' - any extension
|
||||
* Extensions to match on. '*' - any extension
|
||||
* @default (set of all valid JavaScript/TypeScript extensions)
|
||||
*/
|
||||
extensions?: string[] | "*";
|
||||
|
||||
/**
|
||||
* 'nextjs-app' builds routes out of directories with `page.tsx` and `layout.tsx`
|
||||
* 'nextjs-pages' builds routes out of any `.tsx` file and layouts with `_layout.tsx`.
|
||||
* 'nextjs-app' builds routes out of directories with `page.tsx` and
|
||||
* `layout.tsx` 'nextjs-pages' builds routes out of any `.tsx` file and
|
||||
* layouts with `_layout.tsx`.
|
||||
*
|
||||
* Eventually, an API will be added to add custom styles.
|
||||
*/
|
||||
style: "nextjs-pages" | "nextjs-app-ui" | "nextjs-app-routes" | CustomFileSystemRouterFunction;
|
||||
|
||||
/**
|
||||
* If true, this will track route layouts and provide them as an array during SSR.
|
||||
* @default false
|
||||
*/
|
||||
layouts?: boolean | undefined;
|
||||
|
||||
// /**
|
||||
// * If true, layouts act as navigation endpoints. This can be used to
|
||||
// * implement Remix.run's router design, where `hello._index` and `hello`
|
||||
@@ -331,8 +341,8 @@ declare module "bun" {
|
||||
};
|
||||
|
||||
/**
|
||||
* Bun will call this function for every found file. This
|
||||
* function classifies each file's role in the file system routing.
|
||||
* Bun will call this function for every found file. This function
|
||||
* classifies each file's role in the file system routing.
|
||||
*/
|
||||
type CustomFileSystemRouterFunction = (candidatePath: string) => CustomFileSystemRouterResult;
|
||||
|
||||
@@ -341,8 +351,8 @@ declare module "bun" {
|
||||
| undefined
|
||||
| null
|
||||
/**
|
||||
* Use this file as a route. Routes may nest, where a framework
|
||||
* can use parent routes to implement layouts.
|
||||
* Use this file as a route. Routes may nest, where a framework can use
|
||||
* parent routes to implement layouts.
|
||||
*/
|
||||
| {
|
||||
/**
|
||||
@@ -356,8 +366,8 @@ declare module "bun" {
|
||||
};
|
||||
|
||||
/**
|
||||
* Will be resolved from the point of view of the framework user's project root
|
||||
* Examples: `react-dom`, `./entry_point.tsx`, `/absolute/path.js`
|
||||
* Will be resolved from the point of view of the framework user's project
|
||||
* root Examples: `react-dom`, `./entry_point.tsx`, `/absolute/path.js`
|
||||
*/
|
||||
type ImportSource<T = unknown> = string;
|
||||
|
||||
@@ -366,8 +376,8 @@ declare module "bun" {
|
||||
* Bun passes the route's module as an opaque argument `routeModule`. The
|
||||
* framework implementation decides and enforces the shape of the module.
|
||||
*
|
||||
* A common pattern would be to enforce the object is
|
||||
* `{ default: ReactComponent }`
|
||||
* A common pattern would be to enforce the object is `{ default:
|
||||
* ReactComponent }`
|
||||
*/
|
||||
render: (request: Request, routeMetadata: RouteMetadata) => Awaitable<Response>;
|
||||
/**
|
||||
@@ -376,8 +386,8 @@ declare module "bun" {
|
||||
* not named `staticRender` as it is invoked during a dynamic build to
|
||||
* allow deterministic routes to be prerendered.
|
||||
*
|
||||
* Note that `import.meta.env.STATIC` will be inlined to true during
|
||||
* a static build.
|
||||
* Note that `import.meta.env.STATIC` will be inlined to true during a
|
||||
* static build.
|
||||
*/
|
||||
prerender?: (routeMetadata: RouteMetadata) => Awaitable<PrerenderResult | null>;
|
||||
// TODO: prerenderWithoutProps (for partial prerendering)
|
||||
@@ -397,11 +407,11 @@ declare module "bun" {
|
||||
* "exhaustive" tells Bun that the list is complete. If it is not, a
|
||||
* static site cannot be generated as it would otherwise be missing
|
||||
* routes. A non-exhaustive list can speed up build times by only
|
||||
* specifying a few important pages (such as 10 most recent), leaving
|
||||
* the rest to be generated on-demand at runtime.
|
||||
* specifying a few important pages (such as 10 most recent), leaving the
|
||||
* rest to be generated on-demand at runtime.
|
||||
*
|
||||
* To stream results, `getParams` may return an async iterator, which
|
||||
* Bun will start rendering as more parameters are provided:
|
||||
* To stream results, `getParams` may return an async iterator, which Bun
|
||||
* will start rendering as more parameters are provided:
|
||||
*
|
||||
* export async function* getParams(meta: Bake.ParamsMetadata) {
|
||||
* yield { slug: await fetchSlug() };
|
||||
@@ -410,9 +420,10 @@ declare module "bun" {
|
||||
* }
|
||||
*/
|
||||
getParams?: (paramsMetadata: ParamsMetadata) => Awaitable<GetParamIterator>;
|
||||
|
||||
/**
|
||||
* When a dynamic build uses static assets, Bun can map content types in the
|
||||
* user's `Accept` header to the different static files.
|
||||
* When a dynamic build uses static assets, Bun can map content types in
|
||||
* the user's `Accept` header to the different static files.
|
||||
*/
|
||||
contentTypeToStaticFile?: Record<string, string>;
|
||||
}
|
||||
@@ -477,18 +488,24 @@ declare module "bun" {
|
||||
* }
|
||||
*/
|
||||
readonly layouts: ReadonlyArray<any>;
|
||||
/** Received route params. `null` if the route does not take params */
|
||||
|
||||
/**
|
||||
* Received route params. `null` if the route does not take params
|
||||
*/
|
||||
readonly params: null | Record<string, string | string[]>;
|
||||
|
||||
/**
|
||||
* A list of js files that the route will need to be interactive.
|
||||
*/
|
||||
readonly modules: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* A list of js files that should be preloaded.
|
||||
*
|
||||
* <link rel="modulepreload" href="..." />
|
||||
*/
|
||||
readonly modulepreload: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* A list of css files that the route will need to be styled.
|
||||
*/
|
||||
@@ -529,8 +546,10 @@ declare module "bun" {
|
||||
}
|
||||
}
|
||||
|
||||
/** Available in server-side files only. */
|
||||
declare module "bun:bake/server" {
|
||||
/**
|
||||
* Available in server-side files only
|
||||
*/
|
||||
declare module "bun:app/server" {
|
||||
// NOTE: The format of these manifests will likely be customizable in the future.
|
||||
|
||||
/**
|
||||
@@ -541,6 +560,7 @@ declare module "bun:bake/server" {
|
||||
* To perform SSR with client components, see `ssrManifest`
|
||||
*/
|
||||
declare const serverManifest: ServerManifest;
|
||||
|
||||
/**
|
||||
* Entries in this manifest map from client-side files to their respective SSR
|
||||
* bundles. They can be loaded by `await import()` or `require()`.
|
||||
@@ -548,7 +568,7 @@ declare module "bun:bake/server" {
|
||||
declare const ssrManifest: SSRManifest;
|
||||
|
||||
/** (insert teaser trailer) */
|
||||
declare const actionManifest: never;
|
||||
declare const actionManifest: null;
|
||||
|
||||
declare interface ServerManifest {
|
||||
/**
|
||||
@@ -566,12 +586,16 @@ declare module "bun:bake/server" {
|
||||
* Correlates but is not required to be the filename
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The `name` in ReactServerManifest
|
||||
* Correlates but is not required to be the export name
|
||||
*/
|
||||
name: string;
|
||||
/** Currently not implemented; always an empty array */
|
||||
|
||||
/**
|
||||
* Currently not implemented; always an empty array
|
||||
*/
|
||||
chunks: [];
|
||||
}
|
||||
|
||||
@@ -591,17 +615,17 @@ declare module "bun:bake/server" {
|
||||
}
|
||||
}
|
||||
|
||||
/** Available in client-side files. */
|
||||
declare module "bun:bake/client" {
|
||||
/**
|
||||
* Available in client-side files.
|
||||
*/
|
||||
declare module "bun:app/client" {
|
||||
/**
|
||||
* Callback is invoked when server-side code is changed. This can be used to
|
||||
* fetch a non-html version of the updated page to perform a faster reload. If
|
||||
* not provided, the client will perform a hard reload.
|
||||
*
|
||||
* Only one callback can be set. This function overwrites the previous one.
|
||||
* Only one callback can be set. Calling this function will overwrite the
|
||||
* previous callback, if set.
|
||||
*/
|
||||
export function onServerSideReload(cb: () => void | Promise<void>): Promise<void>;
|
||||
}
|
||||
|
||||
/** Available during development */
|
||||
declare module "bun:bake/dev" {}
|
||||
4
src/bake/bake.private.d.ts
vendored
4
src/bake/bake.private.d.ts
vendored
@@ -88,7 +88,7 @@ declare module "react-server-dom-bun/client.browser" {
|
||||
}
|
||||
|
||||
declare module "react-server-dom-bun/client.node.unbundled.js" {
|
||||
import type { ReactClientManifest } from "bun:bake/server";
|
||||
import type { ReactClientManifest } from "bun:app/server";
|
||||
import type { Readable } from "node:stream";
|
||||
export interface Manifest {
|
||||
moduleMap: ReactClientManifest;
|
||||
@@ -107,7 +107,7 @@ declare module "react-server-dom-bun/client.node.unbundled.js" {
|
||||
}
|
||||
|
||||
declare module "react-server-dom-bun/server.node.unbundled.js" {
|
||||
import type { ReactServerManifest } from "bun:bake/server";
|
||||
import type { ReactServerManifest } from "bun:app/server";
|
||||
import type { ReactElement } from "react";
|
||||
|
||||
export interface PipeableStream<T> {
|
||||
|
||||
@@ -52,7 +52,7 @@ function parseV8OrIE(stack: string): Frame[] {
|
||||
return stack
|
||||
.split("\n")
|
||||
.filter(line => !!line.match(CHROME_IE_STACK_REGEXP) && !line.includes("Bun HMR Runtime"))
|
||||
.map(function (line) {
|
||||
.map((line): Frame => {
|
||||
let sanitizedLine = line
|
||||
.replace(/^\s+/, "")
|
||||
.replace(/\(eval code/g, "(")
|
||||
@@ -71,12 +71,14 @@ function parseV8OrIE(stack: string): Frame[] {
|
||||
let functionName = (loc && sanitizedLine) || undefined;
|
||||
let fileName = ["eval", "<anonymous>"].indexOf(locationParts[0]) > -1 ? undefined : locationParts[0];
|
||||
|
||||
return {
|
||||
const frame: Frame = {
|
||||
fn: functionName || "unknown",
|
||||
file: fileName,
|
||||
line: 0 | locationParts[1],
|
||||
col: 0 | locationParts[2],
|
||||
} satisfies Frame;
|
||||
};
|
||||
|
||||
return frame;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import { type SourceMapURL, derefMapping } from "#stack-trace";
|
||||
const registry = new Map<Id, HMRModule>();
|
||||
const registrySourceMapIds = new Map<string, SourceMapURL>();
|
||||
/** Server */
|
||||
export const serverManifest = {};
|
||||
export const serverManifest: Bun.App.ServerManifest = {};
|
||||
/** Server */
|
||||
export const ssrManifest = {};
|
||||
export const ssrManifest: Bun.App.SSRManifest = {};
|
||||
/** Client */
|
||||
export let onServerSideReload: (() => Promise<void>) | null = null;
|
||||
const eventHandlers: Record<HMREvent | string, HotEventHandler[] | undefined> = {};
|
||||
@@ -170,19 +170,19 @@ export class HMRModule {
|
||||
// not destructed.
|
||||
|
||||
accept(
|
||||
arg1?: string | readonly string[] | HotAcceptFunction,
|
||||
arg2?: HotAcceptFunction | HotArrayAcceptFunction | undefined,
|
||||
specifiedOrAcceptFunctionOrFunctions?: string | readonly string[] | HotAcceptFunction,
|
||||
acceptFunctionOrFunctions?: HotAcceptFunction | HotArrayAcceptFunction | undefined,
|
||||
) {
|
||||
if (arg2 == undefined) {
|
||||
if (arg1 == undefined) {
|
||||
if (acceptFunctionOrFunctions == undefined) {
|
||||
if (specifiedOrAcceptFunctionOrFunctions == undefined) {
|
||||
this.selfAccept = implicitAcceptFunction;
|
||||
return;
|
||||
}
|
||||
if (typeof arg1 !== "function") {
|
||||
if (typeof specifiedOrAcceptFunctionOrFunctions !== "function") {
|
||||
throw new Error("import.meta.hot.accept requires a callback function");
|
||||
}
|
||||
// Self-accept function
|
||||
this.selfAccept = arg1;
|
||||
this.selfAccept = specifiedOrAcceptFunctionOrFunctions;
|
||||
} else {
|
||||
throw new Error(
|
||||
'"import.meta.hot.accept" must be directly called with string literals for ' +
|
||||
@@ -941,7 +941,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
// bun:bake/server, bun:bake/client, and bun:wrap are
|
||||
// bun:app/server, bun:app/client, and bun:wrap are
|
||||
// provided by this file instead of the bundler
|
||||
registerSynthetic("bun:wrap", {
|
||||
__name,
|
||||
@@ -953,7 +953,7 @@ registerSynthetic("bun:wrap", {
|
||||
});
|
||||
|
||||
if (side === "server") {
|
||||
registerSynthetic("bun:bake/server", {
|
||||
registerSynthetic("bun:app/server", {
|
||||
serverManifest,
|
||||
ssrManifest,
|
||||
actionManifest: null,
|
||||
@@ -961,7 +961,11 @@ if (side === "server") {
|
||||
}
|
||||
|
||||
if (side === "client") {
|
||||
registerSynthetic("bun:bake/client", {
|
||||
registerSynthetic("bun:app/client", {
|
||||
onServerSideReload: cb => (onServerSideReload = cb),
|
||||
});
|
||||
}
|
||||
|
||||
// `bun:app` exports types only, but we don't want
|
||||
// to throw a module not found error if it's accessed at runtime
|
||||
registerSynthetic("bun:app", {});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable", "DOM.AsyncIterable"],
|
||||
"paths": {
|
||||
"bun-framework-react/*": ["./bun-framework-react/*"],
|
||||
"bun-framework-react/*": ["../../packages/bun-framework-react/*"],
|
||||
"bindgen": ["../codegen/bindgen-lib"]
|
||||
},
|
||||
"jsx": "react-jsx",
|
||||
|
||||
@@ -1058,7 +1058,7 @@ pub const BundleV2 = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// This generates the two asts for 'bun:bake/client' and 'bun:bake/server'. Both are generated
|
||||
/// This generates the two asts for 'bun:app/client' and 'bun:app/server'. Both are generated
|
||||
/// at the same time in one pass over the SCB list.
|
||||
pub fn processServerComponentManifestFiles(this: *BundleV2) OOM!void {
|
||||
// If a server components is not configured, do nothing
|
||||
|
||||
Reference in New Issue
Block a user