@farm.js/create-app 0.1.0-beta.34 → 0.1.0-beta.35

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.
@@ -0,0 +1,465 @@
1
+ @import "@fontsource-variable/geist/wght.css";
2
+ @import "@fontsource-variable/geist-mono/wght.css";
3
+ @import "tailwindcss";
4
+
5
+ :root {
6
+ --paper: #000000;
7
+ --paper-soft: #080808;
8
+ --ink: #f5f5f5;
9
+ --ink-soft: #a1a1a1;
10
+ --ink-faint: #686868;
11
+ --line: rgb(255 255 255 / 0.12);
12
+ --line-strong: rgb(255 255 255 / 0.22);
13
+ --accent: #b8ff5b;
14
+ --accent-soft: rgb(184 255 91 / 0.1);
15
+ --focus: #ffffff;
16
+ --font-sans: "Geist Variable", ui-sans-serif, system-ui, sans-serif;
17
+ --font-mono: "Geist Mono Variable", ui-monospace, monospace;
18
+ color-scheme: dark;
19
+ color: var(--ink);
20
+ font-family: var(--font-sans);
21
+ font-synthesis: none;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ html {
29
+ min-width: 320px;
30
+ min-height: 100%;
31
+ background: var(--paper);
32
+ }
33
+
34
+ body {
35
+ min-height: 100vh;
36
+ margin: 0;
37
+ background: var(--paper);
38
+ color: var(--ink);
39
+ font-family: var(--font-sans);
40
+ text-rendering: optimizeLegibility;
41
+ }
42
+
43
+ button,
44
+ input {
45
+ font: inherit;
46
+ }
47
+
48
+ button,
49
+ a {
50
+ -webkit-tap-highlight-color: transparent;
51
+ }
52
+
53
+ button {
54
+ color: inherit;
55
+ }
56
+
57
+ a {
58
+ color: inherit;
59
+ text-decoration: none;
60
+ }
61
+
62
+ code {
63
+ font-family: var(--font-mono);
64
+ }
65
+
66
+ ::selection {
67
+ background: white;
68
+ color: black;
69
+ }
70
+
71
+ :focus-visible {
72
+ outline: 2px solid var(--focus);
73
+ outline-offset: 3px;
74
+ }
75
+
76
+ .landing-main {
77
+ position: relative;
78
+ isolation: isolate;
79
+ width: 100%;
80
+ min-height: 100vh;
81
+ overflow: hidden;
82
+ background: var(--paper);
83
+ }
84
+
85
+ .hero-section {
86
+ position: relative;
87
+ display: flex;
88
+ width: 100%;
89
+ min-height: 100vh;
90
+ align-items: center;
91
+ justify-content: flex-start;
92
+ padding: clamp(2.5rem, 6vh, 5rem) clamp(1.25rem, 7vw, 6rem);
93
+ }
94
+
95
+ .hero-section::before,
96
+ .hero-section::after {
97
+ position: absolute;
98
+ width: 9px;
99
+ height: 9px;
100
+ border-color: rgb(255 255 255 / 0.3);
101
+ content: "";
102
+ pointer-events: none;
103
+ }
104
+
105
+ .hero-section::before {
106
+ top: -1px;
107
+ left: -1px;
108
+ border-top: 1px solid;
109
+ border-left: 1px solid;
110
+ }
111
+
112
+ .hero-section::after {
113
+ right: 0;
114
+ bottom: -1px;
115
+ border-right: 1px solid;
116
+ border-bottom: 1px solid;
117
+ }
118
+
119
+ .hero-copy {
120
+ width: min(100%, 760px);
121
+ margin: 0;
122
+ }
123
+
124
+ .eyebrow-row {
125
+ display: flex;
126
+ flex-wrap: wrap;
127
+ align-items: center;
128
+ gap: 0.65rem;
129
+ color: var(--ink-faint);
130
+ font-family: var(--font-mono);
131
+ font-size: 0.67rem;
132
+ text-transform: uppercase;
133
+ }
134
+
135
+ .eyebrow-row > span:first-child {
136
+ color: var(--ink);
137
+ }
138
+
139
+ .experimental-label {
140
+ border-left: 1px solid var(--line-strong);
141
+ padding-left: 0.65rem;
142
+ color: var(--accent);
143
+ }
144
+
145
+ .hero-copy h1 {
146
+ max-width: 700px;
147
+ margin: 1.4rem 0 0;
148
+ font-size: clamp(2.15rem, 4vw, 3.55rem);
149
+ font-weight: 510;
150
+ letter-spacing: -0.055em;
151
+ line-height: 1.02;
152
+ }
153
+
154
+ .hero-copy h1 code {
155
+ border: 1px solid var(--line);
156
+ background: rgb(255 255 255 / 0.07);
157
+ padding: 0.04em 0.2em;
158
+ font-size: 0.76em;
159
+ font-weight: 450;
160
+ letter-spacing: -0.035em;
161
+ white-space: nowrap;
162
+ }
163
+
164
+ .hero-summary {
165
+ max-width: 590px;
166
+ margin: 1rem 0 0;
167
+ color: var(--ink-soft);
168
+ font-size: 0.91rem;
169
+ line-height: 1.6;
170
+ }
171
+
172
+ .command-list {
173
+ max-width: 590px;
174
+ margin-top: 1.65rem;
175
+ border-block: 1px solid var(--line);
176
+ }
177
+
178
+ .command-row {
179
+ display: grid;
180
+ min-width: 0;
181
+ grid-template-columns: auto minmax(0, 1fr);
182
+ align-items: center;
183
+ gap: 0.8rem;
184
+ padding: 0.82rem 0;
185
+ }
186
+
187
+ .command-row + .command-row {
188
+ border-top: 1px solid var(--line);
189
+ }
190
+
191
+ .command-row > span {
192
+ color: var(--ink-faint);
193
+ font-family: var(--font-mono);
194
+ font-size: 0.62rem;
195
+ }
196
+
197
+ .command-row code {
198
+ color: var(--ink);
199
+ font-size: 0.75rem;
200
+ overflow-wrap: anywhere;
201
+ }
202
+
203
+ .compiler-status {
204
+ display: inline-flex;
205
+ min-width: 0;
206
+ align-items: center;
207
+ gap: 0.65rem;
208
+ }
209
+
210
+ .status-dot {
211
+ width: 7px;
212
+ height: 7px;
213
+ flex: 0 0 auto;
214
+ border-radius: 50%;
215
+ background: var(--accent);
216
+ box-shadow: 0 0 0 3px var(--accent-soft);
217
+ }
218
+
219
+ .compiler-check {
220
+ margin-top: 1.9rem;
221
+ border: 1px solid var(--line);
222
+ background: var(--paper-soft);
223
+ }
224
+
225
+ .check-heading {
226
+ display: flex;
227
+ align-items: flex-end;
228
+ justify-content: space-between;
229
+ gap: 1.5rem;
230
+ border-bottom: 1px solid var(--line);
231
+ padding: 0.95rem 1rem;
232
+ }
233
+
234
+ .check-heading span {
235
+ color: var(--accent);
236
+ font-family: var(--font-mono);
237
+ font-size: 0.58rem;
238
+ letter-spacing: 0.05em;
239
+ text-transform: uppercase;
240
+ }
241
+
242
+ .check-heading h2 {
243
+ margin: 0.24rem 0 0;
244
+ font-size: 1rem;
245
+ font-weight: 560;
246
+ letter-spacing: -0.025em;
247
+ }
248
+
249
+ .check-heading p {
250
+ margin: 0;
251
+ color: var(--ink-faint);
252
+ font-family: var(--font-mono);
253
+ font-size: 0.62rem;
254
+ }
255
+
256
+ .comparison-grid {
257
+ display: grid;
258
+ grid-template-columns: repeat(2, minmax(0, 1fr));
259
+ }
260
+
261
+ .counter-path {
262
+ min-width: 0;
263
+ padding: 1rem;
264
+ }
265
+
266
+ .counter-path + .counter-path {
267
+ border-left: 1px solid var(--line);
268
+ }
269
+
270
+ .counter-path-compiled {
271
+ box-shadow: inset 0 2px 0 var(--accent);
272
+ }
273
+
274
+ .counter-heading {
275
+ display: flex;
276
+ align-items: center;
277
+ gap: 0.7rem;
278
+ }
279
+
280
+ .path-index {
281
+ display: inline-grid;
282
+ width: 23px;
283
+ height: 23px;
284
+ flex: 0 0 auto;
285
+ place-items: center;
286
+ border: 1px solid var(--line-strong);
287
+ border-radius: 50%;
288
+ color: var(--ink-soft);
289
+ font-family: var(--font-mono);
290
+ font-size: 0.58rem;
291
+ }
292
+
293
+ .counter-path-compiled .path-index {
294
+ border-color: rgb(184 255 91 / 0.35);
295
+ color: var(--accent);
296
+ }
297
+
298
+ .counter-heading h3,
299
+ .counter-heading p {
300
+ margin: 0;
301
+ }
302
+
303
+ .counter-heading h3 {
304
+ font-size: 0.82rem;
305
+ font-weight: 570;
306
+ }
307
+
308
+ .counter-heading p {
309
+ margin-top: 0.12rem;
310
+ color: var(--ink-faint);
311
+ font-family: var(--font-mono);
312
+ font-size: 0.56rem;
313
+ text-transform: uppercase;
314
+ }
315
+
316
+ .counter-metrics {
317
+ display: grid;
318
+ grid-template-columns: repeat(2, minmax(0, 1fr));
319
+ margin: 1rem 0;
320
+ border-block: 1px solid var(--line);
321
+ }
322
+
323
+ .counter-metrics > div {
324
+ padding: 0.75rem 0;
325
+ }
326
+
327
+ .counter-metrics > div + div {
328
+ border-left: 1px solid var(--line);
329
+ padding-left: 0.85rem;
330
+ }
331
+
332
+ .counter-metrics dt {
333
+ color: var(--ink-faint);
334
+ font-family: var(--font-mono);
335
+ font-size: 0.54rem;
336
+ text-transform: uppercase;
337
+ }
338
+
339
+ .counter-metrics dd {
340
+ margin: 0.2rem 0 0;
341
+ font-size: 1.45rem;
342
+ font-weight: 520;
343
+ letter-spacing: -0.04em;
344
+ }
345
+
346
+ .counter-path button {
347
+ display: inline-flex;
348
+ width: 100%;
349
+ min-height: 42px;
350
+ align-items: center;
351
+ justify-content: space-between;
352
+ gap: 1rem;
353
+ border: 1px solid var(--line-strong);
354
+ background: transparent;
355
+ padding: 0.65rem 0.75rem;
356
+ font-family: var(--font-mono);
357
+ font-size: 0.62rem;
358
+ cursor: pointer;
359
+ transition:
360
+ background-color 150ms ease,
361
+ border-color 150ms ease,
362
+ color 150ms ease,
363
+ transform 100ms ease;
364
+ }
365
+
366
+ .counter-path-compiled button {
367
+ border-color: rgb(184 255 91 / 0.42);
368
+ }
369
+
370
+ .counter-path button:active {
371
+ transform: translateY(1px);
372
+ }
373
+
374
+ .resource-links {
375
+ display: flex;
376
+ flex-wrap: wrap;
377
+ gap: 0.65rem;
378
+ margin-top: 1.35rem;
379
+ color: var(--ink-soft);
380
+ font-family: var(--font-mono);
381
+ font-size: 0.68rem;
382
+ text-transform: uppercase;
383
+ }
384
+
385
+ .resource-link-item {
386
+ display: inline-flex;
387
+ align-items: center;
388
+ gap: 0.65rem;
389
+ white-space: nowrap;
390
+ }
391
+
392
+ .resource-separator {
393
+ color: var(--ink-faint);
394
+ user-select: none;
395
+ }
396
+
397
+ .resource-links a {
398
+ display: inline-flex;
399
+ align-items: center;
400
+ gap: 0.45rem;
401
+ border-bottom: 1px solid transparent;
402
+ padding-block: 0.2rem;
403
+ transition:
404
+ border-color 150ms ease,
405
+ color 150ms ease;
406
+ }
407
+
408
+ .resource-icon {
409
+ width: 0.85rem;
410
+ height: 0.85rem;
411
+ flex: 0 0 auto;
412
+ }
413
+
414
+ @media (hover: hover) and (pointer: fine) {
415
+ .counter-path button:hover {
416
+ border-color: var(--ink-soft);
417
+ background: rgb(255 255 255 / 0.05);
418
+ }
419
+
420
+ .counter-path-compiled button:hover {
421
+ border-color: var(--accent);
422
+ background: var(--accent-soft);
423
+ }
424
+
425
+ .resource-links a:hover {
426
+ border-color: currentColor;
427
+ color: var(--ink);
428
+ }
429
+ }
430
+
431
+ @media (max-width: 680px) {
432
+ .hero-section {
433
+ align-items: flex-start;
434
+ padding: 2.25rem 1.25rem;
435
+ }
436
+
437
+ .hero-copy h1 {
438
+ font-size: clamp(2rem, 10vw, 2.75rem);
439
+ overflow-wrap: anywhere;
440
+ }
441
+
442
+ .check-heading {
443
+ align-items: flex-start;
444
+ flex-direction: column;
445
+ gap: 0.45rem;
446
+ }
447
+
448
+ .comparison-grid {
449
+ grid-template-columns: 1fr;
450
+ }
451
+
452
+ .counter-path + .counter-path {
453
+ border-top: 1px solid var(--line);
454
+ border-left: 0;
455
+ }
456
+ }
457
+
458
+ @media (prefers-reduced-motion: reduce) {
459
+ *,
460
+ *::before,
461
+ *::after {
462
+ scroll-behavior: auto !important;
463
+ transition-duration: 0.01ms !important;
464
+ }
465
+ }
@@ -0,0 +1,22 @@
1
+ import type { LayoutProps, Metadata } from "@farm.js/core";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "FARMJS React Compiler Starter",
6
+ description: "Start a FARMJS app with the experimental React AOT compiler.",
7
+ icons: {
8
+ icon: [{ url: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
9
+ },
10
+ };
11
+
12
+ export default function RootLayout({ children }: LayoutProps) {
13
+ return (
14
+ <html lang="en">
15
+ <head>
16
+ <meta charSet="utf-8" />
17
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
18
+ </head>
19
+ <body>{children}</body>
20
+ </html>
21
+ );
22
+ }
@@ -0,0 +1,44 @@
1
+ import { CompilerComparison } from "../components/compiler-comparison";
2
+ import { ResourceLinks } from "../components/resource-links";
3
+
4
+ export default function HomePage() {
5
+ return (
6
+ <main className="landing-main">
7
+ <section className="hero-section">
8
+ <div className="hero-copy">
9
+ <div className="eyebrow-row">
10
+ <span>00</span>
11
+ <span>FARMJS / React Compiler starter</span>
12
+ <span className="experimental-label">Experimental</span>
13
+ </div>
14
+
15
+ <h1>
16
+ Edit <code>page.tsx</code>. The compiler handles the rest.
17
+ </h1>
18
+
19
+ <p className="hero-summary">
20
+ Keep writing ordinary React. FARMJS prepares direct bindings for safe local state
21
+ updates and leaves everything else with React.
22
+ </p>
23
+
24
+ <div className="command-list" aria-label="Getting started">
25
+ <div className="command-row">
26
+ <span>01</span>
27
+ <code>pnpm dev</code>
28
+ </div>
29
+ <div className="command-row">
30
+ <span>02</span>
31
+ <div className="compiler-status">
32
+ <span className="status-dot" aria-hidden="true" />
33
+ <code>experimental.compiler: true</code>
34
+ </div>
35
+ </div>
36
+ </div>
37
+
38
+ <CompilerComparison />
39
+ <ResourceLinks className="resource-links" />
40
+ </div>
41
+ </section>
42
+ </main>
43
+ );
44
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ let compiledExecutions = 0;
6
+ let reactExecutions = 0;
7
+
8
+ export function CompiledCounter() {
9
+ const [count, setCount] = useState(0);
10
+
11
+ return (
12
+ <article className="counter-path counter-path-compiled" data-path="compiled">
13
+ <div className="counter-heading">
14
+ <span className="path-index">A</span>
15
+ <div>
16
+ <h3>AOT compiled</h3>
17
+ <p>Direct DOM binding</p>
18
+ </div>
19
+ </div>
20
+
21
+ <dl className="counter-metrics" aria-live="polite">
22
+ <div>
23
+ <dt>State</dt>
24
+ <dd data-metric="state">{count}</dd>
25
+ </div>
26
+ <div>
27
+ <dt>Executions</dt>
28
+ <dd data-metric="executions">
29
+ {typeof window === "undefined" ? 1 : ++compiledExecutions}
30
+ </dd>
31
+ </div>
32
+ </dl>
33
+
34
+ <button type="button" data-action="update" onClick={() => setCount((value) => value + 1)}>
35
+ Update compiled state
36
+ <span aria-hidden="true">↗</span>
37
+ </button>
38
+ </article>
39
+ );
40
+ }
41
+
42
+ export function BaseReactCounter() {
43
+ "use no compiler";
44
+
45
+ const [count, setCount] = useState(0);
46
+
47
+ return (
48
+ <article className="counter-path" data-path="react">
49
+ <div className="counter-heading">
50
+ <span className="path-index">B</span>
51
+ <div>
52
+ <h3>Base React</h3>
53
+ <p>React reconciliation</p>
54
+ </div>
55
+ </div>
56
+
57
+ <dl className="counter-metrics" aria-live="polite">
58
+ <div>
59
+ <dt>State</dt>
60
+ <dd data-metric="state">{count}</dd>
61
+ </div>
62
+ <div>
63
+ <dt>Executions</dt>
64
+ <dd data-metric="executions">{typeof window === "undefined" ? 1 : ++reactExecutions}</dd>
65
+ </div>
66
+ </dl>
67
+
68
+ <button type="button" data-action="update" onClick={() => setCount((value) => value + 1)}>
69
+ Update React state
70
+ <span aria-hidden="true">↗</span>
71
+ </button>
72
+ </article>
73
+ );
74
+ }
75
+
76
+ export function CompilerComparison() {
77
+ return (
78
+ <section className="compiler-check" aria-labelledby="compiler-check-title">
79
+ <header className="check-heading">
80
+ <div>
81
+ <span>Live check</span>
82
+ <h2 id="compiler-check-title">Same click. Less React work.</h2>
83
+ </div>
84
+ <p>Watch executions after each update.</p>
85
+ </header>
86
+
87
+ <div className="comparison-grid">
88
+ <CompiledCounter />
89
+ <BaseReactCounter />
90
+ </div>
91
+ </section>
92
+ );
93
+ }
@@ -0,0 +1,78 @@
1
+ interface ResourceLinkAction {
2
+ href: string;
3
+ label: string;
4
+ }
5
+
6
+ interface ResourceLinksProps {
7
+ className: string;
8
+ primary?: ResourceLinkAction;
9
+ }
10
+
11
+ function DocsIcon() {
12
+ return (
13
+ <svg
14
+ className="resource-icon"
15
+ viewBox="0 0 24 24"
16
+ fill="none"
17
+ stroke="currentColor"
18
+ strokeWidth="1.8"
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ aria-hidden="true"
22
+ focusable="false"
23
+ >
24
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" />
25
+ <path d="M14 2v6h6M8 13h8M8 17h6" />
26
+ </svg>
27
+ );
28
+ }
29
+
30
+ function GitHubIcon() {
31
+ return (
32
+ <svg
33
+ className="resource-icon"
34
+ viewBox="0 0 24 24"
35
+ fill="currentColor"
36
+ aria-hidden="true"
37
+ focusable="false"
38
+ >
39
+ <path d="M12 .7a11.5 11.5 0 0 0-3.64 22.41c.58.1.79-.25.79-.56v-2.23c-3.22.7-3.9-1.37-3.9-1.37-.52-1.34-1.28-1.69-1.28-1.69-1.05-.72.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.57-.29-5.27-1.29-5.27-5.68 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.47.11-3.05 0 0 .96-.31 3.16 1.18A10.95 10.95 0 0 1 12 6.1c.98 0 1.95.13 2.87.39 2.2-1.49 3.16-1.18 3.16-1.18.62 1.58.23 2.76.11 3.05.74.81 1.18 1.83 1.18 3.09 0 4.4-2.71 5.38-5.29 5.67.42.36.79 1.07.79 2.16v3.27c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .7Z" />
40
+ </svg>
41
+ );
42
+ }
43
+
44
+ function ResourceSeparator() {
45
+ return (
46
+ <span className="resource-separator" aria-hidden="true">
47
+ /
48
+ </span>
49
+ );
50
+ }
51
+
52
+ export function ResourceLinks({ className, primary }: ResourceLinksProps) {
53
+ return (
54
+ <nav className={className} aria-label="Starter resources">
55
+ {primary ? (
56
+ <span className="resource-link-item">
57
+ <a className="resource-link-primary" href={primary.href}>
58
+ {primary.label}
59
+ </a>
60
+ </span>
61
+ ) : null}
62
+ <span className="resource-link-item">
63
+ {primary ? <ResourceSeparator /> : null}
64
+ <a href="https://farm.js.dev">
65
+ <DocsIcon />
66
+ <span>Docs</span>
67
+ </a>
68
+ </span>
69
+ <span className="resource-link-item">
70
+ <ResourceSeparator />
71
+ <a href="https://github.com/farming-labs/farm.js">
72
+ <GitHubIcon />
73
+ <span>GitHub</span>
74
+ </a>
75
+ </span>
76
+ </nav>
77
+ );
78
+ }