@dewtech/dare-cli 2.9.0 → 2.11.0

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.
Files changed (33) hide show
  1. package/README.md +17 -3
  2. package/dist/commands/init.d.ts.map +1 -1
  3. package/dist/commands/init.js +18 -2
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/core/types/project.d.ts +1 -1
  6. package/dist/core/types/project.d.ts.map +1 -1
  7. package/dist/dag-runner/ralph-loop.d.ts.map +1 -1
  8. package/dist/dag-runner/ralph-loop.js +14 -0
  9. package/dist/dag-runner/ralph-loop.js.map +1 -1
  10. package/dist/utils/project-generator.js +95 -1
  11. package/dist/utils/project-generator.js.map +1 -1
  12. package/dist/utils/stack-bootstrap.d.ts +4 -1
  13. package/dist/utils/stack-bootstrap.d.ts.map +1 -1
  14. package/dist/utils/stack-bootstrap.js +327 -1
  15. package/dist/utils/stack-bootstrap.js.map +1 -1
  16. package/dist/utils/templates.d.ts.map +1 -1
  17. package/dist/utils/templates.js +49 -15
  18. package/dist/utils/templates.js.map +1 -1
  19. package/package.json +1 -1
  20. package/templates/frontend/leptos-csr/.cargo/config.toml +2 -0
  21. package/templates/frontend/leptos-csr/Cargo.toml +16 -0
  22. package/templates/frontend/leptos-csr/Trunk.toml +10 -0
  23. package/templates/frontend/leptos-csr/index.html +11 -0
  24. package/templates/frontend/leptos-csr/src/lib.rs +20 -0
  25. package/templates/frontend/leptos-csr/style/main.scss +19 -0
  26. package/templates/frontend/leptos-fullstack/.cargo/config.toml +4 -0
  27. package/templates/frontend/leptos-fullstack/Cargo.toml +56 -0
  28. package/templates/frontend/leptos-fullstack/src/app.rs +49 -0
  29. package/templates/frontend/leptos-fullstack/src/lib.rs +9 -0
  30. package/templates/frontend/leptos-fullstack/src/main.rs +29 -0
  31. package/templates/frontend/leptos-fullstack/style/main.scss +19 -0
  32. package/templates/ide/claude/.claude/commands/dare-rust-leptos.md +269 -0
  33. package/templates/ide/claude/CLAUDE.md +10 -1
