@pyreon/compiler 0.16.0 → 0.19.0

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.
@@ -1,3 +1,59 @@
1
+ //#region src/defer-inline.d.ts
2
+ /**
3
+ * Inline-children transform for `<Defer>`.
4
+ *
5
+ * Rewrites:
6
+ *
7
+ * import { Modal } from './Modal'
8
+ * <Defer when={open()}><Modal title="hi" /></Defer>
9
+ *
10
+ * into:
11
+ *
12
+ * <Defer when={open()} chunk={() => import('./Modal').then((__m) => ({ default: __m.Modal }))}>
13
+ * {(__C) => <__C title="hi" />}
14
+ * </Defer>
15
+ *
16
+ * The static `import { Modal } from './Modal'` is removed when `Modal` is
17
+ * referenced ONLY inside the Defer subtree — otherwise Rolldown would
18
+ * bundle the module statically and the dynamic import becomes a no-op.
19
+ *
20
+ * Scope (v2 — post #587 + this PR):
21
+ * - Multiple Defer elements per file: each rewritten independently.
22
+ * - Children: exactly ONE JSXElement, capitalised name (component
23
+ * reference). Self-closing OR with children. **Props ARE allowed**
24
+ * (post-v2) and pass through unchanged into the render-prop body —
25
+ * closure capture works naturally because the render-prop arrow
26
+ * captures the surrounding lexical scope.
27
+ * - Multiple non-whitespace children → bail with a warning.
28
+ * User must use the explicit `chunk` form with a render-prop.
29
+ * - Imports: default, named, **renamed** (`{ X as Y }`). Namespace
30
+ * imports (`* as M` + `<M.X />`) NOT supported — bail with a warning.
31
+ * - **Multi-specifier imports** (`{ A, B } from './x'`): only the
32
+ * binding used in Defer is removed; siblings stay intact.
33
+ * - Triggers (`when={...}`, `on="visible"`, `on="idle"`) pass through.
34
+ * - Other props on `<Defer>` (e.g. `fallback`) pass through.
35
+ *
36
+ * The transform is intentionally conservative — anything unusual leaves
37
+ * the source unchanged + emits a warning. Pipeline: runs BEFORE
38
+ * `transformJSX()` in the vite plugin. The output is still JSX —
39
+ * `transformJSX` then converts to runtime calls as usual.
40
+ */
41
+ interface DeferInlineWarning {
42
+ message: string;
43
+ line: number;
44
+ column: number;
45
+ code: 'defer-inline/multiple-children' | 'defer-inline/non-component-child' | 'defer-inline/import-not-found' | 'defer-inline/import-used-elsewhere' | 'defer-inline/unsupported-import-shape';
46
+ }
47
+ interface DeferInlineResult {
48
+ /** Transformed source — same as input when no transform applied. */
49
+ code: string;
50
+ /** True when at least one Defer JSX element was rewritten. */
51
+ changed: boolean;
52
+ /** Soft warnings for cases the transform deliberately skipped. */
53
+ warnings: DeferInlineWarning[];
54
+ }
55
+ declare function transformDeferInline(code: string, filename?: string): DeferInlineResult;
56
+ //#endregion
1
57
  //#region src/jsx.d.ts
2
58
  /**
3
59
  * JSX transform — wraps dynamic JSX expressions in `() =>` so the Pyreon runtime
@@ -38,6 +94,34 @@ interface CompilerWarning {
38
94
  /** Warning code for filtering */
39
95
  code: 'signal-call-in-jsx' | 'missing-key-on-for' | 'signal-in-static-prop' | 'circular-prop-derived';
40
96
  }
