jsx_rosetta 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/lib/jsx_rosetta/ir/lowering.rb +66 -10
- data/lib/jsx_rosetta/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57170a56efc4593faf87b9ee0e3ac055f862cb058b9185ed57bb9a12d47f9af4
|
|
4
|
+
data.tar.gz: bd52ab90f51bc6be003f33b7cc10cda266002fa55386bc079f199fbcc9f3130b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 145db314ab8d61384fbebbf8969310664c358c6b561bf1dbf742e97feb71497e58703d300c9d6b1e75159dfee27ee7a0173662a631ab143597b0b6633fff67ff
|
|
7
|
+
data.tar.gz: 423f772ec2e33618ad6e6567d20caa9a4c375c8c05a5cb8425b681010ee89ecddda2fd9a6ddde0c2ab0777962c1d9e51cd3ec69f28236cc0e1390cdf25ccd90e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-05-10
|
|
4
|
+
|
|
5
|
+
Driven by an empirical probe of v0.1.0 against a 39-file Next.js production
|
|
6
|
+
slice (`reserv-web/src/components/rolloverbook`). The slice exposed three
|
|
7
|
+
return-shape gaps and a crash on nested destructure; this release fixes all
|
|
8
|
+
four. Probe outcome: 33/39 → **39/39 emit**.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Nested-destructured props no longer crash the lowering.**
|
|
13
|
+
`function X({ outer: { inner } })` previously surfaced as
|
|
14
|
+
`Inflector.underscore(nil)` in the backend. The lowering now uses the
|
|
15
|
+
outer key as the prop name. Renamed destructures (`{ outer: inner }`)
|
|
16
|
+
similarly use the source-side key (the prop name the parent passes),
|
|
17
|
+
not the renamed local.
|
|
18
|
+
|
|
19
|
+
### Added — return-shape lowering
|
|
20
|
+
|
|
21
|
+
- **Top-level conditional / short-circuit returns** —
|
|
22
|
+
`return cond ? <A/> : <B/>` and `return cond && <A/>` now lower to
|
|
23
|
+
IR::Conditional via a new return-position dispatcher. Previously raised
|
|
24
|
+
"unexpected JSX node in lowering: ConditionalExpression".
|
|
25
|
+
- **Multi-branch `if/else if/else` all-return bodies** — components
|
|
26
|
+
whose every return path lives inside an if-chain (no top-level
|
|
27
|
+
unconditional return) lower to a chained IR::Conditional. Branches
|
|
28
|
+
may be braced single-statement blocks (`if (x) { return <A/>; }`) or
|
|
29
|
+
bare returns (`if (x) return <A/>;`). Branches with side-effect
|
|
30
|
+
statements before the return still raise — those imply behavior we
|
|
31
|
+
can't preserve.
|
|
32
|
+
|
|
3
33
|
## [Unreleased]
|
|
4
34
|
|
|
5
35
|
### Added — translator (lowering)
|
|
@@ -209,14 +209,8 @@ module JsxRosetta
|
|
|
209
209
|
|
|
210
210
|
def lower_object_prop(property)
|
|
211
211
|
value = property[:value]
|
|
212
|
-
if value.type == "AssignmentPattern"
|
|
213
|
-
|
|
214
|
-
name: value[:left][:name],
|
|
215
|
-
default: Interpolation.new(expression: source_of(value[:right]))
|
|
216
|
-
)
|
|
217
|
-
else
|
|
218
|
-
Prop.new(name: value[:name], default: nil)
|
|
219
|
-
end
|
|
212
|
+
default = (Interpolation.new(expression: source_of(value[:right])) if value.type == "AssignmentPattern")
|
|
213
|
+
Prop.new(name: property[:key][:name], default: default)
|
|
220
214
|
end
|
|
221
215
|
|
|
222
216
|
def lower_function_body(body)
|
|
@@ -224,9 +218,12 @@ module JsxRosetta
|
|
|
224
218
|
when "BlockStatement"
|
|
225
219
|
collect_local_bindings(body[:body])
|
|
226
220
|
return_stmt = body[:body].find { |stmt| stmt.type == "ReturnStatement" }
|
|
227
|
-
|
|
221
|
+
return lower_return_value(return_stmt[:argument]) if return_stmt
|
|
228
222
|
|
|
229
|
-
|
|
223
|
+
chained = lower_if_return_chain_from_body(body[:body])
|
|
224
|
+
return chained if chained
|
|
225
|
+
|
|
226
|
+
raise lowering_error("component function has no return statement", node: body)
|
|
230
227
|
when "JSXElement", "JSXFragment"
|
|
231
228
|
@local_jsx = {}
|
|
232
229
|
lower_jsx(body)
|
|
@@ -235,6 +232,65 @@ module JsxRosetta
|
|
|
235
232
|
end
|
|
236
233
|
end
|
|
237
234
|
|
|
235
|
+
# Dispatch a value in return position. Distinct from lower_jsx because
|
|
236
|
+
# `return cond ? <A/> : <B/>` and `return cond && <A/>` are valid return
|
|
237
|
+
# shapes that aren't JSX nodes and need to lower as Conditional.
|
|
238
|
+
def lower_return_value(node)
|
|
239
|
+
case node.type
|
|
240
|
+
when "ConditionalExpression" then lower_ternary_expression(node)
|
|
241
|
+
when "LogicalExpression" then lower_logical_expression(node)
|
|
242
|
+
else lower_jsx(node)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Recognize a body whose only return paths are inside an
|
|
247
|
+
# `if/else if/else` chain at the bottom (no unconditional return).
|
|
248
|
+
# Lowers the chain to nested IR::Conditional. Returns nil when the
|
|
249
|
+
# shape doesn't fit.
|
|
250
|
+
def lower_if_return_chain_from_body(statements)
|
|
251
|
+
if_stmt = statements.last
|
|
252
|
+
return nil unless if_stmt.is_a?(AST::Node) && if_stmt.type == "IfStatement"
|
|
253
|
+
|
|
254
|
+
lower_if_return_chain(if_stmt)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def lower_if_return_chain(if_stmt)
|
|
258
|
+
consequent = lower_return_branch(if_stmt[:consequent])
|
|
259
|
+
return nil unless consequent
|
|
260
|
+
|
|
261
|
+
alternate_node = if_stmt[:alternate]
|
|
262
|
+
alternate = case alternate_node&.type
|
|
263
|
+
when nil then nil
|
|
264
|
+
when "IfStatement" then lower_if_return_chain(alternate_node)
|
|
265
|
+
else lower_return_branch(alternate_node)
|
|
266
|
+
end
|
|
267
|
+
return nil if alternate_node && alternate.nil?
|
|
268
|
+
|
|
269
|
+
Conditional.new(
|
|
270
|
+
test: Interpolation.new(expression: source_of(if_stmt[:test])),
|
|
271
|
+
consequent: consequent,
|
|
272
|
+
alternate: alternate
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# An if-chain branch lowers to a return value only when it is a
|
|
277
|
+
# single-statement block ending in `return X;` (or a bare `return X;`
|
|
278
|
+
# without braces). Multi-statement branches imply side effects we
|
|
279
|
+
# don't preserve, so we bail.
|
|
280
|
+
def lower_return_branch(branch)
|
|
281
|
+
case branch.type
|
|
282
|
+
when "ReturnStatement"
|
|
283
|
+
branch[:argument] && lower_return_value(branch[:argument])
|
|
284
|
+
when "BlockStatement"
|
|
285
|
+
return nil if branch[:body].size != 1
|
|
286
|
+
|
|
287
|
+
inner = branch[:body].first
|
|
288
|
+
return nil unless inner.type == "ReturnStatement" && inner[:argument]
|
|
289
|
+
|
|
290
|
+
lower_return_value(inner[:argument])
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
238
294
|
def collect_local_bindings(statements)
|
|
239
295
|
@local_jsx = {}
|
|
240
296
|
@local_arrows = {}
|
data/lib/jsx_rosetta/version.rb
CHANGED