@pyreon/compiler 0.13.1 → 0.14.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.
@@ -18,20 +18,15 @@
18
18
  * values, and all children are text nodes or other static JSX nodes.
19
19
  *
20
20
  * Template emission:
21
- * - JSX element trees with ≥ 2 DOM elements (no components, no spread attrs)
22
- * are compiled to `_tpl(html, bindFn)` calls instead of nested `h()` calls.
21
+ * - JSX element trees with ≥ 1 DOM elements (no components, no spread attrs on
22
+ * inner elements) are compiled to `_tpl(html, bindFn)` calls instead of nested
23
+ * `h()` calls.
23
24
  * - The HTML string is parsed once via <template>.innerHTML, then cloneNode(true)
24
25
  * for each instance (~5-10x faster than sequential createElement calls).
25
26
  * - Static attributes are baked into the HTML string; dynamic attributes and
26
27
  * text content use renderEffect in the bind function.
27
28
  *
28
- * Implementation: TypeScript parser for positions + magic-string replacements.
29
- * No extra runtime dependencies — `typescript` is already in devDependencies.
30
- *
31
- * Known limitation (v0): expressions inside *nested* JSX within a child
32
- * expression container are not individually wrapped. They are still reactive
33
- * because the outer wrapper re-evaluates the whole subtree, just at a coarser
34
- * granularity. Fine-grained nested wrapping is planned for a future pass.
29
+ * Implementation: Rust native binary (napi-rs) when available, JS fallback via oxc-parser.
35
30
  */
