best-practices: organizing client code across files - #8
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds documentation guidance on splitting a plugin’s client-side screen.js into multiple files in a way that matches the Host’s actual loading/serving behavior (classic script execution, served paths, URL resolution, and re-hydration idempotency).
Changes:
- Introduces a new “Organizing client code across files” section with rules 27–29 (bundling preferred; runtime splitting via
assets/+ absolute URLs; load-once/idempotency). - Renumbers “Shipping & good citizenship” rules to 30–34 and adds a “Split client code” checklist block.
- Updates
CHANGELOG.mdto describe the new best-practices section.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| spec/best-practices.md | Adds new best-practices section + checklist for splitting client code; renumbers later rules. |
| CHANGELOG.md | Documents the added best-practices section in Unreleased notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - [ ] Split files share state via `window.<id>…` (classic script — no `import`/`export` in | ||
| `screen.js`). |
There was a problem hiding this comment.
Fixed: checklist now uses window["<id>"].
ed41f2c to
ee7d269
Compare
Add guidance for splitting a plugin's client JS instead of one monolithic screen.js, ground-truthed against how the Host loads and serves plugin JS. Rules (27-29): - Prefer bundling to one screen.js if you have build tooling — the Host loads one script, sidestepping the runtime gotchas. (No bundler ships in-tree; it's your own build step.) - screen.js runs as a CLASSIC script: no top-level import/export, no import.meta. Split files share state via window (namespaced by id), not ES exports. - Serve extra files from assets/ (the plugin root is NOT servable — a file there 404s; under assets/ the Host serves it traversal-guarded with a JS MIME), or a routes.py-served dir. Reference by ABSOLUTE /api/plugins/<id>/assets/... URLs — a relative import() from a classic script resolves against the document, not the script. Dynamic import() of an ES module from assets/ works; classic <script> injection works for window-attaching helpers. - Load each split file exactly once (idempotent) — the Host may re-run screen.js, so de-dupe with a loaded-set / window-symbol check or re-hydration double-loads. Renumber Shipping to 30-34 and add a checklist block. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: K. O. A. <topkoa@gmail.com>
… await, robust loader) - Rule 27 + checklist: key the window namespace with bracket notation (window['my-plugin']), since ids may contain '-' (window.my-plugin is a syntax error). - Rule 28: the example no longer uses top-level await (a syntax error in a classic script) — use import(...).then / an async IIFE. - Rule 29: rewrite loadScriptOnce to keep its cache on window (survives a screen.js re-run), cache the in-flight promise, and drop the entry on failure so a transient error can be retried. Signed-off-by: K. O. A. <topkoa@gmail.com>
25ce206 to
2c0f693
Compare
Summary
Answers "how do I split
screen.jsso my plugin isn't one giant file?" — ground-truthed against how the Host actually loads and serves plugin JS, so the advice matches what really works (not what you'd assume).The constraints that shape the advice
screen.jsis a classic script, not an ES module — the Host injects it with notype="module". So top-levelimport/exportandimport.metadon't work in it; split files share state throughwindow(namespaced byid).screen.js,screen.html,settings.html,tour.json, and everything underassets/are served./api/plugins/<id>/lib/util.js→ 404;/api/plugins/<id>/assets/lib/util.js→ served (traversal-guarded, correct JS MIME).import('./part.js')from a classic script resolves against the document base, not the script — so extra files must be referenced by absolute/api/plugins/<id>/assets/…URLs.utils//visualization//workers/via its ownroutes.py; highway_3d lazy-loads vendor code fromassets/). Bundling is still the cleanest option, just your own build step.Rules (27–29)
screen.js; split at runtime only when you must. Plus the classic-script constraint andwindow-sharing.assets/, reference by absolute URL.assets/is the built-in served dir; aroutes.pyis the escape hatch for other layouts. Dynamicimport()of an ES module fromassets/works; classic<script>injection works forwindow-attaching helpers. Includes anASSET_BASEexample.screen.js(rule 12), so de-dupe runtime loads with aSet/symbol check. Includes aloadScriptOncehelper.Added a "Split client code" checklist block; Shipping renumbered to 30–34 (contiguous 1–34).
Scope & stacking
Docs only (
spec/best-practices.md+CHANGELOG.md). No version bump. API facts framed as the current Host contract. Stacked on #7. Full stack: #2 → #4 → #3 → #5 → #6 → #7 → #8.mkdocs build --strictandcheck_versions.pypass.🤖 Generated with Claude Code