@pyreon/compiler 0.18.0 → 0.20.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.
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +2081 -1262
- package/lib/types/index.d.ts +310 -125
- package/package.json +14 -12
- package/src/defer-inline.ts +397 -157
- package/src/index.ts +14 -2
- package/src/jsx.ts +784 -19
- package/src/load-native.ts +1 -0
- package/src/manifest.ts +280 -0
- package/src/pyreon-intercept.ts +164 -0
- package/src/react-intercept.ts +59 -0
- package/src/reactivity-lens.ts +190 -0
- package/src/tests/backend-parity-r7-r9.test.ts +91 -0
- package/src/tests/backend-prop-derived-callback-divergence.test.ts +74 -0
- package/src/tests/collapse-bail-census.test.ts +245 -0
- package/src/tests/collapse-key-source-hygiene.test.ts +88 -0
- package/src/tests/defer-inline.test.ts +209 -21
- package/src/tests/detector-tag-consistency.test.ts +2 -0
- package/src/tests/element-valued-const-child.test.ts +61 -0
- package/src/tests/falsy-child-characterization.test.ts +48 -0
- package/src/tests/malformed-input-resilience.test.ts +50 -0
- package/src/tests/manifest-snapshot.test.ts +55 -0
- package/src/tests/native-equivalence.test.ts +104 -3
- package/src/tests/partial-collapse-detector.test.ts +121 -0
- package/src/tests/partial-collapse-emit.test.ts +104 -0
- package/src/tests/partial-collapse-robustness.test.ts +53 -0
- package/src/tests/prop-derived-shadow.test.ts +96 -0
- package/src/tests/pure-call-reactive-args.test.ts +50 -0
- package/src/tests/pyreon-intercept.test.ts +189 -0
- package/src/tests/r13-callback-stmt-equivalence.test.ts +58 -0
- package/src/tests/r14-ssr-mode-parity.test.ts +51 -0
- package/src/tests/r15-elemconst-propderived.test.ts +47 -0
- package/src/tests/r19-defer-inline-robust.test.ts +54 -0
- package/src/tests/r20-backend-equivalence-sweep.test.ts +50 -0
- package/src/tests/react-intercept.test.ts +50 -2
- package/src/tests/reactivity-lens.test.ts +170 -0
- package/src/tests/rocketstyle-collapse.test.ts +208 -0
- package/src/tests/signal-autocall-shadow.test.ts +86 -0
- package/src/tests/sourcemap-fidelity.test.ts +77 -0
- package/src/tests/static-text-baking.test.ts +64 -0
- package/src/tests/transform-state-isolation.test.ts +49 -0
package/lib/types/index.d.ts
CHANGED
|
@@ -5,41 +5,44 @@
|
|
|
5
5
|
* Rewrites:
|
|
6
6
|
*
|
|
7
7
|
* import { Modal } from './Modal'
|
|
8
|
-
* <Defer when={open()}><Modal /></Defer>
|
|
8
|
+
* <Defer when={open()}><Modal title="hi" /></Defer>
|
|
9
9
|
*
|
|
10
10
|
* into:
|
|
11
11
|
*
|
|
12
|
-
* <Defer when={open()} chunk={() => import('./Modal').then(
|
|
13
|
-
* {
|
|
12
|
+
* <Defer when={open()} chunk={() => import('./Modal').then((__m) => ({ default: __m.Modal }))}>
|
|
13
|
+
* {(__C) => <__C title="hi" />}
|
|
14
14
|
* </Defer>
|
|
15
15
|
*
|
|
16
16
|
* The static `import { Modal } from './Modal'` is removed when `Modal` is
|
|
17
17
|
* referenced ONLY inside the Defer subtree — otherwise Rolldown would
|
|
18
18
|
* bundle the module statically and the dynamic import becomes a no-op.
|
|
19
19
|
*
|
|
20
|
-
* Scope
|
|
21
|
-
* -
|
|
22
|
-
* - Children: exactly ONE JSXElement,
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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.
|
|
27
33
|
* - Triggers (`when={...}`, `on="visible"`, `on="idle"`) pass through.
|
|
28
34
|
* - Other props on `<Defer>` (e.g. `fallback`) pass through.
|
|
29
35
|
*
|
|
30
36
|
* The transform is intentionally conservative — anything unusual leaves
|
|
31
|
-
* the source unchanged + emits a warning.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Pipeline: this runs BEFORE `transformJSX()` in the vite plugin. The
|
|
35
|
-
* output is still JSX — `transformJSX` then converts it to `h()` /
|
|
36
|
-
* `_tpl()` calls as usual.
|
|
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.
|
|
37
40
|
*/
|
|
38
41
|
interface DeferInlineWarning {
|
|
39
42
|
message: string;
|
|
40
43
|
line: number;
|
|
41
44
|
column: number;
|
|
42
|
-
code: 'defer-inline/multiple-children' | 'defer-inline/non-component-child' | 'defer-inline/
|
|
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';
|
|
43
46
|
}
|
|
44
47
|
interface DeferInlineResult {
|
|
45
48
|
/** Transformed source — same as input when no transform applied. */
|
|
@@ -49,23 +52,6 @@ interface DeferInlineResult {
|
|
|
49
52
|
/** Soft warnings for cases the transform deliberately skipped. */
|
|
50
53
|
warnings: DeferInlineWarning[];
|
|
51
54
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Main entry. Returns the (possibly transformed) source plus the list
|
|
54
|
-
* of warnings for cases the transform deliberately skipped.
|
|
55
|
-
*
|
|
56
|
-
* Bails (returns input unchanged with `changed: false`) when:
|
|
57
|
-
* - No `<Defer>` JSX element appears in the file (fast path).
|
|
58
|
-
* - The file fails to parse (syntax error — let downstream handle).
|
|
59
|
-
* - No `<Defer>` matches the inline-eligible shape.
|
|
60
|
-
*
|
|
61
|
-
* Per-Defer skips with a warning:
|
|
62
|
-
* - Multiple children → user must use render-prop form
|
|
63
|
-
* - Child has props → user must use render-prop form
|
|
64
|
-
* - Child name isn't imported → can't resolve the chunk source
|
|
65
|
-
* - Child binding is used outside the Defer subtree → can't remove
|
|
66
|
-
* the static import (dynamic import would be a no-op via Rolldown's
|
|
67
|
-
* same-module dedup)
|
|
68
|
-
*/
|
|
69
55
|
declare function transformDeferInline(code: string, filename?: string): DeferInlineResult;
|
|
70
56
|
//#endregion
|
|
71
57
|
//#region src/jsx.d.ts
|
|
@@ -98,6 +84,22 @@ declare function transformDeferInline(code: string, filename?: string): DeferInl
|
|
|
98
84
|
*
|
|
99
85
|
* Implementation: Rust native binary (napi-rs) when available, JS fallback via oxc-parser.
|
|
100
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* V3 source map shape returned by the JS backend. Structurally exactly
|
|
89
|
+
* magic-string's `SourceMap` (a valid V3 map plus `.toString()`/`.toUrl()`),
|
|
90
|
+
* declared locally so `TransformResult` carries no hard type dependency on
|
|
91
|
+
* magic-string's exported types.
|
|
92
|
+
*/
|
|
93
|
+
interface GeneratedSourceMap {
|
|
94
|
+
version: number;
|
|
95
|
+
file?: string;
|
|
96
|
+
sources: string[];
|
|
97
|
+
sourcesContent?: (string | null)[];
|
|
98
|
+
names: string[];
|
|
99
|
+
mappings: string;
|
|
100
|
+
toString(): string;
|
|
101
|
+
toUrl(): string;
|
|
102
|
+
}
|
|
101
103
|
interface CompilerWarning {
|
|
102
104
|
/** Warning message */
|
|
103
105
|
message: string;
|
|
@@ -108,6 +110,34 @@ interface CompilerWarning {
|
|
|
108
110
|
/** Warning code for filtering */
|
|
109
111
|
code: 'signal-call-in-jsx' | 'missing-key-on-for' | 'signal-in-static-prop' | 'circular-prop-derived';
|
|
110
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Reactivity-lens kinds. Each is a RECORD of a codegen decision the compiler
|
|
115
|
+
* already made — never an approximation. Positive claims (`reactive*`) are
|
|
116
|
+
* emitted ONLY where the compiler provably wrapped/tracked the span; absence
|
|
117
|
+
* of a span is "not asserted", never an implicit static claim. `static-text`
|
|
118
|
+
* is the high-precision negative: the literal `else` branch of the
|
|
119
|
+
* reactive-vs-static text decision (the "this `{x}` is baked once / dead"
|
|
120
|
+
* footgun signal when the author expected reactivity).
|
|
121
|
+
*/
|
|
122
|
+
type ReactivityKind = 'reactive' | 'reactive-prop' | 'reactive-attr' | 'static-text' | 'hoisted-static';
|
|
123
|
+
interface ReactivitySpan {
|
|
124
|
+
/** Source byte offset (start) of the spanned expression in the INPUT. */
|
|
125
|
+
start: number;
|
|
126
|
+
/** Source byte offset (end). */
|
|
127
|
+
end: number;
|
|
128
|
+
/** 1-based start line. */
|
|
129
|
+
line: number;
|
|
130
|
+
/** 0-based start column. */
|
|
131
|
+
column: number;
|
|
132
|
+
/** 1-based end line. */
|
|
133
|
+
endLine: number;
|
|
134
|
+
/** 0-based end column. */
|
|
135
|
+
endColumn: number;
|
|
136
|
+
/** Which codegen decision this span records. */
|
|
137
|
+
kind: ReactivityKind;
|
|
138
|
+
/** Human-readable, editor-facing one-liner explaining the decision. */
|
|
139
|
+
detail: string;
|
|
140
|
+
}
|
|
111
141
|
interface TransformResult {
|
|
112
142
|
/** Transformed source code (JSX preserved, only expression containers modified) */
|
|
113
143
|
code: string;
|
|
@@ -115,6 +145,22 @@ interface TransformResult {
|
|
|
115
145
|
usesTemplates?: boolean;
|
|
116
146
|
/** Compiler warnings for common mistakes */
|
|
117
147
|
warnings: CompilerWarning[];
|
|
148
|
+
/**
|
|
149
|
+
* Source map (V3) for the transform — present on the JS backend whenever a
|
|
150
|
+
* transformation actually occurred. `undefined` when nothing changed (the
|
|
151
|
+
* emitted code is byte-identical to the input, so no remapping is needed)
|
|
152
|
+
* and on the native backend (a Rust-side map is a scoped follow-up). The
|
|
153
|
+
* object is magic-string's `SourceMap`: it is a valid V3 map AND has
|
|
154
|
+
* `.toString()` / `.toUrl()`, so Vite/Rollup consume it directly.
|
|
155
|
+
*/
|
|
156
|
+
map?: GeneratedSourceMap;
|
|
157
|
+
/**
|
|
158
|
+
* Reactivity-lens spans — populated ONLY when `TransformOptions.reactivityLens`
|
|
159
|
+
* is `true`. Additive: codegen output is byte-identical whether or not this is
|
|
160
|
+
* collected. Each span is a faithful record of a reactivity decision the
|
|
161
|
+
* compiler made for that source range. See {@link ReactivitySpan}.
|
|
162
|
+
*/
|
|
163
|
+
reactivityLens?: ReactivitySpan[];
|
|
118
164
|
}
|
|
119
165
|
interface TransformOptions {
|
|
120
166
|
/**
|
|
@@ -136,101 +182,84 @@ interface TransformOptions {
|
|
|
136
182
|
* // {count} in JSX → {() => count()}
|
|
137
183
|
*/
|
|
138
184
|
knownSignals?: string[];
|
|
185
|
+
/**
|
|
186
|
+
* Collect the {@link ReactivitySpan} sidecar (`TransformResult.reactivityLens`).
|
|
187
|
+
* Default `false`. Purely additive — the emitted `code` is byte-identical
|
|
188
|
+
* whether this is on or off (asserted by the compiler equivalence tests).
|
|
189
|
+
* The lens records reactivity decisions the compiler ALREADY makes for
|
|
190
|
+
* codegen; it never runs a second analysis pass.
|
|
191
|
+
*/
|
|
192
|
+
reactivityLens?: boolean;
|
|
193
|
+
/**
|
|
194
|
+
* P0 — compile-time rocketstyle wrapper collapse. OFF unless the Vite
|
|
195
|
+
* plugin supplies this (opt-in `pyreon({ collapse: true })`). The plugin
|
|
196
|
+
* scans the module's imports for collapsible component candidates,
|
|
197
|
+
* SSR-resolves each literal-prop call site once (real component, light
|
|
198
|
+
* + dark), and passes the resolved `sites` map keyed by
|
|
199
|
+
* {@link rocketstyleCollapseKey}. The compiler only DETECTS the
|
|
200
|
+
* collapsible shape (bail catalogue — every dimension prop a string
|
|
201
|
+
* literal, no spread, static-text children) and EMITS the collapsed
|
|
202
|
+
* `_rsCollapse` call + the once-per-module rule injection; it never
|
|
203
|
+
* runs the rocketstyle chain itself (RFC decision 2).
|
|
204
|
+
*/
|
|
205
|
+
collapseRocketstyle?: {
|
|
206
|
+
/** Component names imported into this module that MAY collapse. */candidates: Set<string>; /** key → resolved emission data (absent ⇒ bail, keep normal mount). */
|
|
207
|
+
sites: Map<string, {
|
|
208
|
+
templateHtml: string;
|
|
209
|
+
lightClass: string;
|
|
210
|
+
darkClass: string;
|
|
211
|
+
rules: string[];
|
|
212
|
+
ruleKey: string;
|
|
213
|
+
}>; /** Live mode accessor to thread for dual-emit (RFC decision 1). */
|
|
214
|
+
mode: {
|
|
215
|
+
name: string;
|
|
216
|
+
source: string;
|
|
217
|
+
}; /** Module specifier for `_rsCollapse`. Default `@pyreon/runtime-dom`. */
|
|
218
|
+
runtimeDomSource?: string; /** Module specifier for the styler `sheet`. Default `@pyreon/styler`. */
|
|
219
|
+
stylerSource?: string;
|
|
220
|
+
};
|
|
139
221
|
}
|
|
140
|
-
declare function transformJSX(code: string, filename?: string, options?: TransformOptions): TransformResult;
|
|
141
|
-
/** JS fallback implementation — used when the native binary isn't available. */
|
|
142
|
-
declare function transformJSX_JS(code: string, filename?: string, options?: TransformOptions): TransformResult;
|
|
143
|
-
//#endregion
|
|
144
|
-
//#region src/project-scanner.d.ts
|
|
145
222
|
/**
|
|
146
|
-
*
|
|
223
|
+
* Canonical key for a collapsible rocketstyle call site. The Vite plugin
|
|
224
|
+
* computes this when it resolves a site; the compiler recomputes the
|
|
225
|
+
* IDENTICAL key from the JSX node to look the resolution up. Stable
|
|
226
|
+
* ordering of props so attribute order in source doesn't change the key.
|
|
147
227
|
*/
|
|
148
|
-
|
|
149
|
-
path: string;
|
|
150
|
-
name?: string | undefined;
|
|
151
|
-
component?: string | undefined;
|
|
152
|
-
hasLoader: boolean;
|
|
153
|
-
hasGuard: boolean;
|
|
154
|
-
params: string[];
|
|
155
|
-
}
|
|
156
|
-
interface ComponentInfo {
|
|
157
|
-
name: string;
|
|
158
|
-
file: string;
|
|
159
|
-
hasSignals: boolean;
|
|
160
|
-
signalNames: string[];
|
|
161
|
-
props: string[];
|
|
162
|
-
}
|
|
163
|
-
interface IslandInfo {
|
|
164
|
-
name: string;
|
|
165
|
-
file: string;
|
|
166
|
-
hydrate: string;
|
|
167
|
-
}
|
|
168
|
-
interface ProjectContext {
|
|
169
|
-
framework: 'pyreon';
|
|
170
|
-
version: string;
|
|
171
|
-
generatedAt: string;
|
|
172
|
-
routes: RouteInfo[];
|
|
173
|
-
components: ComponentInfo[];
|
|
174
|
-
islands: IslandInfo[];
|
|
175
|
-
}
|
|
176
|
-
declare function generateContext(cwd: string): ProjectContext;
|
|
177
|
-
//#endregion
|
|
178
|
-
//#region src/react-intercept.d.ts
|
|
228
|
+
declare function rocketstyleCollapseKey(componentName: string, props: Record<string, string>, childrenText: string): string;
|
|
179
229
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* - `detectReactPatterns(code)` — returns diagnostics only (non-destructive)
|
|
185
|
-
* - `migrateReactCode(code)` — applies auto-fixes and returns transformed code
|
|
186
|
-
*
|
|
187
|
-
* Designed for three consumers:
|
|
188
|
-
* 1. Compiler pre-pass (warnings during build)
|
|
189
|
-
* 2. CLI `pyreon doctor` (project-wide scanning)
|
|
190
|
-
* 3. MCP server `migrate_react` / `validate` tools (AI agent integration)
|
|
230
|
+
* A collapsible call site found by {@link scanCollapsibleSites}.
|
|
231
|
+
* `componentName` is the LOCAL JSX tag (post-import-alias) — it MUST be
|
|
232
|
+
* what `rocketstyleCollapseKey` is computed from on BOTH sides so the
|
|
233
|
+
* plugin's resolved `sites` map keys match the compiler's lookups.
|
|
191
234
|
*/
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
suggested: string;
|
|
206
|
-
/** Whether migrateReactCode can auto-fix this */
|
|
207
|
-
fixable: boolean;
|
|
208
|
-
}
|
|
209
|
-
interface MigrationChange {
|
|
210
|
-
type: 'replace' | 'remove' | 'add';
|
|
211
|
-
line: number;
|
|
212
|
-
description: string;
|
|
213
|
-
}
|
|
214
|
-
interface MigrationResult {
|
|
215
|
-
/** Transformed source code */
|
|
216
|
-
code: string;
|
|
217
|
-
/** All detected patterns (including unfixable ones) */
|
|
218
|
-
diagnostics: ReactDiagnostic[];
|
|
219
|
-
/** Description of changes applied */
|
|
220
|
-
changes: MigrationChange[];
|
|
221
|
-
}
|
|
222
|
-
declare function detectReactPatterns(code: string, filename?: string): ReactDiagnostic[];
|
|
223
|
-
declare function migrateReactCode(code: string, filename?: string): MigrationResult;
|
|
224
|
-
/** Fast regex check — returns true if code likely contains React patterns worth analyzing */
|
|
225
|
-
declare function hasReactPatterns(code: string): boolean;
|
|
226
|
-
interface ErrorDiagnosis {
|
|
227
|
-
cause: string;
|
|
228
|
-
fix: string;
|
|
229
|
-
fixCode?: string | undefined;
|
|
230
|
-
related?: string | undefined;
|
|
235
|
+
interface CollapsibleSite {
|
|
236
|
+
/** Local JSX tag name (the key + the compiler's detection use this). */
|
|
237
|
+
componentName: string;
|
|
238
|
+
/** Module specifier the component was imported from (for the resolver). */
|
|
239
|
+
source: string;
|
|
240
|
+
/** Imported binding name at `source` (may differ from local if aliased). */
|
|
241
|
+
importedName: string;
|
|
242
|
+
/** Literal string-valued props (the only shape the slice collapses). */
|
|
243
|
+
props: Record<string, string>;
|
|
244
|
+
/** Static text children (trimmed; empty ⇒ none). */
|
|
245
|
+
childrenText: string;
|
|
246
|
+
/** `rocketstyleCollapseKey(componentName, props, childrenText)`. */
|
|
247
|
+
key: string;
|
|
231
248
|
}
|
|
232
|
-
/**
|
|
233
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Pure detector — finds every collapsible rocketstyle call site in a
|
|
251
|
+
* module. Used by `@pyreon/vite-plugin` to know which (component, props,
|
|
252
|
+
* text) tuples to SSR-resolve. The bail catalogue here MUST stay
|
|
253
|
+
* byte-identical to `tryRocketstyleCollapse`'s (RFC decision 3): a
|
|
254
|
+
* candidate PascalCase tag whose import source is in `collapsibleSources`,
|
|
255
|
+
* every attr a plain string literal (no spread, no `{expr}`, no boolean
|
|
256
|
+
* attr), children empty or static text only. A consistency test asserts
|
|
257
|
+
* the keys this produces equal the keys the compiler looks up.
|
|
258
|
+
*/
|
|
259
|
+
declare function scanCollapsibleSites(code: string, filename: string, collapsibleSources: Set<string>): CollapsibleSite[];
|
|
260
|
+
declare function transformJSX(code: string, filename?: string, options?: TransformOptions): TransformResult;
|
|
261
|
+
/** JS fallback implementation — used when the native binary isn't available. */
|
|
262
|
+
declare function transformJSX_JS(code: string, filename?: string, options?: TransformOptions): TransformResult;
|
|
234
263
|
//#endregion
|
|
235
264
|
//#region src/pyreon-intercept.d.ts
|
|
236
265
|
/**
|
|
@@ -249,6 +278,12 @@ declare function diagnoseError(error: string): ErrorDiagnosis | null;
|
|
|
249
278
|
* the component signature; reading is captured once
|
|
250
279
|
* and loses reactivity. Access `props.foo` instead
|
|
251
280
|
* or use `splitProps(props, [...])`.
|
|
281
|
+
* - `props-destructured-body` — `const { foo } = props` written
|
|
282
|
+
* SYNCHRONOUSLY in a component body — the body-scope
|
|
283
|
+
* companion to `props-destructured`. Same capture-
|
|
284
|
+
* once death; nested-function destructures (handler
|
|
285
|
+
* / effect / returned accessor) are NOT flagged
|
|
286
|
+
* (they re-read `props` per invocation).
|
|
252
287
|
* - `process-dev-gate` — `typeof process !== 'undefined' &&
|
|
253
288
|
* process.env.NODE_ENV !== 'production'` is dead
|
|
254
289
|
* code in real Vite browser bundles. Use
|
|
@@ -304,7 +339,7 @@ declare function diagnoseError(error: string): ErrorDiagnosis | null;
|
|
|
304
339
|
* 2. CLI `pyreon doctor`
|
|
305
340
|
* 3. MCP server `validate` tool
|
|
306
341
|
*/
|
|
307
|
-
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';
|
|
342
|
+
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';
|
|
308
343
|
interface PyreonDiagnostic {
|
|
309
344
|
/** Machine-readable code for filtering + programmatic handling */
|
|
310
345
|
code: PyreonDiagnosticCode;
|
|
@@ -325,6 +360,156 @@ declare function detectPyreonPatterns(code: string, filename?: string): PyreonDi
|
|
|
325
360
|
/** Fast regex pre-filter — returns true if the code is worth a full AST walk. */
|
|
326
361
|
declare function hasPyreonPatterns(code: string): boolean;
|
|
327
362
|
//#endregion
|
|
363
|
+
//#region src/reactivity-lens.d.ts
|
|
364
|
+
/** A footgun finding adds `'footgun'` to the structural codegen kinds. */
|
|
365
|
+
type ReactivityFindingKind = ReactivityKind | 'footgun';
|
|
366
|
+
interface ReactivityFinding {
|
|
367
|
+
/** Structural codegen decision, or `'footgun'` for a detected anti-pattern. */
|
|
368
|
+
kind: ReactivityFindingKind;
|
|
369
|
+
/** 1-based line. */
|
|
370
|
+
line: number;
|
|
371
|
+
/** 0-based column. */
|
|
372
|
+
column: number;
|
|
373
|
+
/** 1-based end line. */
|
|
374
|
+
endLine: number;
|
|
375
|
+
/** 0-based end column. */
|
|
376
|
+
endColumn: number;
|
|
377
|
+
/** Editor-facing one-liner. For footguns, the detector's message. */
|
|
378
|
+
detail: string;
|
|
379
|
+
/**
|
|
380
|
+
* For `'footgun'` findings: the static-detector code (e.g.
|
|
381
|
+
* `props-destructured`) so the editor surface can deep-link the
|
|
382
|
+
* anti-pattern catalogue. Absent for structural findings.
|
|
383
|
+
*/
|
|
384
|
+
code?: PyreonDiagnosticCode;
|
|
385
|
+
/** For `'footgun'` findings: whether a mechanical auto-fix is safe. */
|
|
386
|
+
fixable?: boolean;
|
|
387
|
+
}
|
|
388
|
+
interface AnalyzeReactivityResult {
|
|
389
|
+
/** Sorted (line, column) findings — structural facts + footguns merged. */
|
|
390
|
+
findings: ReactivityFinding[];
|
|
391
|
+
/**
|
|
392
|
+
* Raw compiler spans (pre-merge), kept so the drift gate can assert the
|
|
393
|
+
* lens kind faithfully records the codegen decision without re-deriving.
|
|
394
|
+
*/
|
|
395
|
+
spans: ReactivitySpan[];
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Analyze a source file's reactivity. Pure, side-effect-free, deterministic.
|
|
399
|
+
*
|
|
400
|
+
* @param code Source text (`.tsx` / `.jsx` / `.ts`).
|
|
401
|
+
* @param filename Used only for parse-mode (`tsx` vs `jsx`) detection.
|
|
402
|
+
* @param options `knownSignals` is forwarded to the compiler so
|
|
403
|
+
* cross-module imported signals are auto-call-aware.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* const { findings } = analyzeReactivity(
|
|
407
|
+
* `function C(){ const {x}=props; return <div>{count()}</div> }`,
|
|
408
|
+
* )
|
|
409
|
+
* // → footgun(props-destructured) on `{x}`, reactive on `count()`
|
|
410
|
+
*/
|
|
411
|
+
declare function analyzeReactivity(code: string, filename?: string, options?: {
|
|
412
|
+
knownSignals?: string[];
|
|
413
|
+
}): AnalyzeReactivityResult;
|
|
414
|
+
/**
|
|
415
|
+
* Render an annotated source view for CLI / debugging — every analyzed line
|
|
416
|
+
* followed by its reactivity findings. Not the production surface (that's the
|
|
417
|
+
* LSP inlay hints); this is the spike's "can you see reactivity flow" probe
|
|
418
|
+
* and a stable diff target for tests.
|
|
419
|
+
*/
|
|
420
|
+
declare function formatReactivityLens(code: string, result: AnalyzeReactivityResult): string;
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region src/project-scanner.d.ts
|
|
423
|
+
/**
|
|
424
|
+
* Project scanner — extracts route, component, and island information from source files.
|
|
425
|
+
*/
|
|
426
|
+
interface RouteInfo {
|
|
427
|
+
path: string;
|
|
428
|
+
name?: string | undefined;
|
|
429
|
+
component?: string | undefined;
|
|
430
|
+
hasLoader: boolean;
|
|
431
|
+
hasGuard: boolean;
|
|
432
|
+
params: string[];
|
|
433
|
+
}
|
|
434
|
+
interface ComponentInfo {
|
|
435
|
+
name: string;
|
|
436
|
+
file: string;
|
|
437
|
+
hasSignals: boolean;
|
|
438
|
+
signalNames: string[];
|
|
439
|
+
props: string[];
|
|
440
|
+
}
|
|
441
|
+
interface IslandInfo {
|
|
442
|
+
name: string;
|
|
443
|
+
file: string;
|
|
444
|
+
hydrate: string;
|
|
445
|
+
}
|
|
446
|
+
interface ProjectContext {
|
|
447
|
+
framework: 'pyreon';
|
|
448
|
+
version: string;
|
|
449
|
+
generatedAt: string;
|
|
450
|
+
routes: RouteInfo[];
|
|
451
|
+
components: ComponentInfo[];
|
|
452
|
+
islands: IslandInfo[];
|
|
453
|
+
}
|
|
454
|
+
declare function generateContext(cwd: string): ProjectContext;
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/react-intercept.d.ts
|
|
457
|
+
/**
|
|
458
|
+
* React Pattern Interceptor — detects React/Vue patterns in code and provides
|
|
459
|
+
* structured diagnostics with exact fix suggestions for AI-assisted migration.
|
|
460
|
+
*
|
|
461
|
+
* Two modes:
|
|
462
|
+
* - `detectReactPatterns(code)` — returns diagnostics only (non-destructive)
|
|
463
|
+
* - `migrateReactCode(code)` — applies auto-fixes and returns transformed code
|
|
464
|
+
*
|
|
465
|
+
* Designed for three consumers:
|
|
466
|
+
* 1. Compiler pre-pass (warnings during build)
|
|
467
|
+
* 2. CLI `pyreon doctor` (project-wide scanning)
|
|
468
|
+
* 3. MCP server `migrate_react` / `validate` tools (AI agent integration)
|
|
469
|
+
*/
|
|
470
|
+
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';
|
|
471
|
+
interface ReactDiagnostic {
|
|
472
|
+
/** Machine-readable code for filtering and programmatic handling */
|
|
473
|
+
code: ReactDiagnosticCode;
|
|
474
|
+
/** Human-readable message explaining the issue */
|
|
475
|
+
message: string;
|
|
476
|
+
/** 1-based line number */
|
|
477
|
+
line: number;
|
|
478
|
+
/** 0-based column */
|
|
479
|
+
column: number;
|
|
480
|
+
/** The code as written */
|
|
481
|
+
current: string;
|
|
482
|
+
/** The suggested Pyreon equivalent */
|
|
483
|
+
suggested: string;
|
|
484
|
+
/** Whether migrateReactCode can auto-fix this */
|
|
485
|
+
fixable: boolean;
|
|
486
|
+
}
|
|
487
|
+
interface MigrationChange {
|
|
488
|
+
type: 'replace' | 'remove' | 'add';
|
|
489
|
+
line: number;
|
|
490
|
+
description: string;
|
|
491
|
+
}
|
|
492
|
+
interface MigrationResult {
|
|
493
|
+
/** Transformed source code */
|
|
494
|
+
code: string;
|
|
495
|
+
/** All detected patterns (including unfixable ones) */
|
|
496
|
+
diagnostics: ReactDiagnostic[];
|
|
497
|
+
/** Description of changes applied */
|
|
498
|
+
changes: MigrationChange[];
|
|
499
|
+
}
|
|
500
|
+
declare function detectReactPatterns(code: string, filename?: string): ReactDiagnostic[];
|
|
501
|
+
declare function migrateReactCode(code: string, filename?: string): MigrationResult;
|
|
502
|
+
/** Fast regex check — returns true if code likely contains React patterns worth analyzing */
|
|
503
|
+
declare function hasReactPatterns(code: string): boolean;
|
|
504
|
+
interface ErrorDiagnosis {
|
|
505
|
+
cause: string;
|
|
506
|
+
fix: string;
|
|
507
|
+
fixCode?: string | undefined;
|
|
508
|
+
related?: string | undefined;
|
|
509
|
+
}
|
|
510
|
+
/** Diagnose an error message and return structured fix information */
|
|
511
|
+
declare function diagnoseError(error: string): ErrorDiagnosis | null;
|
|
512
|
+
//#endregion
|
|
328
513
|
//#region src/test-audit.d.ts
|
|
329
514
|
type AuditRisk = 'high' | 'medium' | 'low';
|
|
330
515
|
interface TestAuditEntry {
|
|
@@ -457,5 +642,5 @@ interface SsgAuditFormatOptions {
|
|
|
457
642
|
}
|
|
458
643
|
declare function formatSsgAudit(result: SsgAuditResult, _options?: SsgAuditFormatOptions): string;
|
|
459
644
|
//#endregion
|
|
460
|
-
export { 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 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, transformDeferInline, transformJSX, transformJSX_JS };
|
|
645
|
+
export { type AnalyzeReactivityResult, type AuditFormatOptions, type AuditRisk, type CollapsibleSite, 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, rocketstyleCollapseKey, scanCollapsibleSites, transformDeferInline, transformJSX, transformJSX_JS };
|
|
461
646
|
//# sourceMappingURL=index2.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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": {
|
|
@@ -44,22 +44,24 @@
|
|
|
44
44
|
"prepublishOnly": "bun run build"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"magic-string": "^0.30.21",
|
|
47
48
|
"oxc-parser": "^0.129.0"
|
|
48
49
|
},
|
|
49
50
|
"optionalDependencies": {
|
|
50
|
-
"@pyreon/compiler-darwin-arm64": "
|
|
51
|
-
"@pyreon/compiler-darwin-x64": "
|
|
52
|
-
"@pyreon/compiler-linux-arm64-gnu": "
|
|
53
|
-
"@pyreon/compiler-linux-arm64-musl": "
|
|
54
|
-
"@pyreon/compiler-linux-x64-gnu": "
|
|
55
|
-
"@pyreon/compiler-linux-x64-musl": "
|
|
56
|
-
"@pyreon/compiler-win32-x64-msvc": "
|
|
51
|
+
"@pyreon/compiler-darwin-arm64": "^0.20.0",
|
|
52
|
+
"@pyreon/compiler-darwin-x64": "^0.20.0",
|
|
53
|
+
"@pyreon/compiler-linux-arm64-gnu": "^0.20.0",
|
|
54
|
+
"@pyreon/compiler-linux-arm64-musl": "^0.20.0",
|
|
55
|
+
"@pyreon/compiler-linux-x64-gnu": "^0.20.0",
|
|
56
|
+
"@pyreon/compiler-linux-x64-musl": "^0.20.0",
|
|
57
|
+
"@pyreon/compiler-win32-x64-msvc": "^0.20.0"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
|
-
"@pyreon/core": "^0.
|
|
60
|
-
"@pyreon/
|
|
61
|
-
"@pyreon/
|
|
62
|
-
"@pyreon/
|
|
60
|
+
"@pyreon/core": "^0.20.0",
|
|
61
|
+
"@pyreon/manifest": "0.13.1",
|
|
62
|
+
"@pyreon/reactivity": "^0.20.0",
|
|
63
|
+
"@pyreon/runtime-dom": "^0.20.0",
|
|
64
|
+
"@pyreon/test-utils": "^0.13.7",
|
|
63
65
|
"happy-dom": "^20.8.3"
|
|
64
66
|
},
|
|
65
67
|
"peerDependencies": {
|