Skip to content

Commit 5e12e05

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/cjs-vite-interop
# Conflicts: # packages/plugin-vite/src/plugins/patches/commonjs.ts # packages/plugin-vite/src/plugins/patches/commonjs_test.ts
2 parents c31b7c6 + 9875146 commit 5e12e05

328 files changed

Lines changed: 11112 additions & 3699 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CONTRIBUTING.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Contributing Guidelines
22

3-
## Submitting a pull request
3+
For the full contributing guide, see the
4+
[Fresh documentation](https://fresh.deno.dev/docs/latest/contributing).
45

5-
First, please be sure to ensure `deno task ok` is run and successfully passes.
6+
## PR title format
7+
8+
PR titles must be all lowercase and start with one of the following prefixes:
9+
10+
- `feat:` - new features
11+
- `fix:` - bug fixes
12+
- `test:` - test additions or changes
13+
- `chore:` - maintenance, refactoring, docs
14+
- `ci:` - CI/CD changes
15+
16+
Example: `feat: add middleware compilation`
17+
18+
## Before submitting
19+
20+
Run `deno task ok` and ensure it passes.

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ on:
77
branches: [main]
88

99
jobs:
10+
pr-title:
11+
if: github.event_name == 'pull_request'
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Validate PR title
15+
env:
16+
TITLE: ${{ github.event.pull_request.title }}
17+
run: |
18+
if ! echo "$TITLE" | grep -qP '^(chore|ci|docs|feat|fix|perf|refactor|test):'; then
19+
echo "::error::PR title must start with chore:, ci:, docs:, feat:, fix:, perf:, refactor: or test:"
20+
exit 1
21+
fi
22+
1023
test:
1124
runs-on: ${{ matrix.os }}
1225
timeout-minutes: 10
@@ -61,3 +74,7 @@ jobs:
6174
- name: Build fresh.deno.dev
6275
if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'
6376
run: deno task build-www
77+
78+
- name: Check links
79+
if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'
80+
run: deno task check:links

.github/workflows/deploy.yml

Lines changed: 0 additions & 40 deletions
This file was deleted.

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
uses: denoland/setup-deno@v2
2323
with:
2424
cache: true
25+
deno-version: canary
2526

2627
- name: Install dependencies
2728
run: deno install

AGENTS.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Agents
2+
3+
## Repository Overview
4+
5+
Fresh is a web framework for Deno built on Preact. This is a **Deno monorepo**
6+
with workspace members in `packages/*` and `www/`.
7+
8+
### Packages
9+
10+
- **`packages/fresh/`** (`@fresh/core`): Core framework — routing, rendering,
11+
islands, build cache, middlewares, and client/server runtime.
12+
- **`packages/plugin-vite/`** (`@fresh/plugin-vite`): Vite integration plugin
13+
with dev server, SSR/client builds, and HMR.
14+
- **`packages/init/`** (`@fresh/init`): Project scaffolding
15+
(`deno run -Ar jsr:@fresh/init`).
16+
- **`packages/update/`** (`@fresh/update`): Automated Fresh 1.x to 2.x migration
17+
tool using ts-morph for AST transforms.
18+
- **`packages/build-id/`** (`@fresh/build-id`): Build/deployment ID generation.
19+
- **`packages/plugin-tailwindcss/`** (`@fresh/plugin-tailwind`): TailwindCSS v4
20+
plugin.
21+
- **`packages/plugin-tailwindcss-v3/`** (`@fresh/plugin-tailwind-v3`): Legacy
22+
TailwindCSS v3 plugin.
23+
- **`packages/examples/`** (`@fresh/examples`): Example components for tests.
24+
25+
### Other directories
26+
27+
- **`www/`**: Documentation website (fresh.deno.dev), built with Fresh + Vite +
28+
Tailwind. Has its own routes, islands, and vite.config.ts.
29+
- **`docs/`**: Markdown documentation organized by version (`latest/`, `1.x/`,
30+
`canary/`).
31+
- **`tools/`**: `release.ts` (version bumping), `check_docs.ts` (doc
32+
validation), `check_links.ts` (link checker).
33+
- **`vendor/`**: Vendored dependencies (`"vendor": true` in deno.json).
34+
35+
## Git Workflow
36+
37+
- To check out a PR branch, use `gh pr checkout <pr-number>`. Do not set up
38+
remotes manually.
39+
- Always run `deno fmt` before pushing.
40+
- Do not commit `deno.lock` changes unless the PR is specifically about updating
41+
dependencies. Lockfile diffs tend to be noisy and environment-specific.
42+
- **Never amend commits or force push.** Always create new commits.
43+
44+
## Development
45+
46+
- Run `deno task ok` before pushing — it runs the full local CI check (fmt,
47+
lint, type check, tests).
48+
- Run `deno install` if you get missing dependency errors.
49+
- Tests: `deno task test` (all tests, parallel). Tests use `@std/expect` for
50+
assertions and `linkedom` for DOM testing.
51+
- JSX is configured in "precompile" mode with Preact as the import source.
52+
53+
### Lockfile quirks
54+
55+
The lockfile contains remote specifiers pointing to `refs/heads/main` (e.g.
56+
`raw.githubusercontent.com/.../refs/heads/main/...`). These hashes go stale when
57+
upstream pushes. When that happens, manually update the hash in `deno.lock`
58+
since `deno cache --reload` cannot fix it (see
59+
https://github.com/denoland/deno/issues/32991).
60+
61+
## Architecture
62+
63+
### Request lifecycle
64+
65+
1. `App.handler()` receives an HTTP request (`app.ts`)
66+
2. URL is parsed and normalized (double slashes removed)
67+
3. `UrlPatternRouter.match()` finds the matching route — static routes are
68+
checked first via direct `Map` lookup, then dynamic routes via `URLPattern`
69+
4. A `Context` is created with request, params, and build cache
70+
5. The middleware chain executes (built backwards as nested closures)
71+
6. `ctx.render()` composes layouts and app wrapper around the page component
72+
7. Preact's `renderToString()` generates HTML, with option hooks detecting
73+
islands along the way
74+
8. `FreshScripts` component emits the inline boot script with island imports and
75+
serialized props
76+
9. Response is returned with HTML and `Link` modulepreload headers
77+
78+
### Island architecture
79+
80+
Islands are interactive Preact components that hydrate on the client while the
81+
rest of the page stays static HTML.
82+
83+
**Server side** (`runtime/server/preact_hooks.ts`):
84+
85+
- Preact's diff hook intercepts every VNode during SSR
86+
- When a component exists in `buildCache.islandRegistry`, it's wrapped in HTML
87+
comment markers: `<!--frsh:island:NAME:PROPSIDX:KEY-->...<!--/frsh:island-->`
88+
- Island props are collected into a `RenderState.islandProps[]` array
89+
- JSX element props become **slots** — stored in `<template>` elements and
90+
replaced with symbolic references
91+
92+
**Client side** (`runtime/client/reviver.ts`):
93+
94+
- The `boot()` function is called from an inline `<script type="module">`
95+
- DOM is walked to find `<!--frsh:island:...-->` comment markers
96+
- Props are deserialized with custom handlers: signals become reactive, slots
97+
become VNode references
98+
- Each island is hydrated via `render(h(component, props), container)` using
99+
`scheduler.postTask()` for non-blocking hydration
100+
101+
### Routing
102+
103+
Filesystem paths are converted to URL patterns (`router.ts`, `fs_routes.ts`):
104+
105+
- `/routes/blog/[id].tsx` becomes `/blog/:id`
106+
- `/routes/blog/[...rest].tsx` becomes `/blog/:rest*`
107+
- `/routes/(group)/page.tsx` becomes `/page` (groups are transparent)
108+
- `/routes/[[id]].tsx` becomes `/:id?` (optional segment)
109+
110+
Routes form a **segment tree** (`segments.ts`) where each level accumulates
111+
middlewares, layouts, and error handlers. When a route matches, the tree is
112+
walked from root to leaf to build the full middleware chain.
113+
114+
### Build system
115+
116+
Two build paths exist:
117+
118+
- **esbuild-based** (`dev/builder.ts`, `dev/esbuild.ts`): The native Fresh
119+
builder. Discovers islands, creates entry points per island + a
120+
`fresh-runtime` entry, bundles with esbuild-wasm (splitting, tree-shaking, ESM
121+
output). Output goes to `/_fresh/js/{BUILD_ID}/`.
122+
- **Vite-based** (`plugin-vite/`): Uses Vite's environment API for dual
123+
client/SSR builds. Provides the same island discovery and bundling through
124+
Vite's plugin system with HMR in dev.
125+
126+
Both produce: separate island bundles, a client runtime entry, static assets
127+
with content hashing, and a BUILD_ID for cache busting.
128+
129+
Build caches come in two flavors:
130+
131+
- `MemoryBuildCache` / `DiskBuildCache` for development (live rebuilds)
132+
- `ProdBuildCache` for production (snapshot-based, read from `_fresh/`)
133+
134+
### Partials
135+
136+
`<Partial>` components enable incremental page updates without full reloads.
137+
They are wrapped with markers (`<!--frsh:partial:{name}:{mode}:{key}-->`) and
138+
support `replace`, `append`, and `prepend` modes. Elements with `f-client-nav`
139+
enable client-side navigation that fetches and swaps partials instead of full
140+
page loads.
141+
142+
## CI
143+
144+
CI runs on every PR against `main` across a matrix of Deno v2.x + canary on
145+
macOS, Windows, and Ubuntu. Steps:
146+
147+
1. `deno install`
148+
2. `deno fmt --check` (Ubuntu + v2.x only)
149+
3. `deno lint` (Ubuntu + v2.x only)
150+
4. Spell-check via `typos` (Ubuntu + v2.x only)
151+
5. `deno task check:types` (all platforms)
152+
6. `deno task test` (all platforms)
153+
7. `deno task check:docs` (all platforms)
154+
8. `deno task build-www` (Ubuntu + v2.x only)
155+
156+
Publishing to JSR happens automatically on push to `main` via `deno publish`.

deno.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"check:types": "deno check --allow-import",
1717
"check:docs": "deno run -A tools/check_docs.ts",
1818
"ok": "deno fmt --check && deno lint && deno task check:types && deno task test",
19+
"check:links": "deno run -A tools/check_links.ts",
1920
"test:www": "deno test -A www/main_test.*",
2021
"release": "deno run -A tools/release.ts"
2122
},
@@ -47,20 +48,20 @@
4748
"@std/collections": "jsr:@std/collections@^1.1.2",
4849
"@std/dotenv": "jsr:@std/dotenv@^0.225.5",
4950
"@std/http": "jsr:@std/http@^1.0.15",
51+
"@std/net": "jsr:@std/net@^1.0.6",
5052
"@std/uuid": "jsr:@std/uuid@^1.0.7",
5153
"@supabase/postgrest-js": "npm:@supabase/postgrest-js@^1.21.4",
5254
"@types/mime-db": "npm:@types/mime-db@^1.43.6",
5355
"@types/node": "npm:@types/node@^24.3.0",
5456
"@types/pg": "npm:@types/pg@^8.15.5",
5557
"@types/prismjs": "npm:@types/prismjs@^1.26.5",
56-
"docsearch": "https://esm.sh/@docsearch/js@3.5.2?target=es2020",
58+
"docsearch": "npm:@docsearch/js@^3.5.2",
5759
"esbuild": "npm:esbuild@0.25.7",
5860
"esbuild-wasm": "npm:esbuild-wasm@0.25.7",
5961
"fresh": "jsr:@fresh/core@^2.0.0",
6062
"mime-db": "npm:mime-db@^1.54.0",
6163
"preact": "npm:preact@^10.28.2",
6264
"preact-render-to-string": "npm:preact-render-to-string@^6.6.5",
63-
"$ga4": "https://raw.githubusercontent.com/denoland/ga4/main/mod.ts",
6465
"@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0",
6566
"@preact/signals": "npm:@preact/signals@^2.5.1",
6667
"@std/encoding": "jsr:@std/encoding@1",
@@ -73,7 +74,7 @@
7374
"@std/semver": "jsr:@std/semver@1",
7475
"@std/streams": "jsr:@std/streams@1",
7576

76-
"@astral/astral": "jsr:@astral/astral@^0.5.5",
77+
"@astral/astral": "jsr:@astral/astral@^0.5.6",
7778
"@marvinh-test/fresh-island": "jsr:@marvinh-test/fresh-island@^0.0.3",
7879
"linkedom": "npm:linkedom@^0.18.10",
7980
"@std/async": "jsr:@std/async@^1.0.13",
@@ -86,11 +87,11 @@
8687
"tailwindcss": "npm:tailwindcss@^4.1.10",
8788
"postcss": "npm:postcss@8.5.6",
8889

89-
"ts-morph": "npm:ts-morph@^26.0.0",
90+
"ts-morph": "npm:ts-morph@^27.0.2",
9091

9192
"@std/front-matter": "jsr:@std/front-matter@^1.0.5",
9293
"github-slugger": "npm:github-slugger@^2.0.0",
93-
"imagescript": "https://deno.land/x/imagescript@1.3.0/mod.ts",
94+
"imagescript": "jsr:@matmen/imagescript@^1.3.0",
9495
"marked": "npm:marked@^15.0.11",
9596
"marked-mangle": "npm:marked-mangle@^1.1.9",
9697
"prismjs": "npm:prismjs@^1.29.0",

0 commit comments

Comments
 (0)