Skip to content

Commit 352d139

Browse files
feat: better diagnostics, dependency-aware auto render.
1 parent d4216ce commit 352d139

13 files changed

Lines changed: 1556 additions & 887 deletions

docs/issue-62-continuation-prompt.md

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ remaining edge-case regressions without broad UI redesign.
2727
- Entry-role guard is in place (entry tab cannot be removed).
2828
- Entry filename contract is enforced (`App.tsx` or `App.js`).
2929
- One-visible-editor behavior is in place with tab-driven visibility.
30-
- Full Playwright suite was passing after test cleanup and refactor alignment.
30+
- Workspace preview runs in iframe with per-tab virtual module scope (no shared-scope hydration concatenation).
31+
- Deterministic workspace graph errors for missing/circular imports are covered.
32+
- Focused Playwright coverage for workspace isolation and preview regressions is in place.
33+
- Auto-render now gates component edits to the active entry dependency graph (unrelated component-module edits do not rerender preview).
3134

3235
### Remaining focus areas
3336

@@ -40,25 +43,21 @@ remaining edge-case regressions without broad UI redesign.
4043

4144
- Verify entry tab restore and initial-load selection remain stable.
4245
- Keep preview entry resolution aligned with tab metadata (`role: entry`) and documented fallback behavior.
46+
- Ensure startup render order cannot race editor hydration (component/styles) and silently use stale defaults.
4347

4448
3. Remove/add/rename coherence
4549

4650
- Keep fallback tab selection deterministic after remove.
4751
- Ensure add and rename flows do not drift name/path/content synchronization.
4852

49-
4. Dead migration branch cleanup
50-
51-
- Remove clearly obsolete helper paths or style/DOM hooks that are no longer used.
52-
- Avoid speculative cleanup outside touched areas.
53-
54-
5. Workspace import specifier compatibility
53+
4. Workspace import specifier compatibility
5554

5655
- Support ESM-style runtime specifiers for workspace modules (for example importing
5756
`./src/components/module.js` from an underlying `.ts`/`.tsx` tab when appropriate).
5857
- Keep resolution deterministic: exact-match path first, extension-compat fallback second,
5958
with explicit handling for ambiguous matches.
6059

61-
6. Extension-driven tab kind detection
60+
5. Extension-driven tab kind detection
6261

6362
- Ensure new tabs can be created as CSS tabs without relying on active-tab lane assumptions.
6463
- Add flow should expose explicit editor type selection (`Component`, `Styles`, `Auto`)
@@ -68,42 +67,74 @@ remaining edge-case regressions without broad UI redesign.
6867
- `Auto` should infer from extension, while explicit user selection should override inference.
6968
- Keep editor language, tools, and render pipeline wiring aligned with inferred tab kind.
7069

70+
6. Render cadence and stale-error recovery
71+
72+
- Preserve dependency-aware auto-render gating across add/remove/rename and entry changes.
73+
- Eliminate stale error carryover: previous errors must not persist once source/runtime state is corrected.
74+
- Ensure success transitions always clear prior preview error state and stale diagnostics payloads.
75+
- Confirm blob/module disposal and rerender cleanup cannot preserve stale failing module graphs.
76+
77+
7. React runtime correctness in iframe preview
78+
79+
- Verify React mode event handlers execute against the latest compiled module output.
80+
- Investigate and fix runtime regressions such as `TypeError: Assignment to constant variable` (for example from stale/cached module execution or invalid transform output).
81+
- Ensure React mode uses consistent runtime contracts between transpile options, module prelude, and iframe bootstrap render path.
82+
- Ensure React and DOM mode switching does not leave stale runtime state in the iframe.
83+
7184
### Suggested execution sequence
7285

73-
1. Audit high-risk flows in `src/app.js`:
86+
1. Audit iframe diagnostics pipeline end-to-end:
87+
88+
- `src/modules/preview-runtime/iframe-preview-executor.js` (postMessage/error bridge)
89+
- `src/modules/render-runtime.js` (error normalization/surfacing)
90+
- `src/modules/jsx-transform-runtime.js` and transform diagnostics formatting path
91+
92+
2. Verify active-tab/startup coherence in `src/app.js`:
7493

7594
- `setActiveWorkspaceTab`
7695
- `loadWorkspaceTabIntoEditor`
7796
- remove-tab fallback logic
7897
- startup restore path
7998

80-
2. Confirm entry resolution consistency with `src/modules/preview-entry-resolver.js`.
99+
3. Confirm entry resolution and module planning consistency:
100+
101+
- `src/modules/preview-entry-resolver.js`
102+
- `src/modules/preview-runtime/virtual-workspace-modules.js`
81103

82-
3. Re-test high-risk interactions:
104+
4. Re-test high-risk interactions:
83105

