Skip to content

Commit eaed929

Browse files
authored
feat: add jotai and css library support (#29)
1 parent fc3ad60 commit eaed929

14 files changed

Lines changed: 856 additions & 24 deletions

File tree

ARCHITECTURE.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ create-react-forge is a modular, layered CLI tool for scaffolding production-rea
4747
4848
┌────────────────────▼────────────────────────────────┐
4949
│ Docs Layer │
50-
│ (auto-generates ARCHITECTURE.md for projects)
50+
│ (auto-generates README.md & ARCHITECTURE.md)
5151
│ Location: src/docs/ │
5252
└─────────────────────────────────────────────────────┘
5353
```
@@ -111,7 +111,7 @@ interface ProjectConfig {
111111
runtime: 'vite' | 'nextjs';
112112
language: 'javascript' | 'typescript';
113113
styling: { solution: 'css' | 'tailwind' | 'styled-components' | 'css-modules' };
114-
stateManagement: 'none' | 'redux' | 'zustand';
114+
stateManagement: 'none' | 'redux' | 'zustand' | 'jotai';
115115
testing: TestingConfig;
116116
dataFetching: DataFetchingConfig;
117117
linting: LintingConfig;
@@ -144,7 +144,7 @@ interface ProjectConfig {
144144
1. Check if directory exists → fail if yes
145145
2. Load templates based on config
146146
3. Merge all template files
147-
4. Generate ARCHITECTURE.md for the project
147+
4. Generate README.md and ARCHITECTURE.md for the project
148148
5. Merge dependencies and scripts
149149
6. Write files to disk
150150
7. Initialize git (optional)
@@ -184,7 +184,8 @@ src/templates/overlays/
184184
│ └── styled-components/ # styled-components setup
185185
├── state/
186186
│ ├── redux/ # Redux Toolkit store structure
187-
│ └── zustand/ # Zustand store setup
187+
│ ├── zustand/ # Zustand store setup
188+
│ └── jotai/ # Jotai atomic state management
188189
├── features/
189190
│ └── tanstack-query/ # TanStack Query + hooks pattern
190191
├── testing/
@@ -221,7 +222,7 @@ interface TemplateManifest {
221222
1. **Base** — Core React files (components, hooks, lib, types)
222223
2. **Runtime** — Vite or Next.js specific configs
223224
3. **Styling** — Tailwind/CSS/Styled Components setup
224-
4. **State** — Redux/Zustand store setup
225+
4. **State** — Redux/Zustand/Jotai store setup
225226
5. **Features** — TanStack Query, etc.
226227
6. **Testing** — Vitest/Jest + RTL + Playwright
227228

@@ -294,6 +295,7 @@ interface TemplateManifest {
294295
'@reduxjs/toolkit': '^2.5.0',
295296
'react-redux': '^9.2.0',
296297
'zustand': '^5.0.3',
298+
'jotai': '^2.10.0',
297299

298300
// Data Fetching
299301
'@tanstack/react-query': '^5.62.10',
@@ -371,6 +373,12 @@ interface PluginContext {
371373

372374
#### Files:
373375

376+
- **readme-generator.ts** — Generates README.md
377+
- Dynamic project-specific README with tech stack badges
378+
- Package manager-specific commands
379+
- Available scripts table
380+
- Project structure overview
381+
- Documentation links based on config
374382
- **architecture-generator.ts** — Generates ARCHITECTURE.md
375383
- Creates project-specific documentation
376384
- Documents selected configuration
@@ -523,7 +531,7 @@ Load Template Overlays (TemplateRegistry)
523531
524532
Merge All Template Files
525533
526-
Generate ARCHITECTURE.md
534+
Generate README.md & ARCHITECTURE.md
527535
528536
Aggregate Dependencies (from manifests)
529537
@@ -668,16 +676,23 @@ npm run test:coverage # Coverage report
668676

669677
### Styling
670678

679+
Options are conditional based on runtime:
680+
681+
**Vite (4 options)**:
671682
- `tailwind` — Tailwind CSS v4 (recommended)
672-
- `css` — Plain CSS
673683
- `styled-components` — CSS-in-JS
674684
- `css-modules` — Scoped CSS
685+
- `css` — Plain CSS
686+
687+
**Next.js**:
688+
- `tailwind` — Auto-selected (recommended for App Router)
675689

676690
### State Management
677691

678692
- `none` — No setup (default)
679693
- `redux` — Redux Toolkit
680694
- `zustand` — Lightweight alternative
695+
- `jotai` — Primitive and flexible atomic state
681696

682697
### Testing
683698

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ The CLI is **prompt-driven**. You'll choose:
4444
- **Project directory**
4545
- **Runtime**: Vite or Next.js
4646
- **Language**: TypeScript or JavaScript
47-
- **Styling**: Tailwind, CSS, Styled Components, or CSS Modules
48-
- **State**: none, Zustand, or Redux Toolkit
47+
- **Styling**: Tailwind, Styled Components, CSS Modules, or Plain CSS (Vite offers all 4; Next.js auto-selects Tailwind)
48+
- **State**: none, Zustand, Jotai, or Redux Toolkit
4949
- **Testing**: full (unit+component+E2E), unit+component only, or none
5050
- **Unit runner**: Vitest or Jest (if testing enabled)
5151
- **E2E runner**: Playwright or Cypress (if full testing)
@@ -72,6 +72,7 @@ my-app/
7272
│ ├── testing/ # Test utilities, mocks (if selected)
7373
│ └── types/ # Shared types
7474
├── tests/ # E2E tests (if selected)
75+
├── README.md # Auto-generated project README
7576
├── ARCHITECTURE.md # Auto-generated architecture docs
7677
└── [config files]
7778
```
@@ -82,8 +83,8 @@ my-app/
8283
|---|---|
8384
| **Runtime** | `vite`, `nextjs` |
8485
| **Language** | `typescript`, `javascript` |
85-
| **Styling** | `tailwind`, `css`, `styled-components`, `css-modules` |
86-
| **State** | `none`, `zustand`, `redux` |
86+
| **Styling** | `tailwind`, `styled-components`, `css-modules`, `css` (Vite: all 4, Next.js: tailwind only) |
87+
| **State** | `none`, `zustand`, `jotai`, `redux` |
8788
| **Testing** | `full`, `unit-component`, `none` |
8889
| **Unit runner** | `vitest`, `jest` |
8990
| **E2E runner** | `playwright`, `cypress` |
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { STATE_DESCRIPTIONS, STYLING_DESCRIPTIONS } from '../config/defaults';
3+
import { StateManagementSchema, StylingSchema } from '../config/schema';
4+
5+
/**
6+
* Tests for conditional prompts logic
7+
* These tests verify the configuration and validation for runtime-dependent styling options
8+
*/
9+
describe('Conditional Prompts Configuration', () => {
10+
describe('Styling options', () => {
11+
it('should have all 4 styling options defined', () => {
12+
const stylingValues = StylingSchema.options;
13+
14+
expect(stylingValues).toContain('css');
15+
expect(stylingValues).toContain('tailwind');
16+
expect(stylingValues).toContain('styled-components');
17+
expect(stylingValues).toContain('css-modules');
18+
expect(stylingValues).toHaveLength(4);
19+
});
20+
21+
it('should have descriptions for all styling options', () => {
22+
expect(STYLING_DESCRIPTIONS.css).toBeDefined();
23+
expect(STYLING_DESCRIPTIONS.tailwind).toBeDefined();
24+
expect(STYLING_DESCRIPTIONS['styled-components']).toBeDefined();
25+
expect(STYLING_DESCRIPTIONS['css-modules']).toBeDefined();
26+
});
27+
28+
it('Vite should support all 4 styling options', () => {
29+
// For Vite, all 4 options should be valid
30+
const viteOptions = ['tailwind', 'styled-components', 'css-modules', 'css'];
31+
32+
viteOptions.forEach((option) => {
33+
const result = StylingSchema.safeParse(option);
34+
expect(result.success).toBe(true);
35+
});
36+
});
37+
38+
it('Next.js should use tailwind (auto-selected)', () => {
39+
// For Next.js, tailwind is auto-selected
40+
const nextjsDefault = 'tailwind';
41+
const result = StylingSchema.safeParse(nextjsDefault);
42+
43+
expect(result.success).toBe(true);
44+
});
45+
});
46+
47+
describe('State management options', () => {
48+
it('should have all state management options including jotai', () => {
49+
const stateValues = StateManagementSchema.options;
50+
51+
expect(stateValues).toContain('none');
52+
expect(stateValues).toContain('redux');
53+
expect(stateValues).toContain('zustand');
54+
expect(stateValues).toContain('jotai');
55+
expect(stateValues).toHaveLength(4);
56+
});
57+
58+
it('should have descriptions for all state management options', () => {
59+
expect(STATE_DESCRIPTIONS.none).toBeDefined();
60+
expect(STATE_DESCRIPTIONS.redux).toBeDefined();
61+
expect(STATE_DESCRIPTIONS.zustand).toBeDefined();
62+
expect(STATE_DESCRIPTIONS.jotai).toBeDefined();
63+
});
64+
65+
it('should validate jotai as a valid state management option', () => {
66+
const result = StateManagementSchema.safeParse('jotai');
67+
expect(result.success).toBe(true);
68+
});
69+
});
70+
71+
describe('Styling choices logic', () => {
72+
/**
73+
* Helper to get styling choices based on runtime
74+
*/
75+
function getStylingChoicesForRuntime(runtime: 'vite' | 'nextjs'): string[] {
76+
if (runtime === 'vite') {
77+
return ['tailwind', 'styled-components', 'css-modules', 'css'];
78+
}
79+
// Next.js auto-selects tailwind
80+
return ['tailwind'];
81+
}
82+
83+
it('should return 4 styling options for Vite', () => {
84+
const choices = getStylingChoicesForRuntime('vite');
85+
86+
expect(choices).toHaveLength(4);
87+
expect(choices).toContain('tailwind');
88+
expect(choices).toContain('styled-components');
89+
expect(choices).toContain('css-modules');
90+
expect(choices).toContain('css');
91+
});
92+
93+
it('should return only tailwind for Next.js', () => {
94+
const choices = getStylingChoicesForRuntime('nextjs');
95+
96+
expect(choices).toHaveLength(1);
97+
expect(choices).toContain('tailwind');
98+
});
99+
});
100+
});
101+

src/__tests__/integration/template-loading.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('TemplateRegistry', () => {
4747
expect(template.manifest.devDependencies).toHaveProperty('tailwindcss');
4848
});
4949

50-
it.skip('should load css-modules styling template', () => {
50+
it('should load css-modules styling template', () => {
5151
const template = registry.loadAndRegister('styling/css-modules');
5252

5353
expect(template).toBeDefined();
@@ -70,6 +70,22 @@ describe('TemplateRegistry', () => {
7070
expect(template.manifest.dependencies).toHaveProperty('@reduxjs/toolkit');
7171
});
7272

73+
it('should load jotai state template', () => {
74+
const template = registry.loadAndRegister('state/jotai');
75+
76+
expect(template).toBeDefined();
77+
expect(template.manifest).toBeDefined();
78+
expect(template.manifest.dependencies).toHaveProperty('jotai');
79+
});
80+
81+
it('should load styled-components styling template', () => {
82+
const template = registry.loadAndRegister('styling/styled-components');
83+
84+
expect(template).toBeDefined();
85+
expect(template.manifest).toBeDefined();
86+
expect(template.manifest.dependencies).toHaveProperty('styled-components');
87+
});
88+
7389
it('should load vitest testing template', () => {
7490
const template = registry.loadAndRegister('testing/vitest');
7591

@@ -151,6 +167,32 @@ describe('TemplateRegistry', () => {
151167
expect(templates.some(t => t.path === 'state/redux')).toBe(true);
152168
});
153169

170+
it('should load templates for Vite + Jotai config', () => {
171+
const templates = registry.loadTemplatesForConfig({
172+
runtime: 'vite',
173+
styling: { solution: 'styled-components' },
174+
stateManagement: 'jotai',
175+
testing: { enabled: false, e2e: { enabled: false, runner: 'none' } },
176+
dataFetching: { enabled: false },
177+
});
178+
179+
expect(templates.some(t => t.path === 'runtime/vite')).toBe(true);
180+
expect(templates.some(t => t.path === 'state/jotai')).toBe(true);
181+
expect(templates.some(t => t.path === 'styling/styled-components')).toBe(true);
182+
});
183+
184+
it('should load templates for Vite + CSS Modules config', () => {
185+
const templates = registry.loadTemplatesForConfig({
186+
runtime: 'vite',
187+
styling: { solution: 'css-modules' },
188+
stateManagement: 'none',
189+
testing: { enabled: false, e2e: { enabled: false, runner: 'none' } },
190+
dataFetching: { enabled: false },
191+
});
192+
193+
expect(templates.some(t => t.path === 'styling/css-modules')).toBe(true);
194+
});
195+
154196
it('should load testing templates when testing is enabled', () => {
155197
const templates = registry.loadTemplatesForConfig({
156198
runtime: 'vite',
@@ -279,9 +321,11 @@ describe('TemplateRegistry', () => {
279321
'runtime/vite',
280322
'runtime/nextjs',
281323
'styling/tailwind',
282-
// 'styling/css-modules', // Skipped - not available in CLI
324+
'styling/css-modules',
325+
'styling/styled-components',
283326
'state/zustand',
284327
'state/redux',
328+
'state/jotai',
285329
'testing/vitest',
286330
'testing/jest',
287331
'testing/playwright',
@@ -301,6 +345,8 @@ describe('TemplateRegistry', () => {
301345
const templatePaths = [
302346
'state/zustand',
303347
'state/redux',
348+
'state/jotai',
349+
'styling/styled-components',
304350
'testing/vitest',
305351
'testing/playwright',
306352
'features/tanstack-query',

0 commit comments

Comments
 (0)