+11
.gitignore
+11
.gitignore
+16
README.md
+16
README.md
···
···
1
+
# Fresh project
2
+
3
+
Your new Fresh project is ready to go. You can follow the Fresh "Getting
4
+
Started" guide here: https://fresh.deno.dev/docs/getting-started
5
+
6
+
### Usage
7
+
8
+
Make sure to install Deno: https://deno.land/manual/getting_started/installation
9
+
10
+
Then start the project:
11
+
12
+
```
13
+
deno task start
14
+
```
15
+
16
+
This will watch the project directory and restart as necessary.
+12
components/Button.tsx
+12
components/Button.tsx
···
···
1
+
import { JSX } from "preact";
2
+
import { IS_BROWSER } from "$fresh/runtime.ts";
3
+
4
+
export function Button(props: JSX.HTMLAttributes<HTMLButtonElement>) {
5
+
return (
6
+
<button
7
+
{...props}
8
+
disabled={!IS_BROWSER || props.disabled}
9
+
class="px-2 py-1 border-gray-500 border-2 rounded bg-white hover:bg-gray-200 transition-colors"
10
+
/>
11
+
);
12
+
}
+39
deno.json
+39
deno.json
···
···
1
+
{
2
+
"lock": false,
3
+
"tasks": {
4
+
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
5
+
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
6
+
"manifest": "deno task cli manifest $(pwd)",
7
+
"start": "deno run -A --watch=static/,routes/ dev.ts",
8
+
"build": "deno run -A dev.ts build",
9
+
"preview": "deno run -A main.ts",
10
+
"update": "deno run -A -r https://fresh.deno.dev/update ."
11
+
},
12
+
"lint": {
13
+
"rules": {
14
+
"tags": [
15
+
"fresh",
16
+
"recommended"
17
+
]
18
+
}
19
+
},
20
+
"exclude": [
21
+
"**/_fresh/*"
22
+
],
23
+
"imports": {
24
+
"$fresh/": "https://deno.land/x/fresh@1.7.3/",
25
+
"preact": "https://esm.sh/preact@10.22.0",
26
+
"preact/": "https://esm.sh/preact@10.22.0/",
27
+
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
28
+
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
29
+
"tailwindcss": "npm:tailwindcss@3.4.1",
30
+
"tailwindcss/": "npm:/tailwindcss@3.4.1/",
31
+
"tailwindcss/plugin": "npm:/tailwindcss@3.4.1/plugin.js",
32
+
"$std/": "https://deno.land/std@0.216.0/"
33
+
},
34
+
"compilerOptions": {
35
+
"jsx": "react-jsx",
36
+
"jsxImportSource": "preact"
37
+
},
38
+
"nodeModulesDir": true
39
+
}
+8
dev.ts
+8
dev.ts
+6
fresh.config.ts
+6
fresh.config.ts
+27
fresh.gen.ts
+27
fresh.gen.ts
···
···
1
+
// DO NOT EDIT. This file is generated by Fresh.
2
+
// This file SHOULD be checked into source version control.
3
+
// This file is automatically updated during development when running `dev.ts`.
4
+
5
+
import * as $_404 from "./routes/_404.tsx";
6
+
import * as $_app from "./routes/_app.tsx";
7
+
import * as $api_joke from "./routes/api/joke.ts";
8
+
import * as $greet_name_ from "./routes/greet/[name].tsx";
9
+
import * as $index from "./routes/index.tsx";
10
+
import * as $Counter from "./islands/Counter.tsx";
11
+
import type { Manifest } from "$fresh/server.ts";
12
+
13
+
const manifest = {
14
+
routes: {
15
+
"./routes/_404.tsx": $_404,
16
+
"./routes/_app.tsx": $_app,
17
+
"./routes/api/joke.ts": $api_joke,
18
+
"./routes/greet/[name].tsx": $greet_name_,
19
+
"./routes/index.tsx": $index,
20
+
},
21
+
islands: {
22
+
"./islands/Counter.tsx": $Counter,
23
+
},
24
+
baseUrl: import.meta.url,
25
+
} satisfies Manifest;
26
+
27
+
export default manifest;
+16
islands/Counter.tsx
+16
islands/Counter.tsx
···
···
1
+
import type { Signal } from "@preact/signals";
2
+
import { Button } from "../components/Button.tsx";
3
+
4
+
interface CounterProps {
5
+
count: Signal<number>;
6
+
}
7
+
8
+
export default function Counter(props: CounterProps) {
9
+
return (
10
+
<div class="flex gap-8 py-6">
11
+
<Button onClick={() => props.count.value -= 1}>-1</Button>
12
+
<p class="text-3xl tabular-nums">{props.count}</p>
13
+
<Button onClick={() => props.count.value += 1}>+1</Button>
14
+
</div>
15
+
);
16
+
}
+13
main.ts
+13
main.ts
···
···
1
+
/// <reference no-default-lib="true" />
2
+
/// <reference lib="dom" />
3
+
/// <reference lib="dom.iterable" />
4
+
/// <reference lib="dom.asynciterable" />
5
+
/// <reference lib="deno.ns" />
6
+
7
+
import "$std/dotenv/load.ts";
8
+
9
+
import { start } from "$fresh/server.ts";
10
+
import manifest from "./fresh.gen.ts";
11
+
import config from "./fresh.config.ts";
12
+
13
+
await start(manifest, config);
+27
routes/_404.tsx
+27
routes/_404.tsx
···
···
1
+
import { Head } from "$fresh/runtime.ts";
2
+
3
+
export default function Error404() {
4
+
return (
5
+
<>
6
+
<Head>
7
+
<title>404 - Page not found</title>
8
+
</Head>
9
+
<div class="px-4 py-8 mx-auto bg-[#86efac]">
10
+
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
11
+
<img
12
+
class="my-6"
13
+
src="/logo.svg"
14
+
width="128"
15
+
height="128"
16
+
alt="the Fresh logo: a sliced lemon dripping with juice"
17
+
/>
18
+
<h1 class="text-4xl font-bold">404 - Page not found</h1>
19
+
<p class="my-4">The page you were looking for doesn't exist.</p>
20
+
<a href="/" class="underline">
21
+
Go back home
22
+
</a>
23
+
</div>
24
+
</div>
25
+
</>
26
+
);
27
+
}
+16
routes/_app.tsx
+16
routes/_app.tsx
···
···
1
+
import { type PageProps } from "$fresh/server.ts";
2
+
export default function App({ Component }: PageProps) {
3
+
return (
4
+
<html>
5
+
<head>
6
+
<meta charset="utf-8" />
7
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+
<title>airport</title>
9
+
<link rel="stylesheet" href="/styles.css" />
10
+
</head>
11
+
<body>
12
+
<Component />
13
+
</body>
14
+
</html>
15
+
);
16
+
}
+21
routes/api/joke.ts
+21
routes/api/joke.ts
···
···
1
+
import { FreshContext } from "$fresh/server.ts";
2
+
3
+
// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/
4
+
const JOKES = [
5
+
"Why do Java developers often wear glasses? They can't C#.",
6
+
"A SQL query walks into a bar, goes up to two tables and says “can I join you?”",
7
+
"Wasn't hard to crack Forrest Gump's password. 1forrest1.",
8
+
"I love pressing the F5 key. It's refreshing.",
9
+
"Called IT support and a chap from Australia came to fix my network connection. I asked “Do you come from a LAN down under?”",
10
+
"There are 10 types of people in the world. Those who understand binary and those who don't.",
11
+
"Why are assembly programmers often wet? They work below C level.",
12
+
"My favourite computer based band is the Black IPs.",
13
+
"What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.",
14
+
"An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.",
15
+
];
16
+
17
+
export const handler = (_req: Request, _ctx: FreshContext): Response => {
18
+
const randomIndex = Math.floor(Math.random() * JOKES.length);
19
+
const body = JOKES[randomIndex];
20
+
return new Response(body);
21
+
};
+5
routes/greet/[name].tsx
+5
routes/greet/[name].tsx
+25
routes/index.tsx
+25
routes/index.tsx
···
···
1
+
import { useSignal } from "@preact/signals";
2
+
import Counter from "../islands/Counter.tsx";
3
+
4
+
export default function Home() {
5
+
const count = useSignal(3);
6
+
return (
7
+
<div class="px-4 py-8 mx-auto bg-[#86efac]">
8
+
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
9
+
<img
10
+
class="my-6"
11
+
src="/logo.svg"
12
+
width="128"
13
+
height="128"
14
+
alt="the Fresh logo: a sliced lemon dripping with juice"
15
+
/>
16
+
<h1 class="text-4xl font-bold">Welcome to Fresh</h1>
17
+
<p class="my-4">
18
+
Try updating this message in the
19
+
<code class="mx-2">./routes/index.tsx</code> file, and refresh.
20
+
</p>
21
+
<Counter count={count} />
22
+
</div>
23
+
</div>
24
+
);
25
+
}
static/favicon.ico
static/favicon.ico
This is a binary file and will not be displayed.
+6
static/logo.svg
+6
static/logo.svg
···
···
1
+
<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+
<path d="M34.092 8.845C38.929 20.652 34.092 27 30 30.5c1 3.5-2.986 4.222-4.5 2.5-4.457 1.537-13.512 1.487-20-5C2 24.5 4.73 16.714 14 11.5c8-4.5 16-7 20.092-2.655Z" fill="#FFDB1E"/>
3
+
<path d="M14 11.5c6.848-4.497 15.025-6.38 18.368-3.47C37.5 12.5 21.5 22.612 15.5 25c-6.5 2.587-3 8.5-6.5 8.5-3 0-2.5-4-5.183-7.75C2.232 23.535 6.16 16.648 14 11.5Z" fill="#fff" stroke="#FFDB1E"/>
4
+
<path d="M28.535 8.772c4.645 1.25-.365 5.695-4.303 8.536-3.732 2.692-6.606 4.21-7.923 4.83-.366.173-1.617-2.252-1.617-1 0 .417-.7 2.238-.934 2.326-1.365.512-4.223 1.29-5.835 1.29-3.491 0-1.923-4.754 3.014-9.122.892-.789 1.478-.645 2.283-.645-.537-.773-.534-.917.403-1.546C17.79 10.64 23 8.77 25.212 8.42c.366.014.82.35.82.629.41-.14 2.095-.388 2.503-.278Z" fill="#FFE600"/>
5
+
<path d="M14.297 16.49c.985-.747 1.644-1.01 2.099-2.526.566.121.841-.08 1.29-.701.324.466 1.657.608 2.453.701-.715.451-1.057.852-1.452 2.106-1.464-.611-3.167-.302-4.39.42Z" fill="#fff"/>
6
+
</svg>