diff --git a/docs/01-getting-started/01-installation.md b/docs/01-getting-started/01-installation.md
index 827302826..d93f1476c 100644
--- a/docs/01-getting-started/01-installation.md
+++ b/docs/01-getting-started/01-installation.md
@@ -10,13 +10,12 @@ related:
### System Requirements
-- Bun [1.0.4]( https://bun.sh/) or later
+- Bun [1.0.4](https://bun.sh/) or later
- macOS, Windows (including WSL), and Linux are supported.
-
### Automatic Installation
-We recommend starting a new Brisa app using `create-brisa-app``, which sets up everything automatically for you.
+We recommend starting a new Brisa app using `create-brisa-app``, which sets up everything automatically for you.
```sh
npx create-brisa-app
@@ -55,9 +54,9 @@ You need to add the jsx-runtime of Brisa in your `tsconfig.json` file:
```json
{
"compilerOptions": {
- // ...rest
+ // ...rest
"jsx": "react-jsx",
- "jsxImportSource": "brisa",
+ "jsxImportSource": "brisa"
}
}
```
@@ -70,7 +69,7 @@ Then, add an `index.tsx` file inside your `src/pages` folder. This will be your
```tsx
export default function Page() {
- return
Hello, Brisa!
+ return
Hello, Brisa!
;
}
```
diff --git a/docs/01-getting-started/02-project-structure.md b/docs/01-getting-started/02-project-structure.md
index 3b884c1ba..ab5febfba 100644
--- a/docs/01-getting-started/02-project-structure.md
+++ b/docs/01-getting-started/02-project-structure.md
@@ -8,33 +8,29 @@ This page provides an overview of the file and folder structure of a Brisa proje
## `src`-level folders
-| | |
-| ------------------------------------------------------------------------ | ---------------------------------- |
-| [`pages`](/docs/building-your-application/routing#pages) | Pages Router |
-| [`api`](/docs/building-your-application/routing#api) | Api Router |
-| [`public`](/docs/building-your-application/optimizing/static-assets) | Static assets to be served |
-| [`middleware`](/docs/building-your-application/configuring/middleware) | Middleware |
-| [`layout`](/docs/building-your-application/configuring/layout) | Layout / Layouts |
-| [`websocket`](/docs/building-your-application/configuring/websocket) | Websocket |
-| [`i18n`](/docs/building-your-application/configuring/i18n) | Internationalization routing & translations |
-
-
+| | |
+| ---------------------------------------------------------------------- | ------------------------------------------- |
+| [`pages`](/docs/building-your-application/routing#pages) | Pages Router |
+| [`api`](/docs/building-your-application/routing#api) | Api Router |
+| [`public`](/docs/building-your-application/optimizing/static-assets) | Static assets to be served |
+| [`middleware`](/docs/building-your-application/configuring/middleware) | Middleware |
+| [`layout`](/docs/building-your-application/configuring/layout) | Layout / Layouts |
+| [`websocket`](/docs/building-your-application/configuring/websocket) | Websocket |
+| [`i18n`](/docs/building-your-application/configuring/i18n) | Internationalization routing & translations |
## Top-level files
-| | |
-| ------------------------------------------------------------------------------------------- | --------------------------------------- |
-| | |
-| [`brisa.config.js`](/docs/app/api-reference/brisa-config-js) | Configuration file for Next.
-
-
+| | |
+| ------------------------------------------------------------ | ---------------------------- |
+| | |
+| [`brisa.config.js`](/docs/app/api-reference/brisa-config-js) | Configuration file for Next. |
### Special Files in `src/pages`
-| | | |
-| ----------------------------------------------------------------------------------------------------------- | ------------------- | ----------------- |
-| [`_404`](/docs/pages/building-your-application/routing/custom-error#404-page) | `.js` `.jsx` `.tsx` | 404 Error Page |
-| [`_500`](/docs/pages/building-your-application/routing/custom-error#500-page) | `.js` `.jsx` `.tsx` | 500 Error Page |
+| | | |
+| ----------------------------------------------------------------------------- | ------------------- | -------------- |
+| [`_404`](/docs/pages/building-your-application/routing/custom-error#404-page) | `.js` `.jsx` `.tsx` | 404 Error Page |
+| [`_500`](/docs/pages/building-your-application/routing/custom-error#500-page) | `.js` `.jsx` `.tsx` | 500 Error Page |
### Routes
@@ -58,4 +54,4 @@ This page provides an overview of the file and folder structure of a Brisa proje
| **File convention** | | |
| [`[file]`](/docs/pages/building-your-application/routing/dynamic-routes) | `.js` `.jsx` `.tsx` | Dynamic route segment |
| [`[...file]`](/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments) | `.js` `.jsx` `.tsx` | Catch-all route segment |
-| [`[[...file]]`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all route segment |
\ No newline at end of file
+| [`[[...file]]`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all route segment |
diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md
index e0d703444..bab7a5a0c 100644
--- a/docs/01-getting-started/index.md
+++ b/docs/01-getting-started/index.md
@@ -1,4 +1,4 @@
---
title: Getting Started
description: Learn how to create full-stack web applications with Brisa.
----
\ No newline at end of file
+---
diff --git a/docs/02-building-your-application/01-routing/01-pages-and-layouts.md b/docs/02-building-your-application/01-routing/01-pages-and-layouts.md
index b9d5e8d64..dc7ad660e 100644
--- a/docs/02-building-your-application/01-routing/01-pages-and-layouts.md
+++ b/docs/02-building-your-application/01-routing/01-pages-and-layouts.md
@@ -13,7 +13,7 @@ In Brisa framework, a **page** is a [Brisa Component](/docs/components-details)
```jsx
export default function About() {
- return
About
+ return
About
;
}
```
@@ -43,7 +43,6 @@ Brisa supports pages with dynamic routes. For example, if you create a file call
The global layout is defined inside `/src/layout/index`. By default Brisa supports a default layout, but you can modify it here.
-
```jsx filename="src/layout/index.js"
import { RequestContext } from "brisa";
@@ -107,4 +106,4 @@ export default async function Layout({ children }: { children: JSX.Element }, {
}
```
-The `fetch` is directly native and has no wrapper to control the cache. We recommend that you do not do the same `fetch` in several places, but use the [`context`](/docs/building-your-application/data-fetching/request-context) to store the data and consume it from any component.
\ No newline at end of file
+The `fetch` is directly native and has no wrapper to control the cache. We recommend that you do not do the same `fetch` in several places, but use the [`context`](/docs/building-your-application/data-fetching/request-context) to store the data and consume it from any component.
diff --git a/docs/02-building-your-application/01-routing/02-dynamic-routes.md b/docs/02-building-your-application/01-routing/02-dynamic-routes.md
index eee690751..f4e3b8cdd 100644
--- a/docs/02-building-your-application/01-routing/02-dynamic-routes.md
+++ b/docs/02-building-your-application/01-routing/02-dynamic-routes.md
@@ -17,7 +17,7 @@ For example, a blog could include the following route `src/pages/blog/[slug].js`
```jsx
export default function Page(props, { route }) {
- return
Post: {route.query.slug}
+ return
Post: {route.query.slug}
;
}
```
@@ -54,4 +54,4 @@ The difference between **catch-all** and **optional catch-all** segments is that
| `pages/shop/[[...slug]].js` | `/shop` | `{}` |
| `pages/shop/[[...slug]].js` | `/shop/a` | `{ slug: ['a'] }` |
| `pages/shop/[[...slug]].js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` |
-| `pages/shop/[[...slug]].js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |
\ No newline at end of file
+| `pages/shop/[[...slug]].js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |
diff --git a/docs/02-building-your-application/01-routing/03-linking-and-navigating.md b/docs/02-building-your-application/01-routing/03-linking-and-navigating.md
index f3b025713..2084358a1 100644
--- a/docs/02-building-your-application/01-routing/03-linking-and-navigating.md
+++ b/docs/02-building-your-application/01-routing/03-linking-and-navigating.md
@@ -19,7 +19,7 @@ export default function Home() {
Blog Post
- )
+ );
}
```
@@ -29,7 +29,6 @@ The example above uses multiple `a` tags. Each one maps a path (`href`) to a kno
- `/about` → `src/pages/about.js`
- `/blog/hello-world` → `src/pages/blog/[slug].js`
-
## Navigation to dynamic paths
You can also use interpolation to create the path, which comes in handy for [dynamic route segments](/docs/building-your-application/routing/dynamic-routes). For example, to show a list of posts which have been passed to the component as a prop:
@@ -40,26 +39,23 @@ export default function Posts({ posts }) {
- )
+ );
}
```
> [`encodeURIComponent`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) is used in the example to keep the path utf-8 compatible.
-
## I18n navigation
If you have [i18n routing](/docs/routing/internationalization) enabled, during navigation you always have to forget about route translations and during the render of the page will be translated to correct translated page.
```jsx
export default function Home() {
- return About Us
+ return About Us;
}
```
@@ -77,7 +73,7 @@ It is always possible to force a specific route in case you want to change the l
```jsx
export default function Home() {
- return About Us in Spanish
+ return About Us in Spanish;
}
```
diff --git a/docs/02-building-your-application/01-routing/04-custom-error.md b/docs/02-building-your-application/01-routing/04-custom-error.md
index 0de853650..3935fbda2 100644
--- a/docs/02-building-your-application/01-routing/04-custom-error.md
+++ b/docs/02-building-your-application/01-routing/04-custom-error.md
@@ -5,40 +5,38 @@ description: Override and extend the built-in Error page to handle custom errors
## 404 Page
-To create a custom 404 page you can create a `src/pages/_404.js` file.
+To create a custom 404 page you can create a `src/pages/_404.js` file.
```jsx filename="src/pages/_404.js"
export default function Custom404() {
- return
404 - Page Not Found
+ return
404 - Page Not Found
;
}
```
> **Good to know**: In this page you can access to the `request context`, `fetch` data, change the `head` content (meta tags, etc), and change the `response headers`, in the same way of the rest of pages.
-
## 500 Page
To customize the 500 page you can create a `src/pages/_500.js` file.
```jsx filename="src/pages/_500.js"
export default function Custom500({ error }, requestContext) {
- return
500 - {error.message}
+ return
500 - {error.message}
;
}
```
> **Good to know**: In this page you can access to the `request context`, `fetch` data, change the `head` content (meta tags, etc), and change the `response headers`, in the same way of the rest of pages.
-
### Errors in component-level
If you want to control errors at the component level instead of displaying a whole new page with the error, you can make the components have the error extension by adding the `ComponentName.error`:
```jsx
export default function SomeComponent() {
- return /* some JSX */
+ return; /* some JSX */
}
SomeComponent.error = ({ error }, requestContext) => {
- return
Oops! {error.message}
-}
+ return
Oops! {error.message}
;
+};
```
diff --git a/docs/02-building-your-application/01-routing/05-api-routes.md b/docs/02-building-your-application/01-routing/05-api-routes.md
index bae531a04..bf8f87b93 100644
--- a/docs/02-building-your-application/01-routing/05-api-routes.md
+++ b/docs/02-building-your-application/01-routing/05-api-routes.md
@@ -16,21 +16,21 @@ import { type RequestContext } from "brisa";
export function GET(request: RequestContext) {
const responseData = JSON.stringify({
- message: "Hello world from Brisa!"
- })
+ message: "Hello world from Brisa!",
+ });
const responseOptions = {
- headers: { "content-type": "application/json" }
- }
+ headers: { "content-type": "application/json" },
+ };
return new Response(responseData, responseOptions);
}
```
-
## Query and parameters
If we want for example to use a dynamic route for users and know which username it is:
+
- `/api/user/aralroca?id=3` → `src/api/user/[username].ts`
We have access to the route through the `RequestContext` and we can access both the parameters and the query.
@@ -39,7 +39,7 @@ We have access to the route through the `RequestContext` and we can access both
import { type RequestContext } from "brisa";
export function GET({ route: { query, params } }: RequestContext) {
- const { id } = params
+ const { id } = params;
return new Response(`Hello world ${query.username} with id=${id}!`);
}
```
@@ -48,7 +48,6 @@ export function GET({ route: { query, params } }: RequestContext) {
The request that arrives is an extension of the native [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request), where apart from having everything that the request has, it has extra information of the request, such as the `i18n`, the `route` and more. If you want to know more take a look at [`request context`](/docs/building-your-application/data-fetching/request-context).
-
## Response
The [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) is the native one, so you can find out [here](https://developer.mozilla.org/en-US/docs/Web/API/Response) how it works.
@@ -65,27 +64,26 @@ Example:
import { type RequestContext } from "brisa";
export function GET({ i18n, route: { query, params } }: RequestContext) {
- const { id } = params
- return new Response(i18n.t('hello', { name: params.username, id }));
+ const { id } = params;
+ return new Response(i18n.t("hello", { name: params.username, id }));
}
```
And this inside `src/i18n/index.ts` or `src/i18n.ts` file:
-
```ts filename="src/i18n/index.ts" switcher
export default {
- locales: ['en', 'es'],
- defaultLocale: 'en',
+ locales: ["en", "es"],
+ defaultLocale: "en",
messages: {
en: {
- hello: 'Hello {{name}} with id={{id}}!',
+ hello: "Hello {{name}} with id={{id}}!",
},
es: {
- hello: '¡Hola {{name}} con id={{id}}!',
+ hello: "¡Hola {{name}} con id={{id}}!",
},
},
-}
+};
```
## Dynamic routes, catch all and optional catch all routes
@@ -97,15 +95,14 @@ API Routes support [dynamic routes](/docs/building-your-application/routing/dyna
It can be extended to catch all paths by adding three dots (`...`) inside the brackets. For example:
- `/api/post/a` → `pages/api/post/[...slug].js`
-- `/api/post/a/b` → `pages/api/post/[...slug].js`
- `/api/post/a/b/c` and so on. → `pages/api/post/[...slug].js`
+- `/api/post/a/b` → `pages/api/post/[...slug].js`
+ `/api/post/a/b/c` and so on. → `pages/api/post/[...slug].js`
Catch all routes can be made optional by including the parameter in double brackets (`[[...slug]]`).
-- `/api/post` → `pages/api/post/[[...slug]].js`
-- `/api/post/a` → `pages/api/post/[[...slug]].js`
-- `/api/post/a/b`, and so on. → `pages/api/post/[[...slug]].js`
-
+- `/api/post` → `pages/api/post/[[...slug]].js`
+- `/api/post/a` → `pages/api/post/[[...slug]].js`
+- `/api/post/a/b`, and so on. → `pages/api/post/[[...slug]].js`
> **Good to know**: You can use names other than `slug`, such as: `[[...param]]`
@@ -115,14 +112,14 @@ You can set CORS headers on a `Response` using the standard Web API methods:
```ts
export async function GET(request: Request) {
- return new Response('Hello, Brisa!', {
+ return new Response("Hello, Brisa!", {
status: 200,
headers: {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
},
- })
+ });
}
```
diff --git a/package.json b/package.json
index 05c455aac..26a43ff27 100644
--- a/package.json
+++ b/package.json
@@ -64,4 +64,4 @@
"peerDependencies": {
"typescript": "5.2.2"
}
-}
\ No newline at end of file
+}
diff --git a/src/__fixtures__/middleware.ts b/src/__fixtures__/middleware.ts
index 7d135f63b..0fa520e1b 100644
--- a/src/__fixtures__/middleware.ts
+++ b/src/__fixtures__/middleware.ts
@@ -1,5 +1,7 @@
-export default async function middleware(request: Request) {
- const url = new URL(request.url);
+import { RequestContext } from "../types";
+
+export default async function middleware(request: RequestContext) {
+ const url = new URL(request.finalURL);
if (url.pathname !== "/test") return;
diff --git a/src/cli/serve.tsx b/src/cli/serve.tsx
index d793661e6..eb90bfd4e 100644
--- a/src/cli/serve.tsx
+++ b/src/cli/serve.tsx
@@ -3,7 +3,7 @@ import path from "node:path";
import LoadLayout from "../utils/load-layout";
import getRouteMatcher from "../utils/get-route-matcher";
-import { RequestContext, renderToReadableStream } from "../core";
+import { renderToReadableStream } from "../core";
import { LiveReloadScript } from "./dev-live-reload";
import { MatchedRoute, ServerWebSocket } from "bun";
import importFileIfExists from "../utils/import-file-if-exists";
@@ -11,6 +11,8 @@ import getConstants from "../constants";
import handleI18n from "../utils/handle-i18n";
import redirectTrailingSlash from "../utils/redirect-trailing-slash";
import getImportableFilepath from "../utils/get-importable-filepath";
+import extendRequestContext from "../utils/extend-request-context";
+import { RequestContext } from "../types";
const {
IS_PRODUCTION,
@@ -60,8 +62,8 @@ Bun.serve({
development: !IS_PRODUCTION,
async fetch(req: Request, server) {
if (server.upgrade(req)) return;
- const request = new RequestContext(req);
- const url = new URL(request.url);
+ const request = extendRequestContext({ originalRequest: req });
+ const url = new URL(request.finalURL);
const assetPath = path.join(ASSETS_DIR, url.pathname);
const isHome = url.pathname === "/";
const isAnAsset = !isHome && fs.existsSync(assetPath);
@@ -81,7 +83,7 @@ Bun.serve({
request.getIP = () => server.requestIP(req);
return (
- handleRequest(request, isAnAsset)
+ handleRequest(request, isAnAsset, req)
// 500 page
.catch((error) => {
const route500 = pagesRouter.reservedRoutes[PAGE_500];
@@ -121,9 +123,13 @@ console.log(
////////////////////// HELPERS ///////////////////////
///////////////////////////////////////////////////////
-async function handleRequest(req: RequestContext, isAnAsset: boolean) {
+async function handleRequest(
+ req: RequestContext,
+ isAnAsset: boolean,
+ originalReq: Request,
+) {
const locale = req.i18n.locale;
- const url = new URL(req.url);
+ const url = new URL(req.finalURL);
const pathname = url.pathname;
const { route, isReservedPathname } = pagesRouter.match(req);
const isApi = pathname.startsWith(locale ? `/${locale}/api/` : "/api/");
@@ -149,7 +155,7 @@ async function handleRequest(req: RequestContext, isAnAsset: boolean) {
req.route = api.route;
- const response = module[method]?.(req)
+ const response = module[method]?.(req);
if (response) return response;
}
diff --git a/src/core/index.ts b/src/core/index.ts
index c3c647464..71b5f030b 100644
--- a/src/core/index.ts
+++ b/src/core/index.ts
@@ -1,5 +1,4 @@
import renderToReadableStream from "./render-to-readable-stream";
-import RequestContext from "./request-context";
import dangerHTML from "./danger-html";
-export { renderToReadableStream, RequestContext, dangerHTML };
+export { renderToReadableStream, dangerHTML };
diff --git a/src/core/render-to-readable-stream/index.test.tsx b/src/core/render-to-readable-stream/index.test.tsx
index 3a5cf5fd1..017818306 100644
--- a/src/core/render-to-readable-stream/index.test.tsx
+++ b/src/core/render-to-readable-stream/index.test.tsx
@@ -1,12 +1,14 @@
import { describe, it, expect, mock, afterEach, afterAll } from "bun:test";
import renderToReadableStream from ".";
-import { RequestContext } from "..";
import streamToText from "../../__fixtures__/stream-to-text";
import dangerHTML from "../danger-html";
import getConstants from "../../constants";
-import { ComponentType } from "../../types";
+import { ComponentType, RequestContext } from "../../types";
+import extendRequestContext from "../../utils/extend-request-context";
-const testRequest = new RequestContext(new Request("http://test.com/"));
+const testRequest = extendRequestContext({
+ originalRequest: new Request("http://test.com/"),
+});
const mockConsoleError = mock(() => {});
const consoleError = console.error;
console.error = mockConsoleError;
@@ -84,11 +86,11 @@ describe("brisa core", () => {
it("should be possible to access to the request object inside components", async () => {
const Component = (
{ name, title }: { name: string; title: string },
- request: Request,
+ request: RequestContext,
) => (