@pyreon/compiler 0.24.5 → 0.24.6

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 (64) hide show
  1. package/package.json +11 -13
  2. package/src/defer-inline.ts +0 -686
  3. package/src/event-names.ts +0 -65
  4. package/src/index.ts +0 -61
  5. package/src/island-audit.ts +0 -675
  6. package/src/jsx.ts +0 -2792
  7. package/src/load-native.ts +0 -156
  8. package/src/lpih.ts +0 -270
  9. package/src/manifest.ts +0 -280
  10. package/src/project-scanner.ts +0 -214
  11. package/src/pyreon-intercept.ts +0 -1029
  12. package/src/react-intercept.ts +0 -1217
  13. package/src/reactivity-lens.ts +0 -190
  14. package/src/ssg-audit.ts +0 -513
  15. package/src/test-audit.ts +0 -435
  16. package/src/tests/backend-parity-r7-r9.test.ts +0 -91
  17. package/src/tests/backend-prop-derived-callback-divergence.test.ts +0 -74
  18. package/src/tests/collapse-bail-census.test.ts +0 -330
  19. package/src/tests/collapse-key-source-hygiene.test.ts +0 -88
  20. package/src/tests/component-child-no-wrap.test.ts +0 -204
  21. package/src/tests/defer-inline.test.ts +0 -387
  22. package/src/tests/depth-stress.test.ts +0 -16
  23. package/src/tests/detector-tag-consistency.test.ts +0 -101
  24. package/src/tests/dynamic-collapse-detector.test.ts +0 -164
  25. package/src/tests/dynamic-collapse-emit.test.ts +0 -192
  26. package/src/tests/dynamic-collapse-scan.test.ts +0 -111
  27. package/src/tests/element-valued-const-child.test.ts +0 -61
  28. package/src/tests/falsy-child-characterization.test.ts +0 -48
  29. package/src/tests/island-audit.test.ts +0 -524
  30. package/src/tests/jsx.test.ts +0 -2908
  31. package/src/tests/load-native.test.ts +0 -53
  32. package/src/tests/lpih.test.ts +0 -404
  33. package/src/tests/malformed-input-resilience.test.ts +0 -50
  34. package/src/tests/manifest-snapshot.test.ts +0 -55
  35. package/src/tests/native-equivalence.test.ts +0 -924
  36. package/src/tests/partial-collapse-detector.test.ts +0 -121
  37. package/src/tests/partial-collapse-emit.test.ts +0 -104
  38. package/src/tests/partial-collapse-robustness.test.ts +0 -53
  39. package/src/tests/project-scanner.test.ts +0 -269
  40. package/src/tests/prop-derived-shadow.test.ts +0 -96
  41. package/src/tests/pure-call-reactive-args.test.ts +0 -50
  42. package/src/tests/pyreon-intercept.test.ts +0 -816
  43. package/src/tests/r13-callback-stmt-equivalence.test.ts +0 -58
  44. package/src/tests/r14-ssr-mode-parity.test.ts +0 -51
  45. package/src/tests/r15-elemconst-propderived.test.ts +0 -47
  46. package/src/tests/r19-defer-inline-robust.test.ts +0 -54
  47. package/src/tests/r20-backend-equivalence-sweep.test.ts +0 -50
  48. package/src/tests/react-intercept.test.ts +0 -1104
  49. package/src/tests/reactivity-lens.test.ts +0 -170
  50. package/src/tests/rocketstyle-collapse.test.ts +0 -208
  51. package/src/tests/runtime/control-flow.test.ts +0 -159
  52. package/src/tests/runtime/dom-properties.test.ts +0 -138
  53. package/src/tests/runtime/events.test.ts +0 -301
  54. package/src/tests/runtime/harness.ts +0 -94
  55. package/src/tests/runtime/pr-352-shapes.test.ts +0 -121
  56. package/src/tests/runtime/reactive-props.test.ts +0 -81
  57. package/src/tests/runtime/signals.test.ts +0 -129
  58. package/src/tests/runtime/whitespace.test.ts +0 -106
  59. package/src/tests/signal-autocall-shadow.test.ts +0 -86
  60. package/src/tests/sourcemap-fidelity.test.ts +0 -77
  61. package/src/tests/ssg-audit.test.ts +0 -402
  62. package/src/tests/static-text-baking.test.ts +0 -64
  63. package/src/tests/test-audit.test.ts +0 -549
  64. package/src/tests/transform-state-isolation.test.ts +0 -49
