@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,53 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { getPlatformPackageName } from '../load-native'
3
-
4
- describe('getPlatformPackageName', () => {
5
- it('returns @pyreon/compiler-darwin-arm64 on Apple Silicon', () => {
6
- expect(getPlatformPackageName('darwin', 'arm64', null)).toBe('@pyreon/compiler-darwin-arm64')
7
- })
8
-
9
- it('returns @pyreon/compiler-darwin-x64 on Intel Mac', () => {
10
- expect(getPlatformPackageName('darwin', 'x64', null)).toBe('@pyreon/compiler-darwin-x64')
11
- })
12
-
13
- it('returns @pyreon/compiler-linux-x64-gnu on glibc Linux x64', () => {
14
- expect(getPlatformPackageName('linux', 'x64', 'gnu')).toBe('@pyreon/compiler-linux-x64-gnu')
15
- })
16
-
17
- it('returns @pyreon/compiler-linux-arm64-gnu on glibc Linux ARM64', () => {
18
- expect(getPlatformPackageName('linux', 'arm64', 'gnu')).toBe('@pyreon/compiler-linux-arm64-gnu')
19
- })
20
-
21
- it('returns @pyreon/compiler-win32-x64-msvc on Windows x64', () => {
22
- expect(getPlatformPackageName('win32', 'x64', 'msvc')).toBe('@pyreon/compiler-win32-x64-msvc')
23
- })
24
-
25
- it('returns null for unsupported platform (freebsd)', () => {
26
- // freebsd is intentionally not in the supported allowlist — caller
27
- // skips per-platform resolution and falls through to JS.
28
- expect(getPlatformPackageName('freebsd', 'x64', null)).toBeNull()
29
- })
30
-
31
- it('returns null for unsupported arch on a supported platform (linux ia32)', () => {
32
- // ia32 is not in the matrix — released-native.yml builds x64 + arm64 only.
33
- expect(getPlatformPackageName('linux', 'ia32', 'gnu')).toBeNull()
34
- })
35
-
36
- it('returns null for darwin arm32 (not a real combo)', () => {
37
- expect(getPlatformPackageName('darwin', 'arm', null)).toBeNull()
38
- })
39
-
40
- it('returns null for win32 arm64 (not yet shipped)', () => {
41
- // Phase 4 matrix doesn't include Windows ARM64. When it's added,
42
- // bump both .github/workflows/release-native.yml AND the supported
43
- // map in load-native.ts together.
44
- expect(getPlatformPackageName('win32', 'arm64', 'msvc')).toBeNull()
45
- })
46
-
47
- it('handles linux musl distinct from glibc', () => {
48
- // The libc dimension is real — Alpine's musl binary is NOT
49
- // ABI-compatible with Debian's glibc binary. Per-platform packages
50
- // must differentiate.
51
- expect(getPlatformPackageName('linux', 'x64', 'musl')).toBe('@pyreon/compiler-linux-x64-musl')
52
- })
53
- })
@@ -1,404 +0,0 @@
1
- /**
2
- * Live Program Inlay Hints — pure merge-function tests.
3
- *
4
- * Proves the end-to-end story: static findings from `analyzeReactivity()`
5
- * merge with runtime fire data into enriched findings that an LSP can
6
- * serve as inlay hints. The runtime side is tested separately in
7
- * `@pyreon/reactivity` (`lpih-source-location.test.ts`).
8
- */
9
- import { describe, expect, it } from 'vitest'
10
- import {
11
- firesToCreationSiteFindings,
12
- type LPIHFireDatum,
13
- mergeFireDataIntoFindings,
14
- } from '../lpih'
15
- import { analyzeReactivity } from '../reactivity-lens'
16
- import type { ReactivityFinding } from '../reactivity-lens'
17
-
18
- const finding = (
19
- kind: ReactivityFinding['kind'],
20
- line: number,
21
- detail: string,
22
- ): ReactivityFinding => ({
23
- kind,
24
- line,
25
- column: 0,
26
- endLine: line,
27
- endColumn: 10,
28
- detail,
29
- })
30
-
31
- const fire = (
32
- file: string,
33
- line: number,
34
- count: number,
35
- kind?: LPIHFireDatum['kind'],
36
- ): LPIHFireDatum => ({ file, line, count, kind })
37
-
38
- describe('mergeFireDataIntoFindings — basic shape', () => {
39
- it('passes findings through unchanged when no fires', () => {
40
- const findings = [finding('reactive', 5, 'live')]
41
- const out = mergeFireDataIntoFindings(findings, [], 'app.tsx')
42
- expect(out).toEqual(findings)
43
- expect(out).toBe(findings) // identity preserved on no-op
44
- })
45
-
46
- it('passes findings through unchanged when no fires match the file', () => {
47
- const findings = [finding('reactive', 5, 'live')]
48
- const out = mergeFireDataIntoFindings(
49
- findings,
50
- [fire('other.tsx', 5, 3, 'signal')],
51
- 'app.tsx',
52
- )
53
- expect(out[0]?.detail).toBe('live') // not enriched
54
- })
55
-
56
- it('enriches a matching reactive finding with the fire count + kind', () => {
57
- const findings = [finding('reactive', 5, 'live')]
58
- const out = mergeFireDataIntoFindings(
59
- findings,
60
- [fire('app.tsx', 5, 240, 'signal')],
61
- 'app.tsx',
62
- )
63
- expect(out[0]?.detail).toBe('live — signal fired 240×')
64
- })
65
-
66
- it('does NOT mutate the input findings', () => {
67
- const findings = [finding('reactive', 5, 'live')]
68
- const before = findings[0]?.detail
69
- mergeFireDataIntoFindings(
70
- findings,
71
- [fire('app.tsx', 5, 240, 'signal')],
72
- 'app.tsx',
73
- )
74
- expect(findings[0]?.detail).toBe(before) // unchanged
75
- })
76
- })
77
-
78
- describe('mergeFireDataIntoFindings — span-kind filtering', () => {
79
- it('skips footgun findings (not runtime-active reactive reads)', () => {
80
- const findings = [finding('footgun', 5, 'props destructured')]
81
- const out = mergeFireDataIntoFindings(
82
- findings,
83
- [fire('app.tsx', 5, 5, 'signal')],
84
- 'app.tsx',
85
- )
86
- expect(out[0]?.detail).toBe('props destructured') // unchanged
87
- })
88
-
89
- it('skips hoisted-static findings', () => {
90
- const findings = [finding('hoisted-static', 5, 'hoisted')]
91
- const out = mergeFireDataIntoFindings(
92
- findings,
93
- [fire('app.tsx', 5, 5, 'signal')],
94
- 'app.tsx',
95
- )
96
- expect(out[0]?.detail).toBe('hoisted')
97
- })
98
-
99
- it('skips static-text findings', () => {
100
- const findings = [finding('static-text', 5, 'baked')]
101
- const out = mergeFireDataIntoFindings(
102
- findings,
103
- [fire('app.tsx', 5, 5, 'signal')],
104
- 'app.tsx',
105
- )
106
- expect(out[0]?.detail).toBe('baked')
107
- })
108
-
109
- it('enriches reactive-prop kinds', () => {
110
- const findings = [finding('reactive-prop', 7, 'reactive prop')]
111
- const out = mergeFireDataIntoFindings(
112
- findings,
113
- [fire('app.tsx', 7, 12, 'signal')],
114
- 'app.tsx',
115
- )
116
- expect(out[0]?.detail).toBe('reactive prop — signal fired 12×')
117
- })
118
-
119
- it('enriches reactive-attr kinds', () => {
120
- const findings = [finding('reactive-attr', 9, 'live attr')]
121
- const out = mergeFireDataIntoFindings(
122
- findings,
123
- [fire('app.tsx', 9, 3, 'derived')],
124
- 'app.tsx',
125
- )
126
- expect(out[0]?.detail).toBe('live attr — derived fired 3×')
127
- })
128
- })
129
-
130
- describe('mergeFireDataIntoFindings — aggregation', () => {
131
- it('sums fires at the same line', () => {
132
- const findings = [finding('reactive', 5, 'live')]
133
- const out = mergeFireDataIntoFindings(
134
- findings,
135
- [
136
- fire('app.tsx', 5, 10, 'signal'),
137
- fire('app.tsx', 5, 30, 'signal'),
138
- ],
139
- 'app.tsx',
140
- )
141
- expect(out[0]?.detail).toBe('live — signal fired 40×')
142
- })
143
-
144
- it('uses latest lastFire + corresponding kind when summing', () => {
145
- const findings = [finding('reactive', 5, 'live')]
146
- const out = mergeFireDataIntoFindings(
147
- findings,
148
- [
149
- { file: 'app.tsx', line: 5, count: 10, lastFire: 100, kind: 'signal' },
150
- { file: 'app.tsx', line: 5, count: 5, lastFire: 999, kind: 'derived' },
151
- ],
152
- 'app.tsx',
153
- )
154
- // Latest fire is `derived` at ts=999, so the kind label is 'derived'.
155
- expect(out[0]?.detail).toBe('live — derived fired 15×')
156
- })
157
-
158
- it('keeps the earlier kind when incoming fire has no lastFire', () => {
159
- const findings = [finding('reactive', 5, 'live')]
160
- const out = mergeFireDataIntoFindings(
161
- findings,
162
- [
163
- { file: 'app.tsx', line: 5, count: 10, lastFire: 100, kind: 'signal' },
164
- { file: 'app.tsx', line: 5, count: 5, kind: 'derived' }, // no lastFire
165
- ],
166
- 'app.tsx',
167
- )
168
- expect(out[0]?.detail).toBe('live — signal fired 15×')
169
- })
170
- })
171
-
172
- describe('mergeFireDataIntoFindings — file-normalization', () => {
173
- it('uses normalizeFile to compare paths', () => {
174
- const findings = [finding('reactive', 5, 'live')]
175
- const norm = (p: string): string => p.replace(/^.*\//, '')
176
- const out = mergeFireDataIntoFindings(
177
- findings,
178
- [fire('/abs/path/app.tsx', 5, 7, 'signal')],
179
- 'workspace://app.tsx',
180
- { normalizeFile: norm },
181
- )
182
- expect(out[0]?.detail).toBe('live — signal fired 7×')
183
- })
184
- })
185
-
186
- describe('mergeFireDataIntoFindings — custom format', () => {
187
- it('uses formatDetail when provided', () => {
188
- const findings = [finding('reactive', 5, 'live')]
189
- const out = mergeFireDataIntoFindings(
190
- findings,
191
- [fire('app.tsx', 5, 42, 'signal')],
192
- 'app.tsx',
193
- { formatDetail: (d, f) => `${d} [${f.count}]` },
194
- )
195
- expect(out[0]?.detail).toBe('live [42]')
196
- })
197
- })
198
-
199
- describe('firesToCreationSiteFindings — synthetic creation-site hints', () => {
200
- it('returns empty when no fires', () => {
201
- expect(firesToCreationSiteFindings([], 'app.tsx')).toEqual([])
202
- })
203
-
204
- it('produces one finding per unique line', () => {
205
- const out = firesToCreationSiteFindings(
206
- [
207
- fire('app.tsx', 5, 100, 'signal'),
208
- fire('app.tsx', 8, 50, 'derived'),
209
- ],
210
- 'app.tsx',
211
- )
212
- expect(out).toHaveLength(2)
213
- expect(out[0]?.line).toBe(5)
214
- expect(out[0]?.detail).toBe('signal fired 100×')
215
- expect(out[1]?.line).toBe(8)
216
- expect(out[1]?.detail).toBe('derived fired 50×')
217
- })
218
-
219
- it('aggregates multiple fires on the same line', () => {
220
- const out = firesToCreationSiteFindings(
221
- [
222
- fire('app.tsx', 5, 100, 'signal'),
223
- fire('app.tsx', 5, 50, 'signal'),
224
- ],
225
- 'app.tsx',
226
- )
227
- expect(out).toHaveLength(1)
228
- expect(out[0]?.detail).toBe('signal fired 150×')
229
- })
230
-
231
- it('skips fires from other files', () => {
232
- const out = firesToCreationSiteFindings(
233
- [
234
- fire('app.tsx', 5, 100, 'signal'),
235
- fire('other.tsx', 5, 50, 'signal'),
236
- ],
237
- 'app.tsx',
238
- )
239
- expect(out).toHaveLength(1)
240
- expect(out[0]?.line).toBe(5)
241
- })
242
-
243
- it('uses live-fire kind for the synthetic finding', () => {
244
- const out = firesToCreationSiteFindings(
245
- [fire('app.tsx', 5, 100, 'signal')],
246
- 'app.tsx',
247
- )
248
- expect(out[0]?.kind).toBe('live-fire')
249
- })
250
-
251
- it('sorts findings by line ascending', () => {
252
- const out = firesToCreationSiteFindings(
253
- [
254
- fire('app.tsx', 10, 1, 'signal'),
255
- fire('app.tsx', 3, 1, 'signal'),
256
- fire('app.tsx', 7, 1, 'signal'),
257
- ],
258
- 'app.tsx',
259
- )
260
- expect(out.map((f) => f.line)).toEqual([3, 7, 10])
261
- })
262
-
263
- it('honors normalizeFile for cross-realm paths', () => {
264
- const out = firesToCreationSiteFindings(
265
- [fire('/abs/app.tsx', 5, 100, 'signal')],
266
- 'workspace://app.tsx',
267
- { normalizeFile: (p) => p.replace(/^.*\//, '') },
268
- )
269
- expect(out).toHaveLength(1)
270
- expect(out[0]?.detail).toBe('signal fired 100×')
271
- })
272
-
273
- it('honors custom formatDetail', () => {
274
- const out = firesToCreationSiteFindings(
275
- [fire('app.tsx', 5, 42, 'signal')],
276
- 'app.tsx',
277
- { formatDetail: (_, f) => `🔥 ${f.count}` },
278
- )
279
- expect(out[0]?.detail).toBe('🔥 42')
280
- })
281
- })
282
-
283
- describe('end-to-end — analyzeReactivity + merge', () => {
284
- it('enriches the static analysis output with runtime data', () => {
285
- const code = `function App() {
286
- const count = signal(0)
287
- return <div>{count()}</div>
288
- }`
289
- const { findings } = analyzeReactivity(code, 'app.tsx')
290
- // The reactive {count()} span is on line 3.
291
- const reactiveFinding = findings.find(
292
- (f) => f.kind === 'reactive' && f.line === 3,
293
- )
294
- expect(reactiveFinding).toBeDefined()
295
- const enriched = mergeFireDataIntoFindings(
296
- findings,
297
- [fire('app.tsx', 3, 50, 'signal')],
298
- 'app.tsx',
299
- )
300
- const target = enriched.find((f) => f.kind === 'reactive' && f.line === 3)
301
- expect(target?.detail).toContain('fired 50×')
302
- })
303
-
304
- it('leaves footguns visible even after merge (LPIH is additive)', () => {
305
- // `const { x } = props` triggers the props-destructured footgun.
306
- const code = `function App(props) {
307
- const { x } = props
308
- return <div>{x}</div>
309
- }`
310
- const { findings } = analyzeReactivity(code, 'app.tsx')
311
- const footgun = findings.find((f) => f.kind === 'footgun')
312
- expect(footgun).toBeDefined()
313
- const enriched = mergeFireDataIntoFindings(
314
- findings,
315
- [fire('app.tsx', footgun?.line ?? 0, 5, 'signal')],
316
- 'app.tsx',
317
- )
318
- const afterFootgun = enriched.find((f) => f.kind === 'footgun')
319
- expect(afterFootgun?.detail).toBe(footgun?.detail) // unchanged
320
- })
321
- })
322
-
323
- describe('rate1s — label formatting', () => {
324
- it('omits rate when below threshold (dormant)', () => {
325
- const findings = [finding('reactive', 5, 'live')]
326
- const out = mergeFireDataIntoFindings(
327
- findings,
328
- [{ file: 'app.tsx', line: 5, count: 100, kind: 'signal', rate1s: 0.1 }],
329
- 'app.tsx',
330
- )
331
- expect(out[0]?.detail).toBe('live — signal fired 100×')
332
- expect(out[0]?.detail).not.toContain('/s')
333
- })
334
-
335
- it('omits rate when undefined (older runtime / no field)', () => {
336
- const findings = [finding('reactive', 5, 'live')]
337
- const out = mergeFireDataIntoFindings(
338
- findings,
339
- [{ file: 'app.tsx', line: 5, count: 100, kind: 'signal' }],
340
- 'app.tsx',
341
- )
342
- expect(out[0]?.detail).toBe('live — signal fired 100×')
343
- })
344
-
345
- it('includes 1-decimal rate when above threshold and < 10/s', () => {
346
- const findings = [finding('reactive', 5, 'live')]
347
- const out = mergeFireDataIntoFindings(
348
- findings,
349
- [{ file: 'app.tsx', line: 5, count: 100, kind: 'signal', rate1s: 3.7 }],
350
- 'app.tsx',
351
- )
352
- expect(out[0]?.detail).toBe('live — signal fired 100× (3.7/s)')
353
- })
354
-
355
- it('rounds rate to integer at >= 10/s', () => {
356
- const findings = [finding('reactive', 5, 'live')]
357
- const out = mergeFireDataIntoFindings(
358
- findings,
359
- [{ file: 'app.tsx', line: 5, count: 1000, kind: 'signal', rate1s: 47.3 }],
360
- 'app.tsx',
361
- )
362
- expect(out[0]?.detail).toBe('live — signal fired 1000× (47/s)')
363
- })
364
-
365
- it('firesToCreationSiteFindings respects rate1s in label', () => {
366
- const out = firesToCreationSiteFindings(
367
- [
368
- { file: 'app.tsx', line: 5, count: 50, kind: 'signal', rate1s: 5.2 },
369
- { file: 'app.tsx', line: 8, count: 100, kind: 'effect', rate1s: 0.0 },
370
- ],
371
- 'app.tsx',
372
- )
373
- expect(out).toHaveLength(2)
374
- expect(out[0]?.detail).toBe('signal fired 50× (5.2/s)')
375
- expect(out[1]?.detail).toBe('effect fired 100×')
376
- })
377
-
378
- it('sums rates when multiple fires share the same line', () => {
379
- const findings = [finding('reactive', 5, 'live')]
380
- const out = mergeFireDataIntoFindings(
381
- findings,
382
- [
383
- { file: 'app.tsx', line: 5, count: 30, kind: 'signal', rate1s: 2.0 },
384
- { file: 'app.tsx', line: 5, count: 20, kind: 'signal', rate1s: 3.5 },
385
- ],
386
- 'app.tsx',
387
- )
388
- expect(out[0]?.detail).toBe('live — signal fired 50× (5.5/s)')
389
- })
390
-
391
- it('custom formatDetail receives rate1s in the fire object', () => {
392
- const findings = [finding('reactive', 5, 'live')]
393
- const out = mergeFireDataIntoFindings(
394
- findings,
395
- [{ file: 'app.tsx', line: 5, count: 100, kind: 'signal', rate1s: 7.5 }],
396
- 'app.tsx',
397
- {
398
- formatDetail: (d, f) =>
399
- `${d} [rate=${f.rate1s?.toFixed(1) ?? 'n/a'}]`,
400
- },
401
- )
402
- expect(out[0]?.detail).toBe('live [rate=7.5]')
403
- })
404
- })
@@ -1,50 +0,0 @@
1
- /**
2
- * Compiler hardening — Round 10 (resilience gate; no bug found).
3
- *
4
- * `transformJSX` runs per-file inside the Vite dev server; a throw on
5
- * malformed input crashes the dev server (the documented contract is "a Rust
6
- * panic / parse error must not crash Vite — fall back gracefully"). Probed 15
7
- * adversarial inputs (unclosed/mismatched tags, stray brace, invalid attr,
8
- * unterminated string, 500-deep nesting, BOM, raw control bytes, empty,
9
- * comment-only, JSX in type position) through BOTH backends — all returned a
10
- * `{ code: string }` result without throwing. This locks that resilience so a
11
- * future change can't regress the compiler into throwing on bad input.
12
- */
13
- import { describe, expect, it } from 'vitest'
14
- import { transformJSX, transformJSX_JS } from '../jsx'
15
-
16
- const INPUTS: Array<[string, string]> = [
17
- ['unclosed-tag', `function C(){ return <div>oops }`],
18
- ['mismatched-tags', `function C(){ return <div></span> }`],
19
- ['invalid-attr', `function C(){ return <div class=></div> }`],
20
- ['stray-brace', `function C(){ return <div>{</div> }`],
21
- ['empty', ``],
22
- ['whitespace-only', ` \n `],
23
- ['non-jsx-ts', `const x: number = 1; export function f(){ return x }`],
24
- ['deeply-unbalanced', `function C(){ return <a><b><c></a> }`],
25
- ['unterminated-string-attr', `function C(){ return <div title="abc>x</div> }`],
26
- ['huge-nesting-500', `function C(){ return ${'<a>'.repeat(500)}x${'</a>'.repeat(500)} }`],
27
- ['bom-prefixed', `function C(){ return <div>ok</div> }`],
28
- ['comment-only', `// just a comment`],
29
- ['fragment-unclosed', `function C(){ return <>x }`],
30
- ['raw-control-garbage', String.fromCharCode(0, 1) + ' not code <div'],
31
- ]
32
-
33
- describe('Round 10 — transform never throws on malformed input (Vite-dev-server resilience)', () => {
34
- for (const [name, src] of INPUTS) {
35
- it(`JS backend tolerates: ${name}`, () => {
36
- let res: { code?: unknown } | undefined
37
- expect(() => {
38
- res = transformJSX_JS(src, 'c.tsx')
39
- }).not.toThrow()
40
- expect(typeof res?.code).toBe('string')
41
- })
42
- it(`native backend tolerates: ${name}`, () => {
43
- let res: { code?: unknown } | undefined
44
- expect(() => {
45
- res = transformJSX(src, 'c.tsx')
46
- }).not.toThrow()
47
- expect(typeof res?.code).toBe('string')
48
- })
49
- }
50
- })
@@ -1,55 +0,0 @@
1
- import {
2
- renderApiReferenceEntries,
3
- renderLlmsFullSection,
4
- renderLlmsTxtLine,
5
- } from '@pyreon/manifest'
6
- import manifest from '../manifest'
7
-
8
- describe('gen-docs — compiler snapshot', () => {
9
- it('renders a llms.txt bullet starting with the package prefix', () => {
10
- const line = renderLlmsTxtLine(manifest)
11
- expect(line.startsWith('- @pyreon/compiler —')).toBe(true)
12
- })
13
-
14
- it('renders a llms-full.txt section with the right header', () => {
15
- const section = renderLlmsFullSection(manifest)
16
- expect(section.startsWith('## @pyreon/compiler —')).toBe(true)
17
- expect(section).toContain('```typescript')
18
- })
19
-
20
- it('renders MCP api-reference entries for every api[] item', () => {
21
- const record = renderApiReferenceEntries(manifest)
22
- expect(Object.keys(record).sort()).toEqual([
23
- 'compiler/analyzeReactivity',
24
- 'compiler/auditIslands',
25
- 'compiler/auditSsg',
26
- 'compiler/auditTestEnvironment',
27
- 'compiler/detectPyreonPatterns',
28
- 'compiler/detectReactPatterns',
29
- 'compiler/diagnoseError',
30
- 'compiler/formatIslandAudit',
31
- 'compiler/formatReactivityLens',
32
- 'compiler/formatSsgAudit',
33
- 'compiler/formatTestAudit',
34
- 'compiler/generateContext',
35
- 'compiler/hasPyreonPatterns',
36
- 'compiler/hasReactPatterns',
37
- 'compiler/migrateReactCode',
38
- 'compiler/transformDeferInline',
39
- 'compiler/transformJSX',
40
- 'compiler/transformJSX_JS',
41
- ])
42
- })
43
-
44
- it('flags the experimental Reactivity-Lens entries', () => {
45
- const r = renderApiReferenceEntries(manifest)
46
- expect(r['compiler/analyzeReactivity']?.notes).toContain('[EXPERIMENTAL]')
47
- expect(r['compiler/formatReactivityLens']?.notes).toContain('[EXPERIMENTAL]')
48
- })
49
-
50
- it('carries the foot-gun catalog into MCP mistakes for flagship APIs', () => {
51
- const r = renderApiReferenceEntries(manifest)
52
- expect(r['compiler/transformJSX']?.mistakes).toBeTruthy()
53
- expect(r['compiler/detectPyreonPatterns']?.mistakes).toContain('fixable')
54
- })
55
- })