@noego/app 0.0.7 → 0.0.10
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/.claude/settings.local.json +11 -4
- package/DEVELOPING.md +73 -0
- package/docs/asset-serving-fix.md +381 -0
- package/package.json +6 -2
- package/scripts/watch-harness.mjs +246 -0
- package/src/args.js +3 -1
- package/src/build/bootstrap.js +107 -5
- package/src/build/html.js +4 -2
- package/src/build/runtime-manifest.js +1 -1
- package/src/build/server.js +14 -4
- package/src/build/ssr.js +130 -13
- package/src/build/ui-common.js +19 -2
- package/src/client.js +14 -2
- package/src/commands/dev.js +239 -40
- package/src/config.js +10 -0
- package/src/runtime/runtime.js +49 -6
- package/test/asset-mounting.test.js +211 -0
- package/test/config-pipeline.test.js +353 -0
- package/test/path-resolution.test.js +164 -0
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
4
|
+
"Read(//Users/shavauhngabay/dev/noego/dinner/**)",
|
|
5
|
+
"Read(//Users/shavauhngabay/dev/ego/sqlstack/**)",
|
|
6
|
+
"Read(//Users/shavauhngabay/dev/ego/forge/**)",
|
|
7
|
+
"Read(//Users/shavauhngabay/dev/noblelaw/ui/**)",
|
|
8
|
+
"Read(//Users/shavauhngabay/dev/noblelaw/**)",
|
|
9
|
+
"mcp__chrome-devtools__take_screenshot",
|
|
10
|
+
"Bash(npm run build:ui:ssr:*)",
|
|
11
|
+
"mcp__chrome-devtools__navigate_page",
|
|
12
|
+
"Bash(node dist/hammer.js:*)",
|
|
13
|
+
"Bash(tee:*)",
|
|
14
|
+
"mcp__chrome-devtools__list_console_messages"
|
|
8
15
|
],
|
|
9
16
|
"deny": [],
|
|
10
17
|
"ask": []
|
package/DEVELOPING.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
**Purpose**
|
|
2
|
+
- Run and test dev watchers without blocking your terminal.
|
|
3
|
+
- Verify backend/frontend restarts on file changes using short, backgrounded runs with logs and PIDs.
|
|
4
|
+
|
|
5
|
+
**Prerequisites**
|
|
6
|
+
- Node 20+.
|
|
7
|
+
- This repo installed (run `npm install` once).
|
|
8
|
+
|
|
9
|
+
**Local Watch Harness (Fast, Backgrounded)**
|
|
10
|
+
- Start (runs ~10–15s, then exits on its own):
|
|
11
|
+
- `Timestamp=$(date +%s); LOG=watch-harness-$Timestamp.log; node scripts/watch-harness.mjs > $LOG 2>&1 & echo $! > watch-harness.pid`
|
|
12
|
+
- Observe:
|
|
13
|
+
- `tail -f $LOG`
|
|
14
|
+
- Look for lines like:
|
|
15
|
+
- `🔄 change detected (change): server/stitch.yaml` (BACKEND)
|
|
16
|
+
- `🔄 change detected (change): hammer.config.yml` (SHARED)
|
|
17
|
+
- `🚀 [backend restart #X]` / `🚀 [frontend restart #X]`
|
|
18
|
+
- `✅ restart sequence complete`
|
|
19
|
+
- Stop (if ever needed):
|
|
20
|
+
- `kill $(cat watch-harness.pid)`
|
|
21
|
+
- What it does
|
|
22
|
+
- Seeds a temp tree under `.watch-harness/`.
|
|
23
|
+
- Watches directories (server/ui/middleware/openapi/repo) and classifies events using your globs.
|
|
24
|
+
- Spawns mock backend/frontend child processes, simulates backend/frontend/shared changes, restarts the right process(es), then shuts down.
|
|
25
|
+
|
|
26
|
+
**Real App Dev (noblelaw) in Background**
|
|
27
|
+
- Start dev for the noblelaw project in the background with logs:
|
|
28
|
+
- `NO_COLOR=1 node ./bin/app.js dev --root /Users/shavauhngabay/dev/noblelaw > noblelaw-dev.log 2>&1 & echo $! > noblelaw-dev.pid`
|
|
29
|
+
- Observe:
|
|
30
|
+
- `tail -f noblelaw-dev.log`
|
|
31
|
+
- Expect on startup: backend on port 3002, frontend (Vite) on 3001, router on 3000.
|
|
32
|
+
- Make edits to trigger restarts:
|
|
33
|
+
- Backend restart: edit `server/services/*.ts`, `middleware/**/*.ts`, `server/openapi/**/*.yaml`, or `server/repo/**/*.sql`.
|
|
34
|
+
- Frontend restart: edit `ui/**/*.ts`, `ui/openapi/**/*.yaml`.
|
|
35
|
+
- Shared restart: edit `index.ts` or `hammer.config.yml`.
|
|
36
|
+
- Note: `.svelte` changes do not restart the server; Vite HMR handles them.
|
|
37
|
+
- Look for logs:
|
|
38
|
+
- `🔄 FILE CHANGE DETECTED: …`
|
|
39
|
+
- `Type: BACKEND | FRONTEND | SHARED`
|
|
40
|
+
- `🚀 [RESTART #…] Starting …`
|
|
41
|
+
- `✅ Restart complete`
|
|
42
|
+
- Stop:
|
|
43
|
+
- `kill $(cat noblelaw-dev.pid)`
|
|
44
|
+
- Free busy ports (if you see EADDRINUSE):
|
|
45
|
+
- `for p in 3000 3001 3002; do P=$(lsof -t -nP -iTCP:$p -sTCP:LISTEN 2>/dev/null); [ -n "$P" ] && kill -9 $P; done`
|
|
46
|
+
|
|
47
|
+
**How The Watcher Works**
|
|
48
|
+
- Chokidar now watches concrete directories/files so it always attaches:
|
|
49
|
+
- Directories: `server/`, `ui/`, `middleware/`, `server/openapi/`, `server/repo/`, `ui/openapi/`.
|
|
50
|
+
- Files: `index.ts`, `hammer.config.yml`, `server/stitch.yaml`, `ui/stitch.yaml`.
|
|
51
|
+
- Classification still uses your original globs (picomatch):
|
|
52
|
+
- Backend: `server/**/*.ts`, `middleware/**/*.ts`, `server/stitch.yaml`, `server/openapi/**/*.yaml`, `server/repo/**/*.sql`.
|
|
53
|
+
- Frontend: `ui/**/*.ts`, `ui/stitch.yaml`, `ui/openapi/**/*.yaml`.
|
|
54
|
+
- Shared: `index.ts`, `hammer.config.yml` (plus any app-level watch entries).
|
|
55
|
+
- This avoids the “ready but no watchers” problem when globs don’t match initial files.
|
|
56
|
+
|
|
57
|
+
**Troubleshooting**
|
|
58
|
+
- No restart logs after editing a file:
|
|
59
|
+
- Confirm the file lives under a watched directory and matches one of the classification globs above.
|
|
60
|
+
- `.svelte` isn’t supposed to restart—use Vite HMR in the browser.
|
|
61
|
+
- Watcher error (EMFILE or similar):
|
|
62
|
+
- The CLI now fails fast on watcher errors with a clear log and exit.
|
|
63
|
+
- Ensure you’re not watching the entire repo root or `node_modules/`; keep to `server/`, `ui/`, `middleware/` and the specific YAML/SQL roots.
|
|
64
|
+
- Ports in use (EADDRINUSE):
|
|
65
|
+
- Free 3000/3001/3002 using the snippet above and restart dev.
|
|
66
|
+
- Clean up stray mock processes from the harness (if any):
|
|
67
|
+
- `pkill -f "\[backend\]"; pkill -f "\[frontend\]"`
|
|
68
|
+
|
|
69
|
+
**Operational Tips**
|
|
70
|
+
- Always background long-running scripts and write the PID to a file.
|
|
71
|
+
- Tail logs for verification and kill by PID when done.
|
|
72
|
+
- Keep a hard timeout in any custom test harness so it self-exits if something goes wrong.
|
|
73
|
+
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Asset Serving Architecture Investigation
|
|
2
|
+
|
|
3
|
+
**Status:** Under Investigation
|
|
4
|
+
**Date:** 2025-01-11 (Updated)
|
|
5
|
+
**Author:** Engineering Team
|
|
6
|
+
**Context:** Deep dive after initial runtime.js fix revealed deeper Forge integration issue
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Investigation Summary
|
|
11
|
+
|
|
12
|
+
After fixing the initial asset serving issue in `runtime.js` (mounting `.app` at root `/`), we discovered a **deeper architectural problem**: The browser is loading SSR components instead of client-bundled components, causing `effect_orphan` errors.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## What We Know FOR SURE (Confirmed via Code Review & Logs)
|
|
17
|
+
|
|
18
|
+
### 1. Build Output Structure
|
|
19
|
+
```
|
|
20
|
+
dist/
|
|
21
|
+
├── .app/
|
|
22
|
+
│ ├── assets/ ← Client-bundled components (Vite output)
|
|
23
|
+
│ │ ├── chunks/ ← Shared code chunks
|
|
24
|
+
│ │ └── components/ ← Browser-ready .js files
|
|
25
|
+
│ │ ├── layout/
|
|
26
|
+
│ │ │ └── root.js ← VITE-BUNDLED, imports from ../../chunks/
|
|
27
|
+
│ │ └── views/
|
|
28
|
+
│ │ └── home.js
|
|
29
|
+
│ └── ssr/ ← SSR components (server-side)
|
|
30
|
+
│ ├── chunks/
|
|
31
|
+
│ └── components/
|
|
32
|
+
│ ├── layout/
|
|
33
|
+
│ │ └── root.js ← SSR VERSION, bare imports fail in browser
|
|
34
|
+
│ └── views/
|
|
35
|
+
│ └── home.js
|
|
36
|
+
├── no_ego.js ← Bootstrap config
|
|
37
|
+
└── [compiled app files]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Generated Config Values (from `no_ego.js`)
|
|
41
|
+
```javascript
|
|
42
|
+
{
|
|
43
|
+
"component_dir": ".app/ssr/components",
|
|
44
|
+
"component_dir_ssr": ".app/ssr/components",
|
|
45
|
+
"component_dir_client": "/assets/components",
|
|
46
|
+
"component_base_path": "/assets",
|
|
47
|
+
"assets_build_dir": ".app/assets/components",
|
|
48
|
+
"component_suffix": "components"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Express Static Mounts (4 total)
|
|
53
|
+
|
|
54
|
+
**Mount 1** (`runtime.js` line 701-702):
|
|
55
|
+
```javascript
|
|
56
|
+
app.use('/client', express.static('/dist/.app/assets'))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Mount 2** (`runtime.js` line 703-704):
|
|
60
|
+
```javascript
|
|
61
|
+
app.use('/chunks', express.static('/dist/.app/ssr/chunks'))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Mount 3** (Forge `server.ts` via `client.js` assets):
|
|
65
|
+
```javascript
|
|
66
|
+
app.use('/', express.static('/dist/.app/assets'))
|
|
67
|
+
app.use('/assets', express.static('/dist/.app/assets'))
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Mount 4** (Forge `server.ts` line 135-137):
|
|
71
|
+
```javascript
|
|
72
|
+
app.use('/.app/ssr/components', express.static('/dist/.app/ssr/components'))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. Browser Behavior (Confirmed via Network Tab)
|
|
76
|
+
```
|
|
77
|
+
Browser requests: http://localhost:3050/.app/ssr/components/layout/root.js
|
|
78
|
+
Server responds: 200 OK (serves SSR component file)
|
|
79
|
+
Browser executes: SSR JavaScript with bare imports
|
|
80
|
+
Result: Module not found errors + effect_orphan error
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 5. Forge's Component Directory Injection (Line 264)
|
|
84
|
+
```typescript
|
|
85
|
+
// express_server_adapter.ts:264
|
|
86
|
+
const clientComponentDir = this.isProd ? this.componentDir : '/assets';
|
|
87
|
+
|
|
88
|
+
// Line 281:
|
|
89
|
+
window.__COMPONENT_DIR__ = ${JSON.stringify(clientComponentDir)}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**In production mode:**
|
|
93
|
+
- `this.componentDir` = `".app/ssr/components"` (passed from hammer/client.js)
|
|
94
|
+
- Therefore: `window.__COMPONENT_DIR__ = ".app/ssr/components"`
|
|
95
|
+
- Browser constructs URLs: `/.app/ssr/components/layout/root.js`
|
|
96
|
+
|
|
97
|
+
### 6. Forge's Single component_dir Design (Confirmed)
|
|
98
|
+
**File:** `/Users/shavauhngabay/dev/forge/src/options/ServerOptions.ts`
|
|
99
|
+
```typescript
|
|
100
|
+
export interface ServerOptions {
|
|
101
|
+
component_dir?: string; // SINGLE option for BOTH SSR and client
|
|
102
|
+
// NO component_dir_ssr
|
|
103
|
+
// NO component_dir_client
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Usage in `server.ts` line 67:**
|
|
108
|
+
```typescript
|
|
109
|
+
const COMPONENT_DIR = !full_options.component_dir ? root : path.join(root, full_options.component_dir);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Used for:**
|
|
113
|
+
- SSR: `ProdComponentLoader(COMPONENT_DIR)` - loads components server-side
|
|
114
|
+
- Client: Passed to `ExpressServerAdapter` → becomes `window.__COMPONENT_DIR__`
|
|
115
|
+
- Static serving: `app.use(\`/\${component_dir}\`, express.static(COMPONENT_DIR))`
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## The Core Problem
|
|
120
|
+
|
|
121
|
+
### What's Happening
|
|
122
|
+
1. **hammer/client.js** passes: `component_dir: '.app/ssr/components'`
|
|
123
|
+
2. **Forge** uses this for EVERYTHING:
|
|
124
|
+
- SSR component loading ✅ (correct - Node.js can load SSR files)
|
|
125
|
+
- Client directory injection ❌ (wrong - browser gets SSR path)
|
|
126
|
+
- Static file serving ✅ (Mount 4 serves SSR files at `/.app/ssr/components`)
|
|
127
|
+
3. **Browser** receives: `window.__COMPONENT_DIR__ = ".app/ssr/components"`
|
|
128
|
+
4. **Browser** requests: `/.app/ssr/components/layout/root.js`
|
|
129
|
+
5. **Express** serves SSR component (via Mount 4) ✅
|
|
130
|
+
6. **Browser** executes SSR JavaScript:
|
|
131
|
+
- Contains bare imports: `import { onMount } from 'svelte'`
|
|
132
|
+
- Contains chunk imports: `await import("../../chunks/liveWebsocket-BL1FcyHq.js")`
|
|
133
|
+
- Resolves to: `.app/ssr/chunks/liveWebsocket-BL1FcyHq.js` ❌ (doesn't exist)
|
|
134
|
+
7. **Result:** Module not found → `effect_orphan` error
|
|
135
|
+
|
|
136
|
+
### Why SSR Components Fail in Browser
|
|
137
|
+
SSR components are compiled for Node.js:
|
|
138
|
+
- Use bare module imports (`'svelte'`, `'svelte/server'`)
|
|
139
|
+
- Expect Node.js module resolution
|
|
140
|
+
- Cannot run in browser environment
|
|
141
|
+
|
|
142
|
+
Client components are Vite-bundled for browsers:
|
|
143
|
+
- All imports are resolved and bundled
|
|
144
|
+
- Chunks are properly linked
|
|
145
|
+
- Can execute in browser
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## What We're GUESSING / Hypothesizing
|
|
150
|
+
|
|
151
|
+
### Hypothesis 1: We're Passing the Wrong Path
|
|
152
|
+
**Theory:** We should pass `component_dir_client` to Forge instead of `component_dir_ssr`.
|
|
153
|
+
|
|
154
|
+
**Problem:** If we pass `/assets/components`:
|
|
155
|
+
- Browser would load client components ✅ (correct)
|
|
156
|
+
- But SSR would try to load from `/dist/assets/components/` ❌
|
|
157
|
+
- SSR files are actually at `/dist/.app/ssr/components/`
|
|
158
|
+
- SSR would break
|
|
159
|
+
|
|
160
|
+
**Conclusion:** Forge's single-path design doesn't support separate SSR/client directories.
|
|
161
|
+
|
|
162
|
+
### Hypothesis 2: Build Output Should Match Forge's Expectations
|
|
163
|
+
**Theory:** Maybe we should output components to a single directory that works for both SSR and client.
|
|
164
|
+
|
|
165
|
+
**Problem:** SSR and client components are fundamentally different:
|
|
166
|
+
- SSR: ES modules with bare imports (for Node.js)
|
|
167
|
+
- Client: Vite-bundled with resolved imports (for browser)
|
|
168
|
+
- They MUST be separate files
|
|
169
|
+
|
|
170
|
+
**Conclusion:** We need two directories, not one.
|
|
171
|
+
|
|
172
|
+
### Hypothesis 3: Forge Needs to Support Separate Paths
|
|
173
|
+
**Theory:** Modify Forge to accept `component_dir_ssr` and `component_dir_client`.
|
|
174
|
+
|
|
175
|
+
**Changes needed:**
|
|
176
|
+
1. `ServerOptions.ts`: Add new optional fields
|
|
177
|
+
2. `server.ts`: Calculate both paths
|
|
178
|
+
3. `express_server_adapter.ts`: Use client path for `window.__COMPONENT_DIR__`
|
|
179
|
+
4. `ProdComponentLoader`: Use SSR path for server-side loading
|
|
180
|
+
|
|
181
|
+
**Status:** This seems like the correct solution, but requires Forge modifications.
|
|
182
|
+
|
|
183
|
+
### Hypothesis 4: Pass Relative Path "components" to Forge
|
|
184
|
+
**Theory:** The original hammer.config.yml specifies `componentDir: frontend/components`. Relative to `frontend/frontend.ts`, this is just `components`. Maybe we should pass just `"components"` to Forge?
|
|
185
|
+
|
|
186
|
+
**Analysis:**
|
|
187
|
+
- Forge would calculate: `COMPONENT_DIR = /dist/components`
|
|
188
|
+
- Browser would request: `/components/layout/root.js`
|
|
189
|
+
- But files don't exist at `/dist/components/` ❌
|
|
190
|
+
- Files are at `/dist/.app/assets/components/` and `/dist/.app/ssr/components/`
|
|
191
|
+
|
|
192
|
+
**Conclusion:** This doesn't match our current build structure.
|
|
193
|
+
|
|
194
|
+
### Hypothesis 5: Change Build Structure
|
|
195
|
+
**Theory:** Output components to `/dist/components/` (single location) instead of separate SSR/client dirs.
|
|
196
|
+
|
|
197
|
+
**Problems:**
|
|
198
|
+
- How would we differentiate SSR vs client builds?
|
|
199
|
+
- Vite outputs to one location, SSR to another
|
|
200
|
+
- Would require major build pipeline changes
|
|
201
|
+
- Unclear if this is even possible with current tools
|
|
202
|
+
|
|
203
|
+
**Status:** Needs more investigation into Vite's capabilities.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Original Config Intent
|
|
208
|
+
|
|
209
|
+
**From `hammer.config.yml`:**
|
|
210
|
+
```yaml
|
|
211
|
+
client:
|
|
212
|
+
main: frontend/frontend.ts # UI root: /project/frontend/
|
|
213
|
+
componentDir: frontend/components # Components: /project/frontend/components/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Relative calculation:**
|
|
217
|
+
- UI root: `frontend/`
|
|
218
|
+
- Component dir: `frontend/components/`
|
|
219
|
+
- Relative path: `components/` (just the subdirectory)
|
|
220
|
+
|
|
221
|
+
**Bootstrap calculates:**
|
|
222
|
+
- `component_dir_ssr`: `.app/ssr/components`
|
|
223
|
+
- `component_dir_client`: `/assets/components`
|
|
224
|
+
|
|
225
|
+
**The question:** Should Forge receive:
|
|
226
|
+
- Option A: `.app/ssr/components` (full SSR path, current behavior)
|
|
227
|
+
- Option B: `/assets/components` (full client path, breaks SSR)
|
|
228
|
+
- Option C: `components` (relative path, no files at `/dist/components/`)
|
|
229
|
+
- Option D: Both paths via new Forge API
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Potential Solutions
|
|
234
|
+
|
|
235
|
+
### Solution 1: Modify Forge (Most Correct)
|
|
236
|
+
**Pros:**
|
|
237
|
+
- Clean separation of concerns
|
|
238
|
+
- SSR and client use correct directories
|
|
239
|
+
- No breaking changes to build pipeline
|
|
240
|
+
|
|
241
|
+
**Cons:**
|
|
242
|
+
- Requires Forge modifications
|
|
243
|
+
- Need to update Forge across all projects
|
|
244
|
+
|
|
245
|
+
**Files to change:**
|
|
246
|
+
1. `forge/src/options/ServerOptions.ts` - Add `component_dir_ssr` and `component_dir_client`
|
|
247
|
+
2. `forge/src/server/server.ts` - Calculate both paths
|
|
248
|
+
3. `forge/src/routing/server_adapter/express_server_adapter.ts` - Use client path for injection
|
|
249
|
+
4. `hammer/src/client.js` - Pass both paths
|
|
250
|
+
|
|
251
|
+
### Solution 2: Change Hammer Build to Output Single Component Directory
|
|
252
|
+
**Approach:** Build both SSR and client to same location, differentiate by filename suffix.
|
|
253
|
+
|
|
254
|
+
**Example:**
|
|
255
|
+
```
|
|
256
|
+
dist/components/
|
|
257
|
+
├── layout/
|
|
258
|
+
│ ├── root.ssr.js # For server-side rendering
|
|
259
|
+
│ └── root.js # For browser
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Pros:**
|
|
263
|
+
- Works with Forge's current API
|
|
264
|
+
- No Forge modifications needed
|
|
265
|
+
|
|
266
|
+
**Cons:**
|
|
267
|
+
- Major build pipeline changes
|
|
268
|
+
- Unclear if Vite supports this pattern
|
|
269
|
+
- May break component loader expectations
|
|
270
|
+
- High risk of regressions
|
|
271
|
+
|
|
272
|
+
### Solution 3: Pass Client Path to Forge, Handle SSR Separately
|
|
273
|
+
**Approach:** Pass `/assets/components` to Forge, manually load SSR components elsewhere.
|
|
274
|
+
|
|
275
|
+
**Problems:**
|
|
276
|
+
- How would SSR loading work?
|
|
277
|
+
- Would need to bypass Forge's component loader
|
|
278
|
+
- Unclear if this is even possible
|
|
279
|
+
- High complexity, fragile
|
|
280
|
+
|
|
281
|
+
### Solution 4: Mount Client Components at SSR Path
|
|
282
|
+
**Approach:** Add another Express mount that serves client components at `/.app/ssr/components`.
|
|
283
|
+
|
|
284
|
+
**Example:**
|
|
285
|
+
```javascript
|
|
286
|
+
app.use('/.app/ssr/components', express.static('/dist/.app/assets/components'))
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Pros:**
|
|
290
|
+
- No code changes to Forge
|
|
291
|
+
- Quick fix
|
|
292
|
+
|
|
293
|
+
**Cons:**
|
|
294
|
+
- Conceptually wrong (SSR path serves client files)
|
|
295
|
+
- Confusing for developers
|
|
296
|
+
- SSR would still try to load from wrong location
|
|
297
|
+
- Doesn't fix the root problem
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Open Questions
|
|
302
|
+
|
|
303
|
+
1. **Can Vite output to a single component directory?**
|
|
304
|
+
- Can we build SSR and client to same location with different naming?
|
|
305
|
+
- Does this conflict with Vite's chunk optimization?
|
|
306
|
+
|
|
307
|
+
2. **Does Forge's dev mode work correctly?**
|
|
308
|
+
- Dev mode uses Vite dev server
|
|
309
|
+
- Does it have the same SSR/client split issue?
|
|
310
|
+
- Line 264: `clientComponentDir = this.isProd ? this.componentDir : '/assets'`
|
|
311
|
+
- In dev: Always uses `/assets` - why does this work?
|
|
312
|
+
|
|
313
|
+
3. **Is the `.app/` directory structure mandatory?**
|
|
314
|
+
- Could we output to `/dist/ssr/` and `/dist/assets/` instead?
|
|
315
|
+
- Would this simplify path calculations?
|
|
316
|
+
|
|
317
|
+
4. **What did the original Forge design expect?**
|
|
318
|
+
- Was Forge designed for projects with separate SSR/client builds?
|
|
319
|
+
- Or was it designed for unified component directories?
|
|
320
|
+
|
|
321
|
+
5. **Are there other projects using this stack successfully?**
|
|
322
|
+
- How do they structure their builds?
|
|
323
|
+
- Can we learn from existing patterns?
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Next Steps
|
|
328
|
+
|
|
329
|
+
1. **Decide on solution approach:**
|
|
330
|
+
- Option 1: Modify Forge (cleanest, most correct)
|
|
331
|
+
- Option 2: Restructure build output (risky, high effort)
|
|
332
|
+
- Option 3: Quick mount hack (temporary workaround)
|
|
333
|
+
|
|
334
|
+
2. **If Option 1 (Modify Forge):**
|
|
335
|
+
- Design the new API surface
|
|
336
|
+
- Write tests for dual-path support
|
|
337
|
+
- Implement changes in Forge
|
|
338
|
+
- Update hammer to use new API
|
|
339
|
+
- Test with markdown_view, liftlog, noblelaw
|
|
340
|
+
|
|
341
|
+
3. **If Option 2 (Restructure Build):**
|
|
342
|
+
- Research Vite multi-output capabilities
|
|
343
|
+
- Design new directory structure
|
|
344
|
+
- Update build pipeline
|
|
345
|
+
- Test thoroughly across all projects
|
|
346
|
+
|
|
347
|
+
4. **Validate the fix:**
|
|
348
|
+
- Run all 18 unit tests
|
|
349
|
+
- Test markdown_view in production mode
|
|
350
|
+
- Verify no `effect_orphan` errors
|
|
351
|
+
- Confirm browser loads correct (client) components
|
|
352
|
+
- Verify SSR still works
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Timeline Estimate
|
|
357
|
+
|
|
358
|
+
- **Option 1 (Modify Forge):** 2-3 hours implementation + 1 hour testing
|
|
359
|
+
- **Option 2 (Restructure Build):** 1-2 days research + 2-3 days implementation + 2 hours testing
|
|
360
|
+
- **Option 3 (Quick Hack):** 30 minutes, but doesn't solve root cause
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Files Reference
|
|
365
|
+
|
|
366
|
+
### Confirmed File Locations
|
|
367
|
+
- `/Users/shavauhngabay/dev/hammer/src/client.js` - Passes component_dir to Forge
|
|
368
|
+
- `/Users/shavauhngabay/dev/hammer/src/runtime/runtime.js` - Express static mounts
|
|
369
|
+
- `/Users/shavauhngabay/dev/hammer/src/build/bootstrap.js` - Calculates component paths
|
|
370
|
+
- `/Users/shavauhngabay/dev/forge/src/server/server.ts` - Forge server initialization
|
|
371
|
+
- `/Users/shavauhngabay/dev/forge/src/options/ServerOptions.ts` - Forge options interface
|
|
372
|
+
- `/Users/shavauhngabay/dev/forge/src/routing/server_adapter/express_server_adapter.ts` - Injects window.__COMPONENT_DIR__
|
|
373
|
+
- `/Users/shavauhngabay/dev/forge/src/routing/component_loader/component_loader.ts` - ProdComponentLoader
|
|
374
|
+
- `/Users/shavauhngabay/dev/markdown_view/hammer.config.yml` - Project configuration
|
|
375
|
+
- `/Users/shavauhngabay/dev/markdown_view/dist/no_ego.js` - Generated runtime config
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
**END OF DOCUMENT**
|
|
380
|
+
|
|
381
|
+
For questions or to proceed with implementation, contact the engineering team.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noego/app",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Production build tool for Dinner/Forge apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"author": "App Build CLI",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"deepmerge": "^4.3.1",
|
|
33
|
+
"glob-parent": "^6.0.2",
|
|
34
|
+
"http-proxy": "^1.18.1",
|
|
33
35
|
"picomatch": "^2.3.1",
|
|
34
36
|
"yaml": "^2.6.0"
|
|
35
37
|
},
|
|
@@ -38,5 +40,7 @@
|
|
|
38
40
|
"@noego/forge": "*",
|
|
39
41
|
"express": "*"
|
|
40
42
|
},
|
|
41
|
-
"devDependencies": {
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"chokidar": "^4.0.3"
|
|
45
|
+
}
|
|
42
46
|
}
|