84106
- first load/restore with entry tab
85107
- tab switching across component and style tabs
86108
- add tab, rename tab, remove non-entry tab
87109
- style mode switches with preview render continuity
110+
- JSX syntax/transform failures still reported as `[jsx] ...`
111+
- iframe runtime exceptions surfaced with stable, non-duplicated messaging
112+
- repeated source edits do not trigger runaway rerender loops or duplicate execution
113+
- unrelated non-entry module edits do not rerender preview unless the module is in the active entry import graph
114+
- correcting an error fully recovers preview output without requiring unrelated edits
115+
- React mode click handlers work reliably after multiple rerenders/mode switches
88116
- importing workspace modules via `.js` specifiers when source tabs are `.ts`/`.tsx`
89117
- creating and renaming tabs with style extensions to verify styles-tab behavior
90118

91-
4. Run validation:
119+
5. Run validation:
92120

93121
```bash
94122
npm run lint
95123
npm run build
96124
npm run test:e2e
97125
```
98126

99-
5. Update docs only if behavior contract changes.
127+
6. Update docs only if behavior contract changes.
100128

101129
### Definition of done
102130

103131
- Active tab id, visible editor, and persisted content remain synchronized.
104132
- Entry tab is stable on startup and remains renderable.
105133
- Remove/add/rename flows are deterministic under rapid interaction.
134+
- JSX transform failures are surfaced as `[jsx]` diagnostics with codeframe/help when available.
135+
- Iframe runtime and module errors are surfaced deterministically without duplicate/noisy reporting.
136+
- Render pipeline does not over-execute, and stale error state is fully cleared on valid rerender.
137+
- React mode event handlers execute correctly without stale-cache/runtime corruption errors.
106138
- Workspace import resolution supports documented ESM-style extension compatibility.
107139
- New tab behavior correctly recognizes style-file extensions and routes to styles semantics.
108-
- No stale migration branches remain in touched code.
109140
- Lint/build/e2e pass.

playwright/helpers/app-test-helpers.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,50 @@ export const expectPreviewHasRenderedContent = async (page: Page) => {
117117
.toBeGreaterThan(0)
118118
}
119119

120+
const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
121+
122+
export const getPreviewFrame = (page: Page) => page.frameLocator('#preview-host iframe')
123+
124+
export const addWorkspaceTab = async (page: Page) => {
125+
await page.getByRole('button', { name: 'Add tab' }).click()
126+
}
127+
128+
export const openWorkspaceTab = async (page: Page, fileName: string) => {
129+
const pattern = new RegExp(`^Open tab ${escapeRegex(fileName)}$`)
130+
await page.getByRole('tab', { name: pattern }).click()
131+
}
132+
133+
export const setWorkspaceTabSource = async (
134+
page: Page,
135+
{
136+
fileName,
137+
source,
138+
kind = 'component',
139+
}: {
140+
fileName: string
141+
source: string
142+
kind?: 'component' | 'styles'
143+
},
144+
) => {
145+
await openWorkspaceTab(page, fileName)
146+
const editorContent = page
147+
.locator(`.editor-panel[data-editor-kind="${kind}"] .cm-content`)
148+
.first()
149+
await editorContent.fill(source)
150+
await editorContent.press('End')
151+
await editorContent.type(' ')
152+
await editorContent.press('Backspace')
153+
}
154+
120155
export const setComponentEditorSource = async (page: Page, source: string) => {
121156
await page.getByRole('tab', { name: 'Open tab App.tsx' }).click()
122157
const editorContent = page
123158
.locator('.editor-panel[data-editor-kind="component"] .cm-content')
124159
.first()
125160
await editorContent.fill(source)
161+
await editorContent.press('End')
162+
await editorContent.type(' ')
163+
await editorContent.press('Backspace')
126164
}
127165

128166
export const setStylesEditorSource = async (page: Page, source: string) => {
@@ -131,6 +169,9 @@ export const setStylesEditorSource = async (page: Page, source: string) => {
131169
.locator('.editor-panel[data-editor-kind="styles"] .cm-content')
132170
.first()
133171
await editorContent.fill(source)
172+
await editorContent.press('End')
173+
await editorContent.type(' ')
174+
await editorContent.press('Backspace')
134175
}
135176

136177
export const getActiveComponentEditorLineNumber = async (page: Page) => {

playwright/layout-panels.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
test('renders default playground preview', async ({ page }) => {
1111
await waitForInitialRender(page)
1212

13-
await page.getByLabel('ShadowRoot').uncheck()
1413
await expect(page.getByRole('status', { name: 'App status' })).toHaveText('Rendered')
1514
await expectPreviewHasRenderedContent(page)
1615
})

0 commit comments

Comments
 (0)