97
+ /**
98
+ * Reactivity-lens kinds. Each is a RECORD of a codegen decision the compiler
99
+ * already made — never an approximation. Positive claims (`reactive*`) are
100
+ * emitted ONLY where the compiler provably wrapped/tracked the span; absence
101
+ * of a span is "not asserted", never an implicit static claim. `static-text`
102
+ * is the high-precision negative: the literal `else` branch of the
103
+ * reactive-vs-static text decision (the "this `{x}` is baked once / dead"
104
+ * footgun signal when the author expected reactivity).
105
+ */
106
+ type ReactivityKind = 'reactive' | 'reactive-prop' | 'reactive-attr' | 'static-text' | 'hoisted-static';
107
+ interface ReactivitySpan {
108
+ /** Source byte offset (start) of the spanned expression in the INPUT. */
109
+ start: number;
110
+ /** Source byte offset (end). */
111
+ end: number;
112
+ /** 1-based start line. */
113
+ line: number;
114
+ /** 0-based start column. */
115
+ column: number;
116
+ /** 1-based end line. */
117
+ endLine: number;
118
+ /** 0-based end column. */
119
+ endColumn: number;
120
+ /** Which codegen decision this span records. */
121
+ kind: ReactivityKind;
122
+ /** Human-readable, editor-facing one-liner explaining the decision. */
123
+ detail: string;
124
+ }
41
125
  interface TransformResult {
42
126
  /** Transformed source code (JSX preserved, only expression containers modified) */
43
127
  code: string;
@@ -45,6 +129,13 @@ interface TransformResult {
45
129
  usesTemplates?: boolean;
46
130
  /** Compiler warnings for common mistakes */
47
131
  warnings: CompilerWarning[];
132
+ /**
133
+ * Reactivity-lens spans — populated ONLY when `TransformOptions.reactivityLens`
134
+ * is `true`. Additive: codegen output is byte-identical whether or not this is
135
+ * collected. Each span is a faithful record of a reactivity decision the
136
+ * compiler made for that source range. See {@link ReactivitySpan}.
137
+ */
138
+ reactivityLens?: ReactivitySpan[];
48
139
  }
49
140
  interface TransformOptions {
50
141
  /**
@@ -66,102 +157,19 @@ interface TransformOptions {
66
157
  * // {count} in JSX → {() => count()}
67
158
  */
68
159
  knownSignals?: string[];
160
+ /**
161
+ * Collect the {@link ReactivitySpan} sidecar (`TransformResult.reactivityLens`).
162
+ * Default `false`. Purely additive — the emitted `code` is byte-identical
163
+ * whether this is on or off (asserted by the compiler equivalence tests).
164
+ * The lens records reactivity decisions the compiler ALREADY makes for
165
+ * codegen; it never runs a second analysis pass.
166
+ */
167
+ reactivityLens?: boolean;
69
168
  }
70
169
  declare function transformJSX(code: string, filename?: string, options?: TransformOptions): TransformResult;
71
170
  /** JS fallback implementation — used when the native binary isn't available. */
72
171
  declare function transformJSX_JS(code: string, filename?: string, options?: TransformOptions): TransformResult;
73
172
  //#endregion
74
- //#region src/project-scanner.d.ts
75
- /**
76
- * Project scanner — extracts route, component, and island information from source files.
77
- */
78
- interface RouteInfo {
79
- path: string;
80
- name?: string | undefined;
81
- component?: string | undefined;
82
- hasLoader: boolean;
83
- hasGuard: boolean;
84
- params: string[];
85
- }
86
- interface ComponentInfo {
87
- name: string;
88
- file: string;
89
- hasSignals: boolean;
90
- signalNames: string[];
91
- props: string[];
92
- }
93
- interface IslandInfo {
94
- name: string;
95
- file: string;
96
- hydrate: string;
97
- }
98
- interface ProjectContext {
99
- framework: 'pyreon';
100
- version: string;
101
- generatedAt: string;
102
- routes: RouteInfo[];
103
- components: ComponentInfo[];
104
- islands: IslandInfo[];
105
- }
106
- declare function generateContext(cwd: string): ProjectContext;
107
- //#endregion
108
- //#region src/react-intercept.d.ts
109
- /**
110
- * React Pattern Interceptor — detects React/Vue patterns in code and provides
111
- * structured diagnostics with exact fix suggestions for AI-assisted migration.
112
- *
113
- * Two modes:
114
- * - `detectReactPatterns(code)` — returns diagnostics only (non-destructive)
115
- * - `migrateReactCode(code)` — applies auto-fixes and returns transformed code
116
- *
117
- * Designed for three consumers:
118
- * 1. Compiler pre-pass (warnings during build)
119
- * 2. CLI `pyreon doctor` (project-wide scanning)
120
- * 3. MCP server `migrate_react` / `validate` tools (AI agent integration)
121
- */
122
- type ReactDiagnosticCode = 'react-import' | 'react-dom-import' | 'react-router-import' | 'use-state' | 'use-effect-mount' | 'use-effect-deps' | 'use-effect-no-deps' | 'use-memo' | 'use-callback' | 'use-ref-dom' | 'use-ref-box' | 'use-reducer' | 'use-layout-effect' | 'memo-wrapper' | 'forward-ref' | 'class-name-prop' | 'html-for-prop' | 'on-change-input' | 'dangerously-set-inner-html' | 'dot-value-signal' | 'array-map-jsx' | 'key-on-for-child' | 'create-context-import' | 'use-context-import';
123
- interface ReactDiagnostic {
124
- /** Machine-readable code for filtering and programmatic handling */
125
- code: ReactDiagnosticCode;
126
- /** Human-readable message explaining the issue */
127
- message: string;
128
- /** 1-based line number */
129
- line: number;
130
- /** 0-based column */
131
- column: number;
132
- /** The code as written */
133
- current: string;
134
- /** The suggested Pyreon equivalent */
135
- suggested: string;
136
- /** Whether migrateReactCode can auto-fix this */
137
- fixable: boolean;
138
- }
139
- interface MigrationChange {
140
- type: 'replace' | 'remove' | 'add';
141
- line: number;
142
- description: string;
143
- }
144
- interface MigrationResult {
145
- /** Transformed source code */
146
- code: string;
147
- /** All detected patterns (including unfixable ones) */
148
- diagnostics: ReactDiagnostic[];
149
- /** Description of changes applied */
150
- changes: MigrationChange[];
151
- }
152
- declare function detectReactPatterns(code: string, filename?: string): ReactDiagnostic[];
153
- declare function migrateReactCode(code: string, filename?: string): MigrationResult;
154
- /** Fast regex check — returns true if code likely contains React patterns worth analyzing */
155
- declare function hasReactPatterns(code: string): boolean;
156
- interface ErrorDiagnosis {
157
- cause: string;
158
- fix: string;
159
- fixCode?: string | undefined;
160
- related?: string | undefined;
161
- }
162
- /** Diagnose an error message and return structured fix information */
163
- declare function diagnoseError(error: string): ErrorDiagnosis | null;
164
- //#endregion
165
173
  //#region src/pyreon-intercept.d.ts
166
174
  /**
167
175
  * Pyreon Pattern Interceptor — detects Pyreon-specific anti-patterns in
@@ -179,6 +187,12 @@ declare function diagnoseError(error: string): ErrorDiagnosis | null;
179
187
  * the component signature; reading is captured once
180
188
  * and loses reactivity. Access `props.foo` instead
181
189
  * or use `splitProps(props, [...])`.
190
+ * - `props-destructured-body` — `const { foo } = props` written
191
+ * SYNCHRONOUSLY in a component body — the body-scope
192
+ * companion to `props-destructured`. Same capture-
193
+ * once death; nested-function destructures (handler
194
+ * / effect / returned accessor) are NOT flagged
195
+ * (they re-read `props` per invocation).
182
196
  * - `process-dev-gate` — `typeof process !== 'undefined' &&
183
197
  * process.env.NODE_ENV !== 'production'` is dead
184
198
  * code in real Vite browser bundles. Use
@@ -234,7 +248,7 @@ declare function diagnoseError(error: string): ErrorDiagnosis | null;
234
248
  * 2. CLI `pyreon doctor`
235
249
  * 3. MCP server `validate` tool
236
250
  */
237
- type PyreonDiagnosticCode = 'for-missing-by' | 'for-with-key' | 'props-destructured' | 'process-dev-gate' | 'empty-theme' | 'raw-add-event-listener' | 'raw-remove-event-listener' | 'date-math-random-id' | 'on-click-undefined' | 'signal-write-as-call' | 'static-return-null-conditional' | 'as-unknown-as-vnodechild' | 'island-never-with-registry-entry';
251
+ type PyreonDiagnosticCode = 'for-missing-by' | 'for-with-key' | 'props-destructured' | 'props-destructured-body' | 'process-dev-gate' | 'empty-theme' | 'raw-add-event-listener' | 'raw-remove-event-listener' | 'date-math-random-id' | 'on-click-undefined' | 'signal-write-as-call' | 'static-return-null-conditional' | 'as-unknown-as-vnodechild' | 'island-never-with-registry-entry' | 'query-options-as-function';
238
252
  interface PyreonDiagnostic {
239
253
  /** Machine-readable code for filtering + programmatic handling */
240
254
  code: PyreonDiagnosticCode;
@@ -255,6 +269,156 @@ declare function detectPyreonPatterns(code: string, filename?: string): PyreonDi
255
269
  /** Fast regex pre-filter — returns true if the code is worth a full AST walk. */
256
270
  declare function hasPyreonPatterns(code: string): boolean;
257
271
  //#endregion
272
+ //#region src/reactivity-lens.d.ts
273
+ /** A footgun finding adds `'footgun'` to the structural codegen kinds. */
274
+ type ReactivityFindingKind = ReactivityKind | 'footgun';
275
+ interface ReactivityFinding {
276
+ /** Structural codegen decision, or `'footgun'` for a detected anti-pattern. */
277
+ kind: ReactivityFindingKind;
278
+ /** 1-based line. */
279
+ line: number;
280
+ /** 0-based column. */
281
+ column: number;
282
+ /** 1-based end line. */
283
+ endLine: number;
284
+ /** 0-based end column. */
285
+ endColumn: number;
286
+ /** Editor-facing one-liner. For footguns, the detector's message. */
287
+ detail: string;
288
+ /**
289
+ * For `'footgun'` findings: the static-detector code (e.g.
290
+ * `props-destructured`) so the editor surface can deep-link the
291
+ * anti-pattern catalogue. Absent for structural findings.
292
+ */
293
+ code?: PyreonDiagnosticCode;
294
+ /** For `'footgun'` findings: whether a mechanical auto-fix is safe. */
295
+ fixable?: boolean;
296
+ }
297
+ interface AnalyzeReactivityResult {
298
+ /** Sorted (line, column) findings — structural facts + footguns merged. */
299
+ findings: ReactivityFinding[];
300
+ /**
301
+ * Raw compiler spans (pre-merge), kept so the drift gate can assert the
302
+ * lens kind faithfully records the codegen decision without re-deriving.
303
+ */
304
+ spans: ReactivitySpan[];
305
+ }
306
+ /**
307
+ * Analyze a source file's reactivity. Pure, side-effect-free, deterministic.
308
+ *
309
+ * @param code Source text (`.tsx` / `.jsx` / `.ts`).
310
+ * @param filename Used only for parse-mode (`tsx` vs `jsx`) detection.
311
+ * @param options `knownSignals` is forwarded to the compiler so
312
+ * cross-module imported signals are auto-call-aware.
313
+ *
314
+ * @example
315
+ * const { findings } = analyzeReactivity(
316
+ * `function C(){ const {x}=props; return <div>{count()}</div> }`,
317
+ * )
318
+ * // → footgun(props-destructured) on `{x}`, reactive on `count()`
319
+ */
320
+ declare function analyzeReactivity(code: string, filename?: string, options?: {
321
+ knownSignals?: string[];
322
+ }): AnalyzeReactivityResult;
323
+ /**
324
+ * Render an annotated source view for CLI / debugging — every analyzed line
325
+ * followed by its reactivity findings. Not the production surface (that's the
326
+ * LSP inlay hints); this is the spike's "can you see reactivity flow" probe
327
+ * and a stable diff target for tests.
328
+ */
329
+ declare function formatReactivityLens(code: string, result: AnalyzeReactivityResult): string;
330
+ //#endregion
331
+ //#region src/project-scanner.d.ts
332
+ /**
333
+ * Project scanner — extracts route, component, and island information from source files.
334
+ */
335
+ interface RouteInfo {
336
+ path: string;
337
+ name?: string | undefined;
338
+ component?: string | undefined;
339
+ hasLoader: boolean;
340
+ hasGuard: boolean;
341
+ params: string[];
342
+ }
343
+ interface ComponentInfo {
344
+ name: string;
345
+ file: string;
346
+ hasSignals: boolean;
347
+ signalNames: string[];
348
+ props: string[];
349
+ }
350
+ interface IslandInfo {
351
+ name: string;
352
+ file: string;
353
+ hydrate: string;
354
+ }
355
+ interface ProjectContext {
356
+ framework: 'pyreon';
357
+ version: string;
358
+ generatedAt: string;
359
+ routes: RouteInfo[];
360
+ components: ComponentInfo[];
361
+ islands: IslandInfo[];
362
+ }
363
+ declare function generateContext(cwd: string): ProjectContext;
364
+ //#endregion
365
+ //#region src/react-intercept.d.ts
366
+ /**
367
+ * React Pattern Interceptor — detects React/Vue patterns in code and provides
368
+ * structured diagnostics with exact fix suggestions for AI-assisted migration.
369
+ *
370
+ * Two modes:
371
+ * - `detectReactPatterns(code)` — returns diagnostics only (non-destructive)
372
+ * - `migrateReactCode(code)` — applies auto-fixes and returns transformed code
373
+ *
374
+ * Designed for three consumers:
375
+ * 1. Compiler pre-pass (warnings during build)
376
+ * 2. CLI `pyreon doctor` (project-wide scanning)
377
+ * 3. MCP server `migrate_react` / `validate` tools (AI agent integration)
378
+ */
379
+ type ReactDiagnosticCode = 'react-import' | 'react-dom-import' | 'react-router-import' | 'use-state' | 'use-effect-mount' | 'use-effect-deps' | 'use-effect-no-deps' | 'use-memo' | 'use-callback' | 'use-ref-dom' | 'use-ref-box' | 'use-reducer' | 'use-layout-effect' | 'memo-wrapper' | 'forward-ref' | 'class-name-prop' | 'html-for-prop' | 'on-change-input' | 'dangerously-set-inner-html' | 'dot-value-signal' | 'array-map-jsx' | 'key-on-for-child' | 'create-context-import' | 'use-context-import';
380
+ interface ReactDiagnostic {
381
+ /** Machine-readable code for filtering and programmatic handling */
382
+ code: ReactDiagnosticCode;
383
+ /** Human-readable message explaining the issue */
384
+ message: string;
385
+ /** 1-based line number */
386
+ line: number;
387
+ /** 0-based column */
388
+ column: number;
389
+ /** The code as written */
390
+ current: string;
391
+ /** The suggested Pyreon equivalent */
392
+ suggested: string;
393
+ /** Whether migrateReactCode can auto-fix this */
394
+ fixable: boolean;
395
+ }
396
+ interface MigrationChange {
397
+ type: 'replace' | 'remove' | 'add';
398
+ line: number;
399
+ description: string;
400
+ }
401
+ interface MigrationResult {
402
+ /** Transformed source code */
403
+ code: string;
404
+ /** All detected patterns (including unfixable ones) */
405
+ diagnostics: ReactDiagnostic[];
406
+ /** Description of changes applied */
407
+ changes: MigrationChange[];
408
+ }
409
+ declare function detectReactPatterns(code: string, filename?: string): ReactDiagnostic[];
410
+ declare function migrateReactCode(code: string, filename?: string): MigrationResult;
411
+ /** Fast regex check — returns true if code likely contains React patterns worth analyzing */
412
+ declare function hasReactPatterns(code: string): boolean;
413
+ interface ErrorDiagnosis {
414
+ cause: string;
415
+ fix: string;
416
+ fixCode?: string | undefined;
417
+ related?: string | undefined;
418
+ }
419
+ /** Diagnose an error message and return structured fix information */
420
+ declare function diagnoseError(error: string): ErrorDiagnosis | null;
421
+ //#endregion
258
422
  //#region src/test-audit.d.ts
259
423
  type AuditRisk = 'high' | 'medium' | 'low';
260
424
  interface TestAuditEntry {
@@ -387,5 +551,5 @@ interface SsgAuditFormatOptions {
387
551
  }
388
552
  declare function formatSsgAudit(result: SsgAuditResult, _options?: SsgAuditFormatOptions): string;
389
553
  //#endregion
390
- export { type AuditFormatOptions, type AuditRisk, type CompilerWarning, type ComponentInfo, type ErrorDiagnosis, type IslandAuditFormatOptions, type IslandAuditResult, type IslandFinding, type IslandFindingCode, type IslandInfo, type IslandLocation, type MigrationChange, type MigrationResult, type ProjectContext, type PyreonDiagnostic, type PyreonDiagnosticCode, type ReactDiagnostic, type ReactDiagnosticCode, type RouteInfo, type SsgAuditFormatOptions, type SsgAuditResult, type SsgFinding, type SsgFindingCode, type SsgLocation, type TestAuditEntry, type TestAuditResult, type TransformResult, auditIslands, auditSsg, auditTestEnvironment, detectPyreonPatterns, detectReactPatterns, diagnoseError, formatIslandAudit, formatSsgAudit, formatTestAudit, generateContext, hasPyreonPatterns, hasReactPatterns, migrateReactCode, transformJSX, transformJSX_JS };
554
+ export { type AnalyzeReactivityResult, type AuditFormatOptions, type AuditRisk, type CompilerWarning, type ComponentInfo, type DeferInlineResult, type DeferInlineWarning, type ErrorDiagnosis, type IslandAuditFormatOptions, type IslandAuditResult, type IslandFinding, type IslandFindingCode, type IslandInfo, type IslandLocation, type MigrationChange, type MigrationResult, type ProjectContext, type PyreonDiagnostic, type PyreonDiagnosticCode, type ReactDiagnostic, type ReactDiagnosticCode, type ReactivityFinding, type ReactivityFindingKind, type ReactivityKind, type ReactivitySpan, type RouteInfo, type SsgAuditFormatOptions, type SsgAuditResult, type SsgFinding, type SsgFindingCode, type SsgLocation, type TestAuditEntry, type TestAuditResult, type TransformResult, analyzeReactivity, auditIslands, auditSsg, auditTestEnvironment, detectPyreonPatterns, detectReactPatterns, diagnoseError, formatIslandAudit, formatReactivityLens, formatSsgAudit, formatTestAudit, generateContext, hasPyreonPatterns, hasReactPatterns, migrateReactCode, transformDeferInline, transformJSX, transformJSX_JS };
391
555
  //# sourceMappingURL=index2.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/compiler",
3
- "version": "0.16.0",
3
+ "version": "0.19.0",
4
4
  "description": "Template and JSX compiler for Pyreon",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/compiler#readme",
6
6
  "bugs": {
@@ -47,19 +47,20 @@
47
47
  "oxc-parser": "^0.129.0"
48
48
  },
49
49
  "optionalDependencies": {
50
- "@pyreon/compiler-darwin-arm64": "workspace:^",
51
- "@pyreon/compiler-darwin-x64": "workspace:^",
52
- "@pyreon/compiler-linux-arm64-gnu": "workspace:^",
53
- "@pyreon/compiler-linux-arm64-musl": "workspace:^",
54
- "@pyreon/compiler-linux-x64-gnu": "workspace:^",
55
- "@pyreon/compiler-linux-x64-musl": "workspace:^",
56
- "@pyreon/compiler-win32-x64-msvc": "workspace:^"
50
+ "@pyreon/compiler-darwin-arm64": "^0.19.0",
51
+ "@pyreon/compiler-darwin-x64": "^0.19.0",
52
+ "@pyreon/compiler-linux-arm64-gnu": "^0.19.0",
53
+ "@pyreon/compiler-linux-arm64-musl": "^0.19.0",
54
+ "@pyreon/compiler-linux-x64-gnu": "^0.19.0",
55
+ "@pyreon/compiler-linux-x64-musl": "^0.19.0",
56
+ "@pyreon/compiler-win32-x64-msvc": "^0.19.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@pyreon/core": "^0.16.0",
60
- "@pyreon/reactivity": "^0.16.0",
61
- "@pyreon/runtime-dom": "^0.16.0",
62
- "@pyreon/test-utils": "^0.13.3",
59
+ "@pyreon/core": "^0.19.0",
60
+ "@pyreon/manifest": "0.13.1",
61
+ "@pyreon/reactivity": "^0.19.0",
62
+ "@pyreon/runtime-dom": "^0.19.0",
63
+ "@pyreon/test-utils": "^0.13.6",
63
64
  "happy-dom": "^20.8.3"
64
65
  },
65
66
  "peerDependencies": {