feat(vue): add @modular-vue/runtime registry (PR-21) - #63
Conversation
Port registry.ts from @react-router-modules/runtime as the first half of the vue-router runtime: createRegistry with register/registerLazy/use plugin machinery and an idempotent resolveManifest that assembles navigation, slots, module descriptors, and plugin extensions. Validation and the deps snapshot reuse the neutral @modular-frontend/core helpers directly. The router-owning resolve() entry, the Providers context component, and the router.addRoute() route-builder are deferred to PR-22, matching the boundary the @modular-vue/vue context.ts note already anticipates. The real Vue journeys plugin lands in PR-30/PR-32, so plugin machinery is covered here with a synthetic journeys-shaped plugin (registry-plugins.test.ts). React source: packages/react-router-runtime/src/registry.ts 30 tests (registry.test.ts, registry-plugins.test.ts, registry.test-d.ts); workspace typecheck (120 tasks) and vite build (JS + dts) pass. Updates the Vue support tracker status board and PR-21 writeup.
📝 WalkthroughWalkthroughRenames the vue-router package family from ChangesPackage Rename
New
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- use() collision guard: check Object.hasOwn(registry, key) instead of `key in registry`, so a plugin contributing a method named after an Object.prototype key (toString, hasOwnProperty, valueOf, ...) is no longer falsely rejected. Adds a covering test. - registerLazy: warn (dev only) when lazy modules are registered, since route grafting is deferred to PR-22 and they otherwise contribute nothing to the resolved manifest with no diagnostic. Adds a covering test asserting the modules are absent but the warning fires. - CommonAssembly doc comment now lists every field carried inert for PR-22 (stores/services/reactiveServices as well as slotsSignal/ dynamicSlotFactories/slotFilter), so they no longer read as accidental dead code.
Rename @vue-router-modules/{core,runtime} to @modular-vue/{core,runtime}.
Vue has a single router (vue-router), so a router-named scope carries no
disambiguating information the way @react-router-modules vs
@tanstack-react-modules does on the React side. Folding into @modular-vue
drops a namespace and sits these packages beside @modular-vue/vue and
@modular-vue/testing.
Names stay unprefixed (core/runtime, not router-core/router-runtime): the
router-neutral core already lives at @modular-frontend/core, so there is no
@modular-vue/core framework-alias to collide with.
Updates package names and deps, source imports and error prefixes, both
package READMEs, packages/vue/README.md, the tracker (D1 decision + package
map) and port-analysis, and regenerates the lockfile. Both packages are
unpublished, so there are no consumers to migrate.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/vue-port-analysis.md`:
- Line 41: The package name row in the Vue port analysis uses invented
router-prefixed names, which conflicts with the standardized naming used
elsewhere in the PR. Update the text in this section to use the actual package
names referenced by the rest of the document—@modular-vue/core,
`@modular-vue/runtime`, `@modular-vue/testing`, and `@modular-vue/cli`—so the naming
stays consistent and searchable.
In `@docs/vue-support-tracker.md`:
- Around line 28-36: The support-tracker table currently mixes released and
deferred Vue APIs, so update the map in the docs to separate what has already
shipped from what is still planned. Adjust the entries for `@modular-vue/testing`
and `@modular-vue/runtime` so deferred items like renderJourney, route-builder,
app/providers plugin, and zones are either moved into a later-phase section or
clearly marked as end-state only. Keep the same package names and mirrors, but
make the phase boundaries explicit so the tracker reflects actual delivery
status.
In `@packages/vue-router-runtime/README.md`:
- Around line 22-31: Update the Registry documentation in README to explicitly
state the current limitation of registry.registerLazy(descriptor) and
Registry.resolveManifest(options?): lazy registrations are validated but do not
yet contribute to the resolved manifest (navigation, moduleDescriptors, or other
outputs) until the router-building work lands. Keep the existing references to
registry.register, registry.registerLazy, registry.use, and resolveManifest, but
add a clear caveat that lazy entries currently contribute nothing in
packages/vue-router-runtime/src/registry.ts so readers do not assume they are
included today.
- Around line 12-16: The installation section in README should make it clear
that installing `@modular-vue/runtime` alone is not sufficient because vue,
vue-router, `@modular-frontend/core`, `@modular-vue/core`, and `@modular-vue/vue` are
peer dependencies. Update the install guidance in the README so it either shows
the full installation command including those peer packages or explicitly states
that they must already be installed before using the runtime package. Use the
package.json peerDependencies list and the Installation section as the locations
to update.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3b95a3e5-180a-4554-8db0-70dd4771314d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
docs/vue-port-analysis.mddocs/vue-support-tracker.mdpackages/vue-router-core/README.mdpackages/vue-router-core/package.jsonpackages/vue-router-core/src/index.test.tspackages/vue-router-core/src/route-meta.tspackages/vue-router-runtime/README.mdpackages/vue-router-runtime/package.jsonpackages/vue-router-runtime/src/index.tspackages/vue-router-runtime/src/registry-plugins.test.tspackages/vue-router-runtime/src/registry.test-d.tspackages/vue-router-runtime/src/registry.test.tspackages/vue-router-runtime/src/registry.tspackages/vue-router-runtime/src/types.tspackages/vue-router-runtime/tsconfig.jsonpackages/vue-router-runtime/vite.config.tspackages/vue-router-runtime/vitest.config.tspackages/vue/README.md
| - The class-based error boundary → a small wrapper component using `onErrorCaptured`. | ||
| - Zone/slot rendering → `<component :is>` over the contribution lists. | ||
| 3. `@vue-router-modules/{core,runtime,testing,cli}` (~2.5-3k LOC): the router bridge. This is where Vue is actively easier than either existing family: | ||
| 3. `@modular-vue/{router-core,router-runtime,testing,router-cli}` (~2.5-3k LOC): the router bridge. This is where Vue is actively easier than either existing family: |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the actual package names here.
This row still invents router-* package names, but the rest of the PR standardizes on @modular-vue/core / runtime / testing / cli. Keeping one naming scheme avoids search and migration confusion.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/vue-port-analysis.md` at line 41, The package name row in the Vue port
analysis uses invented router-prefixed names, which conflicts with the
standardized naming used elsewhere in the PR. Update the text in this section to
use the actual package names referenced by the rest of the
document—@modular-vue/core, `@modular-vue/runtime`, `@modular-vue/testing`, and
`@modular-vue/cli`—so the naming stays consistent and searchable.
| | New package | Mirrors | Contents | | ||
| | --------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `@modular-vue/vue` | `@modular-react/react` | Injection keys and providers (modules, navigation, slots), store composables, scoped-store composable, error-capture wrapper, entry resolution via `defineAsyncComponent`, module-exit | | ||
| | `@modular-vue/journeys` | React parts of `@modular-react/journeys` | Journey provider, instance composables, outlet, module-tab, wait-for-exit | | ||
| | `@modular-vue/compositions` | React parts of `@modular-react/compositions` | Composition provider, composables, outlet | | ||
| | `@modular-vue/testing` | `@modular-react/testing` + `@react-router-modules/testing` | `resolveModule`, `createMockStore`, `preloadEntries`, `renderModule`, `renderJourney`, mock store (Vue folds both React testing packages into one) | | ||
| | `@modular-vue/core` | `@react-router-modules/core` | `defineModule` (with `createRoutes(): RouteRecordRaw[]`), `defineSlots`, shared composable context, scoped store, types | | ||
| | `@modular-vue/runtime` | `@react-router-modules/runtime` | Registry, route-builder, app/providers as a Vue plugin, zones, active-zones, route-data | | ||
| | `@modular-vue/cli` | `@react-router-modules/cli` | `cli-core` preset + SFC templates | |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Split the target map by phase.
This table now mixes shipped and deferred APIs: @modular-vue/testing lists renderJourney, but PR-24 below says it lands with PR-32, and the runtime row includes route-builder / app-shell / zones that PR-22/23 still defer. Split the row or rename this as an eventual end-state map so the tracker doesn't read like those APIs already shipped.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/vue-support-tracker.md` around lines 28 - 36, The support-tracker table
currently mixes released and deferred Vue APIs, so update the map in the docs to
separate what has already shipped from what is still planned. Adjust the entries
for `@modular-vue/testing` and `@modular-vue/runtime` so deferred items like
renderJourney, route-builder, app/providers plugin, and zones are either moved
into a later-phase section or clearly marked as end-state only. Keep the same
package names and mirrors, but make the phase boundaries explicit so the tracker
reflects actual delivery status.
| ## Installation | ||
|
|
||
| ```bash | ||
| npm install @modular-vue/runtime | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
List the peer deps in the install step.
package.json declares vue, vue-router, @modular-frontend/core, @modular-vue/core, and @modular-vue/vue as peers, so npm install @modular-vue/runtime`` by itself is not a complete setup for a fresh app. Please either show the full install command or call out that those packages must already be present. Based on packages/vue-router-runtime/package.json, these packages are peers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/vue-router-runtime/README.md` around lines 12 - 16, The installation
section in README should make it clear that installing `@modular-vue/runtime`
alone is not sufficient because vue, vue-router, `@modular-frontend/core`,
`@modular-vue/core`, and `@modular-vue/vue` are peer dependencies. Update the
install guidance in the README so it either shows the full installation command
including those peer packages or explicitly states that they must already be
installed before using the runtime package. Use the package.json
peerDependencies list and the Installation section as the locations to update.
| - **`registry.register(module)`** / **`registry.registerLazy(descriptor)`** — | ||
| register eager and lazily-loaded modules. | ||
| - **`registry.use(plugin)`** — attach a plugin; its `extend` surface is | ||
| intersected onto the returned registry reference, so contributed methods | ||
| (e.g. a future `registerJourney`) are typed on the same object. | ||
| - **`registry.resolveManifest(options?)`** — resolve all modules into a | ||
| `ResolvedManifest`: `navigation`, `slots`, `modules`, `moduleDescriptors`, | ||
| plugin `extensions` (+ the `journeys` alias), `onModuleExit`, and | ||
| `recalculateSlots`. Idempotent — the first call does the work and caches the | ||
| result; later calls return it and must pass no options. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Clarify the lazy-module limitation.
registerLazy() is currently validated but intentionally omitted from resolveManifest() until the router-building PR lands, so this wording reads as if lazy registrations already contribute to navigation / moduleDescriptors. Please note the current caveat explicitly. Based on packages/vue-router-runtime/src/registry.ts, lazy registrations currently contribute nothing to the resolved manifest until PR-22.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/vue-router-runtime/README.md` around lines 22 - 31, Update the
Registry documentation in README to explicitly state the current limitation of
registry.registerLazy(descriptor) and Registry.resolveManifest(options?): lazy
registrations are validated but do not yet contribute to the resolved manifest
(navigation, moduleDescriptors, or other outputs) until the router-building work
lands. Keep the existing references to registry.register, registry.registerLazy,
registry.use, and resolveManifest, but add a clear caveat that lazy entries
currently contribute nothing in packages/vue-router-runtime/src/registry.ts so
readers do not assume they are included today.
What
First half of the vue-router runtime:
@modular-vue/runtime(0.1.0). Portsregistry.tsfrom@react-router-modules/runtime:createRegistrywithregister/registerLazy/useplugin machinery, and an idempotentresolveManifest()that assembles the navigation manifest, slots, module entries + descriptors, and plugin extensions.React source:
packages/react-router-runtime/src/registry.ts.Implements PR-21 of the Vue support initiative.
Naming
This branch also consolidates the Vue router packages under the existing
@modular-vuescope instead of a separate@vue-router-modulesscope:@vue-router-modules/core->@modular-vue/core@vue-router-modules/runtime->@modular-vue/runtimeVue has a single router (
vue-router), so a router-named scope carries no disambiguating information the way it does on the React side, where@react-router-modulesand@tanstack-react-modulesdistinguish two real router integrations. Folding into@modular-vuedrops a namespace and sits these packages beside the existing@modular-vue/vueand@modular-vue/testing. Names are unprefixed (core/runtime, notrouter-core/router-runtime): the router-neutral core already lives at@modular-frontend/core, so there is no@modular-vue/coreframework-alias to collide with. Decision D1 in the tracker records this. The rename spans the already-merged core package (PR-20) as well as this runtime package; both are unpublished, so there are no consumers to migrate.Scope / boundary
Validation and the deps snapshot reuse the neutral
@modular-frontend/corehelpers directly (validateNoDuplicateIds,validateDependencies,validateEntryExitShape,buildDepsSnapshot). The idempotency contract (first call captures options + caches; later calls return the cache and reject options) and theonRegister-once / flip-before-throw guard match the React source.Deferred to later PRs, by design:
resolve()entry, theProviderscontext component, therouter.addRoute()route-builder, and the auth guard. The@modular-vue/vuecontext.tsnote already anticipates this ("the runtime plugin (PR-22) provides it at the app root"). SoresolveManifest()returns the resolved data surface (navigation,slots,modules,moduleDescriptors,extensions+ thejourneysalias,onModuleExit,recalculateSlots) but notProviders/routes.plugin.providers()(provider-stack contribution) is deferred with the stack.registry-plugins.test.ts) covering extend / validate / contributeNavigation / onResolve, the.extensionsbag, the.journeysalias, duplicate-name and method-collision guards, and launcher nav contribution.RegistryConfigis re-exported from@modular-frontend/core(itsStore<T>bucket already covers zustand and the core store), so the runtime doesn't redeclare a zustand-typed copy the way the React runtime does.Tests
30 tests:
registry.test.ts(17): assembly, validation,onRegister-once, idempotency,recalculateSlotsno-op vs live,onModuleExitforwarding,moduleDescriptors.registry-plugins.test.ts(9): plugin machinery via the synthetic plugin.registry.test-d.ts(4): plugin-extend intersection, base-surface exclusion,extensionstyping,journeys-alias-never.Full workspace typecheck and
vite build(JS + dts) pass; externals (vue,vue-router,@modular-frontend/core,@modular-vue/vue,@modular-vue/core) stay unbundled. Tracker status board and PR-21 writeup updated in the same commit.Summary by CodeRabbit
New Features
Documentation
@modular-vue/*package naming.Chores
@modular-vue/*.