From dace00861149242cd4d4976db5f3cb4f4cf0e9a7 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Fri, 1 Dec 2023 16:20:40 +0100 Subject: [PATCH] chore: release 1.6.0 (#2113) --- docs/canary/concepts/ahead-of-time-builds.md | 142 ------------ docs/canary/concepts/error-pages.md | 82 ------- docs/canary/concepts/plugins.md | 192 ---------------- docs/canary/concepts/server-configuration.md | 212 ------------------ docs/latest/concepts/ahead-of-time-builds.md | 7 + docs/latest/concepts/error-pages.md | 13 ++ docs/latest/concepts/plugins.md | 54 ++++- docs/latest/concepts/server-configuration.md | 30 +++ .../examples/migrating-to-tailwind.md | 0 docs/toc.ts | 9 +- versions.json | 1 + www/routes/index.tsx | 4 +- 12 files changed, 111 insertions(+), 635 deletions(-) delete mode 100644 docs/canary/concepts/ahead-of-time-builds.md delete mode 100644 docs/canary/concepts/error-pages.md delete mode 100644 docs/canary/concepts/plugins.md delete mode 100644 docs/canary/concepts/server-configuration.md rename docs/{canary => latest}/examples/migrating-to-tailwind.md (100%) diff --git a/docs/canary/concepts/ahead-of-time-builds.md b/docs/canary/concepts/ahead-of-time-builds.md deleted file mode 100644 index 233498b6d0c..00000000000 --- a/docs/canary/concepts/ahead-of-time-builds.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -description: | - Fresh optimize assets ahead of time, which makes pages load way quicker. ---- - -Fresh enables you to pre-optimize frontend assets before the code is deployed. -During that process the code for Islands will be compressed and optimized, so -that Fresh can send as little code as possible to the browser. Depending on the -amount of code an island needs, this process can take several seconds if done on -the fly serverside. - -Doing those optimizations ahead-of-time and deploying the already optimized -assets alongside with your code, allows Fresh to treat them as like any other -static file and can serve it immediately without any further processing. On -pages with islands, having to do no processing greatly speeds up page load -times. - -Plugins can build static assets during ahead-of-time builds. This can be used to -preprocess or generate CSS files, for example. - -## Creating an optimized build - -To have Fresh optimize all the assets, run one of the following commands: - -```sh -# As a task in newer Fresh projects -deno task build -# or invoke it manually -deno run -A dev.ts build -``` - -This will create a `_fresh` folder in the project directory. That folder -contains the optimized assets and a `snapshot.json` file which includes some -metadata for Fresh. - -Any other static files generated by plugins will be stored in the -`_fresh/static` subfolder. They will be served the same as other -[static files](/docs/concepts/static-files.md). - -> [info]: The `_fresh` folder should not be committed to the repository. Add an -> entry in the `.gitignore` file to ensure that it is not committed. Create that -> file at the root of your git repository if not present. -> -> ```gitignore .gitignore -> # Ignore fresh build directory -> _fresh/ -> ``` - -## Running Fresh with optimized assets - -When Fresh is started in non-development mode (usually via `main.ts`), Fresh -will automatically pick up optimized assets when a `_fresh` folder exists. If -found, Fresh will print the following message to the terminal: - -```sh Terminal output -Using snapshot found at /path/to/project/_fresh -``` - -## Deploying an optimized Fresh project - -To generate optimized assets whenever you push changes to the `main` branch of -your project, copy the following GitHub action and add it under -`.github/workflows/deploy.yml` in your project. - -```yml .github/workflows/deploy.yml -name: Deploy -on: - push: - branches: [main] - pull_request: - branches: main - -jobs: - deploy: - name: Deploy - runs-on: ubuntu-latest - - permissions: - id-token: write # Needed for auth with Deno Deploy - contents: read # Needed to clone the repository - - steps: - - name: Clone repository - uses: actions/checkout@v3 - - - name: Install Deno - uses: denoland/setup-deno@v1 - with: - deno-version: v1.x - - - name: Build step - run: "deno task build" # 📝 Update the build command(s) if necessary - - - name: Upload to Deno Deploy - uses: denoland/deployctl@v1 - with: - project: "example-project" # 📝 Update the deploy project name if necessary - entrypoint: "./main.ts" # 📝 Update the entrypoint if necessary -``` - -> ℹī¸ **Important:** The project name here must match the project name you've -> picked in Deno Deploy. - -On the Deno Deploy side change the GitHub integration mode to GitHub Actions. -You need to unlink first, if you have an existing project that's linked with the -"Automatic" mode. - -![Deno Deploy UI screenshot that shows the project dropdown and highlights the GitHub Action option](/docs/deno-deploy-gh-action.jpg) - -Once this is set up you're ready for your next deployment. Whenever a new PR is -merged into the `main` branch on GitHub the deploy action will be executed and -deploy the optimized assets to Deno Deploy. - -## Migrating existing projects with Plugins - -If you're using Fresh plugins, extract them into a `fresh.config.ts` file, so -that both the `dev.ts` and `main.ts` script have access to them. - -```ts fresh.config.ts -import { defineConfig } from "$fresh/server.ts"; -import twindPlugin from "$fresh/plugins/twind.ts"; -import twindConfig from "./twind.config.ts"; - -export default defineConfig({ - plugins: [twindPlugin(twindConfig)], -}); -``` - -```ts main.ts -import { start } from "$fresh/server.ts"; -import manifest from "./fresh.gen.ts"; -import config from "./fresh.config.ts"; - -await start(manifest, config); -``` - -```ts dev.ts -import dev from "$fresh/dev.ts"; -import config from "./fresh.config.ts"; - -await dev(import.meta.url, "./main.ts", config); -``` diff --git a/docs/canary/concepts/error-pages.md b/docs/canary/concepts/error-pages.md deleted file mode 100644 index e657d7df535..00000000000 --- a/docs/canary/concepts/error-pages.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -description: | - Error pages can be used to customize the page that is shown when an error occurs in the application. ---- - -Fresh supports customizing the `404 Not Found`, and the -`500 Internal Server Error` pages. These are shown when a request is made but no -matching route exists, and when a middleware, route handler, or page component -throws an error respectively. - -### 404: Not Found - -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 routes/_404.tsx -import { UnknownPageProps } from "$fresh/server.ts"; - -export default function NotFoundPage({ url }: UnknownPageProps) { - return

404 not found: {url.pathname}

; -} -``` - -#### Manually render 404 pages - -The `_404.tsx` file will be invoked automatically when no route matches the URL. -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 routes/blog/[slug].tsx -import { Handlers, PageProps } from "$fresh/server.ts"; - -export const handler: Handlers = { - async GET(req, ctx) { - const blogpost = await fetchBlogpost(ctx.params.slug); - if (!blogpost) { - return ctx.renderNotFound({ - custom: "prop", - }); - } - return ctx.render({ blogpost }); - }, -}; - -export default function BlogpostPage({ data }) { - return ( -
-

{data.blogpost.title}

- {/* rest of your page */} -
- ); -} -``` - -This can also be achieved by throwing an error, if you're uninterested in -passing specific data to your 404 page: - -```tsx -import { Handlers } from "$fresh/server.ts"; - -export const handler: Handlers = { - GET(_req, _ctx) { - throw new Deno.errors.NotFound(); - }, -}; -``` - -### 500: Internal Server Error - -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 routes/_500.tsx -import { ErrorPageProps } from "$fresh/server.ts"; - -export default function Error500Page({ error }: ErrorPageProps) { - return

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

; -} -``` diff --git a/docs/canary/concepts/plugins.md b/docs/canary/concepts/plugins.md deleted file mode 100644 index bef0e8d4543..00000000000 --- a/docs/canary/concepts/plugins.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -description: Plugins can add new functionality to Fresh without requiring significant complexity. ---- - -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 -import { start } from "$fresh/server.ts"; -import manifest from "./fresh.gen.ts"; - -import twindPlugin from "$fresh/plugins/twind.ts"; -import twindConfig from "./twind.config.js"; - -await start(manifest, { - plugins: [ - // This line configures Fresh to use the first-party twind plugin. - twindPlugin(twindConfig), - ], -}); -``` - -Currently, the only available first-party plugin is the Twind plugin. -Third-party plugins are also supported - they can be imported from any HTTP -server, like any other Deno module. - -Plugin hooks are executed in the order that the plugins are defined in the -`plugins` array. This means that the first plugin in the array will be executed -first, and the last plugin in the array will be executed last. For many plugins, -this does not matter, but for some plugins it may. - -## Creating a plugin - -Fresh plugins are in essence a collection of hooks that allow the plugin to hook -into various systems inside of Fresh. Currently only a `render` hook is -available (explained below). - -A Fresh plugin is just a JavaScript object that conforms to the -[Plugin](https://deno.land/x/fresh/server.ts?s=Plugin) interface. The only -required property of a plugin is its name. Names must only contain the -characters `a`-`z`, and `_`. - -```ts -import { Plugin } from "$fresh/server.ts"; - -const plugin: Plugin = { - name: "my_plugin", -}; -``` - -A plugin containing only a name is technically valid, but not very useful. To be -able to do anything with a plugin, it must register some hooks, middlewares, or -routes. - -### Hook: `render` - -The render hook allows plugins to: - -- Control timing of the synchronous render of a page. -- Inject additional CSS and JS into the rendered page. - -This is commonly used to set thread local variables for the duration of the -render (for example preact global context, preact option hooks, or for style -libraries like Twind). After render is complete, the plugin can inject inline -CSS and JS modules (with attached state) into the page. - -The render hook is called with the -[`PluginRenderContext`](https://deno.land/x/fresh/server.ts?s=PluginRenderContext) -object, which contains a `render()` method. This method must be invoked during -the render hook to actually render the page. It is a terminal error to not call -the `render()` method during the render hook. - -The `render()` method returns a -[`PluginRenderFunctionResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderFunctionResult) -object which contains the HTML text of the rendered page, as well as a boolean -indicating whether the page contains any islands that will be hydrated on the -client. - -The `render` hook needs to synchronously return a -[`PluginRenderResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderResult) -object. Additional CSS and JS modules can be added to be injected into the page -by adding them to `styles`, `links` and `scripts` arrays in this object. The -plugin can also replace the the HTML in side the ``-element of the page by -including a `htmlText` string in this object. - -`styles` are injected into the `` of the page as inline CSS. Each entry -can define the CSS text to inject, as well as an optional `id` for the style -tag, and an optional `media` attribute for the style tag. - -`links` are injected into the `` of the page as `` tags. A link tag -is created for each entry, with attributes from the entry's properties. - -`scripts` define JavaScript/TypeScript modules to be injected into the page. The -possibly loaded modules need to be defined up front in the `Plugin#entrypoints` -property. Each defined module must be a JavaScript/TypeScript module that has a -default export of a function that takes one (arbitrary) argument, and returns -nothing (or a promise resolving to nothing). Fresh will call this function with -the state defined in the `scripts` entry. The state can be any arbitrary JSON -serializable JavaScript value. - -For an example of a plugin that uses the `render` hook, see the first-party -[Twind plugin](https://github.com/denoland/fresh/blob/main/plugins/twind.ts). - -### Hook: `renderAsync` - -This hook is largely the same as the `render` hook, with a couple of key -differences to make asynchronous style and script generation possible. It must -asynchronously return its -[`PluginRenderResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderResult), -either from an `async/await` function or wrapped within a promise. - -The render hook is called with the -[`PluginAsyncRenderContext`](https://deno.land/x/fresh/server.ts?s=PluginAsyncRenderContext) -object, which contains a `renderAsync()` method. This method must be invoked -during the render hook to actually render the page. It is a terminal error to -not call the `renderAsync()` method during the render hook. - -This is useful for when plugins are generating styles and scripts with -asynchronous dependencies based on the `htmlText`. Unlike the synchronous render -hook, async render hooks for multiple pages can be running at the same time. -This means that unlike the synchronous render hook, you can not use global -variables to propagate state between the render hook and the renderer. - -The `renderAsync` hooks start before any page rendering occurs, and finish after -all rendering is complete -- they wrap around the underlying JSX->string -rendering, plugin `render` hooks, and the -[`RenderFunction`](https://deno.land/x/fresh/server.ts?s=RenderFunction) that -may be provided to Fresh's `start` entrypoint in the `main.ts` file. - -### Hook: `buildStart` - -This hook is run at the start of the Fresh -[ahead-of-time build task](/docs/concepts/ahead-of-time-builds). It may be -synchronous or asynchronous. - -The build start hook is called with the -[`ResolvedFreshConfig`](https://deno.land/x/fresh/src/server/types.ts?s=ResolvedFreshConfig) -object, which contains the full Fresh configuration. - -This hook may be used to generate precompiled static assets. Any files saved to -the `static` subfolder of `config.build.outDir` (typically `_fresh`) will be -served the same as other [static files](/docs/concepts/static-files). - -### Hook: `buildEnd` - -This hook is run at the end of the Fresh -[ahead-of-time build task](/docs/concepts/ahead-of-time-builds). It may be -synchronous or asynchronous. - -### Routes and Middlewares - -You can create routes and middlewares that get loaded and rendered like the -normal [routes](/docs/concepts/routes) and -[middlewares](/docs/concepts/middleware). - -The plugin routes and middlewares need a defined path in the format of a file -name without a filetype inside the routes directory(E.g. `blog/index`, -`blog/[slug]`). - -For more examples see the [Concepts: Routing](/docs/concepts/routing) page. - -To create a middleware you need to create a `MiddlewareHandler` function. - -And to create a route you can create both a Handler and/or component. - -A very basic example can be found -[here](https://github.com/denoland/fresh/blob/main/tests/fixture_plugin/utils/route-plugin.ts). - -### Islands - -Islands from plugins can be loaded by specifying a list of file paths in your -plugin. Those files will be treated by Fresh as if they had been placed inside -the `islands/` directory. They will be processed and bundled for the browser in -the same way. - -```tsx my-island-plugin.ts -import { Plugin } from "$fresh/server.ts"; - -export default function myIslandPlugin(): Plugin { - return { - name: "my-island-plugin", - islands: { - baseLocation: import.meta.url, - paths: [ - "./plugin/MyPluginIsland.tsx", - "./plugin/OtherPluginIsland.tsx", - ], - }, - }; -} -``` diff --git a/docs/canary/concepts/server-configuration.md b/docs/canary/concepts/server-configuration.md deleted file mode 100644 index 77407a20afc..00000000000 --- a/docs/canary/concepts/server-configuration.md +++ /dev/null @@ -1,212 +0,0 @@ ---- -description: | - The ability to configure the core Fresh server leads to its flexibility. ---- - -In this page we discuss how the server can be configured during startup. - -The signature of the primary method looks like this: - -```ts main.ts -export async function start(manifest: Manifest, config: FreshConfig = {}); -``` - -## Configuration - -`Manifest` comes from `fresh.gen.ts`, so nothing to do there. `config` is where -things get interesting. -[`FreshConfig`](https://deno.land/x/fresh/server.ts?s=FreshConfig) looks like -this: - -```ts -export interface FreshConfig { - build?: { - /** - * The directory to write generated files to when `dev.ts build` is run. - * This can be an absolute path, a file URL or a relative path. - */ - outDir?: string; - /** - * This sets the target environment for the generated code. Newer - * language constructs will be transformed to match the specified - * support range. See https://esbuild.github.io/api/#target - * @default {"es2022"} - */ - target?: string | string[]; - }; - render?: RenderFunction; - plugins?: Plugin[]; - staticDir?: string; - router?: RouterOptions; - server?: Partial; -} -``` - -And for completeness here are the remaining two types: - -```ts -export type RenderFunction = ( - ctx: RenderContext, - render: InnerRenderFunction, -) => void | Promise; - -export interface RouterOptions { - /** - * Controls whether Fresh will append a trailing slash to the URL. - * @default {false} - */ - trailingSlash?: boolean; - /** - * Configures the pattern of files to ignore in islands and routes. - * - * By default Fresh will ignore test files, - * for example files with a `.test.ts` or a `_test.ts` suffix. - * - * @default {/(?:[^/]*_|[^/]*\.|)test\.(?:ts|tsx|mts|js|mjs|jsx|)\/*$/} - */ - ignoreFilePattern?: RegExp; - /** - * Serve fresh from a base path instead of from the root. - * "/foo/bar" -> http://localhost:8000/foo/bar - * @default {undefined} - */ - basePath?: string; -} -``` - -## Build - -### outDir - -As the comment suggests, this can be used to configure where generated files are -written: - -```tsx -await dev(import.meta.url, "./main.ts", { - build: { - outDir: Deno.env.get("FRESH_TEST_OUTDIR") ?? undefined, - }, -}); -``` - -### target - -This should be a valid ES Build target. - -```tsx -await dev(import.meta.url, "./main.ts", { - build: { - target: "es2015", - }, -}); -``` - -## Plugins - -See the [docs](/docs/concepts/plugins) on this topic for more detail. But as a -quick example, you can do something like this to load plugins: - -```ts main.ts -await start(manifest, { plugins: [twindPlugin(twindConfig)] }); -``` - -## StaticDir - -This allows you to specify the location where your site's static assets are -stored. Here's an example: - -```ts main.ts -await start(manifest, { staticDir: "./custom_static" }); -``` - -## Render - -This is by far the most complicated option currently available. It allows you to -configure how your components get rendered. - -## RouterOptions - -### TrailingSlash - -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 main.ts -await start(manifest, { router: { trailingSlash: true } }); -``` - -### ignoreFilePattern - -By default Fresh ignores test files which are co-located next routes and -islands. If you want, you can change the pattern Fresh uses ignore these files - -### basePath - -This setting allows you to serve a Fresh app from sub-path of a domain. A value -of `/foo/bar` would serve the app from `http://localhost:8000/foo/bar` instead -of `http://localhost:8000/` for example. - -The `basePath` will be automatically applied to absolute links in your app. For -example, when the `basePath` is `/foo/bar`, linking to `/about` will -automatically become `/foo/bar/about`. - -```jsx -About; -``` - -Rendered HTML: - -```html -About -``` - -The `basePath` is also applied to the `src` and `srcset` attribute of -``-tags, the `href` attribute of `` and the `src` attribute of -`