Skip to content
Open
2 changes: 1 addition & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@
"apps/docs/public/llms/react/**/llm*.txt",
"apps/docs/markdown-pages/docs/**/**/llms.mdx",
"apps/docs/markdown-pages/docs/manual/installation.mdx",
".yarn/releases/yarn-4.12.0.cjs"
".yarn/releases/*.cjs"
]
}
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode", "chenglou92.rescript-vscode"]
"recommendations": ["oxc.oxc-vscode", "chenglou92.rescript-vscode"]
}
127 changes: 71 additions & 56 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,64 @@

## Project Overview

This is the official documentation website for the [ReScript](https://rescript-lang.org) programming language. It is primarily pre-rendered at build time, with runtime server-side rendering for `/try` through Cloudflare Pages Functions. It is built with **ReScript v12 + React 19 + React Router v8 + Vite 8 + Tailwind CSS v4**, deployed to **Cloudflare Pages**.
This monorepo contains the official documentation website for the [ReScript](https://rescript-lang.org) programming language and the interactive guide. Both use **ReScript v12 + React 19 + React Router v8 + Vite 8**. The docs site uses Tailwind CSS v4 and is primarily pre-rendered at build time, with runtime server-side rendering for `/try` through Cloudflare Pages Functions. The guide pre-renders its entry page and runs its interactive lessons in the browser, with assets served by a Cloudflare Worker.

## System Requirements

- Node.js ≥ 26
- Yarn 4.12.0 (via Corepack)
- Yarn 4.13.0 (via Corepack, pinned in `package.json` and `.yarnrc.yml`)

## Project Structure

```
app/ → React Router app shell (root layout, route definitions, route modules)
routes/ → Route components with loaders (e.g. BlogRoute.res, MdxRoute.res)
src/ → Core ReScript source code
bindings/ → Zero-cost bindings to JS libraries (@module externals)
common/ → Shared utilities, hooks, helpers (non-component modules)
components/ → Reusable React components (e.g. SidebarNav, Breadcrumbs)
ffi/ → Plain JS interop files (prefer %raw over adding new files here)
layouts/ → Page layout components (DocsLayout, SidebarPageLayout, etc.)
markdown-pages/ → MDX content (blog posts, docs, community pages, syntax-lookup)
data/ → Hand-curated data (API docs JSON, sidebar ordering)
styles/ → CSS files (Tailwind v4 config in main.css, custom utilities)
scripts/ → Build/codegen scripts (ReScript + JS)
functions/ → Cloudflare Pages Functions (e.g. OG image generation)
compilers/ → Bundled ReScript compiler versions (for the playground)
plugins/ → HighlightJS & CodeMirror plugins
public/ → Static assets (images, fonts, favicons)
__tests__/ → Vitest browser-mode tests (Playwright)
apps/docs/ → Documentation website workspace (@rescript-lang/docs)
app/ → React Router shell, layouts, and route modules
routes/ → Page components and loaders (e.g. BlogRoute.res)
src/ → Website ReScript code
bindings/ → App-specific bindings to JS libraries
common/ → Shared website utilities and hooks
components/ → Reusable React components
ffi/ → Legacy plain JS interop
layouts/ → Page layout components
markdown/ → MDX loading, compilation, and frontmatter parsing
markdown-pages/ → Blog, docs, community, and syntax-lookup MDX
data/ → Sidebar ordering and content metadata
styles/ → Tailwind v4 entry points, theme, and custom CSS
scripts/ → Build, code generation, and validation scripts
functions/ → Pages Functions for /try, /try.data, and OG images
server/ → React Router request handler for Pages Functions
public/ → Static assets (images, fonts, favicons)
__tests__/ → Vitest browser and visual tests
e2e/ → Cypress browser tests
apps/guide/ → Interactive guide workspace (@rescript-lang/guide)
packages/playground/ → Shared browser playground (@rescript-lang/playground)
packages/shared/ → Shared bindings and utilities (@rescript-lang/shared)
src/ → ReactRouter, Vitest, CodeMirror, highlighting, etc.
plugins/ → CodeMirror 6 ReScript/Reason grammar
data/api/ → Compiler-published API JSON snapshots
.github/ → CI, deployment, and screenshot workflows
```

Paths in these guidelines are relative to the repository root. Run the commands
below from there; use `yarn workspace <name> <command>` for workspace commands.
The playground loads published browser compiler bundles. Markdown example checks
use the installed `rescript` package, not a separate compiler installation.

## Key Commands

| Command | Purpose |
| ---------------- | -------------------------------------------------------------- |
| `yarn dev` | Run ReScript watcher + Vite dev server + Wrangler (parallel) |
| `yarn build` | Full production build (ReScript → scripts → Vite/React Router) |
| `yarn build:res` | ReScript compilation only |
| `yarn dev:res` | ReScript watch mode only |
| `yarn format` | Run Prettier + ReScript formatter |
| `yarn test` | Run example and href validation scripts |
| `yarn vitest` | Run Vitest browser tests with Playwright |
| `make` | Build (install deps + ReScript compile + update index) |
| Command | Purpose |
| ------------------- | ------------------------------------------------------------ |
| `yarn dev` | Run ReScript watcher + Vite dev server + Wrangler (parallel) |
| `yarn build` | Build and pre-render the docs site |
| `yarn build:guide` | Build and pre-render the interactive guide |
| `yarn build:res` | Compile ReScript across all four workspaces |
| `yarn dev:res` | ReScript watch mode only |
| `yarn format` | Run Oxfmt and format ReScript across all four workspaces |
| `yarn test` | Run example, href, and redirect validation scripts |
| `yarn vitest` | Run docs Vitest browser tests in watch mode |
| `yarn vitest:guide` | Run guide Vitest browser tests in watch mode |
| `yarn ci:test` | Run docs and guide Vitest browser tests headlessly |
| `make` | Build (install deps + ReScript compile + update index) |

## Coding Best Practices

Expand All @@ -52,11 +69,11 @@ __tests__/ → Vitest browser-mode tests (Playwright)
- `.res` files must always be capitalized (PascalCase), matching ReScript module conventions.
- Use the pipe-first operator (`->`) for chaining, which is idiomatic ReScript.
- Resolve all warnings and treat them as errors. The project has `"error": "+8"` in `rescript.json`.
- Never directly edit an `.jsx` files, you must edit the corresponding `.res` file. The `.jsx` files are generated by the ReScript compiler and will be overwritten on the next compile.
- Never directly edit generated `.jsx` files; edit the corresponding `.res` file and rebuild. Most compiler output is ignored by Git; the guide's `apps/guide/app/root.jsx` and `apps/guide/app/routes.jsx` are tracked and must be regenerated with their sources.

## ReScript Rules

- You use **ReScript v12** (latest). Ensure all suggestions match this version.
- This branch uses **ReScript v12**. Ensure all suggestions match this version.
- Ensure any produced JSX matches ReScript JSX v4 syntax (configured with `"preserve": true`).
- **Never use the `Belt` or `Js` modules** — these are legacy. Use the modern `Stdlib` / core modules instead.
- Always use the `JSON.t` type for JSON values.
Expand All @@ -78,7 +95,7 @@ __tests__/ → Vitest browser-mode tests (Playwright)

The project uses several patterns for JavaScript interop. Follow the existing conventions:

- **`@module` externals** for binding to npm packages (see `src/bindings/` for examples):
- **`@module` externals** for binding to npm packages (see `apps/docs/src/bindings/` and `packages/shared/src/` for examples):
```
@module("react-router") external useNavigate: unit => string => unit = "useNavigate"
```
Expand All @@ -87,21 +104,17 @@ The project uses several patterns for JavaScript interop. Follow the existing co
@module("react-router") @react.component
external make: (~children: React.element) => React.element = "Outlet"
```
- **`%raw()`** for inline JS when no clean binding is possible:
```
let copyToClipboard: string => bool = %raw(`function(str) { ... }`)
```
- **`%%raw()`** (double percent) for top-level side-effectful JS imports.
- `%raw` and `%%raw` embed JavaScript; use them only when specifically requested.
- **`@scope`** for binding to object properties like `process.env`.
- **`external ... = "%identity"`** for zero-cost type coercions.
- Put new bindings in `src/bindings/` as a dedicated module (e.g. `Fuse.res`, `DocSearch.res`).
- Put app-specific bindings in `apps/docs/src/bindings/` as a dedicated module (e.g. `Fuse.res`, `DocSearch.res`). Bindings shared with the guide or playground belong in `packages/shared/src/`.

## ReScript React

- This project uses **React 19** and **React Router v8** (framework mode).
- The site uses hybrid rendering: most routes are pre-rendered during the build, while `/try` is server-rendered by Cloudflare Pages Functions at runtime.
- Loaders for pre-rendered routes can access the filesystem during the build. Runtime SSR loaders, including shared loader code reachable from `/try`, execute in Cloudflare and must not depend on Node-only filesystem APIs or other build-time-only operations.
- Route modules live in `app/routes/` and export a `loader` and a `default` component.
- Docs route modules live in `apps/docs/app/routes/`; the guide's route modules live in `apps/guide/app/`. Routes can export a `loader`, `links`, and a `default` component as needed.
- Route modules **require** both a `.res` and a `.resi` (interface) file for Vite HMR to work.
- Only a single React component can be exposed from a module's JS output.

Expand Down Expand Up @@ -153,21 +166,21 @@ let default: unit => React.element

## Styling

- **Tailwind CSS v4** configured via the Vite plugin (`@tailwindcss/vite`). There is no `tailwind.config.js`.
- Tailwind design tokens and custom utilities live in `styles/_theme.css`. It is imported by `styles/main.css` and referenced by route-specific stylesheets.
- The docs site uses **Tailwind CSS v4**, configured via the Vite plugin (`@tailwindcss/vite`). Design tokens and custom utilities live in `apps/docs/styles/_theme.css`, imported by `apps/docs/styles/main.css` and referenced by route-specific stylesheets.
- The guide uses its own plain CSS in `apps/guide/styles/main.css`.
- **LightningCSS** is used as the CSS transformer.
- The project defines custom design tokens in `styles/_theme.css`:
- The project defines custom design tokens in `apps/docs/styles/_theme.css`:
- Custom colors: `gray-*`, `fire-*`, `sky-*`, `berry-*`, `water`, `turtle`, `orange-*`
- Custom font sizes: `text-11` through `text-68`
- Custom utility classes: `hl-title`, `hl-1`–`hl-5`, `body-lg`, `body-md`, `body-sm`, `captions`
- Fonts: Inter (sans), Roboto Mono (mono)
- Use existing custom utilities and design tokens. Check `styles/_theme.css` before creating new ones.
- Use existing custom utilities and design tokens. Check `apps/docs/styles/_theme.css` before creating new ones.

## Testing

- Tests use **Vitest 5** in browser mode with **Playwright** (Chromium).
- Test files live in `__tests__/` and are named `ComponentName_.test.res` (compiled to `.test.jsx`).
- Tests use custom ReScript bindings in `src/bindings/Vitest.res`.
- Browser tests live in `apps/docs/__tests__/` and `apps/guide/__tests__/`. Docs visual tests live in `apps/docs/__tests__/visual/`. Test modules are named `ComponentName_.test.res` (compiled to `.test.jsx`).
- Tests use custom ReScript bindings in `packages/shared/src/Vitest.res`.
- Tests are visual/integration tests that render components and assert visibility, interactions, and screenshots.
- Test pattern:

Expand All @@ -185,22 +198,25 @@ let default: unit => React.element

- Components requiring React Router context must be wrapped in `<MemoryRouter>`.
- Do not use `<BrowserRouter>` in tests.
- Run tests with `yarn vitest --browser.headless --run`.
- Do not update snapshots without asking for confirmation.
- Run `yarn build:res` first so the tests use current compiler output. Run `yarn vitest --browser.headless --run` for docs tests or `yarn vitest:guide --browser.headless --run` for guide tests. Run `yarn ci:test` to check both suites.
- Do not update snapshots without asking for confirmation. Approved docs visual baseline changes use the `Update Visual Regression Screenshots` GitHub Actions workflow against the feature branch; pull and review the generated commit. Local screenshot updates are blocked by the Vitest configuration.

## MDX Content

- Documentation content is in `markdown-pages/` organized by section (blog, docs, community, syntax-lookup).
- MDX is processed by `react-router-mdx` with remark/rehype plugins.
- Custom MDX components are mapped in `app/routes/MdxRoute.res` (e.g. `<Info>`, `<Warn>`, `<CodeTab>`, `<Video>`).
- Documentation content is in `apps/docs/markdown-pages/`, organized by section (blog, docs, community, syntax-lookup).
- `apps/docs/src/markdown/MdxFile.res` loads frontmatter and compiles MDX with `@mdx-js/mdx` and remark plugins. Route loaders pass the compiled content to `MdxContent`.
- `apps/docs/src/components/MdxContent.res` evaluates compiled MDX with `runSync` and maps custom components (e.g. `<Info>`, `<Warn>`, `<CodeTab>`, `<Video>`).
- Code examples in markdown use ` ```res ` (runnable), ` ```res sig ` (signature), and ` ```res prelude ` (shared context).
- `yarn test` validates examples, relative links, and redirects. For matching CI's link-check failure behavior locally, run `CI=1 yarn test`; the link checker reports findings locally but only exits unsuccessfully for them when `CI` is set. It scans the full MDX tree and does not accept a glob filter.

## Formatting & Git Hooks

- **Prettier** with the `@prettier/plugin-oxc` parser for JS formatting.
- **ReScript formatter** (`rescript format`) for `.res` files.
- **Lefthook** runs `yarn format` on pre-commit (auto-stages fixed files).
- Generated `.mjs`/`.jsx` output files from ReScript are git-tracked but excluded from Prettier.
- **Oxfmt** formats JavaScript, JSON, Markdown/MDX, CSS, and YAML, using `.oxfmtrc.json`.
- **ReScript formatter** (`rescript format`) formats `.res` and `.resi` files.
- `yarn format` runs both formatters from the repository root, covering all four workspaces. The docs workspace's `format` command delegates to the root command.
- **Lefthook** runs Oxfmt and the ReScript formatter on matching staged files in parallel, then auto-stages formatting fixes. See `lefthook.yml` for the file patterns and commands.
- Most generated `.jsx` output is ignored by Git. The two tracked guide entry modules are excluded from Oxfmt; regenerate them with the compiler.
- `.vscode/extensions.json` recommends the Oxc formatter integration and the ReScript extension.

## Pull Requests and Commits

Expand All @@ -212,6 +228,5 @@ let default: unit => React.element

- Do **not** modify generated `.jsx` / `.mjs` files directly — they are ReScript compiler output.
- Do **not** use `@genType` — the project does not use it.
- The `src/ffi/` directory is legacy; prefer `%raw` statements for new JS interop.
- The README references some outdated structures (Next.js, `pages/` directory) — ignore those references. The project has migrated to React Router v8.
- The `apps/docs/src/ffi/` directory is legacy; prefer bindings in `apps/docs/src/bindings/` or `packages/shared/src/` for new JS interop.
- When editing route files, always update both the `.res` and `.resi` files.
22 changes: 18 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,21 @@ We try to keep our contribution guidelines to a minimum. Please keep following r

The less code we write, the better. If there's a way to do rendering on the server, or enhance existing markdown files, we prefer that over client-side rendering and external loading.

We also try to keep our third-party dependencies to a minimum. We use specific frameworks to make things work (`unified`, `remark`, `mdx`, `bs-platform`, etc). Please try to keep a small JS footprint, especially for client side code (to keep the bundle size small).
We also try to keep our third-party dependencies to a minimum. The site uses ReScript v12, React, React Router, `@mdx-js/mdx`, and `remark`. Please try to keep a small JS footprint, especially for client side code (to keep the bundle size small).

### Repository layout and tooling

This is a Yarn workspace monorepo. The main website and its MDX content live in
`apps/docs/`, the interactive guide lives in `apps/guide/`, and shared code lives
in `packages/playground/` and `packages/shared/`. Run the root commands from the
repository root; use `yarn workspace <name> <command>` to target one workspace.
See the [README](README.md#project-structure-overview) for the directory map.

Use Node.js 26 or newer and the Yarn 4.13.0 release pinned by the repository via
Corepack. `yarn format` runs Oxfmt and formats ReScript across all four workspaces.
The pre-commit hook formats matching staged files and stages its fixes. Edit
ReScript sources rather than generated JavaScript; include regenerated guide
entry modules when their sources change.

### Think about the target audience & UX

Expand All @@ -72,9 +86,9 @@ Always check if there are any designs for certain UI components and think about

### Tailwind for CSS Development

We use [TailwindCSS](https://tailwindcss.com) for our component styling. Check out the [tailwind.config.js](tailwind.config.js) file for configured tailwind features, colors, border-radius values etc.. If you are not familiar with Tailwind, check out existing components for inspiration.
The docs site uses [Tailwind CSS v4](https://tailwindcss.com) through the Vite plugin for component styling. Design tokens and custom utilities live in [apps/docs/styles/\_theme.css](apps/docs/styles/_theme.css). If you are not familiar with Tailwind, check out existing components for inspiration.

We sometimes also need to fall back to common css (with tailwind `@apply` directives to enforce our style system). You can find the CSS main entrypoint in [styles/main.css](styles/main.css).
We sometimes also need to fall back to common CSS (with Tailwind `@apply` directives to enforce our style system). The docs entry point is [apps/docs/styles/main.css](apps/docs/styles/main.css). The guide uses its own plain CSS in [apps/guide/styles/main.css](apps/guide/styles/main.css).

**Only fall back to plain CSS if absolutely necessary**

Expand All @@ -84,6 +98,6 @@ Most of the stuff we want to build can be built as components with Tailwind clas

- Clone the project and follow the [README](README.md) instructions
- Always run the page locally and verify your changes (especially when working on code examples)
- When writing markdown with code examples, always run `yarn test` to prevent broken code
- When writing markdown with code examples, run `CI=1 yarn test` to check examples, relative links, and redirects with CI's link-check failure behavior. See the [README](README.md#markdown-example-and-link-checks) for targeted example checks and script working directories.
- Feel free to open `Draft PRs` when you are working on bigger features (good for visibility and asking for feedback)
- Improve code based on last feedback until the code is ready to be merged
Loading
Loading