36
31
  interface CompilerWarning {
37
32
  /** Warning message */
@@ -54,14 +49,27 @@ interface TransformResult {
54
49
  interface TransformOptions {
55
50
  /**
56
51
  * Compile for server-side rendering. When true, the compiler skips the
57
- * `_tpl()` template optimization (which mutates a real DOM via
58
- * `document.createElement` etc.) and falls back to plain `h()` calls so
59
- * `@pyreon/runtime-server` can walk the VNode tree. Client builds keep
60
- * the `_tpl()` fast path. Default: false.
52
+ * `_tpl()` template optimization and falls back to plain `h()` calls so
53
+ * `@pyreon/runtime-server` can walk the VNode tree. Default: false.
61
54
  */
62
55
  ssr?: boolean;
56
+ /**
57
+ * Known signal variable names from resolved imports.
58
+ * The Vite plugin maintains a cross-module signal export registry and
59
+ * passes imported signal names here so the compiler can auto-call them
60
+ * in JSX even though the `signal()` declaration is in another file.
61
+ *
62
+ * @example
63
+ * // store.ts: export const count = signal(0)
64
+ * // component.tsx: import { count } from './store'
65
+ * transformJSX(code, 'component.tsx', { knownSignals: ['count'] })
66
+ * // {count} in JSX → {() => count()}
67
+ */
68
+ knownSignals?: string[];
63
69
  }
64
70
  declare function transformJSX(code: string, filename?: string, options?: TransformOptions): TransformResult;
71
+ /** JS fallback implementation — used when the native binary isn't available. */
72
+ declare function transformJSX_JS(code: string, filename?: string, options?: TransformOptions): TransformResult;
65
73
  //#endregion
66
74
  //#region src/project-scanner.d.ts
67
75
  /**
@@ -154,5 +162,123 @@ interface ErrorDiagnosis {
154
162
  /** Diagnose an error message and return structured fix information */
155
163
  declare function diagnoseError(error: string): ErrorDiagnosis | null;
156
164
  //#endregion
157
- export { type CompilerWarning, type ComponentInfo, type ErrorDiagnosis, type IslandInfo, type MigrationChange, type MigrationResult, type ProjectContext, type ReactDiagnostic, type ReactDiagnosticCode, type RouteInfo, type TransformResult, detectReactPatterns, diagnoseError, generateContext, hasReactPatterns, migrateReactCode, transformJSX };
165
+ //#region src/pyreon-intercept.d.ts
166
+ /**
167
+ * Pyreon Pattern Interceptor — detects Pyreon-specific anti-patterns in
168
+ * code that has ALREADY committed to the framework (imports are Pyreon,
169
+ * not React). Complements `react-intercept.ts` — the React detector
170
+ * catches "coming from React" mistakes; this one catches "using Pyreon
171
+ * wrong" mistakes.
172
+ *
173
+ * Catalog of detected patterns (grounded in `.claude/rules/anti-patterns.md`):
174
+ *
175
+ * - `for-missing-by` — `<For each={...}>` without a `by` prop
176
+ * - `for-with-key` — `<For key={...}>` (JSX reserves `key`; the keying
177
+ * prop is `by` in Pyreon)
178
+ * - `props-destructured` — `({ foo }: Props) => <JSX />` destructures at
179
+ * the component signature; reading is captured once
180
+ * and loses reactivity. Access `props.foo` instead
181
+ * or use `splitProps(props, [...])`.
182
+ * - `process-dev-gate` — `typeof process !== 'undefined' &&
183
+ * process.env.NODE_ENV !== 'production'` is dead
184
+ * code in real Vite browser bundles. Use
185
+ * `import.meta.env?.DEV` instead.
186
+ * - `empty-theme` — `.theme({})` chain is a no-op; remove it.
187
+ * - `raw-add-event-listener` — raw `addEventListener(...)` in a component
188
+ * or hook body. Use `useEventListener(...)` from
189
+ * `@pyreon/hooks` for auto-cleanup.
190
+ * - `raw-remove-event-listener` — same, for removeEventListener.
191
+ * - `date-math-random-id` — `Date.now() + Math.random()` / template-concat
192
+ * variants. Under rapid operations (paste, clone)
193
+ * collision probability is non-trivial. Use a
194
+ * monotonic counter.
195
+ * - `on-click-undefined` — `onClick={undefined}` explicitly; the runtime
196
+ * used to crash on this pattern. Omit the prop.
197
+ *
198
+ * Two-mode surface mirrors `react-intercept.ts`:
199
+ * - `detectPyreonPatterns(code)` — diagnostics only
200
+ * - `hasPyreonPatterns(code)` — fast regex pre-filter
201
+ *
202
+ * ## fixable: false (invariant)
203
+ *
204
+ * Every Pyreon diagnostic reports `fixable: false` — no exceptions.
205
+ * The `migrate_react` MCP tool only knows React mappings, so claiming
206
+ * a Pyreon code is auto-fixable would mislead a consumer who wires
207
+ * their UX off the flag and finds nothing applies the fix. Flip to
208
+ * `true` ONLY when a companion `migrate_pyreon` tool ships in a
209
+ * subsequent PR. The invariant is locked in
210
+ * `tests/pyreon-intercept.test.ts` under "fixable contract".
211
+ *
212
+ * Designed for three consumers:
213
+ * 1. Compiler pre-pass warnings during build
214
+ * 2. CLI `pyreon doctor`
215
+ * 3. MCP server `validate` tool
216
+ */
217
+ 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';
218
+ interface PyreonDiagnostic {
219
+ /** Machine-readable code for filtering + programmatic handling */
220
+ code: PyreonDiagnosticCode;
221
+ /** Human-readable message explaining the issue */
222
+ message: string;
223
+ /** 1-based line number */
224
+ line: number;
225
+ /** 0-based column */
226
+ column: number;
227
+ /** The code as written */
228
+ current: string;
229
+ /** The suggested Pyreon fix */
230
+ suggested: string;
231
+ /** Whether a mechanical auto-fix is safe */
232
+ fixable: boolean;
233
+ }
234
+ declare function detectPyreonPatterns(code: string, filename?: string): PyreonDiagnostic[];
235
+ /** Fast regex pre-filter — returns true if the code is worth a full AST walk. */
236
+ declare function hasPyreonPatterns(code: string): boolean;
237
+ //#endregion
238
+ //#region src/test-audit.d.ts
239
+ type AuditRisk = 'high' | 'medium' | 'low';
240
+ interface TestAuditEntry {
241
+ /** Absolute path to the test file */
242
+ path: string;
243
+ /** Path relative to the repo root for readable reporting */
244
+ relPath: string;
245
+ /** Count of object-literal `{ type: ..., props: ..., children: ... }` patterns */
246
+ mockVNodeLiteralCount: number;
247
+ /** Count of `vnode` / `mockVNode` / `createVNode` helper DEFINITIONS */
248
+ mockHelperCount: number;
249
+ /**
250
+ * Count of CALLS to a known mock-helper name. Captures pervasiveness:
251
+ * a file with one helper definition and 50 call-sites has the same
252
+ * `mockHelperCount` (1) as one with zero calls, but very different
253
+ * exposure. This metric surfaces that.
254
+ */
255
+ mockHelperCallCount: number;
256
+ /** Count of lines that look like real `h(...)` calls (`h(Tag, props)` / `h(Component, ...)` shape) */
257
+ realHCallCount: number;
258
+ /** True if the file imports `h` from `@pyreon/core` */
259
+ importsH: boolean;
260
+ /** Risk classification */
261
+ risk: AuditRisk;
262
+ }
263
+ interface TestAuditResult {
264
+ /** Repo root discovered by walking up for `packages/` */
265
+ root: string | null;
266
+ /** Every test file scanned, sorted by risk (high → low) then path */
267
+ entries: TestAuditEntry[];
268
+ /** Total files scanned */
269
+ totalScanned: number;
270
+ }
271
+ declare function auditTestEnvironment(startDir: string): TestAuditResult;
272
+ interface AuditFormatOptions {
273
+ /** Only include entries at or above this risk level. Default 'medium'. */
274
+ minRisk?: AuditRisk | undefined;
275
+ /** Maximum entries to show per risk group. Default 20. */
276
+ limit?: number | undefined;
277
+ }
278
+ declare function formatTestAudit(result: TestAuditResult, {
279
+ minRisk,
280
+ limit
281
+ }?: AuditFormatOptions): string;
282
+ //#endregion
283
+ export { type AuditFormatOptions, type AuditRisk, type CompilerWarning, type ComponentInfo, type ErrorDiagnosis, type IslandInfo, type MigrationChange, type MigrationResult, type ProjectContext, type PyreonDiagnostic, type PyreonDiagnosticCode, type ReactDiagnostic, type ReactDiagnosticCode, type RouteInfo, type TestAuditEntry, type TestAuditResult, type TransformResult, auditTestEnvironment, detectPyreonPatterns, detectReactPatterns, diagnoseError, formatTestAudit, generateContext, hasPyreonPatterns, hasReactPatterns, migrateReactCode, transformJSX, transformJSX_JS };
158
284
  //# sourceMappingURL=index2.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/jsx.ts","../../../src/project-scanner.ts","../../../src/react-intercept.ts"],"mappings":";;AAqCA;;;;;;;;;;AAeA;;;;;;;;;;AAwCA;;;;;AAWA;;;;;;;;UAlEiB,eAAA;EAsEC;EApEhB,OAAA;;EAEA,IAAA;;EAEA,MAAA;ECpCwB;EDsCxB,IAAA;AAAA;AAAA,UAOe,eAAA;EC3Cf;ED6CA,IAAA;EC3CA;ED6CA,aAAA;EC3CA;ED6CA,QAAA,EAAU,eAAA;AAAA;AAAA,UAkCK,gBAAA;EC5Ea;;;;;;;EDoF5B,GAAA;AAAA;AAAA,iBAGc,YAAA,CACd,IAAA,UACA,QAAA,WACA,OAAA,GAAS,gBAAA,GACR,eAAA;;;;AAtEH;;UC9BiB,SAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;EACA,SAAA;EACA,QAAA;EACA,MAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA;EACA,IAAA;EACA,UAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,UAGe,UAAA;EACf,IAAA;EACA,IAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,SAAA;EACA,OAAA;EACA,WAAA;EACA,MAAA,EAAQ,SAAA;EACR,UAAA,EAAY,aAAA;EACZ,OAAA,EAAS,UAAA;AAAA;AAAA,iBAGK,eAAA,CAAgB,GAAA,WAAc,cAAA;;;;ADF9C;;;;;;;;;;AAeA;;KEhCY,mBAAA;AAAA,UA0BK,eAAA;EFQf;EENA,IAAA,EAAM,mBAAA;EFUN;EERA,OAAA;EFQyB;EENzB,IAAA;EFwCe;EEtCf,MAAA;;EAEA,OAAA;EF4CG;EE1CH,SAAA;EF6C0B;EE3C1B,OAAA;AAAA;AAAA,UAGe,eAAA;EACf,IAAA;EACA,IAAA;EACA,WAAA;AAAA;AAAA,UAGe,eAAA;EFsCC;EEpChB,IAAA;;EAEA,WAAA,EAAa,eAAA;EDlEE;ECoEf,OAAA,EAAS,eAAA;AAAA;AAAA,iBAsgBK,mBAAA,CAAoB,IAAA,UAAc,QAAA,YAAyB,eAAA;AAAA,iBAqW3D,gBAAA,CAAiB,IAAA,UAAc,QAAA,YAAyB,eAAA;;iBA0ExD,gBAAA,CAAiB,IAAA;AAAA,UAuBhB,cAAA;EACf,KAAA;EACA,GAAA;EACA,OAAA;EACA,OAAA;AAAA;;iBAkGc,aAAA,CAAc,KAAA,WAAgB,cAAA"}
1
+ {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/jsx.ts","../../../src/project-scanner.ts","../../../src/react-intercept.ts","../../../src/pyreon-intercept.ts","../../../src/test-audit.ts"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/compiler",
3
- "version": "0.13.1",
3
+ "version": "0.14.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": {
@@ -41,6 +41,9 @@
41
41
  "lint": "oxlint .",
42
42
  "prepublishOnly": "bun run build"
43
43
  },
44
+ "dependencies": {
45
+ "oxc-parser": "^0.123.0"
46
+ },
44
47
  "peerDependencies": {
45
48
  "typescript": ">=5.0.0"
46
49
  }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // @pyreon/compiler — JSX reactive transform for Pyreon
2
2
 
3
3
  export type { CompilerWarning, TransformResult } from './jsx'
4
- export { transformJSX } from './jsx'
4
+ export { transformJSX, transformJSX_JS } from './jsx'
5
5
  export type { ComponentInfo, IslandInfo, ProjectContext, RouteInfo } from './project-scanner'
6
6
  export { generateContext } from './project-scanner'
7
7
  export type {
@@ -17,3 +17,12 @@ export {
17
17
  hasReactPatterns,
18
18
  migrateReactCode,
19
19
  } from './react-intercept'
20
+ export type { PyreonDiagnostic, PyreonDiagnosticCode } from './pyreon-intercept'
21
+ export { detectPyreonPatterns, hasPyreonPatterns } from './pyreon-intercept'
22
+ export type {
23
+ AuditFormatOptions,
24
+ AuditRisk,
25
+ TestAuditEntry,
26
+ TestAuditResult,
27
+ } from './test-audit'
28
+ export { auditTestEnvironment, formatTestAudit } from './test-audit'