create-reactra 0.1.0-alpha.0 → 0.1.0-alpha.1

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/index.mjs CHANGED
@@ -63,6 +63,32 @@ const copyTemplate = (src, dest, tokens) => {
63
63
  }
64
64
  }
65
65
 
66
+ /**
67
+ * Write the Reactra primer where the common AI coding agents auto-read project
68
+ * context, so any agent that opens the workspace knows the .tsx files are Reactra
69
+ * DSL (compiled to React) and won't "fix" them into plain React. Single source —
70
+ * templates/_agent-primer.md — fanned out to: AGENTS.md (the cross-tool convention,
71
+ * read by Cursor/Codex/others), .github/copilot-instructions.md (GitHub Copilot),
72
+ * and CLAUDE.md (Claude Code; imports AGENTS.md so there's one source of truth).
73
+ */
74
+ const writeAgentContext = (targetDir, name, tokens) => {
75
+ let primer = readFileSync(join(TEMPLATES, "_agent-primer.md"), "utf8")
76
+ for (const [k, v] of Object.entries(tokens)) primer = primer.replaceAll(k, v)
77
+
78
+ writeFileSync(join(targetDir, "AGENTS.md"), primer)
79
+
80
+ mkdirSync(join(targetDir, ".github"), { recursive: true })
81
+ writeFileSync(join(targetDir, ".github", "copilot-instructions.md"), primer)
82
+
83
+ // Claude Code reads CLAUDE.md and resolves `@path` imports — keep it a thin
84
+ // pointer so the primer has a single source.
85
+ writeFileSync(
86
+ join(targetDir, "CLAUDE.md"),
87
+ `# ${name}\n\nThis project uses **Reactra** (a compiler-first React 19 DSL). ` +
88
+ `The full agent guide — syntax, the compile pipeline, and good/bad practices — is in AGENTS.md:\n\n@AGENTS.md\n`,
89
+ )
90
+ }
91
+
66
92
  const main = async () => {
67
93
  const opts = parseArgs(argv.slice(2))
68
94
  const interactive = stdin.isTTY && !opts.yes
@@ -107,11 +133,11 @@ const main = async () => {
107
133
  exit(1)
108
134
  }
109
135
 
136
+ const tokens = { __PROJECT_NAME__: name, __REACTRA_VERSION__: VERSIONS.reactra }
137
+
110
138
  mkdirSync(targetDir, { recursive: true })
111
- copyTemplate(templateDir, targetDir, {
112
- __PROJECT_NAME__: name,
113
- __REACTRA_VERSION__: VERSIONS.reactra,
114
- })
139
+ copyTemplate(templateDir, targetDir, tokens)
140
+ writeAgentContext(targetDir, name, tokens)
115
141
 
116
142
  if (opts.git) {
117
143
  try {
@@ -135,7 +161,8 @@ const main = async () => {
135
161
  console.log(` cd ${name}`)
136
162
  if (!opts.install) console.log(` ${installCmd}`)
137
163
  console.log(` ${devCmd}\n`)
138
- console.log("Docs: https://reactra.dev\n")
164
+ console.log("Docs: https://reactra-docs.vercel.app")
165
+ console.log("AI agents: AGENTS.md / CLAUDE.md / .github/copilot-instructions.md were added — your editor's AI assistant now knows Reactra.\n")
139
166
  }
140
167
 
141
168
  main().catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-reactra",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "description": "Scaffold a new Reactra app — a compiler-first, React-19-compatible framework.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,82 @@
1
+ # Reactra — guide for AI coding agents
2
+
3
+ This project uses **Reactra**, a compiler-first framework for React 19. The `.tsx`
4
+ files under `src/` use **Reactra DSL syntax — not plain React**. At build time a
5
+ Vite/Babel plugin compiles the DSL to ordinary React 19 (`useState`,
6
+ `useSyncExternalStore`, `React.use`, `useTransition`, …) with **zero runtime
7
+ framework**. Full docs: <https://reactra-docs.vercel.app>
8
+
9
+ ## ⚠️ Do not "fix" the DSL into plain React
10
+ `export component X { state n = 0; view { … } }` is **valid Reactra**, not broken
11
+ TypeScript. Do **not** convert it to a function component, add `useState`/`useEffect`
12
+ imports, or turn `state`/`derived`/`view` into hooks. Edit *within* the DSL. The
13
+ compiler turns it into the React you'd otherwise hand-write.
14
+
15
+ ## The DSL on one screen
16
+ ```reactra
17
+ export component Counter {
18
+ state count: number = 0 // reactive local state → useState (ephemeral; resets on unmount)
19
+ derived doubled = count * 2 // computed value; recomputes when inputs change. Read it; never call/assign it.
20
+ action increment() { count = count + 1 } // mutate state ONLY inside an action (or a JSX event handler)
21
+ view { // the JSX this component renders
22
+ <button onClick={increment}>{count} · {doubled}</button>
23
+ }
24
+ }
25
+ ```
26
+
27
+ **Keywords by scope** — declarations: `component` `store` `service`. Component body:
28
+ `state` `derived` `action` `command` `resource` `effect` `mount` `view` `ref` `meta`
29
+ `uses` `errorBoundary`. Routing: `param` `query` `prefetch` `transition`. Dependency
30
+ injection: `inject` (`inject store` / `inject service`), `provide`, `implements`.
31
+ Store-only: `input`, `preserved state`.
32
+
33
+ ## Async data — `resource` + `await`
34
+ ```reactra
35
+ resource user(id) => api.getUser(id) // the arg list is the dependency (refetch on change); arrow body is the fetcher
36
+ view {
37
+ await(user) { <Profile data={user.data} /> }
38
+ pending { <Spinner /> }
39
+ error(e) { <Err msg={String(e)} /> }
40
+ }
41
+ ```
42
+ - The dependency **must be an identifier** (a `state`/`derived`/`param`/`query`/store
43
+ field) — never a member expression. Alias via `derived` if you need one.
44
+ - Modifiers go **before** `=>`, in any order: `cache`, `swr`, `retry`, `signal`,
45
+ `select(fn)`. `select(u => u.name)` slices `.data` and re-renders the consumer
46
+ only when the slice changes.
47
+
48
+ ## Writes — `command`
49
+ The async-write primitive. **Block** form `command save() { … }` → `useActionState`
50
+ (exposes `.pending` / `.result` / `.error`). **Arrow** form `command like() => api.toggle()`
51
+ → `useTransition`, with optional clauses `optimistic { … }` (instant UI + auto-revert),
52
+ `invalidate [resourceName]` (refetch on success), `rollback(e) { … }` (failure handler).
53
+
54
+ ## Persisting state — use a store, not component `state`
55
+ Component `state` is ephemeral (`useState` — it resets when the route unmounts). To
56
+ share or persist state across navigation, declare a `store` and acquire it:
57
+ ```reactra
58
+ inject store cartStore // bare = subscribe to it
59
+ inject store cartStore({ userId }) // argumented (object literal) = own/instantiate it
60
+ inject service api // services require the `service` qualifier
61
+ ```
62
+ Stores auto-register on first use; **services are registered in `src/main.tsx` via
63
+ `configureServices(...)`**.
64
+
65
+ ## Good / bad practice
66
+ - **DO** route every `state`/store-field write through an `action` or a JSX event
67
+ handler. Assigning in `view`/`derived`/`effect`/`mount` is a compile error (R002/R023).
68
+ - **DO** use `derived` for computed values; never call or reassign a `derived`.
69
+ - **DO** put effect teardown in the `return` of `mount` / `effect on(...)` — there is
70
+ no `cleanup` keyword.
71
+ - **DON'T** pass a freshly-allocated object/array literal as a `resource` dependency
72
+ (it never cache-equals → refetches every render; warned as R028).
73
+ - **DON'T** write a `key`-less `.map()` inside a `view` (R029).
74
+
75
+ ## Debugging & reference
76
+ - The compiler emits **readable React 19** — to understand a file, read its compiled
77
+ output, or the DSL⇄compiled examples in the docs.
78
+ - `@reactra/devtools` adds an in-page panel (components, stores, router, time-travel).
79
+ - Error-code prefixes: `R###` component DSL · `S###` store · `RO###` router ·
80
+ `SVC###` service · `RES###` resource — all searchable in the docs.
81
+
82
+ **Docs & full keyword reference:** <https://reactra-docs.vercel.app>
@@ -1,3 +1,3 @@
1
1
  {
2
- "reactra": "0.1.0-alpha.0"
2
+ "reactra": "0.1.0-alpha.1"
3
3
  }