toiljs 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +315 -1
- package/assets/logo.svg +37 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/configure.js +10 -4
- package/build/cli/create.js +60 -32
- package/build/cli/diagnostics.d.ts +55 -0
- package/build/cli/diagnostics.js +333 -0
- package/build/cli/doctor.d.ts +6 -0
- package/build/cli/doctor.js +249 -0
- package/build/cli/index.js +26 -0
- package/build/cli/proc.d.ts +5 -0
- package/build/cli/proc.js +20 -0
- package/build/cli/ui.d.ts +1 -0
- package/build/cli/ui.js +1 -0
- package/build/cli/update.d.ts +7 -0
- package/build/cli/update.js +117 -0
- package/build/cli/updates.d.ts +10 -0
- package/build/cli/updates.js +45 -0
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/error-overlay.js +1 -1
- package/build/client/head/metadata.js +3 -1
- package/build/client/index.d.ts +5 -1
- package/build/client/index.js +2 -0
- package/build/client/navigation/navigation.js +1 -1
- package/build/client/routing/Router.js +2 -2
- package/build/client/search/search.d.ts +26 -0
- package/build/client/search/search.js +101 -0
- package/build/client/search/use-page-search.d.ts +8 -0
- package/build/client/search/use-page-search.js +21 -0
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/generate.js +35 -26
- package/build/compiler/index.d.ts +2 -0
- package/build/compiler/index.js +1 -0
- package/build/compiler/pages.d.ts +8 -0
- package/build/compiler/pages.js +37 -0
- package/build/compiler/plugin.js +3 -1
- package/build/compiler/prerender.d.ts +1 -0
- package/build/compiler/prerender.js +11 -5
- package/build/compiler/seo.js +10 -3
- package/build/compiler/vite.js +7 -0
- package/build/io/.tsbuildinfo +1 -1
- package/examples/basic/client/components/Header.tsx +43 -38
- package/examples/basic/client/components/HoneycombBackground.tsx +223 -230
- package/examples/basic/client/layout.tsx +4 -1
- package/examples/basic/client/public/index.html +18 -16
- package/examples/basic/client/routes/(legal)/privacy.tsx +18 -0
- package/examples/basic/client/routes/(legal)/terms.tsx +15 -0
- package/examples/basic/client/routes/about.tsx +21 -19
- package/examples/basic/client/routes/blog/[id].tsx +26 -12
- package/examples/basic/client/routes/features/actions.tsx +67 -0
- package/examples/basic/client/routes/features/error/error.tsx +16 -0
- package/examples/basic/client/routes/features/error/index.tsx +27 -0
- package/examples/basic/client/routes/features/head.tsx +38 -0
- package/examples/basic/client/routes/features/index.tsx +83 -0
- package/examples/basic/client/routes/features/realtime.tsx +34 -0
- package/examples/basic/client/routes/features/script.tsx +31 -0
- package/examples/basic/client/routes/features/seo.tsx +39 -0
- package/examples/basic/client/routes/features/template/b.tsx +14 -0
- package/examples/basic/client/routes/features/template/index.tsx +20 -0
- package/examples/basic/client/routes/features/template/template.tsx +16 -0
- package/examples/basic/client/routes/files/[[...slug]].tsx +21 -0
- package/examples/basic/client/routes/gallery/@modal/(.)photo/[id].tsx +23 -0
- package/examples/basic/client/routes/gallery/index.tsx +42 -0
- package/examples/basic/client/routes/gallery/layout.tsx +13 -0
- package/examples/basic/client/routes/gallery/photo/[id].tsx +18 -0
- package/examples/basic/client/routes/get-started.tsx +157 -84
- package/examples/basic/client/routes/index.tsx +137 -87
- package/examples/basic/client/routes/loader-demo/index.tsx +59 -50
- package/examples/basic/client/routes/search.tsx +61 -0
- package/examples/basic/client/routes/test.tsx +7 -8
- package/examples/basic/client/styles/main.css +624 -552
- package/examples/basic/client/toil.tsx +2 -4
- package/package.json +3 -2
- package/presets/eslint.js +10 -3
- package/src/cli/configure.ts +363 -353
- package/src/cli/create.ts +563 -530
- package/src/cli/diagnostics.ts +421 -0
- package/src/cli/doctor.ts +318 -0
- package/src/cli/features.ts +166 -160
- package/src/cli/index.ts +242 -211
- package/src/cli/proc.ts +30 -0
- package/src/cli/ui.ts +111 -103
- package/src/cli/update.ts +150 -0
- package/src/cli/updates.ts +69 -0
- package/src/client/components/Image.tsx +91 -89
- package/src/client/dev/error-overlay.tsx +193 -197
- package/src/client/head/metadata.ts +94 -92
- package/src/client/index.ts +79 -64
- package/src/client/navigation/Link.tsx +94 -100
- package/src/client/navigation/navigation.ts +215 -218
- package/src/client/routing/Router.tsx +210 -193
- package/src/client/routing/hooks.ts +110 -114
- package/src/client/routing/lazy.ts +77 -81
- package/src/client/search/search.ts +189 -0
- package/src/client/search/use-page-search.ts +73 -0
- package/src/compiler/config.ts +173 -171
- package/src/compiler/fonts.ts +89 -87
- package/src/compiler/generate.ts +378 -364
- package/src/compiler/image-report.ts +88 -85
- package/src/compiler/index.ts +2 -0
- package/src/compiler/pages.ts +70 -0
- package/src/compiler/plugin.ts +51 -47
- package/src/compiler/prerender.ts +152 -130
- package/src/compiler/routes.ts +132 -131
- package/src/compiler/seo.ts +381 -356
- package/src/compiler/vite.ts +155 -130
- package/src/io/FastSet.ts +99 -96
- package/test/configure.test.ts +94 -90
- package/test/doctor.test.ts +140 -0
- package/test/dom/Image.test.tsx +73 -46
- package/test/dom/Script.test.tsx +48 -45
- package/test/dom/action.test.tsx +146 -129
- package/test/dom/error-overlay.test.tsx +44 -44
- package/test/dom/loader.test.tsx +2 -2
- package/test/dom/revalidate.test.tsx +1 -1
- package/test/dom/route-head.test.tsx +35 -2
- package/test/dom/slot.test.tsx +131 -109
- package/test/dom/view-transitions.test.tsx +53 -51
- package/test/features.test.ts +149 -142
- package/test/fonts.test.ts +28 -26
- package/test/head.test.ts +45 -35
- package/test/metadata.test.ts +42 -41
- package/test/pages.test.ts +105 -0
- package/test/prerender.test.ts +54 -46
- package/test/search.test.ts +114 -0
- package/test/seo.test.ts +164 -142
- package/test/slot-layouts.test.ts +69 -0
- package/test/update.test.ts +44 -0
package/README.md
CHANGED
|
@@ -1 +1,315 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.svg" alt="ToilJS" width="128" height="128" />
|
|
4
|
+
|
|
5
|
+
# ToilJS
|
|
6
|
+
|
|
7
|
+
### Everything React forgot to ship.
|
|
8
|
+
|
|
9
|
+
<sub>Fast by design. Architecture chosen for hyper scale: 50 Gbit/s on commodity hardware.</sub>
|
|
10
|
+
|
|
11
|
+
<br/>
|
|
12
|
+
|
|
13
|
+
[](https://www.npmjs.com/package/toiljs)
|
|
14
|
+
[](https://www.typescriptlang.org/)
|
|
15
|
+
[](https://react.dev/)
|
|
16
|
+
[](#built-for-scale)
|
|
17
|
+
[](./LICENSE)
|
|
18
|
+
|
|
19
|
+
<br/>
|
|
20
|
+
|
|
21
|
+
<img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
|
|
22
|
+
<img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
23
|
+
<img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
|
|
24
|
+
<img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
React gives you a renderer and leaves the rest to you: a router, a bundler, data fetching, SEO, an image pipeline, a server. ToilJS is all of it, already wired.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx toiljs create my-app
|
|
34
|
+
cd my-app
|
|
35
|
+
npm run dev
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Drop a `.tsx` file in `client/routes/` and it is a route: typed, code-split, prefetched, data loaded before render. The `server/` compiles to WebAssembly and self-hosts on uWebSockets. You configured nothing.
|
|
39
|
+
|
|
40
|
+
## Built for scale
|
|
41
|
+
|
|
42
|
+
The backend is the point. `server/` is [ToilScript](https://www.npmjs.com/package/toilscript) compiled to a single WebAssembly module (Binaryen), and `toiljs start` self-hosts the app on [hyper-express](https://github.com/kartikk221/hyper-express), backed by [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js), the same core that serves millions of HTTP requests per second.
|
|
43
|
+
|
|
44
|
+
<div align="center">
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+

|
|
48
|
+

|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
- **WebAssembly compute.** Your server logic runs as native-speed WASM, not interpreted JS.
|
|
54
|
+
- **Binary on the wire.** The client and server share `BinaryWriter` / `BinaryReader` and `FastMap` / `FastSet`, so you move bytes, not JSON.
|
|
55
|
+
- **HTTP/3 and WebTransport** over QUIC, low-latency streaming without the TCP head-of-line tax.
|
|
56
|
+
- **Built for 50 Gbit/s on commodity hardware** and millions of requests per second, not toy demos.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// server/index.ts
|
|
60
|
+
export function add(a: i32, b: i32): i32 {
|
|
61
|
+
return a + b;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## On by default
|
|
66
|
+
|
|
67
|
+
Every one of these works the moment you run `create`. No plugins to install, no config to write:
|
|
68
|
+
|
|
69
|
+
- **Build-time SEO for an SPA**: prerendered `<head>`, `robots.txt`, `sitemap.xml`, `llms.txt`
|
|
70
|
+
- **AI-crawler rules**: per-bot `robots.txt` plus `llms.txt`, one switch
|
|
71
|
+
- **Image optimization on**: imports and plain `<img>` become resized webp
|
|
72
|
+
- **Fonts preloaded** at build
|
|
73
|
+
- **Typed routes**: `href` and `params` checked against your real files
|
|
74
|
+
- **Loaders and mutations** with caching and revalidation, no data library, no `useEffect` fetching
|
|
75
|
+
- **Parallel and intercepting routes** for modals and dashboards
|
|
76
|
+
- **Instant navigation** and **animated view transitions**
|
|
77
|
+
- **WebAssembly backend** on uWebSockets, with **binary IO** on both sides
|
|
78
|
+
|
|
79
|
+
Anywhere else, that is a dozen packages and their config. Most teams wire three and ship the rest half-done.
|
|
80
|
+
|
|
81
|
+
## AI-ready
|
|
82
|
+
|
|
83
|
+
ToilJS treats AI crawlers as first-class. Turn on SEO and the build emits an `llms.txt` describing your site for LLMs, and a `robots.txt` with explicit rules for the AI bots, allow or block GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, anthropic-ai, Google-Extended, PerplexityBot, CCBot, Applebot-Extended, Bytespider, Amazonbot, and Meta-ExternalAgent, with one switch.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
seo: { llms: { instructions: 'Docs live at /docs.' }, robots: { ai: 'disallow' } }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
That one line produces a real `robots.txt`:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
User-agent: *
|
|
93
|
+
Allow: /
|
|
94
|
+
|
|
95
|
+
User-agent: GPTBot
|
|
96
|
+
Disallow: /
|
|
97
|
+
|
|
98
|
+
User-agent: ClaudeBot
|
|
99
|
+
Disallow: /
|
|
100
|
+
|
|
101
|
+
User-agent: Google-Extended
|
|
102
|
+
Disallow: /
|
|
103
|
+
|
|
104
|
+
Sitemap: https://example.com/sitemap.xml
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The `toiljs create` wizard can also scaffold assistant files (CLAUDE.md, AGENTS.md, Cursor and Copilot configs) so your repo is ready for coding agents on day one.
|
|
108
|
+
|
|
109
|
+
## Everything, at a glance
|
|
110
|
+
|
|
111
|
+
| | |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| **Routing** | File-based. Dynamic, catch-all, optional catch-all, route groups, nested layouts, templates, parallel slots, and intercepting routes. Every `href` and `params` is typed. |
|
|
114
|
+
| **Data** | A `loader` resolves before render. `useAction` / `<Form>` write then revalidate. Per-route caching. No fetch waterfalls. |
|
|
115
|
+
| **SEO** | Per-route metadata baked into static HTML, plus `robots.txt`, `sitemap.xml`, `llms.txt`, OpenGraph, Twitter, JSON-LD, canonical, theme-color, early hints. |
|
|
116
|
+
| **Assets** | Imported images compressed to webp and resized. Fonts preloaded. React split for caching. Build logs what it saved. |
|
|
117
|
+
| **Realtime** | Built-in WebSocket channels: `connectChannel` / `useChannel`. WebTransport over HTTP/3. |
|
|
118
|
+
| **DX** | HMR, instant navigation, view transitions, typed routes, and a dev error overlay. |
|
|
119
|
+
| **Server** | ToilScript compiled to WebAssembly, self-hosted on uWebSockets with HTTP/3. Binary IO built in. |
|
|
120
|
+
| **Tooling** | Strict TypeScript, ESLint, and Prettier, configured and enforced out of the box. Tailwind v4 optional. |
|
|
121
|
+
|
|
122
|
+
## Routing
|
|
123
|
+
|
|
124
|
+
The filesystem is the router.
|
|
125
|
+
|
|
126
|
+
| File or folder | Route |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `index.tsx` | `/` |
|
|
129
|
+
| `about.tsx` | `/about` |
|
|
130
|
+
| `blog/[id].tsx` | `/blog/:id` |
|
|
131
|
+
| `docs/[...slug].tsx` | catch-all |
|
|
132
|
+
| `docs/[[...slug]].tsx` | optional catch-all |
|
|
133
|
+
| `(marketing)/about.tsx` | route group, adds no URL segment |
|
|
134
|
+
| `layout.tsx` | wraps the segment, persists across navigation |
|
|
135
|
+
| `template.tsx` | a layout that re-mounts on every navigation |
|
|
136
|
+
| `loading.tsx` | Suspense fallback while the route and its data load |
|
|
137
|
+
| `error.tsx` | error boundary for the segment |
|
|
138
|
+
| `global-error.tsx` | catches errors in the root layout itself |
|
|
139
|
+
| `404.tsx` | not-found page |
|
|
140
|
+
| `@modal/...` | parallel slot, placed with `<Toil.Slot name="modal" />` |
|
|
141
|
+
| `@modal/(.)photo/[id]` | intercepting route: modal on soft nav, full page on reload |
|
|
142
|
+
|
|
143
|
+
Navigation comes with it:
|
|
144
|
+
|
|
145
|
+
- **`<Toil.Link>`** and **`<Toil.NavLink>`** (active class + `aria-current`), with `href` checked against your real routes.
|
|
146
|
+
- **`navigate` / `back` / `forward` / `refresh`**, plus **`useRouter`**, **`useNavigate`**, **`useLocation`**, **`usePathname`**, **`useParams`**, **`useSearchParams`**, **`useNavigationPending`**.
|
|
147
|
+
- **Hover and viewport prefetching**, so chunks are warm before you click.
|
|
148
|
+
- **Scroll restoration** on back/forward, scroll-to-`#hash`, and scroll-to-top on new routes.
|
|
149
|
+
- **Instant navigation**: visited pages render synchronously, no flash.
|
|
150
|
+
- **View transitions** (`client.viewTransitions: true`) for animated page changes, respecting `prefers-reduced-motion`.
|
|
151
|
+
|
|
152
|
+
## Data
|
|
153
|
+
|
|
154
|
+
Read with a `loader`, write with an action. Both keep the UI in sync without manual refetching.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
export const loader = async ({ params }: Toil.LoaderArgs) => fetchPost(params.id);
|
|
158
|
+
export const revalidate: Toil.Revalidate = 10; // reuse for 10s, false = forever, omit = every nav
|
|
159
|
+
|
|
160
|
+
function SaveButton({ title }: { title: string }) {
|
|
161
|
+
const save = Toil.useAction((t: string) => api.save(t), { revalidate: true });
|
|
162
|
+
return (
|
|
163
|
+
<button disabled={save.pending} onClick={() => void save.run(title)}>
|
|
164
|
+
{save.pending ? 'Saving' : 'Save'}
|
|
165
|
+
</button>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **`loader`** resolves in parallel with the route chunk; the page suspends until ready (its `loading.tsx` shows).
|
|
171
|
+
- **`useLoaderData(loader)`** is typed straight from the loader, no generics.
|
|
172
|
+
- **`revalidate`** sets the cache policy per route; **`router.revalidate()`** / **`revalidate(href)`** bust it after a mutation.
|
|
173
|
+
- **`useAction`** and **`<Toil.Form>`** track pending and error state and revalidate on success.
|
|
174
|
+
|
|
175
|
+
## Components
|
|
176
|
+
|
|
177
|
+
Zero-import, on the `Toil` global:
|
|
178
|
+
|
|
179
|
+
- **`Image`** drops in for `<img>`: reserves space (no layout shift), lazy-loads, async-decodes, `priority` for the LCP image, `fill` + `objectFit`, optional blur placeholder.
|
|
180
|
+
- **`Script`** loads external or inline scripts with a `strategy` (`afterInteractive` / `lazyOnload` / `beforeInteractive`), deduplicated so a script never runs twice.
|
|
181
|
+
- **`Form`** submits to an action without a reload, revalidates on success, exposes pending state, optionally resets fields.
|
|
182
|
+
- **`Slot`** renders a parallel `@slot` route, the basis for modal overlays.
|
|
183
|
+
- **`Head`** / **`useHead`** / **`useTitle`** set the title and `<meta>` / `<link>` tags imperatively and compose across the tree.
|
|
184
|
+
|
|
185
|
+
## Head and SEO
|
|
186
|
+
|
|
187
|
+
A single-page app serves an empty shell. ToilJS pre-renders each route's `<head>` at build, so Google, Facebook, Discord, Slack, and the AI crawlers see real per-page tags without running your JavaScript.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
// toil.config.ts
|
|
191
|
+
export default defineConfig({
|
|
192
|
+
client: {
|
|
193
|
+
seo: {
|
|
194
|
+
url: 'https://example.com',
|
|
195
|
+
title: 'My App',
|
|
196
|
+
openGraph: { siteName: 'My App', type: 'website', image: '/og.png' },
|
|
197
|
+
twitter: { card: 'summary_large_image' },
|
|
198
|
+
jsonLd: { '@context': 'https://schema.org', '@type': 'WebSite' },
|
|
199
|
+
themeColor: '#2563ff', // also the Discord / Slack embed accent
|
|
200
|
+
preconnect: ['https://cdn.example.com'],
|
|
201
|
+
robots: { ai: 'allow' },
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- **Per-route `metadata`** (or `generateMetadata` derived from the loader's data) wins per page over layout defaults.
|
|
208
|
+
- **Static prerender** writes a `<route>/index.html` for every static route with that route's head baked in.
|
|
209
|
+
- **`robots.txt`**, **`sitemap.xml`**, and **`llms.txt`** generated together.
|
|
210
|
+
- Full **OpenGraph** (image alt/width/height/type, locale), **Twitter card**, **`fb:app_id`**, **JSON-LD**, **canonical**, **theme-color**, and **`preconnect` / `dns-prefetch`** early hints.
|
|
211
|
+
- Output is **XSS-hardened**: attribute values and inline JSON-LD are escaped so injected data can't break out.
|
|
212
|
+
|
|
213
|
+
## Build and assets
|
|
214
|
+
|
|
215
|
+
ToilJS owns Vite and does the boring optimization for you. The build tells you what it did:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
$ npm run build
|
|
219
|
+
✓ optimized 3 images
|
|
220
|
+
client/hero.png
|
|
221
|
+
→ images/hero.webp 148.0 kB → 19.3 kB -87%
|
|
222
|
+
✓ preloaded 2 fonts
|
|
223
|
+
→ fonts/inter-latin.woff2 24.10 kB
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- **Images** (`vite-imagetools` + `sharp`): every imported raster is compressed to webp, resize and reformat with `?w=400;800&format=webp&as=srcset`. The build logs the savings.
|
|
227
|
+
- **Fonts**: bundled `@font-face` fonts get a `<link rel="preload">` so text paints sooner, also logged.
|
|
228
|
+
- **Chunking**: React is split into its own long-lived chunk; assets land in tidy `images/`, `fonts/`, and `css/` folders.
|
|
229
|
+
- **Node polyfills** (`Buffer`, `global`, `process`) for libraries that expect them.
|
|
230
|
+
- **Styling**: plain CSS out of the box, with Sass, Less, Stylus, and Tailwind v4 a `toiljs configure` away.
|
|
231
|
+
|
|
232
|
+
## Realtime
|
|
233
|
+
|
|
234
|
+
A typed WebSocket channel to the server, built in.
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
const messages = Toil.useChannel<Message>('/chat');
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`connectChannel` / `useChannel` / `resolveChannelUrl` handle connection, reconnection, and message decoding.
|
|
241
|
+
|
|
242
|
+
## Binary IO
|
|
243
|
+
|
|
244
|
+
The same primitives on both sides of the wire, available as globals (and `toiljs/io`): `BinaryWriter`, `BinaryReader`, `FastMap`, `FastSet`. Move structured data without the JSON tax.
|
|
245
|
+
|
|
246
|
+
## Tooling is the standard
|
|
247
|
+
|
|
248
|
+
ToilJS sets the toolchain so nobody argues about it. Strict TypeScript, ESLint, and Prettier come configured and enforced from the first commit. New apps are wired automatically, nothing to set up, nothing to copy, nothing to bikeshed. This is the standard, not a suggestion.
|
|
249
|
+
|
|
250
|
+
## CLI
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
toiljs create [name] scaffold a new app (styling, AI files, package manager)
|
|
254
|
+
toiljs dev dev server with HMR
|
|
255
|
+
toiljs build production build
|
|
256
|
+
toiljs start self-host the build (hyper-express / uWebSockets)
|
|
257
|
+
toiljs configure toggle styling, image, font, and SEO features
|
|
258
|
+
toiljs doctor diagnose project setup and dependencies (--json for CI)
|
|
259
|
+
toiljs update check for and apply dependency updates (-y to apply all)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Tech
|
|
263
|
+
|
|
264
|
+
<div align="center">
|
|
265
|
+
|
|
266
|
+
<img src="https://img.shields.io/badge/React_19-20232a?style=for-the-badge&logo=react&logoColor=61dafb" alt="React 19" />
|
|
267
|
+
<img src="https://img.shields.io/badge/TypeScript-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
268
|
+
<img src="https://img.shields.io/badge/Vite-646cff?style=for-the-badge&logo=vite&logoColor=white" alt="Vite" />
|
|
269
|
+
<img src="https://img.shields.io/badge/WebAssembly-654ff0?style=for-the-badge&logo=webassembly&logoColor=white" alt="WebAssembly" />
|
|
270
|
+
<img src="https://img.shields.io/badge/sharp-99cc00?style=for-the-badge&logo=sharp&logoColor=white" alt="sharp" />
|
|
271
|
+
<img src="https://img.shields.io/badge/ESLint-4b32c3?style=for-the-badge&logo=eslint&logoColor=white" alt="ESLint" />
|
|
272
|
+
<img src="https://img.shields.io/badge/Prettier-f7b93e?style=for-the-badge&logo=prettier&logoColor=black" alt="Prettier" />
|
|
273
|
+
<img src="https://img.shields.io/badge/Tailwind_v4-06b6d4?style=for-the-badge&logo=tailwindcss&logoColor=white" alt="Tailwind v4" />
|
|
274
|
+
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
React 19, TypeScript, Vite, ToilScript (compiles to WebAssembly, on Binaryen), hyper-express + uWebSockets.js, vite-imagetools + sharp, ESLint (typescript-eslint, react-hooks, react-refresh, @eslint-react), Prettier, Tailwind v4 (optional).
|
|
278
|
+
|
|
279
|
+
## One file does a lot
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
// client/routes/posts/[id].tsx -> /posts/:id
|
|
283
|
+
interface Post {
|
|
284
|
+
title: string;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export const metadata: Toil.Metadata = { title: 'Post' }; // SEO, baked into the HTML at build
|
|
288
|
+
|
|
289
|
+
export const loader = async ({ params }: Toil.LoaderArgs): Promise<Post> => {
|
|
290
|
+
const res = await fetch(`/api/posts/${params.id}`); // runs before render, no useEffect
|
|
291
|
+
return res.json();
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export default function PostPage() {
|
|
295
|
+
const post = Toil.useLoaderData(loader); // typed Post, no generics
|
|
296
|
+
return (
|
|
297
|
+
<article>
|
|
298
|
+
<h1>{post.title}</h1>
|
|
299
|
+
<Toil.Link href="/posts">All posts</Toil.Link> {/* href is type-checked */}
|
|
300
|
+
</article>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
No imports. `Toil` is a fully-typed global, tree-shaken at build. The page renders with its data already loaded.
|
|
306
|
+
|
|
307
|
+
## Start
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npx toiljs create my-app
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Everything in this README is already on. You just build the app.
|
|
314
|
+
|
|
315
|
+
<div align="center"><br/><sub>Apache-2.0</sub></div>
|
package/assets/logo.svg
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500">
|
|
3
|
+
<!-- Generator: Adobe Illustrator 30.4.0, SVG Export Plug-In . SVG Version: 2.1.4 Build 226) -->
|
|
4
|
+
<defs>
|
|
5
|
+
<style>
|
|
6
|
+
.st0 {
|
|
7
|
+
fill: #fff;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.st1 {
|
|
11
|
+
fill: url(#linear-gradient1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.st2 {
|
|
15
|
+
fill: url(#linear-gradient);
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
<linearGradient id="linear-gradient" x1="43.27" y1="43.27" x2="467.12" y2="467.12" gradientUnits="userSpaceOnUse">
|
|
19
|
+
<stop offset="0" stop-color="#6990ff"/>
|
|
20
|
+
<stop offset=".03" stop-color="#6479f9"/>
|
|
21
|
+
<stop offset=".08" stop-color="#5d57f0"/>
|
|
22
|
+
<stop offset=".12" stop-color="#583de9"/>
|
|
23
|
+
<stop offset=".17" stop-color="#542ae3"/>
|
|
24
|
+
<stop offset=".23" stop-color="#521ee0"/>
|
|
25
|
+
<stop offset=".28" stop-color="#521be0"/>
|
|
26
|
+
<stop offset=".66" stop-color="#6900f4"/>
|
|
27
|
+
<stop offset="1" stop-color="#7f00f6"/>
|
|
28
|
+
</linearGradient>
|
|
29
|
+
<linearGradient id="linear-gradient1" x1="149.99" y1="355.49" x2="149.99" y2="0" gradientUnits="userSpaceOnUse">
|
|
30
|
+
<stop offset=".15" stop-color="#6990ff" stop-opacity=".6"/>
|
|
31
|
+
<stop offset=".55" stop-color="#531ae1"/>
|
|
32
|
+
</linearGradient>
|
|
33
|
+
</defs>
|
|
34
|
+
<rect class="st2" width="500" height="500" rx="130" ry="130"/>
|
|
35
|
+
<path class="st1" d="M299.98,0L0,355.49v-225.49C0,58.2,58.2,0,130,0h169.98Z"/>
|
|
36
|
+
<path class="st0" d="M106.17,111.11h285.24c9.9,0,16.7,9.96,13.09,19.18l-17.98,45.96c-2.11,5.39-7.31,8.94-13.09,8.94h-74.65c-7.76,0-14.06,6.29-14.06,14.06v214.94c0,7.76-6.29,14.06-14.06,14.06h-45.96c-7.76,0-14.06-6.29-14.06-14.06v-217.25c0-7.76-6.29-14.06-14.06-14.06h-73.66c-5.82,0-11.04-3.59-13.12-9.02l-16.76-43.64c-3.54-9.21,3.26-19.1,13.12-19.1Z"/>
|
|
37
|
+
</svg>
|