@@ -0,0 +1,56 @@
1
+ [package]
2
+ name = "{{PROJECT_NAME}}"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [lib]
7
+ crate-type = ["cdylib", "rlib"]
8
+
9
+ [dependencies]
10
+ axum = { version = "0.7", optional = true }
11
+ console_error_panic_hook = "0.1"
12
+ leptos = { version = "0.7", features = [] }
13
+ leptos_axum = { version = "0.7", optional = true }
14
+ leptos_meta = { version = "0.7" }
15
+ leptos_router = { version = "0.7", features = [] }
16
+ tokio = { version = "1", features = ["full"], optional = true }
17
+ tower = { version = "0.5", optional = true }
18
+ tower-http = { version = "0.6", features = ["fs"], optional = true }
19
+ wasm-bindgen = "0.2"
20
+
21
+ [features]
22
+ default = []
23
+ hydrate = ["leptos/hydrate"]
24
+ ssr = [
25
+ "dep:axum",
26
+ "dep:leptos_axum",
27
+ "dep:tokio",
28
+ "dep:tower",
29
+ "dep:tower-http",
30
+ "leptos/ssr",
31
+ "leptos_meta/ssr",
32
+ "leptos_router/ssr",
33
+ ]
34
+
35
+ [package.metadata.leptos]
36
+ output-name = "{{PROJECT_NAME}}"
37
+ site-root = "target/site"
38
+ site-pkg-dir = "pkg"
39
+ style-file = "style/main.scss"
40
+ browserquery = "defaults"
41
+ env = "DEV"
42
+ bin-features = ["ssr"]
43
+ lib-features = ["hydrate"]
44
+
45
+ [[bin]]
46
+ name = "{{PROJECT_NAME}}"
47
+ path = "src/main.rs"
48
+ required-features = ["ssr"]
49
+
50
+ [profile.release]
51
+ codegen-units = 1
52
+ lto = true
53
+
54
+ [profile.wasm-release]
55
+ inherits = "release"
56
+ opt-level = "z"
@@ -0,0 +1,49 @@
1
+ use leptos::prelude::*;
2
+ use leptos_meta::*;
3
+ use leptos_router::{components::Router, path};
4
+
5
+ pub fn shell(options: LeptosOptions) -> impl IntoView {
6
+ view! {
7
+ <!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8"/>
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
12
+ <AutoReload options=options.clone()/>
13
+ <HydrationScripts options=options.clone()/>
14
+ <MetaTags/>
15
+ </head>
16
+ <body>
17
+ <App/>
18
+ </body>
19
+ </html>
20
+ }
21
+ }
22
+
23
+ #[component]
24
+ pub fn App() -> impl IntoView {
25
+ provide_meta_context();
26
+
27
+ view! {
28
+ <Title text="{{PROJECT_NAME}}"/>
29
+ <Router>
30
+ <main>
31
+ <Routes fallback=|| view! { <p>"Not found."</p> }>
32
+ <Route path=path!("/") view=HomePage/>
33
+ </Routes>
34
+ </main>
35
+ </Router>
36
+ }
37
+ }
38
+
39
+ #[component]
40
+ fn HomePage() -> impl IntoView {
41
+ let (count, set_count) = signal(0);
42
+
43
+ view! {
44
+ <h1>"{{PROJECT_NAME}}"</h1>
45
+ <button on:click=move |_| set_count.update(|n| *n += 1)>
46
+ "Count: " {count}
47
+ </button>
48
+ }
49
+ }
@@ -0,0 +1,9 @@
1
+ pub mod app;
2
+
3
+ #[cfg(feature = "hydrate")]
4
+ #[wasm_bindgen::prelude::wasm_bindgen]
5
+ pub fn hydrate() {
6
+ use app::App;
7
+ console_error_panic_hook::set_once();
8
+ leptos::mount::hydrate_body(App);
9
+ }
@@ -0,0 +1,29 @@
1
+ #[cfg(feature = "ssr")]
2
+ #[tokio::main]
3
+ async fn main() {
4
+ use axum::Router;
5
+ use leptos::prelude::*;
6
+ use leptos_axum::{generate_route_list, LeptosRoutes};
7
+ // Import from the lib target of this crate (same crate, lib target)
8
+ use app::{shell, App};
9
+
10
+ let conf = get_configuration(None).unwrap();
11
+ let leptos_options = conf.leptos_options;
12
+ let addr = leptos_options.site_addr;
13
+ let routes = generate_route_list(App);
14
+
15
+ let app = Router::new()
16
+ .leptos_routes(&leptos_options, routes, {
17
+ let leptos_options = leptos_options.clone();
18
+ move || shell(leptos_options.clone())
19
+ })
20
+ .fallback(leptos_axum::file_and_error_handler(shell))
21
+ .with_state(leptos_options);
22
+
23
+ println!("Listening on http://{}", addr);
24
+ let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
25
+ axum::serve(listener, app).await.unwrap();
26
+ }
27
+
28
+ #[cfg(not(feature = "ssr"))]
29
+ pub fn main() {}
@@ -0,0 +1,19 @@
1
+ :root {
2
+ font-family: system-ui, sans-serif;
3
+ }
4
+
5
+ body {
6
+ max-width: 800px;
7
+ margin: 0 auto;
8
+ padding: 2rem;
9
+ }
10
+
11
+ h1 {
12
+ color: #b7410e;
13
+ }
14
+
15
+ button {
16
+ padding: 0.5rem 1rem;
17
+ font-size: 1rem;
18
+ cursor: pointer;
19
+ }
@@ -0,0 +1,269 @@
1
+ # /dare-rust-leptos
2
+
3
+ Guia de desenvolvimento Leptos para projetos DARE. Cobre decisão de variante, idioms obrigatórios, antipatterns, tipos compartilhados e templates de tasks.
4
+
5
+ ## Como usar
6
+
7
+ ```
8
+ /dare-rust-leptos ← decide variante + configura workspace
9
+ /dare-rust-leptos --csr ← guia específico para CSR + trunk
10
+ /dare-rust-leptos --fullstack ← guia específico para SSR + cargo-leptos
11
+ /dare-rust-leptos --shared-types ← padrão cfg_attr para tipos server + WASM
12
+ ```
13
+
14
+ ---
15
+
16
+ ## 1. Decisão de variante: CSR vs Fullstack
17
+
18
+ | Critério | CSR (trunk) | Fullstack (cargo-leptos) |
19
+ |---|---|---|
20
+ | SEO necessário | ❌ | ✅ |
21
+ | Time-to-interactive crítico | ❌ | ✅ |
22
+ | Dashboard interno / admin | ✅ | ✅ |
23
+ | Backend Axum existente | indiferente | ✅ integração direta |
24
+ | Simplicidade de deploy | ✅ arquivos estáticos (CDN) | ⚠️ binário Axum |
25
+ | Server functions (`#[server]`) | ❌ não existe | ✅ |
26
+
27
+ **Regra de ouro:**
28
+ - Se o projeto vive atrás de login e SEO não importa → **CSR**
29
+ - Se precisa de SEO, carregamento inicial rápido, ou server functions → **Fullstack**
30
+ - Se já tem `rust-axum` como backend no monorepo → **Fullstack** (workspace unificado)
31
+
32
+ ---
33
+
34
+ ## 2. Ferramentas — nunca misturar
35
+
36
+ | Variante | Build | Dev server | Test |
37
+ |---|---|---|---|
38
+ | CSR | `trunk build --release` | `trunk serve` | `cargo test --workspace` |
39
+ | Fullstack | `cargo leptos build --release` | `cargo leptos watch` | `cargo test --workspace` |
40
+
41
+ > ⚠️ `cargo leptos test` **não existe**. Use sempre `cargo test --workspace`.
42
+ > ⚠️ Não use `trunk` para fullstack nem `cargo leptos` para CSR — ferramentas erradas para o target errado.
43
+
44
+ ---
45
+
46
+ ## 3. Idioms obrigatórios Leptos 0.7
47
+
48
+ ### Estado reativo
49
+ ```rust
50
+ // ✅ Fine-grained signals — só re-renderiza o que usa o signal
51
+ let (count, set_count) = signal(0);
52
+ let doubled = move || count.get() * 2; // derived (memo inline)
53
+
54
+ // Para estado complexo ou compartilhado entre componentes:
55
+ let count = RwSignal::new(0); // read + write em um só
56
+ ```
57
+
58
+ ### Dados assíncronos
59
+ ```rust
60
+ // ✅ Resource — fetch declarativo, integra com Suspense
61
+ let user = Resource::new(|| user_id(), |id| async move { fetch_user(id).await });
62
+
63
+ // ✅ Suspense — loading state automático
64
+ view! {
65
+ <Suspense fallback=|| view! { <p>"Loading..."</p> }>
66
+ {move || user.get().map(|u| view! { <p>{u.name}</p> })}
67
+ </Suspense>
68
+ }
69
+
70
+ // ❌ Nunca — Effect que faz fetch (re-executa em todo render)
71
+ Effect::new(move |_| { spawn_local(async { fetch_user(id).await }); });
72
+ ```
73
+
74
+ ### Mutações
75
+ ```rust
76
+ // ✅ Action — para submits, forms, operações que mudam estado
77
+ let save = Action::new(|input: &String| {
78
+ let input = input.clone();
79
+ async move { api::save(input).await }
80
+ });
81
+
82
+ view! {
83
+ <button on:click=move |_| save.dispatch("hello".to_string())>
84
+ "Save"
85
+ </button>
86
+ <Show when=move || save.pending().get()>
87
+ <p>"Saving..."</p>
88
+ </Show>
89
+ }
90
+ ```
91
+
92
+ ### Renderização condicional e listas
93
+ ```rust
94
+ // ✅ Show — condicional com lazy evaluation
95
+ view! {
96
+ <Show when=move || logged_in.get() fallback=|| view! { <Login/> }>
97
+ <Dashboard/>
98
+ </Show>
99
+ }
100
+
101
+ // ✅ For — lista reativa com key para reconciliação eficiente
102
+ view! {
103
+ <For
104
+ each=move || items.get()
105
+ key=|item| item.id
106
+ children=move |item| view! { <ItemRow item=item/> }
107
+ />
108
+ }
109
+ ```
110
+
111
+ ### Server functions (fullstack only)
112
+ ```rust
113
+ // ✅ #[server] macro — compila para HTTP call no client, fn real no server
114
+ #[server(SaveUser, "/api")]
115
+ pub async fn save_user(name: String) -> Result<User, ServerFnError> {
116
+ // Este código só roda no server (feature = "ssr")
117
+ let user = db::create_user(name).await?;
118
+ Ok(user)
119
+ }
120
+
121
+ // No componente — usa como Action normal
122
+ let save = ServerAction::<SaveUser>::new();
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 4. Tipos compartilhados server + WASM
128
+
129
+ Tipos que precisam existir tanto no server (SQLx, Axum) quanto no client (WASM) usam `cfg_attr`:
130
+
131
+ ```rust
132
+ // crates/<projeto>-domain/src/lib.rs (ou src/models.rs)
133
+ use serde::{Deserialize, Serialize};
134
+
135
+ #[cfg_attr(feature = "ssr", derive(sqlx::FromRow))] // só no server
136
+ #[derive(Clone, Debug, Serialize, Deserialize)] // server + WASM
137
+ pub struct SecurityEvent {
138
+ pub id: uuid::Uuid,
139
+ pub attack_type: String,
140
+ pub risk_score: f32,
141
+ pub blocked: bool,
142
+ }
143
+ ```
144
+
145
+ **Cargo.toml do crate domain:**
146
+ ```toml
147
+ [dependencies]
148
+ serde = { version = "1.0", features = ["derive"] }
149
+ uuid = { version = "1.10", features = ["v4", "serde"] }
150
+
151
+ [dependencies.sqlx]
152
+ version = "0.8"
153
+ features = ["postgres", "runtime-tokio", "uuid"]
154
+ optional = true
155
+
156
+ [features]
157
+ ssr = ["dep:sqlx"]
158
+ ```
159
+
160
+ > O crate domain é compilado duas vezes: uma para x86 (server, com sqlx) e uma para wasm32 (client, sem sqlx). O `cfg_attr` garante que `sqlx::FromRow` só aparece na compilação server.
161
+
162
+ ---
163
+
164
+ ## 5. Configuração workspace misto (WASM + nativo)
165
+
166
+ Quando o workspace tem crates Leptos/WASM **e** crates nativos (ex: `napi-rs`, `aya`, Axum):
167
+
168
+ ```toml
169
+ # .cargo/config.toml na raiz do workspace
170
+ # NÃO definir [build] target global aqui.
171
+ # Cada crate define seu próprio target via features e cargo-leptos/trunk.
172
+ ```
173
+
174
+ ```toml
175
+ # Cargo.toml do workspace
176
+ [workspace]
177
+ resolver = "2"
178
+ members = [
179
+ "crates/ars-core", # lib nativa (x86)
180
+ "crates/ars-server", # bin nativo — Axum
181
+ "crates/ars-web", # bin+lib WASM — Leptos
182
+ "crates/ars-cli", # bin nativo
183
+ ]
184
+ # crates napi-rs ficam em workspace separado ou excluídos do members padrão
185
+ ```
186
+
187
+ ```toml
188
+ # crates/ars-web/Cargo.toml — features separam server de WASM
189
+ [features]
190
+ default = []
191
+ hydrate = ["leptos/hydrate"]
192
+ ssr = [
193
+ "dep:axum",
194
+ "dep:leptos_axum",
195
+ "dep:tokio",
196
+ "leptos/ssr",
197
+ ]
198
+ ```
199
+
200
+ > ⚠️ **Antipattern crítico**: adicionar `[build] target = "wasm32-unknown-unknown"` no `.cargo/config.toml` raiz quebra todos os crates nativos. `cargo leptos` e `trunk` gerenciam o target WASM internamente — não interfira.
201
+
202
+ ---
203
+
204
+ ## 6. Antipatterns a evitar
205
+
206
+ | Antipattern | Por quê | Alternativa |
207
+ |---|---|---|
208
+ | `wasm_bindgen` direto | Bypassa abstrações do Leptos, código frágil | Use APIs do Leptos (`web_sys` via feature quando necessário) |
209
+ | `panic!` em componentes | Derruba o app inteiro sem `ErrorBoundary` | `Result<_, ServerFnError>` + `ErrorBoundary` |
210
+ | `Effect` que faz fetch | Re-executa a cada render, difícil de cancelar | `Resource::new()` |
211
+ | `tokio::spawn` no client | `tokio` não existe no WASM | `spawn_local()` (wasm) ou só em server functions |
212
+ | `std::thread` no client | Não existe no WASM | Leptos signals para paralelismo reativo |
213
+ | `cargo leptos test` | Não existe — comando inválido | `cargo test --workspace` |
214
+ | `[build] target` global | Quebra crates nativos no workspace misto | Sem target global; cargo-leptos gerencia internamente |
215
+
216
+ ---
217
+
218
+ ## 7. Templates de tasks DARE para Leptos
219
+
220
+ Cole no `DARE/dare-dag.yaml` após gerar o blueprint:
221
+
222
+ ```yaml
223
+ # Task 1 — Workspace + AppShell + Router
224
+ - id: leptos-001
225
+ title: "Workspace, AppShell e Router base"
226
+ description: |
227
+ Configurar Cargo workspace com resolver = "2".
228
+ Criar App component com leptos_router::Router.
229
+ Criar layout base (header, main, footer).
230
+ Rota "/" → HomePage component.
231
+ Ralph Loop: cargo leptos build --release + cargo test --workspace + cargo clippy
232
+ depends_on: []
233
+
234
+ # Task 2 — Form com Action + server function
235
+ - id: leptos-002
236
+ title: "Form com Action e validação server-side"
237
+ description: |
238
+ Criar #[server] fn para processar o form.
239
+ Criar componente com ActionForm ou Action manual.
240
+ Validar input no server (retorna ServerFnError em caso de erro).
241
+ Exibir pending state com Action::pending() signal.
242
+ Ralph Loop: cargo leptos build --release + cargo test --workspace
243
+ depends_on: [leptos-001]
244
+
245
+ # Task 3 — Lista paginada com Resource + Suspense
246
+ - id: leptos-003
247
+ title: "Lista paginada com Resource, Suspense e error handling"
248
+ description: |
249
+ Criar Resource que recebe página como signal de parâmetro.
250
+ Envolver em Suspense com fallback de loading.
251
+ Usar ErrorBoundary para erros de fetch.
252
+ Adicionar paginação com For component e key por ID.
253
+ Ralph Loop: cargo leptos build --release + cargo test --workspace
254
+ depends_on: [leptos-001]
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 8. O que fazer agora
260
+
261
+ 1. **Leia `DARE/DESIGN.md`** para entender quais componentes e server functions são necessários
262
+ 2. **Rode `cargo leptos watch`** (fullstack) ou **`trunk serve`** (CSR) para confirmar que o scaffold compila
263
+ 3. **Gere `DARE/BLUEPRINT.md`** com `/dare-blueprint` — inclua:
264
+ - Tabela de componentes (nome, props, signals usados)
265
+ - Lista de server functions com assinatura
266
+ - Tipos compartilhados no crate domain
267
+ 4. **Use `/dare-rust-workspace`** se precisar decidir estrutura multi-crate
268
+
269
+ $ARGUMENTS
@@ -12,7 +12,7 @@ Você é o Claude Code, assistente de desenvolvimento seguindo o método DARE:
12
12
  - Atualize o status em `DARE/TASKS.md` ao concluir cada task
