Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update canary for Fresh 2 #2558

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/canary/build-and-deploy/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
description: |
Building your Fresh app for production.
---

Fresh comes with built-in capabilities to compress assets to improve page load
speed.

## Building

You can build your Fresh app by invoking the `deno task build` script. This will
run the `dev.ts` file, which will then call Fresh's `Builder` class and generate
optimized bundles for the browser. Put any additional tasks that should be done
to prepare your app for deployment here. You can check if the build task was
invoke by checking for `Deno.args.includes("build")`.

Here is an example what the `dev.ts` file looks like for the Fresh documentation
website:

```ts dev.ts
import { Builder } from "fresh/dev";
import { app } from "./main.ts";
import { tailwind } from "@fresh/plugin-tailwind";

const builder = new Builder({ target: "safari12" });
tailwind(builder, app, {});

if (Deno.args.includes("build")) {
await builder.build(app);
} else {
await builder.listen(app);
}
```

## Preview your app

You can preview your production app locally by running the `deno task start` or
running `deno run -A main.ts` directly.
21 changes: 21 additions & 0 deletions docs/canary/build-and-deploy/deno-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
description: |
Deploy Fresh to Deno Deploy which is an optimized edge platform.
---

[Deno Deploy](https://deno.com/deploy) is a hassle-free deployment platform for
serverless TypeScript/JavaScript applications created by us. Fresh works best
with that.

## Creating a new Deploy project

Head over to your [Deno Deploy Dashboard](https://dash.deno.com/projects) and
click on the "New Project" button. Select the GitHub repository of your project
and the branch you want to deploy from. The default branch is typically `main`
for production deployments. Follow the rest of the wizard and finish deployment
by clicking the "Deploy Project" button at the bottom. Each time new code lands
in the `main` branch, a new deployment will be made to update your website
automatically.

> [info]: Deno deploy will automatically integrate with GitHub to create preview
> deployments for every PR.
54 changes: 54 additions & 0 deletions docs/canary/build-and-deploy/docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
description: |
Deploy Fresh via Docker yourself.
---

You can deploy Fresh to any platform that can run Docker containers. Docker is a
tool to containerize projects and portably run them on any supported platform.

## Creating a Docker image

When packaging your Fresh app for Docker, it is important that you set the
`DENO_DEPLOYMENT_ID` environment variable in your container. This variable needs
to be set to an opaque string ID that represents the version of your application
that is currently being run. This could be a Git commit hash, or a hash of all
files in your project. It is critical for the function of Fresh that this ID
changes when _any_ file in your project changes - if it doesn't, incorrect
caching **will** cause your project to not function correctly.

Here is an example `Dockerfile` for a Fresh project:

```dockerfile Dockerfile
# Pick the latest deno version here
FROM denoland/deno:1.44.4

ARG GIT_REVISION
ENV DENO_DEPLOYMENT_ID=${GIT_REVISION}

WORKDIR /app

COPY . .
RUN deno cache main.ts

EXPOSE 8000

CMD ["run", "-A", "main.ts"]
```

To build your Docker image inside of a Git repository:

```sh Terminal
$ docker build --build-arg GIT_REVISION=$(git rev-parse HEAD) -t my-fresh-app .
```

Then run your Docker container:

```sh Terminal
$ docker run -t -i -p 80:8000 my-fresh-app
```

To deploy to a cloud provider, push it to a container registry and follow their
documentation.

- [Amazon Web Services](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-container-image.html#create-container-image-push-ecr)
- [Google Cloud](https://cloud.google.com/container-registry/docs/pushing-and-pulling)
79 changes: 79 additions & 0 deletions docs/canary/concepts/app-wrapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
description: |
Add a global app wrapper to provide common meta tags or context for application routes.
---

An app wrapper is defined in an `_app.tsx` file in `routes/` folder and is
typically used to create the outer structure of an HTML document. It must
contain a default export that is a regular Preact component. Only one such
wrapper is allowed per application.

The component to be wrapped is received via props, in addition to a few other
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 { define } from "../utils/state.ts";

export default define.page(function App({ Component, state }) {
// do something with state here
return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Fresh app</title>
</head>
<body>
<Component />
</body>
</html>
);
});
```

## Async app wrapper

Similar to routes and layouts, the app wrapper can be made asynchronous. This
changes the function signature so that the first argument is the `Request`
instance and the second one is the `FreshContext`.

```tsx routes/_app.tsx
import { define } from "../utils/state.ts";

export default define.page(async function App(props) {
const data = await somehowLoadData();

return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Fresh app</title>
</head>
<body>
<h1>Hello {data.name}</h1>
<props.Component />
</body>
</html>
);
});
```

## Disabling the app wrapper

Rendering the app wrapper can be skipped on a route or layout basis. To do that,
set `skipAppWrapper: true` to the layout or route config.

```tsx routes/my-special-route.tsx
import { RouteConfig } from "fresh";

export const config: RouteConfig = {
skipAppWrapper: true, // Skip the app wrapper during rendering
};

export default define.page(function Page() {
// ...
});
```
113 changes: 113 additions & 0 deletions docs/canary/concepts/forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
description: |
Robustly handle user inputs using HTML `<form>` elements client side, and form
submission handlers server side.
---

For stronger resiliency and user experience, Fresh relies on native browser
support for form submissions with the HTML `<form>` element.

In the browser, a `<form>` submit will send an HTML action (usually `GET` or
`POST`) to the server, which responds with a new page to render.

## POST request with `application/x-www-form-urlencoded`

Forms typically submit as a `GET` request with data encoded in the URL's search
parameters, or as a `POST` request with either an
`application/x-www-form-urlencoded` or `multipart/form-data` body.

This example demonstrates how to handle `application/x-www-form-urlencoded`
`<form>` submissions:

```tsx routes/subscribe.tsx
import { define } from "../utils/state.ts";

export const handler = define.handler({
async POST(req, ctx) {
const form = await req.formData();
const email = form.get("email")?.toString();

// Add email to list.

// Redirect user to thank you page.
const headers = new Headers();
headers.set("location", "/thanks-for-subscribing");
return new Response(null, {
status: 303, // See Other
headers,
});
},
});

export default define.page<typeof handler>(function Subscribe() {
return (
<form method="post">
<input type="email" name="email" value="" />
<button type="submit">Subscribe</button>
</form>
);
});
```

When the user submits the form, Deno will retrieve the `email` value using the
request's `formData()` method, add the email to a list, and redirect the user to
a thank you page.

## Handling file uploads

File uploads can be handled in a very similar manner to the example above. Note
that this time, we have to explicitly declare the form's encoding to be
`multipart/form-data`.

```tsx routes/subscribe.tsx
import { page } from "fresh";

export const handler = {
async GET(req, ctx) {
return page({ data: { message: null } });
},
async POST(req, ctx) {
const form = await req.formData();
const file = form.get("my-file") as File;

if (!file) {
return page({
data: {
message: `Please try again`,
},
});
}

const name = file.name;
const contents = await file.text();

console.log(contents);

return page({
data: {
message: `${name} uploaded!`,
},
});
},
};

export default define.page<typeof handler>(function Upload(props) {
const { message } = props.data;
return (
<>
<form method="post" encType="multipart/form-data">
<input type="file" name="my-file" />
<button type="submit">Upload</button>
</form>
{message ? <p>{message}</p> : null}
</>
);
});
```

## A note of caution

These examples are simplified to demonstrate how Deno and Fresh handle HTTP
requests. In the Real World™, you'll want to validate your data (_especially the
file type_) and protect against cross-site request forgery. Consider yourself
warned.
Loading
Loading