jsx_rosetta 0.4.0 → 0.5.1
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 +215 -1
- data/ROADMAP.md +92 -0
- data/lib/jsx_rosetta/ast/inflector.rb +15 -0
- data/lib/jsx_rosetta/backend/phlex.rb +372 -110
- data/lib/jsx_rosetta/backend/view_component/expression_translator.rb +297 -26
- data/lib/jsx_rosetta/backend/view_component.rb +214 -30
- data/lib/jsx_rosetta/ir/lowering.rb +445 -40
- data/lib/jsx_rosetta/ir/module_shape_classifier.rb +20 -1
- data/lib/jsx_rosetta/ir/types.rb +78 -17
- data/lib/jsx_rosetta/version.rb +1 -1
- metadata +2 -1
data/lib/jsx_rosetta/ir/types.rb
CHANGED
|
@@ -33,12 +33,19 @@ module JsxRosetta
|
|
|
33
33
|
# inline arrows / const-bound arrows used in onX={...}.
|
|
34
34
|
# When non-empty, backends should emit a sibling
|
|
35
35
|
# Stimulus controller file alongside the .rb/.erb pair.
|
|
36
|
-
# react_hooks : [ReactHookCall] —
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
36
|
+
# react_hooks : [ReactHookCall] — every recognized hook invocation
|
|
37
|
+
# in the component body, regardless of library.
|
|
38
|
+
# Includes React's built-in hooks (useState, useEffect,
|
|
39
|
+
# useRef, useContext, useMemo, useCallback, useReducer,
|
|
40
|
+
# useImperativeHandle, useLayoutEffect), Apollo hooks
|
|
41
|
+
# (useQuery, useMutation, useLazyQuery, useSubscription,
|
|
42
|
+
# useApolloClient), and Next.js navigation hooks
|
|
43
|
+
# (useRouter, usePathname, useSearchParams, useParams,
|
|
44
|
+
# useSelectedLayoutSegment(s)). Each call carries a
|
|
45
|
+
# `library` tag so backends can group them and emit a
|
|
46
|
+
# library-specific TODO pointing at the right Rails
|
|
47
|
+
# analog (Stimulus/server-render for React; controller
|
|
48
|
+
# fetch for Apollo; request.path/params for Next.js).
|
|
42
49
|
# module_bindings : [LocalBinding] — top-level `const`/`let` declarations
|
|
43
50
|
# outside the component function that aren't themselves
|
|
44
51
|
# components. Captured so backends can either translate
|
|
@@ -47,10 +54,24 @@ module JsxRosetta
|
|
|
47
54
|
# Without this capture, references to module-level
|
|
48
55
|
# constants from inside the JSX silently drop and
|
|
49
56
|
# produce unbacked snake_case references at render time.
|
|
57
|
+
# render_methods : [RenderMethod] — local arrow bindings that return JSX
|
|
58
|
+
# and are invoked from the JSX body (`const renderHeader
|
|
59
|
+
# = () => <div/>; ... {renderHeader()}`). Backends emit
|
|
60
|
+
# each as a private method on the generated class and
|
|
61
|
+
# reference it from a LocalRenderCall at the use site.
|
|
62
|
+
# mode : Symbol — `:view` for a normal Phlex/ViewComponent component
|
|
63
|
+
# whose body is rendered as JSX (the default); `:data_factory`
|
|
64
|
+
# for column-descriptor / option-list modules whose top-level
|
|
65
|
+
# export is a function returning an array of object literals.
|
|
66
|
+
# When `:data_factory`, the backend emits a snake_case method
|
|
67
|
+
# that returns the translated data, instead of `view_template`.
|
|
68
|
+
# JSX inside object properties still extracts to private
|
|
69
|
+
# methods on the class via the IR::Lambda path.
|
|
50
70
|
Component = Data.define(:name, :props, :body, :rest_prop_name,
|
|
51
71
|
:local_bindings, :local_binding_names,
|
|
52
72
|
:module_bindings,
|
|
53
|
-
:stimulus_methods, :react_hooks
|
|
73
|
+
:stimulus_methods, :react_hooks,
|
|
74
|
+
:render_methods, :mode) do
|
|
54
75
|
include Node
|
|
55
76
|
end
|
|
56
77
|
|
|
@@ -64,22 +85,37 @@ module JsxRosetta
|
|
|
64
85
|
include Node
|
|
65
86
|
end
|
|
66
87
|
|
|
67
|
-
# A
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
88
|
+
# A hook invocation detected in the component body. Covers React's
|
|
89
|
+
# built-in hooks plus framework hooks we recognize (Apollo's `useQuery`/
|
|
90
|
+
# `useMutation`/etc., Next.js's `useRouter`/`usePathname`/etc.).
|
|
91
|
+
# Surfaced separately from local_bindings so backends can emit a more
|
|
92
|
+
# specific TODO pointing at the Rails equivalent for each library,
|
|
93
|
+
# instead of a generic "translate this JS".
|
|
71
94
|
#
|
|
72
|
-
# hook
|
|
73
|
-
# source
|
|
74
|
-
|
|
95
|
+
# hook : String — hook function name (`"useState"`, `"useQuery"`, …)
|
|
96
|
+
# source : String — verbatim JS of the entire statement.
|
|
97
|
+
# library : Symbol — `:react`, `:apollo`, or `:next_js`. Backends
|
|
98
|
+
# group hooks by library and emit one TODO block per group,
|
|
99
|
+
# since each library maps to a different Rails analog.
|
|
100
|
+
# operation : String | nil — for Apollo hooks called with a bare-Identifier
|
|
101
|
+
# first argument (`useQuery(GET_USERS_QUERY, …)`), the
|
|
102
|
+
# captured operation name. nil when the first argument is
|
|
103
|
+
# not a simple Identifier, or when the hook isn't Apollo.
|
|
104
|
+
# Backends echo it in the TODO so the reviewer can match
|
|
105
|
+
# the operation back to its GraphQL document and to the
|
|
106
|
+
# Rails controller / model fetch it should become.
|
|
107
|
+
ReactHookCall = Data.define(:hook, :source, :library, :operation) do
|
|
75
108
|
include Node
|
|
76
109
|
end
|
|
77
110
|
|
|
78
111
|
# A component prop, possibly with a default value.
|
|
79
112
|
#
|
|
80
|
-
# name
|
|
81
|
-
# default
|
|
82
|
-
|
|
113
|
+
# name : String — the prop name on the parent (e.g. "data-testid").
|
|
114
|
+
# default : Interpolation | nil
|
|
115
|
+
# alias_name : String | nil — the local binding name inside the body when
|
|
116
|
+
# the destructure renames it (`"data-testid": dataTestId`).
|
|
117
|
+
# Use sites of the alias resolve to the prop's ivar.
|
|
118
|
+
Prop = Data.define(:name, :default, :alias_name) do
|
|
83
119
|
include Node
|
|
84
120
|
end
|
|
85
121
|
|
|
@@ -343,5 +379,30 @@ module JsxRosetta
|
|
|
343
379
|
RenderProp = Data.define(:params, :body) do
|
|
344
380
|
include Node
|
|
345
381
|
end
|
|
382
|
+
|
|
383
|
+
# A locally-declared JSX-returning arrow that's invoked inside the
|
|
384
|
+
# render body: `const renderHeader = (count) => <h1>{count}</h1>;
|
|
385
|
+
# ... {renderHeader(headerCount)}`. Backends emit one private method
|
|
386
|
+
# per RenderMethod on the generated class and reference it via a
|
|
387
|
+
# LocalRenderCall at each use site.
|
|
388
|
+
#
|
|
389
|
+
# name : String — snake_case method name on the class.
|
|
390
|
+
# params : [String] — arrow param names (camelCase preserved; backends
|
|
391
|
+
# snake_case to form Ruby parameter names).
|
|
392
|
+
# body : Node — the lowered IR node produced by the arrow's body.
|
|
393
|
+
RenderMethod = Data.define(:name, :params, :body) do
|
|
394
|
+
include Node
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# A call to a locally-declared JSX-returning arrow at its use site.
|
|
398
|
+
# Pairs with a sibling RenderMethod on Component#render_methods.
|
|
399
|
+
#
|
|
400
|
+
# method_name : String — snake_case method name (matches RenderMethod#name).
|
|
401
|
+
# args : [Interpolation] — argument expressions captured verbatim
|
|
402
|
+
# (each Interpolation's expression is translated by the
|
|
403
|
+
# backend's ExpressionTranslator at emission time).
|
|
404
|
+
LocalRenderCall = Data.define(:method_name, :args) do
|
|
405
|
+
include Node
|
|
406
|
+
end
|
|
346
407
|
end
|
|
347
408
|
end
|
data/lib/jsx_rosetta/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jsx_rosetta
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean McCleary
|
|
@@ -28,6 +28,7 @@ files:
|
|
|
28
28
|
- LICENSE.txt
|
|
29
29
|
- PLAN.md
|
|
30
30
|
- README.md
|
|
31
|
+
- ROADMAP.md
|
|
31
32
|
- Rakefile
|
|
32
33
|
- exe/jsx_rosetta
|
|
33
34
|
- lib/jsx_rosetta.rb
|