From baf5223922d10bc1d538d189d4481e4af5d76ad0 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sat, 12 Aug 2023 13:21:17 +0200 Subject: [PATCH 01/11] www: add language switcher to code blocks --- docs/canary/concepts/middleware.md | 30 ++- docs/canary/concepts/route-groups.md | 50 ++--- docs/canary/concepts/route-layout.md | 25 +-- docs/latest/concepts/app-wrapper.md | 6 +- docs/latest/concepts/data-fetching.md | 2 +- docs/latest/concepts/deployment.md | 6 +- docs/latest/concepts/error-pages.md | 12 +- docs/latest/concepts/forms.md | 5 +- docs/latest/concepts/islands.md | 13 +- docs/latest/concepts/middleware.md | 32 ++- docs/latest/concepts/plugins.md | 8 +- docs/latest/concepts/routes.md | 24 +-- docs/latest/concepts/routing.md | 6 +- docs/latest/concepts/server-configuration.md | 16 +- docs/latest/concepts/static-files.md | 2 +- docs/latest/concepts/updating.md | 4 +- docs/latest/examples/changing-the-src-dir.md | 6 +- docs/latest/examples/creating-a-crud-api.md | 72 +++---- docs/latest/examples/dealing-with-cors.md | 31 +-- .../examples/handling-complex-routes.md | 10 +- docs/latest/examples/init-the-server.md | 10 +- docs/latest/examples/modifying-the-head.md | 3 +- docs/latest/examples/rendering-markdown.md | 17 +- docs/latest/examples/setting-the-language.md | 4 +- .../examples/sharing-state-between-islands.md | 70 +++---- docs/latest/examples/using-csp.md | 59 ++---- docs/latest/examples/using-deno-kv-oauth.md | 16 +- .../examples/using-fresh-canary-version.md | 10 +- docs/latest/examples/using-twind-v1.md | 8 +- docs/latest/examples/writing-tests.md | 35 +--- .../getting-started/adding-interactivity.md | 12 +- .../getting-started/create-a-project.md | 2 +- docs/latest/getting-started/create-a-route.md | 6 +- .../latest/getting-started/custom-handlers.md | 8 +- docs/latest/getting-started/dynamic-routes.md | 6 +- docs/latest/getting-started/fetching-data.md | 8 +- .../getting-started/form-submissions.md | 8 +- plugins/markdown.ts | 1 + plugins/markdown/mod.ts | 33 +++ www/fresh.gen.ts | 38 ++-- www/routes/_app.tsx | 59 ++++++ www/routes/docs/[...slug].tsx | 10 +- www/routes/gfm.css.ts | 61 +++++- www/utils/markdown.ts | 195 ++++++++++++++++++ 44 files changed, 635 insertions(+), 404 deletions(-) create mode 100644 plugins/markdown.ts create mode 100644 plugins/markdown/mod.ts create mode 100644 www/routes/_app.tsx diff --git a/docs/canary/concepts/middleware.md b/docs/canary/concepts/middleware.md index cba674c9df0..91f807d57b0 100644 --- a/docs/canary/concepts/middleware.md +++ b/docs/canary/concepts/middleware.md @@ -17,9 +17,8 @@ special [\_app](/docs/concepts/app-wrapper.md) wrapper and normal properties, e.g. `ctx.state.loggedIn = true`, but you can also replace the entire object like `ctx.state = { loggedIn = true }`. -```ts -// routes/_middleware.ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; interface State { data: string; @@ -27,7 +26,7 @@ interface State { export async function handler( req: Request, - ctx: MiddlewareHandlerContext, + ctx: MiddlewareHandlerContext ) { ctx.state.data = "myData"; const resp = await ctx.next(); @@ -36,8 +35,7 @@ export async function handler( } ``` -```ts -// routes/myHandler.ts +```ts { "title": "routes/myHandler.ts" } export const handler: Handlers = { GET(_req, ctx) { return new Response(`middleware data is ${ctx.state.data}`); @@ -52,7 +50,7 @@ specific first). For example, take a project with the following routes: -``` +```txt { "title": "Project structure" } └── routes    ├── _middleware.ts    ├── index.ts @@ -85,9 +83,7 @@ A single middleware file can also define multiple middlewares (all for the same route) by exporting an array of handlers instead of a single handler. For example: -```ts -// routes/_middleware.ts - +```ts { "title": "routes/_middleware.ts"} export const handler = [ async function middleware1(req, ctx) { // do something @@ -103,8 +99,8 @@ export const handler = [ It should be noted that `middleware` has access to route parameters. If you're running a fictitious `routes/[tenant]/admin/_middleware.ts` like this: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/[tenant]/admin/_middleware.ts" } +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; export async function handler(_req: Request, ctx: MiddlewareHandlerContext) { const currentTenant = ctx.params.tenant; @@ -121,7 +117,7 @@ the value of `acme` in your middleware. To set the stage for this section, `MiddlewareHandlerContext` looks like this: -```ts +```ts { "title": "Type definition", "lang_switcher": false} export interface MiddlewareHandlerContext> { next: () => Promise; state: State; @@ -136,7 +132,7 @@ export interface MiddlewareHandlerContext> { and `router.DestinationKind` is defined like this: -```ts +```ts { "title": "Type definition", "lang_switcher": false} export type DestinationKind = "internal" | "static" | "route" | "notFound"; ``` @@ -148,8 +144,8 @@ for a `route`, as opposed to something like `http://localhost:8001/favicon.ico`. Initiate a new Fresh project (`deno run -A -r https://fresh.deno.dev/`) and then create a `_middleware.ts` file in the `routes` folder like this: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; export async function handler(req: Request, ctx: MiddlewareHandlerContext) { console.log(ctx.destination); @@ -161,7 +157,7 @@ export async function handler(req: Request, ctx: MiddlewareHandlerContext) { If you start up your server (`deno task start`) you'll see the following: -``` +```sh { "title": "Terminal output"} Task start deno run -A --watch=static/,routes/ dev.ts Watcher Process started. The manifest has been generated for 4 routes and 1 islands. diff --git a/docs/canary/concepts/route-groups.md b/docs/canary/concepts/route-groups.md index 56a5f1afcd0..9203ed020b8 100644 --- a/docs/canary/concepts/route-groups.md +++ b/docs/canary/concepts/route-groups.md @@ -10,7 +10,7 @@ than another route that's in the same URL segment. Let's illustrate that with an example: -```txt +```txt { "title": "Intended URL to layout mapping"} /about -> layout A /career -> layout A /archive -> layout B @@ -20,13 +20,13 @@ Let's illustrate that with an example: Without any way to group routes this is a problem because every route segment can only have one `_layout` file. -```txt -/routes - /_layout.tsx <-- applies to all routes here :( - /about.tsx - /career.tsx - /archive.tsx - /contact.tsx +```txt { "title": "Project struture "} +routes/ + _layout.tsx <-- applies to all routes here :( + about.tsx + career.tsx + archive.tsx + contact.tsx ``` We can solve this problem with route groups. A route group is a folder which has @@ -34,25 +34,27 @@ a name that is wrapped in braces. For example `(pages)` would be considered a route and so would be `(marketing)`. This enables us to group related routes in a folder and use a different `_layout` file for each group. -```txt -/routes - /(marketing) - /_layout.tsx <-- only applies to about.tsx and career.tsx - /about.tsx - /career.tsx - /(info) - /_layout.tsx <-- only applies to archive.tsx and contact.tsx - /archive.tsx - /contact.tsx +```txt { "title": "Project structure"} +routes/ + (marketing)/ + _layout.tsx <-- only applies to about.tsx and career.tsx + about.tsx + career.tsx + + (info)/ + _layout.tsx <-- only applies to archive.tsx and contact.tsx + archive.tsx + contact.tsx ``` > ℹ️ Be careful about routes in different groups which match to the same URL. > Such scenarios will lead to ambiguity as to which route file should be picked. > -> ```txt -> /routes -> /(group-1) -> /about.tsx <-- Bad: Both routes map to the same `/about` url -> /(group-2) -> /about.tsx <-- Bad: Both routes map to the same `/about` url +> ```txt { "title": "Project structure"} +> routes/ +> (group-1)/ +> about.tsx <-- Bad: Both routes map to the same `/about` url +> +> (group-2)/ +> about.tsx <-- Bad: Both routes map to the same `/about` url > ``` diff --git a/docs/canary/concepts/route-layout.md b/docs/canary/concepts/route-layout.md index 009cd00fa75..8931db852d8 100644 --- a/docs/canary/concepts/route-layout.md +++ b/docs/canary/concepts/route-layout.md @@ -7,14 +7,14 @@ An route layout is defined in a `_layout.tsx` file in any sub directory (at any level) under the `routes/` folder. It must contain a default export that is a regular Preact component. Only one such layout is allowed per sub directory. -```sh +```txt { "title": "Project structure" } routes/ _app.tsx _layout.tsx # will be applied to all routes - /sub + sub/ index.tsx page.tsx - /other + other/ _layout.tsx # will be applied on top of `routes/_layout.tsx` page.tsx ``` @@ -24,10 +24,8 @@ things. This allows for the introduction of a global container functioning as a template which can be conditioned based on state and params. Note that any state set by middleware is available via `props.state`. -```tsx -// routes/sub/_layout.tsx - -import { LayoutProps } from "$fresh/server.ts"; +```tsx { "title": "routes/sub/_layout.tsx" } +import type { LayoutProps } from "$fresh/server.ts"; export default function Layout({ Component, state }: LayoutProps) { //do something with state here @@ -45,10 +43,10 @@ Sometimes you want to opt out of the layout inheritance mechanism for a particular route. This can be done via route configuration. Picture a directory structure like this: -```sh +```txt { "title": "Project structure" } routes/ _layout.tsx - /sub + sub/ _layout.tsx index.tsx special.tsx # should not inherit layouts @@ -57,9 +55,8 @@ routes/ To make `routes/sub/special.tsx` opt out of rendering layouts we can set `rootLayout: true`. -```tsx -// routes/sub/special.tsx -import { RouteConfig } from "$fresh/server.ts"; +```tsx { "title": "routes/sub/special.tsx"} +import type { RouteConfig } from "$fresh/server.ts"; export const config: RouteConfig = { rootLayout: true, // Mark this route as the root layout @@ -77,8 +74,8 @@ Note that you can also mark layouts as root layouts. In very rare cases you might want to disable the root `_app` template for a particular route. This can be done by setting `appTemplate: false`. -```tsx -import { RouteConfig } from "$fresh/server.ts"; +```tsx { "title": "routes/my-page.tsx"} +import type { RouteConfig } from "$fresh/server.ts"; export const config: RouteConfig = { appTemplate: false, // Disable the `_app` template diff --git a/docs/latest/concepts/app-wrapper.md b/docs/latest/concepts/app-wrapper.md index 7d83bafcc82..873a37f27b3 100644 --- a/docs/latest/concepts/app-wrapper.md +++ b/docs/latest/concepts/app-wrapper.md @@ -12,10 +12,8 @@ things. This allows for the introduction of a global container functioning as a template which can be conditioned based on state and params. Note that any state set by middleware is available via `props.state`. -```tsx -// routes/_app.tsx - -import { AppProps } from "$fresh/server.ts"; +```tsx { "title": "routes/_app.tsx" } +import type { AppProps } from "$fresh/server.ts"; export default function App({ Component, state }: AppProps) { //do something with state here diff --git a/docs/latest/concepts/data-fetching.md b/docs/latest/concepts/data-fetching.md index 6581d802b51..b0f5f1a0aa2 100644 --- a/docs/latest/concepts/data-fetching.md +++ b/docs/latest/concepts/data-fetching.md @@ -10,7 +10,7 @@ component through the `data` property on the `props`. Here is an example: -```tsx +```tsx { "title": "routes/projects/[id].tsx" } interface Project { name: string; stars: number; diff --git a/docs/latest/concepts/deployment.md b/docs/latest/concepts/deployment.md index 14e3586dd97..7aaae3f761d 100644 --- a/docs/latest/concepts/deployment.md +++ b/docs/latest/concepts/deployment.md @@ -35,7 +35,7 @@ caching **will** cause your project to not function correctly. Here is an example `Dockerfile` for a Fresh project: -```dockerfile +```dockerfile { "title": "Dockerfile" } FROM denoland/deno:1.35.0 ARG GIT_REVISION @@ -53,13 +53,13 @@ CMD ["run", "-A", "main.ts"] To build your Docker image inside of a Git repository: -```sh +```sh { "title": "Terminal output" } $ docker build --build-arg GIT_REVISION=$(git rev-parse HEAD) -t my-fresh-app . ``` Then run your Docker container: -```sh +```sh { "title": "Terminal output" } $ docker run -t -i -p 80:8000 my-fresh-app ``` diff --git a/docs/latest/concepts/error-pages.md b/docs/latest/concepts/error-pages.md index dc5cd30d336..f8c2ab3ebca 100644 --- a/docs/latest/concepts/error-pages.md +++ b/docs/latest/concepts/error-pages.md @@ -14,8 +14,8 @@ The 404 page can be customized by creating a `_404.tsx` file in the `routes/` folder. The file must have a default export that is a regular Preact component. A props object of type `UnknownPageProps` is passed in as an argument. -```tsx -import { UnknownPageProps } from "$fresh/server.ts"; +```tsx { "title": "routes/_404.tsx" } +import type { UnknownPageProps } from "$fresh/server.ts"; export default function NotFoundPage({ url }: UnknownPageProps) { return

404 not found: {url.pathname}

; @@ -29,8 +29,8 @@ In some cases, one needs to manually trigger the rendering of the 404 page, for example when the route did match, but the requested resource does not exist. This can be achieved with `ctx.renderNotFound`. -```tsx -import { Handlers, PageProps } from "$fresh/server.ts"; +```tsx { "title": "routes/blog/[slug].tsx" } +import type { Handlers, PageProps } from "$fresh/server.ts"; export const handler: Handlers = { async GET(req, ctx) { @@ -60,8 +60,8 @@ The 500 page can be customized by creating a `_500.tsx` file in the `routes/` folder. The file must have a default export that is a regular Preact component. A props object of type `ErrorPageProps` is passed in as an argument. -```tsx -import { ErrorPageProps } from "$fresh/server.ts"; +```tsx { "title": "routes/_500.tsx" } +import type { ErrorPageProps } from "$fresh/server.ts"; export default function Error500Page({ error }: ErrorPageProps) { return

500 internal error: {(error as Error).message}

; diff --git a/docs/latest/concepts/forms.md b/docs/latest/concepts/forms.md index c972a090e78..b7f4cbc1723 100644 --- a/docs/latest/concepts/forms.md +++ b/docs/latest/concepts/forms.md @@ -18,9 +18,8 @@ or as a `POST` request with `multipart/form-data`. This example demonstrates how to handle `multipart/form-data` `
` submissions: -```tsx -// routes/subscribe.tsx -import { Handlers } from "$fresh/server.ts"; +```tsx { "title": "routes/subscribe.tsx" } +import { type Handlers } from "$fresh/server.ts"; export const handler: Handlers = { async GET(req, ctx) { diff --git a/docs/latest/concepts/islands.md b/docs/latest/concepts/islands.md index 2de11c08909..6010dfd32e8 100644 --- a/docs/latest/concepts/islands.md +++ b/docs/latest/concepts/islands.md @@ -11,9 +11,7 @@ Islands are defined by creating a file in the `islands/` folder in a Fresh project. The name of this file must be a PascalCase or kebab-case name of the island. -```tsx -// islands/MyIsland.tsx - +```tsx { "title": "islands/MyIsland.tsx" } import { useSignal } from "@preact/signals"; export default function MyIsland() { @@ -53,8 +51,7 @@ functions is not supported. Islands support passing JSX elements via the `children` property. This allows you to pass static content rendered by the server to an island in the browser. -```jsx -// route/index.tsx +```tsx { "title": "route/index.tsx" } import MyIsland from "../islands/my-island.tsx"; export default function Home() { @@ -77,8 +74,7 @@ Islands can be nested within other islands as well. In that scenario they act like a normal Preact component, but still receive the serialized props if any were present. -```jsx -// route/index.tsx +```tsx { "title": "route/index.tsx" } import MyIsland from "../islands/my-island.tsx"; import OtherIsland from "../islands/other-island.tsx"; @@ -97,8 +93,7 @@ In essence, Fresh allows you to mix static and interactive parts in your app in a way that's most optimal for your use app. We'll keep sending only the JavaScript that is needed for the islands to the browser. -```jsx -// route/index.tsx +```tsx { "title": "route/index.tsx"} import MyIsland from "../islands/my-island.tsx"; import OtherIsland from "../islands/other-island.tsx"; diff --git a/docs/latest/concepts/middleware.md b/docs/latest/concepts/middleware.md index f611d5ffd61..ee4fec47e00 100644 --- a/docs/latest/concepts/middleware.md +++ b/docs/latest/concepts/middleware.md @@ -15,8 +15,7 @@ be used to pass arbitrary data to downstream (or upstream) handlers. This special [\_app](/docs/concepts/app-wrapper.md) wrapper and normal [routes](/docs/concepts/routes.md). -```ts -// routes/_middleware.ts +```ts { "title": "routes/_middleware.ts" } import { MiddlewareHandlerContext } from "$fresh/server.ts"; interface State { @@ -25,7 +24,7 @@ interface State { export async function handler( req: Request, - ctx: MiddlewareHandlerContext, + ctx: MiddlewareHandlerContext ) { ctx.state.data = "myData"; const resp = await ctx.next(); @@ -34,8 +33,7 @@ export async function handler( } ``` -```ts -// routes/myHandler.ts +```ts { "title": "routes/myHandler.ts"} export const handler: Handlers = { GET(_req, ctx) { return new Response(`middleware data is ${ctx.state.data}`); @@ -50,7 +48,7 @@ specific first). For example, take a project with the following routes: -``` +```txt { "title": "Project structure" } └── routes    ├── _middleware.ts    ├── index.ts @@ -83,9 +81,7 @@ A single middleware file can also define multiple middlewares (all for the same route) by exporting an array of handlers instead of a single handler. For example: -```ts -// routes/_middleware.ts - +```ts { "title": "routes/_middleware.ts" } export const handler = [ async function middleware1(req, ctx) { // do something @@ -101,8 +97,8 @@ export const handler = [ It should be noted that `middleware` has access to route parameters. If you're running a fictitious `routes/[tenant]/admin/_middleware.ts` like this: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import { type MiddlewareHandlerContext } from "$fresh/server.ts"; export async function handler(_req: Request, ctx: MiddlewareHandlerContext) { const currentTenant = ctx.params.tenant; @@ -119,7 +115,7 @@ the value of `acme` in your middleware. To set the stage for this section, `MiddlewareHandlerContext` looks like this: -```ts +```ts { "title": "TypeScript", "lang_switcher": false } export interface MiddlewareHandlerContext> { next: () => Promise; state: State; @@ -134,7 +130,7 @@ export interface MiddlewareHandlerContext> { and `router.DestinationKind` is defined like this: -```ts +```ts { "title": "TypeScript", "lang_switcher": false } export type DestinationKind = "internal" | "static" | "route" | "notFound"; ``` @@ -146,8 +142,8 @@ for a `route`, as opposed to something like `http://localhost:8001/favicon.ico`. Initiate a new Fresh project (`deno run -A -r https://fresh.deno.dev/`) and then create a `_middleware.ts` file in the `routes` folder like this: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import { type MiddlewareHandlerContext } from "$fresh/server.ts"; export async function handler(req: Request, ctx: MiddlewareHandlerContext) { console.log(ctx.destination); @@ -159,7 +155,7 @@ export async function handler(req: Request, ctx: MiddlewareHandlerContext) { If you start up your server (`deno task start`) you'll see the following: -``` +```sh { "title": "Terminal output" } Task start deno run -A --watch=static/,routes/ dev.ts Watcher Process started. The manifest has been generated for 4 routes and 1 islands. @@ -202,7 +198,7 @@ is only supposed to deal with routes. If you want to redirect a request from a middleware, you can do so by returning: -```ts +```ts { "title": "Redirect middleware" } export function handler(req: Request): Response { return Response.redirect("https://example.com", 307); } @@ -211,7 +207,7 @@ export function handler(req: Request): Response { `307` stands for temporary redirect. You can also use `301` for permanent redirect. You can also redirect to a relative path by doing: -```ts +```ts { "title": "Redirect middleware" } export function handler(req: Request): Response { return new Response("", { status: 307, diff --git a/docs/latest/concepts/plugins.md b/docs/latest/concepts/plugins.md index c86e3c07fc2..a2b97db8ac5 100644 --- a/docs/latest/concepts/plugins.md +++ b/docs/latest/concepts/plugins.md @@ -6,9 +6,7 @@ Plugins can dynamically add new functionality to Fresh without exposing significant complexity to the user. Users can add plugins by importing and initializing them in their `main.ts` file: -```ts -// main.ts - +```ts { "title": "main.ts" } import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; @@ -43,8 +41,8 @@ A Fresh plugin is just a JavaScript object that conforms to the required property of a plugin is its name. Names must only contain the characters `a`-`z`, and `_`. -```ts -import { Plugin } from "$fresh/server.ts"; +```ts { "title": "my-plugin.ts" } +import type { Plugin } from "$fresh/server.ts"; const plugin: Plugin = { name: "my_plugin", diff --git a/docs/latest/concepts/routes.md b/docs/latest/concepts/routes.md index 7260ab7494c..fa035be0ccc 100644 --- a/docs/latest/concepts/routes.md +++ b/docs/latest/concepts/routes.md @@ -25,9 +25,7 @@ the handler's `render` function. Let's look at a basic route that returns a plain text string: -```tsx -// routes/plain.tsx - +```tsx { "title": "routes/plain.tsx" } import { HandlerContext, Handlers } from "$fresh/server.ts"; export const handler: Handlers = { @@ -48,10 +46,8 @@ have a corresponding handler, a 405 HTTP error is returned. Now, let's render some HTML using the route component: -```tsx -// routes/html.tsx - -import { PageProps } from "$fresh/server.ts"; +```tsx { "title": "routes/html.tsx" } +import { type PageProps } from "$fresh/server.ts"; export default function Page(props: PageProps) { return
You are on the page '{props.url.href}'.
; @@ -71,10 +67,8 @@ should work. In the below example, a custom handler is used to add a custom header to the response after rendering the page component. -```tsx -// routes/html.tsx - -import { HandlerContext, Handlers, PageProps } from "$fresh/server.ts"; +```tsx { "title": "routes/html.tsx" } +import type { HandlerContext, Handlers, PageProps } from "$fresh/server.ts"; export const handler: Handlers = { async GET(_req: Request, ctx: HandlerContext) { @@ -96,7 +90,7 @@ test these in isolation, but can become a bit cumbersome to maintain. They require some additional indirection of declaring an interface for the component `Data` when you're passing it around through `ctx.render()`. -```tsx +```tsx { "title": "routes/my-page.tsx" } interface Data { foo: number; } @@ -117,7 +111,7 @@ When a route has both a component and a `GET` handler, they are typically very closely coupled. With async route components you can merge the two together and avoid having to create the `Data` interface boilerplate. -```tsx +```tsx { "title": "routes/my-page.tsx" } // Async route component export default async function MyPage(req: Request, ctx: RouteContext) { const value = await loadFooValue(); @@ -130,7 +124,7 @@ can think of async route components inlining the `GET` handler into the component function. Note, that you can still add additional HTTP handlers in the same file like before. -```tsx +```tsx { "title": "routes/my-page.tsx" } export const handler: Handlers = { async POST(req) { // ... do something here @@ -148,7 +142,7 @@ export default async function MyPage(req: Request, ctx: RouteContext) { Quite often a route handler needs to render a 404 page or bail out of rendering in another manner. This can be done by returning a `Response` object. -```tsx +```tsx { "title": "routes/my-page.tsx" } // Async route component export default async function MyPage(req: Request, ctx: RouteContext) { const value = await loadFooValue(); diff --git a/docs/latest/concepts/routing.md b/docs/latest/concepts/routing.md index fe7a009f7fc..271df7c8b15 100644 --- a/docs/latest/concepts/routing.md +++ b/docs/latest/concepts/routing.md @@ -41,10 +41,8 @@ Advanced use-cases can require that a more complex pattern be used for matching. A custom [URL pattern][urlpattern] can be specified in the route configuration. This pattern will be used instead of the file path based pattern: -```ts -// routes/x.ts - -import { RouteConfig } from "$fresh/server.ts"; +```ts { "title": "routes/x.ts" } +import type { RouteConfig } from "$fresh/server.ts"; export const config: RouteConfig = { routeOverride: "/x/:module@:version/:path*", diff --git a/docs/latest/concepts/server-configuration.md b/docs/latest/concepts/server-configuration.md index 393dad8adfb..a8d397ee0cd 100644 --- a/docs/latest/concepts/server-configuration.md +++ b/docs/latest/concepts/server-configuration.md @@ -7,7 +7,7 @@ In this page we discuss how the server can be configured during startup. The signature of the primary method looks like this: -```ts +```ts { "title": "Type Signature", "lang_switcher": false } export async function start(routes: Manifest, opts: StartOptions = {}); ``` @@ -16,13 +16,13 @@ export async function start(routes: Manifest, opts: StartOptions = {}); `Manifest` comes from `fresh.gen.ts`, so nothing to do there. `opts` is where things get interesting. `StartOptions` looks like this: -``` +```ts { "title": "Type Definition", "lang_switcher": false } export type StartOptions = ServeInit & FreshOptions; ``` The good stuff is really in... -```ts +```ts { "title": "Type Definition", "lang_switcher": false } export interface FreshOptions { render?: RenderFunction; plugins?: Plugin[]; @@ -33,10 +33,10 @@ export interface FreshOptions { And for brevity here are the remaining two types: -```ts +```ts { "title": "Type Definitions", "lang_switcher": false } export type RenderFunction = ( ctx: RenderContext, - render: InnerRenderFunction, + render: InnerRenderFunction ) => void | Promise; export interface RouterOptions { @@ -52,7 +52,7 @@ export interface RouterOptions { See the [docs](/docs/concepts/plugins) on this topic for more detail. But for completion, you can do something like this to load plugins: -```ts +```ts { "title": "main.ts" } await start(manifest, { plugins: [twindPlugin(twindConfig)] }); ``` @@ -61,7 +61,7 @@ await start(manifest, { plugins: [twindPlugin(twindConfig)] }); This allows you to specify the location where your site's static assets are stored. Here's an example: -```ts +```ts { "title": "main.ts" } await start(manifest, { staticDir: "./custom_static" }); ``` @@ -81,6 +81,6 @@ By default Fresh uses URLs like `https://www.example.com/about`. If you'd like, you can configure this to `https://www.example.com/about/` by using the `trailingSlash` setting. -```ts +```ts { "title": "main.ts" } await start(manifest, { router: { trailingSlash: true } }); ``` diff --git a/docs/latest/concepts/static-files.md b/docs/latest/concepts/static-files.md index a2771402f10..258d09d1909 100644 --- a/docs/latest/concepts/static-files.md +++ b/docs/latest/concepts/static-files.md @@ -29,7 +29,7 @@ version of this path that contains a build ID for cache busting. When the asset is requested at this "locked" path, it will be served with a cache lifetime of one year. -```jsx +```tsx { "title": "routes/my-page.tsx"} import { asset } from "$fresh/runtime.ts"; export default function Page() { diff --git a/docs/latest/concepts/updating.md b/docs/latest/concepts/updating.md index 7098c996576..f1def62010a 100644 --- a/docs/latest/concepts/updating.md +++ b/docs/latest/concepts/updating.md @@ -54,7 +54,7 @@ file in the root of your projects directory. Dependency versions are encoded into the URLs in this file. For example, here is how to update a project from Fresh 1.0.2 to 1.1.3, and update Preact to the latest version: -```diff +```diff { "title": "deno.json" } { "imports": { - "$fresh/": "https://deno.land/x/fresh@1.0.2/", @@ -97,7 +97,7 @@ the recommended way to use JSX in Fresh projects. Instead, starting with version 1.1.0, Fresh projects should use the automatic JSX transform that requires no JSX pragma or preact import. -```diff +```diff { "title": "routes/index.jsx" } - /** @jsx h */ - import { h } from "preact"; diff --git a/docs/latest/examples/changing-the-src-dir.md b/docs/latest/examples/changing-the-src-dir.md index e7f2990362d..a6c1a15db18 100644 --- a/docs/latest/examples/changing-the-src-dir.md +++ b/docs/latest/examples/changing-the-src-dir.md @@ -6,7 +6,7 @@ description: | When you initialize a project with `deno run -A -r https://fresh.deno.dev`, you'll end up with a project like the following: -``` +```txt { "title": "Project structure" } . ├── README.md ├── components @@ -40,7 +40,7 @@ your choosing), then you'll need to do the following things: Here's what the diff of `deno.json` looks like: -```diff +```diff { "title": "deno.json" } { "lock": false, "tasks": { @@ -53,7 +53,7 @@ Here's what the diff of `deno.json` looks like: The resulting file structure looks like this: -``` +```txt { "title": "Project structure" } . ├── README.md ├── deno.json diff --git a/docs/latest/examples/creating-a-crud-api.md b/docs/latest/examples/creating-a-crud-api.md index 62497783e63..29748260234 100644 --- a/docs/latest/examples/creating-a-crud-api.md +++ b/docs/latest/examples/creating-a-crud-api.md @@ -19,7 +19,7 @@ In this example we'll be creating a small API that uses Our project structure will look like this (in addition to the rest of the Fresh code from a new project): -``` +```txt { "title": "Project structure" } ├── routes │   └── api │   └── users @@ -34,15 +34,12 @@ full files are available at the bottom for reference. `POST` (create) is used to create a resource. -```tsx -// routes/api/users/index.ts +```tsx { "title": "routes/api/users/index.ts" } export const handler: Handlers = { async POST(req, _ctx) { - const user = await req.json() as User; + const user = (await req.json()) as User; const userKey = ["user", user.id]; - const ok = await kv.atomic() - .set(userKey, user) - .commit(); + const ok = await kv.atomic().set(userKey, user).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(JSON.stringify(user)); }, @@ -53,7 +50,7 @@ Test this with Postman (or your favorite client) with a URL like `http://localhost:8000/api/users` and a method of `POST`. Make sure to have a payload like: -```json +```json { "title": "Server Response" } { "id": "2", "name": "TestUserName" @@ -62,7 +59,7 @@ payload like: You should receive the same thing back: -```json +```json { "title": "Server Response" } { "id": "2", "name": "TestUserName" } ``` @@ -71,8 +68,7 @@ You should receive the same thing back: `GET` (read) is used to retrieve a resource and is by far the most common HTTP method. You can use `GET` to fetch database content, markdown, or static files. -```tsx -// routes/api/users/[id].ts +```tsx { "title": "routes/api/users/[id].ts" } export const handler: Handlers = { async GET(_req, ctx) { const id = ctx.params.id; @@ -86,7 +82,7 @@ export const handler: Handlers = { Let's practice retrieving our user! A `GET` request to `http://localhost:8000/api/users/2` should return: -```json +```json { "title": "Server Response" } { "id": "2", "name": "TestUserName" } ``` @@ -102,19 +98,15 @@ The short version of it: `PUT` requires the entire object to be submitted, while An example of an update endpoint using `PUT`: -```tsx -// routes/api/users/[id].ts +```tsx { "title": "routes/api/users/[id].ts" } export const handler: Handlers = { async PUT(req, ctx) { const id = ctx.params.id; - const user = await req.json() as User; + const user = (await req.json()) as User; const userKey = ["user", id]; const userRes = await kv.get(userKey); if (!userRes.value) return new Response(`no user with id ${id} found`); - const ok = await kv.atomic() - .check(userRes) - .set(userKey, user) - .commit(); + const ok = await kv.atomic().check(userRes).set(userKey, user).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(JSON.stringify(user)); }, @@ -124,7 +116,7 @@ export const handler: Handlers = { Time to change their name. We'll now `PUT` a request to `http://localhost:8000/api/users/2` like: -```json +```json { "title": "Server Request" } { "id": "2", "name": "New Name" @@ -133,14 +125,14 @@ Time to change their name. We'll now `PUT` a request to We should receive: -```json +```json { "title": "Server Response" } { "id": "2", "name": "New Name" } ``` If, on the other hand, we chose to implement this as a `PATCH` operation, the request would just involve the changed property like this: -```json +```json { "title": "Server Request" } { "name": "New Name" } @@ -152,18 +144,14 @@ No need to send in the id in this case. `DELETE` (delete) is used to delete a resource. -```tsx -// routes/api/users/[id].ts +```tsx { "title": "routes/api/users/[id].ts" } export const handler: Handlers = { async DELETE(_req, ctx) { const id = ctx.params.id; const userKey = ["user", id]; const userRes = await kv.get(userKey); if (!userRes.value) return new Response(`no user with id ${id} found`); - const ok = await kv.atomic() - .check(userRes) - .delete(userKey) - .commit(); + const ok = await kv.atomic().check(userRes).delete(userKey).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(`user ${id} deleted`); }, @@ -173,7 +161,7 @@ export const handler: Handlers = { Try sending `DELETE` to `http://localhost:8000/api/users/2` without a body. We'll get back: -``` +```txt { "title": "Server response" } user 2 deleted ``` @@ -188,8 +176,8 @@ request checks for complex CORS use cases. See more on the
[id].ts -```ts -import { Handlers } from "$fresh/server.ts"; +```ts { "title": "routes/api/users/[id].ts" } +import type { Handlers } from "$fresh/server.ts"; type User = { id: string; @@ -210,23 +198,17 @@ export const handler: Handlers = { const userKey = ["user", id]; const userRes = await kv.get(userKey); if (!userRes.value) return new Response(`no user with id ${id} found`); - const ok = await kv.atomic() - .check(userRes) - .delete(userKey) - .commit(); + const ok = await kv.atomic().check(userRes).delete(userKey).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(`user ${id} deleted`); }, async PUT(req, ctx) { const id = ctx.params.id; - const user = await req.json() as User; + const user = (await req.json()) as User; const userKey = ["user", id]; const userRes = await kv.get(userKey); if (!userRes.value) return new Response(`no user with id ${id} found`); - const ok = await kv.atomic() - .check(userRes) - .set(userKey, user) - .commit(); + const ok = await kv.atomic().check(userRes).set(userKey, user).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(JSON.stringify(user)); }, @@ -238,8 +220,8 @@ export const handler: Handlers = {
index.ts -```ts -import { Handlers } from "$fresh/server.ts"; +```ts { "title": "routes/api/users/index.ts" } +import type { Handlers } from "$fresh/server.ts"; type User = { id: string; @@ -257,11 +239,9 @@ export const handler: Handlers = { return new Response(JSON.stringify(users)); }, async POST(req, _ctx) { - const user = await req.json() as User; + const user = (await req.json()) as User; const userKey = ["user", user.id]; - const ok = await kv.atomic() - .set(userKey, user) - .commit(); + const ok = await kv.atomic().set(userKey, user).commit(); if (!ok) throw new Error("Something went wrong."); return new Response(JSON.stringify(user)); }, diff --git a/docs/latest/examples/dealing-with-cors.md b/docs/latest/examples/dealing-with-cors.md index 2e1303d209a..08a2d61c714 100644 --- a/docs/latest/examples/dealing-with-cors.md +++ b/docs/latest/examples/dealing-with-cors.md @@ -15,13 +15,10 @@ As per the above link, "simple" requests involve `GET`, `HEAD`, or `POST` requests. You can CORS enable all the routes affected by some `middleware` by doing the following: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; -export async function handler( - req: Request, - ctx: MiddlewareHandlerContext, -) { +export async function handler(req: Request, ctx: MiddlewareHandlerContext) { const origin = req.headers.get("Origin") || "*"; const resp = await ctx.next(); const headers = resp.headers; @@ -30,11 +27,11 @@ export async function handler( headers.set("Access-Control-Allow-Credentials", "true"); headers.set( "Access-Control-Allow-Headers", - "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With", + "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With" ); headers.set( "Access-Control-Allow-Methods", - "POST, OPTIONS, GET, PUT, DELETE", + "POST, OPTIONS, GET, PUT, DELETE" ); return resp; @@ -47,13 +44,10 @@ What about for one of the other HTTP methods? Then you'll need to be able to deal with "preflight requests". Let's imagine you're trying to support a `DELETE` route. Then you'd need to do something like this: -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "routes/_middleware.ts" } +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; -export async function handler( - _req: Request, - ctx: MiddlewareHandlerContext, -) { +export async function handler(_req: Request, ctx: MiddlewareHandlerContext) { if (_req.method == "OPTIONS") { const resp = new Response(null, { status: 204, @@ -61,10 +55,7 @@ export async function handler( const origin = _req.headers.get("Origin") || "*"; const headers = resp.headers; headers.set("Access-Control-Allow-Origin", origin); - headers.set( - "Access-Control-Allow-Methods", - "DELETE", - ); + headers.set("Access-Control-Allow-Methods", "DELETE"); return resp; } const origin = _req.headers.get("Origin") || "*"; @@ -75,11 +66,11 @@ export async function handler( headers.set("Access-Control-Allow-Credentials", "true"); headers.set( "Access-Control-Allow-Headers", - "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With", + "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With" ); headers.set( "Access-Control-Allow-Methods", - "POST, OPTIONS, GET, PUT, DELETE", + "POST, OPTIONS, GET, PUT, DELETE" ); return resp; diff --git a/docs/latest/examples/handling-complex-routes.md b/docs/latest/examples/handling-complex-routes.md index eb68af84a3c..4153c5a2856 100644 --- a/docs/latest/examples/handling-complex-routes.md +++ b/docs/latest/examples/handling-complex-routes.md @@ -16,8 +16,8 @@ Here you can define named groups, wildcards, regex groups, and other bits. Let's look at the example from the routing page more closely. We'll flesh out the handler so that we end up with something like the following: -```ts -import { HandlerContext, RouteConfig } from "$fresh/server.ts"; +```ts { "title": "routes/my-page.tsx" } +import type { HandlerContext, RouteConfig } from "$fresh/server.ts"; export const handler = { GET(_req: Request, { params }: HandlerContext) { @@ -35,7 +35,7 @@ Now if we hit the server with a request like `http://localhost:8000/x/bestModule@1.33.7/asdf`, then logging the params will show the following: -``` +```sh { "title": "Terminal output" } { module: "bestModule", version: "1.33.7", @@ -47,8 +47,8 @@ show the following: Let's look at something a bit more complex: -```ts -import { HandlerContext, RouteConfig } from "$fresh/server.ts"; +```ts { "title": "routes/my-page.ts" } +import type { HandlerContext, RouteConfig } from "$fresh/server.ts"; export const handler = { GET(_req: Request, { params }: HandlerContext) { diff --git a/docs/latest/examples/init-the-server.md b/docs/latest/examples/init-the-server.md index 855067c3146..ab8fb45913e 100644 --- a/docs/latest/examples/init-the-server.md +++ b/docs/latest/examples/init-the-server.md @@ -7,7 +7,7 @@ Let's pretend you've just initialized a new Fresh project. You want to do some complicated setup that runs once, before the server is started. This is, fortunately, quite easy. Here's how: -```diff +```diff { "title": "main.ts" } import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; +import { Context } from "./routes/_middleware.ts"; @@ -18,7 +18,7 @@ fortunately, quite easy. Here's how: So your full `main.ts` should look like this: -```ts +```ts { "title": "main.ts" } /// /// /// @@ -37,8 +37,8 @@ await start(manifest); But what's going on in this new `_middleware.ts` we've created? -```ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +```ts { "title": "main.ts"} +import type { MiddlewareHandlerContext } from "$fresh/server.ts"; export interface State { context: Context; @@ -66,7 +66,7 @@ export class Context { export async function handler( req: Request, - ctx: MiddlewareHandlerContext, + ctx: MiddlewareHandlerContext ) { ctx.state.context = Context.instance(); const resp = await ctx.next(); diff --git a/docs/latest/examples/modifying-the-head.md b/docs/latest/examples/modifying-the-head.md index 856544a7fdc..3db9bd605af 100644 --- a/docs/latest/examples/modifying-the-head.md +++ b/docs/latest/examples/modifying-the-head.md @@ -13,8 +13,7 @@ the web page. Some uses include: - Linking to resources like stylesheets using `` - Including third-party JavaScript code using `