@@ -1,524 +0,0 @@
1
- /**
2
- * Fixture-based tests for `auditIslands` (PR C of the islands DX
3
- * roadmap). Each test builds a synthetic monorepo at a tmp path with
4
- * `packages/` + `examples/` subdirs containing minimal `island()` /
5
- * `hydrateIslands()` source — then runs the audit and asserts the
6
- * exact findings.
7
- *
8
- * Bisect-verification is done within the suite itself: for each of
9
- * the 5 finding types, one test asserts the finding fires given the
10
- * "broken" shape, AND a parallel test asserts NO finding fires given
11
- * the "fixed" shape. If a future contributor disables the detector
12
- * by mistake, the broken-shape test fails immediately.
13
- */
14
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
15
- import { tmpdir } from 'node:os'
16
- import { dirname, join } from 'node:path'
17
- import { auditIslands, formatIslandAudit, type IslandFindingCode } from '../island-audit'
18
-
19
- interface Fixture {
20
- root: string
21
- write: (relPath: string, body: string) => void
22
- cleanup: () => void
23
- }
24
-
25
- function makeFixture(): Fixture {
26
- const root = mkdtempSync(join(tmpdir(), 'pyreon-island-audit-fixture-'))
27
- mkdirSync(join(root, 'packages'), { recursive: true })
28
- return {
29
- root,
30
- write: (relPath, body) => {
31
- // Allow the caller to write under either `packages/` or `examples/`.
32
- // If the relPath doesn't start with one of those, default to packages/.
33
- const top = relPath.startsWith('packages/') || relPath.startsWith('examples/')
34
- ? relPath
35
- : `packages/${relPath}`
36
- const full = join(root, top)
37
- mkdirSync(dirname(full), { recursive: true })
38
- writeFileSync(full, body)
39
- },
40
- cleanup: () => rmSync(root, { recursive: true, force: true }),
41
- }
42
- }
43
-
44
- function findingCodes(result: ReturnType<typeof auditIslands>): IslandFindingCode[] {
45
- return result.findings.map((f) => f.code)
46
- }
47
-
48
- // ═══════════════════════════════════════════════════════════════════════════════
49
- // Discovery
50
- // ═══════════════════════════════════════════════════════════════════════════════
51
-
52
- describe('auditIslands — discovery', () => {
53
- it('returns root=null when no packages/ dir exists', () => {
54
- const empty = mkdtempSync(join(tmpdir(), 'pyreon-island-audit-empty-'))
55
- try {
56
- const r = auditIslands(empty)
57
- expect(r.root).toBeNull()
58
- expect(r.findings).toEqual([])
59
- expect(r.summary.filesScanned).toBe(0)
60
- } finally {
61
- rmSync(empty, { recursive: true, force: true })
62
- }
63
- })
64
-
65
- it('walks packages/ AND examples/ for source files', () => {
66
- const f = makeFixture()
67
- try {
68
- mkdirSync(join(f.root, 'examples'), { recursive: true })
69
- f.write('packages/a/src/A.tsx', `export const A = () => null`)
70
- f.write('examples/x/src/X.tsx', `export const X = () => null`)
71
- const r = auditIslands(f.root)
72
- // Two non-test source files
73
- expect(r.summary.filesScanned).toBe(2)
74
- } finally {
75
- f.cleanup()
76
- }
77
- })
78
-
79
- it('skips test files / __tests__ / node_modules / lib / dist', () => {
80
- const f = makeFixture()
81
- try {
82
- f.write('a/src/Real.tsx', `export const A = () => null`)
83
- f.write('a/src/Real.test.tsx', `it('x', () => {})`)
84
- f.write('a/src/__tests__/foo.tsx', `export const F = () => null`)
85
- f.write('a/lib/built.js', `export const Built = () => null`)
86
- f.write('a/dist/out.js', `export const Out = () => null`)
87
- const r = auditIslands(f.root)
88
- expect(r.summary.filesScanned).toBe(1)
89
- } finally {
90
- f.cleanup()
91
- }
92
- })
93
- })
94
-
95
- // ═══════════════════════════════════════════════════════════════════════════════
96
- // duplicate-name
97
- // ═══════════════════════════════════════════════════════════════════════════════
98
-
99
- describe('auditIslands — duplicate-name', () => {
100
- it('flags two island() declarations with the same name (broken shape)', () => {
101
- const f = makeFixture()
102
- try {
103
- f.write(
104
- 'a/src/A.tsx',
105
- `import { island } from '@pyreon/server'
106
- const Counter = island(() => import('./Counter'), { name: 'Counter', hydrate: 'load' })
107
- export { Counter }`,
108
- )
109
- f.write(
110
- 'b/src/B.tsx',
111
- `import { island } from '@pyreon/server'
112
- const Counter = island(() => import('./CounterB'), { name: 'Counter', hydrate: 'load' })
113
- export { Counter }`,
114
- )
115
- // Loader-target stub files so nested-island doesn't kick in
116
- f.write('a/src/Counter.tsx', `export default () => null`)
117
- f.write('b/src/CounterB.tsx', `export default () => null`)
118
- const r = auditIslands(f.root)
119
- const codes = findingCodes(r)
120
- expect(codes).toContain('duplicate-name')
121
- // Two declarations → two findings (each with the other in `related`)
122
- expect(codes.filter((c) => c === 'duplicate-name')).toHaveLength(2)
123
- const dup = r.findings.find((finding) => finding.code === 'duplicate-name')!
124
- expect(dup.related).toBeDefined()
125
- expect(dup.related).toHaveLength(1)
126
- // Bisect-verify message content carries the conflicting name.
127
- expect(dup.message).toContain('"Counter"')
128
- } finally {
129
- f.cleanup()
130
- }
131
- })
132
-
133
- it('does NOT flag distinct names (fixed shape)', () => {
134
- const f = makeFixture()
135
- try {
136
- f.write(
137
- 'a/src/A.tsx',
138
- `import { island } from '@pyreon/server'
139
- const A = island(() => import('./A1'), { name: 'A', hydrate: 'load' })
140
- export { A }`,
141
- )
142
- f.write(
143
- 'b/src/B.tsx',
144
- `import { island } from '@pyreon/server'
145
- const B = island(() => import('./B1'), { name: 'B', hydrate: 'load' })
146
- export { B }`,
147
- )
148
- f.write('a/src/A1.tsx', `export default () => null`)
149
- f.write('b/src/B1.tsx', `export default () => null`)
150
- const r = auditIslands(f.root)
151
- expect(findingCodes(r)).not.toContain('duplicate-name')
152
- } finally {
153
- f.cleanup()
154
- }
155
- })
156
- })
157
-
158
- // ═══════════════════════════════════════════════════════════════════════════════
159
- // never-with-registry-entry
160
- // ═══════════════════════════════════════════════════════════════════════════════
161
-
162
- describe('auditIslands — never-with-registry-entry', () => {
163
- it('flags a never-strategy island that is also in the registry (broken shape)', () => {
164
- const f = makeFixture()
165
- try {
166
- f.write(
167
- 'a/src/Static.tsx',
168
- `import { island } from '@pyreon/server'
169
- const Static = island(() => import('./StaticInner'), { name: 'Static', hydrate: 'never' })
170
- export { Static }`,
171
- )
172
- f.write('a/src/StaticInner.tsx', `export default () => null`)
173
- f.write(
174
- 'app/src/entry-client.ts',
175
- `import { hydrateIslands } from '@pyreon/server/client'
176
- hydrateIslands({
177
- Static: () => import('../../a/src/Static'),
178
- })`,
179
- )
180
- const r = auditIslands(f.root)
181
- const codes = findingCodes(r)
182
- expect(codes).toContain('never-with-registry-entry')
183
- const finding = r.findings.find((x) => x.code === 'never-with-registry-entry')!
184
- expect(finding.message).toContain('Static')
185
- expect(finding.message).toContain("'never'")
186
- expect(finding.related).toBeDefined()
187
- expect(finding.related![0]!.relPath).toContain('Static.tsx')
188
- } finally {
189
- f.cleanup()
190
- }
191
- })
192
-
193
- it('does NOT flag a registered non-never island (fixed shape)', () => {
194
- const f = makeFixture()
195
- try {
196
- f.write(
197
- 'a/src/Counter.tsx',
198
- `import { island } from '@pyreon/server'
199
- const Counter = island(() => import('./CounterInner'), { name: 'Counter', hydrate: 'load' })
200
- export { Counter }`,
201
- )
202
- f.write('a/src/CounterInner.tsx', `export default () => null`)
203
- f.write(
204
- 'app/src/entry-client.ts',
205
- `import { hydrateIslands } from '@pyreon/server/client'
206
- hydrateIslands({ Counter: () => import('../../a/src/Counter') })`,
207
- )
208
- const r = auditIslands(f.root)
209
- expect(findingCodes(r)).not.toContain('never-with-registry-entry')
210
- } finally {
211
- f.cleanup()
212
- }
213
- })
214
- })
215
-
216
- // ═══════════════════════════════════════════════════════════════════════════════
217
- // registry-mismatch
218
- // ═══════════════════════════════════════════════════════════════════════════════
219
-
220
- describe('auditIslands — registry-mismatch', () => {
221
- it('flags a registry key that has no matching island() declaration (broken shape)', () => {
222
- const f = makeFixture()
223
- try {
224
- f.write(
225
- 'a/src/Counter.tsx',
226
- `import { island } from '@pyreon/server'
227
- export const Counter = island(() => import('./CounterInner'), { name: 'Counter', hydrate: 'load' })`,
228
- )
229
- f.write('a/src/CounterInner.tsx', `export default () => null`)
230
- f.write(
231
- 'app/src/entry-client.ts',
232
- `import { hydrateIslands } from '@pyreon/server/client'
233
- hydrateIslands({
234
- Counter: () => import('../../a/src/Counter'),
235
- Counterr: () => import('../../a/src/Counter'), // typo
236
- })`,
237
- )
238
- const r = auditIslands(f.root)
239
- const mismatches = r.findings.filter((x) => x.code === 'registry-mismatch')
240
- expect(mismatches).toHaveLength(1)
241
- expect(mismatches[0]!.message).toContain('Counterr')
242
- } finally {
243
- f.cleanup()
244
- }
245
- })
246
-
247
- it('does NOT flag when every key matches a declared name (fixed shape)', () => {
248
- const f = makeFixture()
249
- try {
250
- f.write(
251
- 'a/src/A.tsx',
252
- `import { island } from '@pyreon/server'
253
- export const A = island(() => import('./AInner'), { name: 'A', hydrate: 'load' })`,
254
- )
255
- f.write('a/src/AInner.tsx', `export default () => null`)
256
- f.write(
257
- 'app/src/entry-client.ts',
258
- `import { hydrateIslands } from '@pyreon/server/client'
259
- hydrateIslands({ A: () => import('../../a/src/A') })`,
260
- )
261
- const r = auditIslands(f.root)
262
- expect(findingCodes(r)).not.toContain('registry-mismatch')
263
- } finally {
264
- f.cleanup()
265
- }
266
- })
267
- })
268
-
269
- // ═══════════════════════════════════════════════════════════════════════════════
270
- // nested-island
271
- // ═══════════════════════════════════════════════════════════════════════════════
272
-
273
- describe('auditIslands — nested-island', () => {
274
- it('flags an island whose loader-target file ALSO contains an island() (broken shape)', () => {
275
- const f = makeFixture()
276
- try {
277
- f.write(
278
- 'a/src/Outer.tsx',
279
- `import { island } from '@pyreon/server'
280
- export const Outer = island(() => import('./Inner'), { name: 'Outer', hydrate: 'load' })`,
281
- )
282
- f.write(
283
- 'a/src/Inner.tsx',
284
- `import { island } from '@pyreon/server'
285
- export const Inner = island(() => import('./Innermost'), { name: 'Inner', hydrate: 'load' })`,
286
- )
287
- f.write('a/src/Innermost.tsx', `export default () => null`)
288
- f.write(
289
- 'app/src/entry-client.ts',
290
- `import { hydrateIslands } from '@pyreon/server/client'
291
- hydrateIslands({
292
- Outer: () => import('../../a/src/Outer'),
293
- Inner: () => import('../../a/src/Inner'),
294
- })`,
295
- )
296
- const r = auditIslands(f.root)
297
- const nested = r.findings.filter((x) => x.code === 'nested-island')
298
- expect(nested.length).toBeGreaterThanOrEqual(1)
299
- // The outer's finding should reference Outer's location and
300
- // related-link the inner's location.
301
- const outerFinding = nested.find((x) => x.location.relPath.includes('Outer.tsx'))!
302
- expect(outerFinding).toBeDefined()
303
- expect(outerFinding.message).toContain('"Outer"')
304
- expect(outerFinding.message).toContain('"Inner"')
305
- expect(outerFinding.related).toBeDefined()
306
- expect(outerFinding.related![0]!.relPath).toContain('Inner.tsx')
307
- } finally {
308
- f.cleanup()
309
- }
310
- })
311
-
312
- it('does NOT flag a flat island whose target has no island() (fixed shape)', () => {
313
- const f = makeFixture()
314
- try {
315
- f.write(
316
- 'a/src/Counter.tsx',
317
- `import { island } from '@pyreon/server'
318
- export const Counter = island(() => import('./CounterImpl'), { name: 'Counter', hydrate: 'load' })`,
319
- )
320
- f.write('a/src/CounterImpl.tsx', `export default () => null`)
321
- const r = auditIslands(f.root)
322
- expect(findingCodes(r)).not.toContain('nested-island')
323
- } finally {
324
- f.cleanup()
325
- }
326
- })
327
- })
328
-
329
- // ═══════════════════════════════════════════════════════════════════════════════
330
- // dead-island
331
- // ═══════════════════════════════════════════════════════════════════════════════
332
-
333
- describe('auditIslands — dead-island', () => {
334
- it('flags an island whose containing file is not imported by any other source (broken shape)', () => {
335
- const f = makeFixture()
336
- try {
337
- f.write(
338
- 'a/src/Orphan.tsx',
339
- `import { island } from '@pyreon/server'
340
- export const Orphan = island(() => import('./OrphanImpl'), { name: 'Orphan', hydrate: 'load' })`,
341
- )
342
- f.write('a/src/OrphanImpl.tsx', `export default () => null`)
343
- // Nothing imports Orphan.tsx — explicitly orphaned
344
- const r = auditIslands(f.root)
345
- const dead = r.findings.filter((x) => x.code === 'dead-island')
346
- expect(dead).toHaveLength(1)
347
- expect(dead[0]!.message).toContain('"Orphan"')
348
- } finally {
349
- f.cleanup()
350
- }
351
- })
352
-
353
- it('does NOT flag an island whose file IS imported (fixed shape — static import)', () => {
354
- const f = makeFixture()
355
- try {
356
- f.write(
357
- 'a/src/Counter.tsx',
358
- `import { island } from '@pyreon/server'
359
- export const Counter = island(() => import('./CounterImpl'), { name: 'Counter', hydrate: 'load' })`,
360
- )
361
- f.write('a/src/CounterImpl.tsx', `export default () => null`)
362
- f.write(
363
- 'a/src/index.ts',
364
- `export { Counter } from './Counter'`,
365
- )
366
- const r = auditIslands(f.root)
367
- expect(findingCodes(r)).not.toContain('dead-island')
368
- } finally {
369
- f.cleanup()
370
- }
371
- })
372
-
373
- it('does NOT flag an island whose file IS imported via dynamic import (auto-registry shape)', () => {
374
- const f = makeFixture()
375
- try {
376
- f.write(
377
- 'a/src/Counter.tsx',
378
- `import { island } from '@pyreon/server'
379
- export const Counter = island(() => import('./CounterImpl'), { name: 'Counter', hydrate: 'load' })`,
380
- )
381
- f.write('a/src/CounterImpl.tsx', `export default () => null`)
382
- // Auto-registry-style dynamic import (mirrors what the vite-plugin
383
- // emits in the virtual module). Both `app/` and `a/` are under
384
- // packages/, so the relative import path is `../../a/src/Counter`.
385
- f.write(
386
- 'app/src/entry-client.ts',
387
- `import { hydrateIslandsAuto } from '@pyreon/server/client'
388
- const registry = { Counter: () => import('../../a/src/Counter') }
389
- hydrateIslandsAuto(registry)`,
390
- )
391
- const r = auditIslands(f.root)
392
- expect(findingCodes(r)).not.toContain('dead-island')
393
- } finally {
394
- f.cleanup()
395
- }
396
- })
397
- })
398
-
399
- // ═══════════════════════════════════════════════════════════════════════════════
400
- // Formatter
401
- // ═══════════════════════════════════════════════════════════════════════════════
402
-
403
- describe('formatIslandAudit', () => {
404
- it('returns a green-light message when no findings are present', () => {
405
- const f = makeFixture()
406
- try {
407
- const r = auditIslands(f.root)
408
- const text = formatIslandAudit(r)
409
- expect(text).toContain('No island findings')
410
- } finally {
411
- f.cleanup()
412
- }
413
- })
414
-
415
- it('groups findings by code and lists locations', () => {
416
- const f = makeFixture()
417
- try {
418
- f.write(
419
- 'a/src/A.tsx',
420
- `import { island } from '@pyreon/server'
421
- const A = island(() => import('./AImpl'), { name: 'Dup', hydrate: 'load' })
422
- export { A }`,
423
- )
424
- f.write(
425
- 'b/src/B.tsx',
426
- `import { island } from '@pyreon/server'
427
- const B = island(() => import('./BImpl'), { name: 'Dup', hydrate: 'load' })
428
- export { B }`,
429
- )
430
- f.write('a/src/AImpl.tsx', `export default () => null`)
431
- f.write('b/src/BImpl.tsx', `export default () => null`)
432
- const r = auditIslands(f.root)
433
- const text = formatIslandAudit(r)
434
- expect(text).toContain('## duplicate-name')
435
- // Both file locations appear in the human-readable output
436
- expect(text).toContain('A.tsx')
437
- expect(text).toContain('B.tsx')
438
- } finally {
439
- f.cleanup()
440
- }
441
- })
442
-
443
- it('emits machine-readable JSON when options.json is true', () => {
444
- const f = makeFixture()
445
- try {
446
- f.write(
447
- 'a/src/Orphan.tsx',
448
- `import { island } from '@pyreon/server'
449
- export const Orphan = island(() => import('./Inner'), { name: 'Orphan', hydrate: 'load' })`,
450
- )
451
- f.write('a/src/Inner.tsx', `export default () => null`)
452
- const r = auditIslands(f.root)
453
- const text = formatIslandAudit(r, { json: true })
454
- const parsed = JSON.parse(text)
455
- expect(parsed).toHaveProperty('findings')
456
- expect(parsed).toHaveProperty('summary')
457
- expect(parsed.summary.findingsByCode).toBeDefined()
458
- } finally {
459
- f.cleanup()
460
- }
461
- })
462
-
463
- it('returns a useful error when invoked outside a monorepo', () => {
464
- const empty = mkdtempSync(join(tmpdir(), 'pyreon-island-audit-no-monorepo-'))
465
- try {
466
- const r = auditIslands(empty)
467
- const text = formatIslandAudit(r)
468
- expect(text).toContain('No monorepo root found')
469
- } finally {
470
- rmSync(empty, { recursive: true, force: true })
471
- }
472
- })
473
- })
474
-
475
- // ═══════════════════════════════════════════════════════════════════════════════
476
- // Integration — clean shape produces zero findings
477
- // ═══════════════════════════════════════════════════════════════════════════════
478
-
479
- describe('auditIslands — integration', () => {
480
- it('produces zero findings on a clean realistic islands-showcase shape', () => {
481
- const f = makeFixture()
482
- try {
483
- // Mirror examples/islands-showcase: 5 islands, one per strategy,
484
- // all with distinct names, all imported via the auto-registry.
485
- const decl = (name: string, hydrate: string) =>
486
- `import { island } from '@pyreon/server'
487
- export const ${name} = island(() => import('./${name}Impl'), { name: '${name}', hydrate: '${hydrate}' })`
488
-
489
- f.write('isl/src/Counter.tsx', decl('Counter', 'load'))
490
- f.write('isl/src/IdleClock.tsx', decl('IdleClock', 'idle'))
491
- f.write('isl/src/Visible.tsx', decl('Visible', 'visible'))
492
- f.write('isl/src/Mobile.tsx', decl('Mobile', 'media((max-width: 600px))'))
493
- f.write('isl/src/Static.tsx', decl('Static', 'never'))
494
- f.write('isl/src/CounterImpl.tsx', `export default () => null`)
495
- f.write('isl/src/IdleClockImpl.tsx', `export default () => null`)
496
- f.write('isl/src/VisibleImpl.tsx', `export default () => null`)
497
- f.write('isl/src/MobileImpl.tsx', `export default () => null`)
498
- f.write('isl/src/StaticImpl.tsx', `export default () => null`)
499
- f.write(
500
- 'app/src/entry-client.ts',
501
- `import { hydrateIslandsAuto } from '@pyreon/server/client'
502
- const registry = {
503
- // Static is correctly OMITTED — it's hydrate: 'never'
504
- Counter: () => import('../../isl/src/Counter'),
505
- IdleClock: () => import('../../isl/src/IdleClock'),
506
- Visible: () => import('../../isl/src/Visible'),
507
- Mobile: () => import('../../isl/src/Mobile'),
508
- }
509
- hydrateIslandsAuto(registry)`,
510
- )
511
- // Static is ALSO referenced (server-side), so it's not dead either.
512
- f.write(
513
- 'app/src/server.ts',
514
- `import { Static } from '../../isl/src/Static'
515
- export { Static }`,
516
- )
517
- const r = auditIslands(f.root)
518
- expect(r.summary.islandsDeclared).toBe(5)
519
- expect(r.findings).toEqual([])
520
- } finally {
521
- f.cleanup()
522
- }
523
- })
524
- })