[Improvement] Docs: symlink Stimulus controllers to gem source instead of copying - #493
Open
djalmaaraujo wants to merge 2 commits into
Open
[Improvement] Docs: symlink Stimulus controllers to gem source instead of copying#493djalmaaraujo wants to merge 2 commits into
djalmaaraujo wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 80 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Collaborator
|
@djalmaaraujo, please take a look on cubic issues! |
cirdes
approved these changes
Jul 28, 2026
…d of copying docs/app/javascript/controllers/ruby_ui/*_controller.js was a hand-maintained copy of gem/lib/ruby_ui/<component>/*_controller.js. The two silently drifted: the gem's accordion fix (0897b2a) never landed in the docs copy, breaking the live accordion preview. Replace all 37 copies with relative symlinks into the gem source, matching how docs already autoloads Phlex components straight from gem/ (config/initializers/ruby_ui.rb) with no copy step. - accordion_controller.js, avatar_controller.js, combobox_controller.js, and sheet_controller.js had genuinely stale docs content pre-migration (beyond just accordion) - the symlink now serves the gem's current, correct version. - esbuild's default symlink handling resolves module imports relative to the symlink's real (gem/) path, which breaks node_modules resolution since gem/ and docs/ are siblings with no shared ancestor node_modules. Build with --preserve-symlinks so imports resolve against the symlink location instead. - Add `rake ruby_ui:sync_controller_symlinks` (docs/lib/tasks/ruby_ui.rake): scans gem/lib/ruby_ui for *_controller.js and creates/repairs the matching symlink. Idempotent. Replaces the old "copy the file by hand" step for new components; stimulus:manifest:update is still required separately to register a brand-new controller in the manifest. - Update CLAUDE.md, docs/CLAUDE.md, gem/AGENTS.md, and the ruby-ui-stimulus skill to describe the symlink relationship and new workflow. Rebased onto main after PR #490 and PR #495 merged. Both edited a docs controller copy that this branch replaces with a symlink (accordion, popover), so both conflicted as modify/delete. Resolved in favour of the symlink: on main the docs copy and the gem source are byte-identical for each of the two, so pointing at the gem loses neither fix, and #490's docs-side change becomes moot now that the file is a symlink.
Address cubic review on #493: - `ruby_ui:sync_controller_symlinks` now detects two gem controllers sharing a basename before creating any link and aborts listing the offenders. The docs controllers directory is flat, so the second symlink would silently clobber the first and one component would ship the wrong JS. - Fix the missing `rake` prefix on `stimulus:manifest:update` in the ruby-ui-stimulus skill.
djalmaaraujo
force-pushed
the
refactor/docs-stimulus-symlinks
branch
from
July 31, 2026 18:26
4ca0534 to
64d756a
Compare
Contributor
Author
|
@cirdes done — both cubic findings addressed in 64d756a, and the branch is rebased onto current
Re-verified on a clean checkout (fresh
Same caveat as before: still no interactive browser click-through, confidence rests on the bundle-content checks above. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
`docs/app/javascript/controllers/ruby_ui/_controller.js` was a hand-maintained copy of `gem/lib/ruby_ui//_controller.js`, kept in sync by manually copying files. This drifted silently: the accordion Stimulus fix landed in the gem (commit 0897b2a) but the docs copy was never updated, so the live accordion preview at rubyui.com/docs/accordion broke (content never revealed on click). That fix is currently sitting in still-open PR #490.
The Ruby side of this exact problem is already solved — `docs/config/initializers/ruby_ui.rb` autoloads Phlex components straight from `gem/lib/ruby_ui` via Zeitwerk, no copy at all. Only the JS side still duplicated.
Fix
Replace all 37 docs controller files with relative symlinks into the corresponding gem source file, e.g.:
```
docs/app/javascript/controllers/ruby_ui/accordion_controller.js
-> ../../../../../gem/lib/ruby_ui/accordion/accordion_controller.js
```
The gem subdirectory doesn't always match the controller basename (e.g. `checkbox_group_controller.js` lives under `gem/lib/ruby_ui/checkbox/`, `select_item_controller.js` under `gem/lib/ruby_ui/select/`) — the mapping was built from a real `find` over both trees, not guessed.
Files with genuinely stale content pre-migration (diffed docs copy vs. gem source before symlinking):
hidden-attribute handling (the bug fixed in PR [Bug Fix] Accordion: sync docs Stimulus controller with gem #490)minPopoverWidth/placementStimulus values, hardcoded'bottom-start'placementopenStimulus value +connect()auto-open behaviorThe other 33 were already byte-identical.
This PR supersedes #490 for `accordion_controller.js` specifically — the symlink now points at the gem's current (correct) source regardless of whether #490 merges. Not touching or closing #490; leaving its fate to a human.
New dev workflow for adding a component
`rake ruby_ui:sync_controller_symlinks` (new task, `docs/lib/tasks/ruby_ui.rake`) scans `gem/lib/ruby_ui/**/*_controller.js` and creates/repairs any missing symlink. Idempotent — safe to re-run. It replaces the old "copy the file by hand" step. It intentionally does not touch the Stimulus manifest — `bin/rails stimulus:manifest:update` is still a required separate step to register a brand-new controller in `controllers/index.js`.
esbuild wrinkle found along the way
esbuild's default symlink handling resolves a symlinked module's imports relative to its real path. Since `gem/` and `docs/` are sibling directories with no shared ancestor `node_modules`, this broke resolution of `@hotwired/stimulus`, `@floating-ui/dom`, and `motion` for every symlinked controller with an external import. Fixed by adding `--preserve-symlinks` to the `build` script in `docs/package.json`, which makes esbuild resolve relative to the symlink's own location instead.
Docs updated in this PR
Test plan
Verified in the docs devcontainer (a fresh instance mounted against this branch, ports 3002/host to avoid clashing with any other running instance):
readlink -fresolving to the correct existing gem file (not just trusting the naming pattern).bin/rails stimulus:manifest:updateregeneratesapp/javascript/controllers/index.jsbyte-identical to before the migration (import paths are relative toapp/javascript/controllers, unaffected by symlinks).rake ruby_ui:sync_controller_symlinksis idempotent on a fully-synced tree, and correctly recreates a symlink I deleted as a test (verified the recreated link's target).pnpm build— failed initially on unresolved@hotwired/stimulus/@floating-ui/dom/motionimports (see esbuild wrinkle above); passes after adding--preserve-symlinks.pnpm build:css— passes.app/assets/builds/application.jsfor controller-specific code (not just import statements): foundcontent.removeAttribute("hidden")/content.setAttribute("hidden", "")from the gem's fixed accordion controller, andcomputePositionfrom the real@floating-ui/dompackage — confirms the bundle contains real gem-sourced content, not broken/empty imports.bin/rails serverin the container andcurl'd it directly:/docs/accordion,/docs/tooltip,/docs/popover,/docs/checkbox,/docs/selectall return 200; fetched the served, fingerprintedapplication-*.jsasset over HTTP and re-confirmed the same accordion/floating-ui strings are present in what the server actually serves.bin/rails test(docs) — 75 runs, 107 assertions, 0 failures, 0 errors.bundle exec standardrb(docs) — clean, exit 0.Not verified: no real browser was available in this environment, so I could not click through the accordion/combobox/tooltip UI interactively. Confidence here rests on the four checks above (manifest diff, bundle content grep, HTTP 200 + asset content over curl, and the existing Rails test suite) rather than a driven browser session — flagging this explicitly rather than overclaiming.
Summary by cubic
Symlinked all docs’ Stimulus controllers to the gem source to eliminate drift and ensure previews always use the latest code. Fixes the broken accordion demo and updates the build to support symlinks.
Refactors
docs/app/javascript/controllers/ruby_ui/with symlinks togem/lib/ruby_ui/<component>/….esbuildbuildscript with--preserve-symlinksso imports from@hotwired/stimulus,@floating-ui/dom, andmotionresolve correctly.ruby_ui:sync_controller_symlinkstask; it creates/repairs symlinks and now aborts on duplicate controller basenames to prevent clobbering.Migration
bin/rails ruby_ui:sync_controller_symlinks, thenbin/rails stimulus:manifest:update.Written for commit 64d756a. Summary will update on new commits.