13
13
  - Nunca pule o Ralph Loop (build → test → lint) antes de marcar uma task como DONE
14
14
  - Aprovação humana obrigatória antes de merge para a branch principal
15
- - Use os slash commands `/dare-design`, `/dare-blueprint`, `/dare-execute`, `/dare-tasks`
15
+ - Use os slash commands `/dare-design`, `/dare-blueprint`, `/dare-execute`, `/dare-tasks`, `/dare-rust-leptos`, `/dare-rust-workspace`
16
16
 
17
17
  ## Estrutura do Projeto
18
18
  ```
@@ -78,6 +78,15 @@ dare execute --parallel # executa tasks em paralelo respeitando depends_on
78
78
  - Pinia para state management
79
79
  - Ralph Loop: `npm run build && npm test && npx eslint src`
80
80
 
81
+ ### Leptos (Rust → WASM) — v2.10+
82
+ - Dois modos: `rust-leptos` (SSR + Axum, cargo-leptos) e `rust-leptos-csr` (WASM puro, trunk)
83
+ - `#[component]` macro, signals reativos, `Resource` para async, `Action` para mutações
84
+ - **Nunca** usar `cargo leptos test` — use `cargo test --workspace`
85
+ - **Nunca** definir `[build] target` global no `.cargo/config.toml` (quebra workspace misto)
86
+ - Ralph Loop fullstack: `cargo leptos build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings`
87
+ - Ralph Loop CSR: `trunk build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings`
88
+ - Use `/dare-rust-leptos` para guia completo de idioms, tipos compartilhados e tasks
89
+
81
90
  ## Knowledge Graph (GraphRAG)
82
91
 
83
92
  O projeto mantém um grafo de conhecimento em `dare-graph.yml`: