Module: Ruact::ServerFunctions::NameBridge
- Defined in:
- lib/ruact/server_functions/name_bridge.rb
Overview
Translates a Ruby symbol into the JS identifier exported from
app/javascript/.ruact/server-functions.ts. The bridge is Ruby-side only;
the Vite plugin reads the already-translated identifier from the JSON
snapshot and emits it verbatim (Story 8.0a design decision: one source of
truth for naming).
Rules (locked by Story 8.0 ADR + 2026-05-13 review-patch tightening):
- Symbol must match
/\A[a-z_][a-z0-9_]*\z/; otherwise raises ConfigurationError at controller-class load time. - A single leading underscore is preserved (e.g.
:_internal_dump→"_internalDump"). - Runs of underscores collapse and uppercase the following alphanumeric
(e.g.
:foo__bar→"fooBar"). - The source symbol cannot be made of underscores alone (
:_,:__, …) — would otherwise emit"_", which carries no semantic content and collides with the common "ignored value" lint convention. - The translated JS identifier cannot match a JavaScript reserved word
or future-reserved word (ES2020+ list plus the module-top-level
await/async).:class→"class"would compile under Babel loose-mode but break undertsc --noEmitand most ESLint configs.
Constant Summary collapse
- VALID_SYMBOL =
/\A[a-z_][a-z0-9_]*\z/- UNDERSCORE_ONLY =
/\A_+\z/- RESERVED_JS_IDENTIFIERS =
ES2020+ reserved + strict-mode reserved + contextually-reserved at module top level + strict-mode invalid binding names. The codegen emits in a module context (the generated
app/javascript/.ruact/server-functions.tsis"type": "module", so all code runs in strict mode), soawait,eval, andargumentsare all reserved as identifier names. Keep this list sorted; matches the MDN reference plus the contextual additions and the strict-modeeval/argumentsban from the 2026-05-13 Re-run review patch. %w[ arguments async await break case catch class const continue debugger default delete do else enum eval export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield ].to_set.freeze
- RESERVED_BY_RUACT =
Story 8.2 (2026-05-17 review patches R2 + R12) — names already bound at the top of
app/javascript/.ruact/server-functions.ts, either by a helper re-export (revalidate,useQuery) or a runtime import (_makeServerFunction,_makeQuery). A server function / query method namedrevalidate(oruse_query) would emit a clashingexport const/ duplicate export next to the existing binding and crash the generated module at load time. The rule fires at route-draw so the failure surfaces during boot, not at first request. Story 9.5 added_makeQuery(the v2 query import) anduseQuery(the query hook re-export) to this list. Story 9.9 removed_makeRef(the demolished v1 runtime export). %w[ _makeQuery _makeServerFunction revalidate useQuery ].to_set.freeze
Class Method Summary collapse
-
.to_js_identifier(symbol) ⇒ String
The corresponding JS identifier.
Class Method Details
.to_js_identifier(symbol) ⇒ String
Returns the corresponding JS identifier.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/ruact/server_functions/name_bridge.rb', line 79 def to_js_identifier(symbol) str = symbol.to_s unless str.match?(VALID_SYMBOL) raise Ruact::ConfigurationError, "ruact server-function name :#{symbol} must match /^[a-z_][a-z0-9_]*$/" end if str.match?(UNDERSCORE_ONLY) raise Ruact::ConfigurationError, "ruact server-function name :#{symbol} cannot be composed " \ "entirely of underscores (no semantic content)" end leading = str.start_with?("_") ? "_" : "" body = str.sub(/\A_+/, "") js_id = leading + body.gsub(/_+([a-z0-9])/) { Regexp.last_match(1).upcase } if RESERVED_JS_IDENTIFIERS.include?(js_id) raise Ruact::ConfigurationError, "ruact server-function name :#{symbol} maps to JS reserved " \ "word \"#{js_id}\" — pick a different name (e.g. :#{symbol}_fn)" end if RESERVED_BY_RUACT.include?(js_id) raise Ruact::ConfigurationError, "ruact server-function name :#{symbol} maps to \"#{js_id}\", " \ "which is already exported by the ruact runtime from " \ "`@/.ruact/server-functions` and would emit a duplicate export. " \ "Pick a different name (e.g. :#{symbol}_fn)." end js_id end |