refactor(core): extract framework-neutral @modular-frontend/core - #54
Conversation
First step of the Vue-support initiative: split the framework-neutral guts of @modular-react/core into a new @modular-frontend/core package so a future Vue binding can reuse them. - New packages/frontend-core (@modular-frontend/core): all the current core logic and types, with no UI-framework dependency. React's ComponentType / ReactNode references become a two-line neutral seam (UiComponent / UiNode) in ui-types.ts. @types/react is dropped from the package. - @modular-react/core becomes a thin facade that re-exports the neutral surface (export * from "@modular-frontend/core"), so all existing consumers keep importing from @modular-react/core unchanged. Name and version are unchanged. - UiComponent is "callable or constructable with props P", which admits React's full ComponentType (function and class) and stays usable as a JSX element type; a Vue binding can narrow it. UiNode is loose (any) so bindings can pass nodes straight into their framework's render slots. - Runtime error prefixes and package-name doc comments in the moved files updated to @modular-frontend/core. - Add project CLAUDE.md documenting the repo-wide oxfmt workflow. Full workspace typecheck (108 tasks) and build (31 tasks) pass; the neutral package's 270 tests pass. The only failing test is the pre-existing Windows EPERM flake in @tanstack-react-modules/cli, unrelated to this change.
|
Warning Review limit reached
Next review available in: 4 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR extracts a new framework-neutral package, ChangesCore Extraction and Facade Refactor
Estimated code review effort: 3 (Moderate) | ~30 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/frontend-core/src/plugin.ts (1)
58-61: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winStale "React" reference in a now framework-neutral doc comment.
The
providersfield's docstring still says "Contribute React providers to the provider stack. Applied after user providers; first element is outermost." despite the type itself being migrated toUiComponent<{ children: UiNode }>. This is inconsistent with the PR's goal of removing framework-specific language from this package.✏️ Suggested doc fix
/** - * Contribute React providers to the provider stack. Applied after user + * Contribute providers to the provider stack. Applied after user * providers; first element is outermost. */🤖 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/frontend-core/src/plugin.ts` around lines 58 - 61, The doc comment on the providers field still uses framework-specific “React providers” language, which is now inconsistent with the UiComponent/UiNode API in plugin.ts. Update the providers documentation to describe framework-neutral UI providers/components instead, keeping the existing ordering note about user providers and outermost placement while removing the React reference.packages/frontend-core/src/runtime-mount.ts (1)
37-40: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winStale "React" reference in a now framework-neutral doc comment.
The
Outletfield's docstring still says "React component that mounts a running instance by id." even thoughOutletis now typed withUiComponent. Same inconsistency as inplugin.ts'sprovidersdoc.✏️ Suggested doc fix
/** - * React component that mounts a running instance by id. The optional + * Component that mounts a running instance by id. The optional * `loadingFallback` propagates through `Suspense` boundaries inside * the embedded runtime (e.g. while a lazy step chunk loads). */🤖 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/frontend-core/src/runtime-mount.ts` around lines 37 - 40, The doc comment for the Outlet field is stale and still refers to a “React component” even though it is now framework-neutral and typed as UiComponent. Update the comment in runtime-mount.ts to describe Outlet in generic terms, and keep the loadingFallback/Suspense wording aligned with the existing behavior; use Outlet and UiComponent as the locating symbols.
🤖 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 `@CLAUDE.md`:
- Around line 15-17: The fenced example in CLAUDE.md is missing a language tag,
which can trip markdown linting. Update the markdown fence around the oxfmt/pnpm
lint:fix example to use the appropriate bash language identifier so the
documented command block is recognized correctly.
In `@packages/frontend-core/README.md`:
- Around line 29-38: The README’s “UiComponent seam” example does not match the
package’s exported UiComponent/UiNode contract, which can mislead consumers
copying the snippet. Update the documentation in the UiComponent/UiNode section
so the example reflects the actual aliases exported by frontend-core, keeping
the wording aligned with the real component and node types used by descriptors
like component, zones, entry-point component, and NavigationItem.icon.
---
Outside diff comments:
In `@packages/frontend-core/src/plugin.ts`:
- Around line 58-61: The doc comment on the providers field still uses
framework-specific “React providers” language, which is now inconsistent with
the UiComponent/UiNode API in plugin.ts. Update the providers documentation to
describe framework-neutral UI providers/components instead, keeping the existing
ordering note about user providers and outermost placement while removing the
React reference.
In `@packages/frontend-core/src/runtime-mount.ts`:
- Around line 37-40: The doc comment for the Outlet field is stale and still
refers to a “React component” even though it is now framework-neutral and typed
as UiComponent. Update the comment in runtime-mount.ts to describe Outlet in
generic terms, and keep the loadingFallback/Suspense wording aligned with the
existing behavior; use Outlet and UiComponent as the locating symbols.
🪄 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: 9d287142-087e-46dd-8ff5-ea820b0764a1
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (53)
CLAUDE.mddocs/vue-port-analysis.mddocs/vue-support-tracker.mdpackages/core/README.mdpackages/core/package.jsonpackages/core/src/index.tspackages/core/vite.config.tspackages/frontend-core/README.mdpackages/frontend-core/package.jsonpackages/frontend-core/src/any-module-descriptor.test.tspackages/frontend-core/src/catalog-meta.tspackages/frontend-core/src/define-module.tspackages/frontend-core/src/define-slots.tspackages/frontend-core/src/define.test.tspackages/frontend-core/src/detection.test.tspackages/frontend-core/src/detection.tspackages/frontend-core/src/dev-env.test.tspackages/frontend-core/src/dev-env.tspackages/frontend-core/src/entry-exit.test-d.tspackages/frontend-core/src/entry-exit.test.tspackages/frontend-core/src/entry-exit.tspackages/frontend-core/src/index.tspackages/frontend-core/src/journey-contracts.tspackages/frontend-core/src/lazy-module.tspackages/frontend-core/src/navigation-item-base.test.tspackages/frontend-core/src/navigation.test.tspackages/frontend-core/src/navigation.tspackages/frontend-core/src/plugin.tspackages/frontend-core/src/remote-manifest.test.tspackages/frontend-core/src/remote-manifest.tspackages/frontend-core/src/route-data-warn.test.tspackages/frontend-core/src/route-data-warn.tspackages/frontend-core/src/route-data.test.tspackages/frontend-core/src/route-data.tspackages/frontend-core/src/runtime-mount.tspackages/frontend-core/src/runtime-types.test.tspackages/frontend-core/src/runtime-types.tspackages/frontend-core/src/semver.test.tspackages/frontend-core/src/semver.tspackages/frontend-core/src/slots.test.tspackages/frontend-core/src/slots.tspackages/frontend-core/src/step-spec.test-d.tspackages/frontend-core/src/store-compat.test.tspackages/frontend-core/src/store-primitives.test.tspackages/frontend-core/src/store.test.tspackages/frontend-core/src/store.tspackages/frontend-core/src/types.tspackages/frontend-core/src/ui-types.tspackages/frontend-core/src/validation.test.tspackages/frontend-core/src/validation.tspackages/frontend-core/tsconfig.jsonpackages/frontend-core/vite.config.tspackages/frontend-core/vitest.config.ts
…ypes/react The frontend-core README documented an earlier single-arm draft of the seam: `UiComponent<P> = (props: P) => any` and `UiNode = unknown`. The actual ui-types.ts is `((props: P) => any) | (new (props: P) => any)` and `UiNode = any`. Update the snippet and prose to match, including the construct arm (class components) and the rationale for `any` over `unknown`. The @modular-react/core facade is now only `export * from "@modular-frontend/core"` with no React type usage, so its @types/react devDependency and optional peer were dead weight. Remove both; they can layer back when a real React refinement of the UiComponent seam lands here.
markdownlint MD040 flags fenced blocks without a language. The oxfmt example block is a shell command, so tag it `bash`.
…eys-engine (#55) * refactor(journeys): extract framework-neutral @modular-frontend/journeys-engine PR-02 of the Vue-support initiative. Split the framework-neutral guts of @modular-react/journeys into a new @modular-frontend/journeys-engine package so a future Vue binding can reuse them, mirroring the @modular-frontend/core extraction from #54. - New packages/journeys-engine (@modular-frontend/journeys-engine): the runtime, validation, persistence, authoring helpers (defineJourney/defineTransition/ selectModule), handles, simulate-journey, the test harness, and the full type surface, plus their non-React tests. Depends only on @modular-frontend/core (happy-dom is a dev dep for the storage-backed persistence tests). Two entries: index + /testing. - @modular-react/journeys keeps its React files (outlet, module-tab, provider, plugin, instance-hooks, use-journey-state, use-wait-for-exit, mount-adapter) and re-exports the engine, so its public export surface is unchanged. A thin testing.ts re-exports the engine's /testing entry, keeping the existing @modular-react/journeys/testing import path working. Two forced deviations from the tracker's PR-02 plan: - mount-adapter.ts stays in the binding, not the engine: createJourneyMountAdapter supplies Outlet: JourneyOutlet (a React component), so it is binding-specific glue over the neutral RuntimeMountAdapter seam rather than engine logic. - JourneyNavContribution.icon used the React.ComponentType namespace; it moves to the neutral UiComponent seam, matching how NavigationItem.icon was neutralized in #54. Source-compatible for authors (a React component still satisfies UiComponent). Error-message prefixes stay [@modular-react/journeys] / [@modular-react/journeys/ testing] on purpose: they name the package users import and point at real import paths, so the moved tests pass unmodified. Verification: engine 346 tests (26 files) + journeys 72 tests (8 files) = the pre-split total; @modular-react/compositions (the createJourneyMountAdapter consumer) 115 tests pass; full workspace typecheck (110 tasks) and pnpm lint clean. The only failing tests are the pre-existing Windows EPERM .test-output flake in the two router CLIs, unrelated to this change. * docs(journeys-engine): drop mount-adapter from the engine README createJourneyMountAdapter stays in the @modular-react/journeys binding (it supplies the React JourneyOutlet), so the engine does not ship it. Listing it under "What's included" advertised an export this package does not provide. * docs(journeys): address PR review comments on the engine extraction - vue-support-tracker: soften the shared-engines header so it no longer reads as if compositions-engine is already extracted, and add the #55 reference to the PR-02 status-board row. - journeys/index.ts: point the getInternals note at @modular-frontend/journeys-engine; runtime.ts no longer lives in this package. - journeys-engine/testing.ts: extract the repeated record + registration + loading guard shared by fireExit/goBack/goForward into one activeRecordAndReg helper.
…ompositions-engine (#56) PR-03 of the Vue-support initiative. Splits the framework-neutral guts of @modular-react/compositions into a new @modular-frontend/compositions-engine package so a future Vue binding can reuse them, mirroring the journeys-engine extraction (#55) and the frontend-core extraction (#54). What moves: the pure files (runtime.ts, stores.ts, validation.ts, define-composition.ts, types.ts) plus their non-.tsx tests. The engine depends only on @modular-frontend/core; happy-dom is a dev dep for the test environment. @modular-react/compositions keeps its React files (outlet.tsx, provider.tsx, plugin.tsx, hooks.ts) and all .tsx tests, and re-exports the engine so its public export surface is unchanged. One React reference had to be neutralized, matching #54/PR-02: CompositionZoneDescriptor.fallback used React.ComponentType; it moves to the neutral UiComponent seam from @modular-frontend/core. Source-compatible for authors (a React component still satisfies UiComponent). Error-message prefixes stay [@modular-react/compositions] on purpose — they name the package users import. Test counts preserved: 52 in the engine + 63 in the binding = the pre-split total of 115. Full workspace typecheck and lint clean. Claude-Session: https://claude.ai/code/session_01Sf7deW3iRQaaSjZuj3akTA Co-authored-by: Claude <noreply@anthropic.com>
First step of the Vue-support initiative: split the framework-neutral guts of
@modular-react/coreinto a new@modular-frontend/corepackage so a future Vue binding can reuse them.What changed
packages/frontend-core(@modular-frontend/core): all the current core logic and types, with no UI-framework dependency. The only React coupling was type-level (React.ComponentType/React.ReactNode); it becomes a two-line neutral seam (UiComponent/UiNode) inui-types.ts.@types/reactis dropped from the package.@modular-react/corebecomes a thin facade that re-exports the neutral surface (export * from "@modular-frontend/core"). All 11 existing consumers keep importing from@modular-react/coreunchanged; name and version are unchanged.@modular-frontend/core.CLAUDE.mddocumenting the repo-wide oxfmt workflow.The UiComponent / UiNode seam
The neutral core never renders, calls, or inspects a component (
defineModuleis identity; validation only checksid/requires/ entry-exit shape), so components are carried as opaque values. Instead of a threaded generic, two aliases capture the seam:UiComponentis "callable or constructable with propsP", which admits React's fullComponentType(function and class components) and stays usable as a JSX element type, while keeping function-component props checked againstModuleEntryProps.UiNodeis loose (any) so a binding can pass nodes straight into its framework's render slots (e.g. a React<Suspense fallback>). A future@modular-vue/corenarrows both aliases to Vue's types.Verification
@tanstack-react-modules/cli(.test-outputcleanup), unrelated to this change.Summary by CodeRabbit
New Features
Documentation
Bug Fixes