Ship the frontend runtime JavaScript as an installable npm package

Offen
#7,609 1 Kommentar 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Anfängerfreundlichkeit
35/100
Issue-Typ
Feature
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
bun, javascript, python, react, vite

Rechercherichtung

Start by reading the pending-work checklist and the implementation branch claude/reflex-frontend-js-package-ockox9, then compare its install-flow changes with #7235 and #7451. Rebase the branch onto current main, open a PR, and review the breaking-change fragment and local-packages documentation. Done means the PR is ready and the release pipeline confirms the tarball is bundled in a pre-release wheel.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

roadmap

Problem

The JavaScript Reflex ships to every app (state.js, react-theme.js, upload.js, the helpers and components under utils/, style-reset.css, the vite plugins) lives in packages/reflex-base/src/reflex_base/.templates/web/ and is copied verbatim into each app's .web/ directory. That design has several costs:

  • Compiler/runtime drift. Compiled output and the copied runtime are only guaranteed to match by wiping and re-initializing .web on every Reflex version change, which also throws away node_modules and lockfiles.
  • Untestable runtime. The shipped modules import per-app generated artifacts ($/env.json, $/reflex.json, $/utils/context.js), so they cannot be loaded outside a compiled app, let alone unit-tested. state.js importing reflex.json is also one of the two causes of non-reproducible builds in #7562.
  • Dependency versions live in Python. react, react-dom, react-router, socket.io-client, universal-cookie and the build toolchain are maintained as PackageJson.DEPENDENCIES/DEV_DEPENDENCIES constants and pinned at the top level of the app's package.json; the REACT_VERSION/REACT_ROUTER_VERSION env vars are the only override. json5 is used but never declared.
  • Path-coupled imports. Component packages and user custom code reference the runtime through $/utils/... specifiers tied to the .web layout.

Proposal

Ship the non-generated frontend runtime as a real npm package, @reflex-dev/reflex-base, at packages/reflex-base/src/reflex_base/frontend/, bundled inside the reflex-base wheel.

Packaging

  • A stdlib-only hatch build hook packs a deterministic npm-format tarball into the wheel. The release build has no JS toolchain, and byte-stable tarballs keep the file: integrity hashes in committed reflex.lock lockfiles valid across rebuilds. The sdist keeps the JS source so wheels rebuild from it.
  • The package version derives from reflex-base's own version.
  • A verify-frontend-tgz release check asserts the wheel bundles exactly the right tarball and leaks neither source nor node_modules.

Install into .web

  • Each run installs the package like any other dependency (file:./<tarball>). Editable installs of reflex-base link the live source directory instead (link: for bun, file: for npm) so framework JS edits hot-reload without a reinstall.
  • The install flow writes the package entry itself and reconciles it: an unchanged entry keeps the frozen-lockfile install byte-for-byte; a superseded tarball frozen-installs first and is rewritten after; an unresolvable entry is rewritten up front. Legacy top-level framework pins are swept, superseded tarballs pruned, and the install cache keyed on the package version.
  • A Reflex version change no longer wipes .web: needs_reinit() narrows to missing-directory cases and a new refresh_web_directory() overwrite-copies the remaining templates, deletes files owned by older versions and re-renders generated configs, keeping node_modules, lockfiles and the project hash.

Runtime decoupling

  • Shipped modules stop importing generated files. A @reflex-dev/reflex-base/runtime registry is populated by the generated utils/context.js via configureReflexRuntime() in its module body; the two module-scope reads of app config (EVENTURL, the embed-mode locationRef seed) become lazy.
  • ColorModeContext moves into react-theme.js and the generated context re-exports it. Compiled components read env values through getEnv() instead of importing env.json.

Compiler and imports

  • Generated code and component import declarations reference the package by bare specifier (@reflex-dev/reflex-base/state, .../react-theme, .../helpers/*, .../components/*, .../style-reset.css). constants.FrontendPackage is the single Python-side source of these specifiers, with a unit test asserting each resolves to a shipped file.
  • Back-compat for user code is three-layered: merge_imports normalizes legacy $/utils/... specifiers in Python import declarations, the generated vite config aliases them for user JS, and the app root keeps the window.__reflex["$/utils/state"] registry key for backend-evaluated dynamic components. Dirs.STATE_PATH and ResetStylesheet remain as deprecated fallbacks.

Dependencies

  • Runtime framework deps resolve transitively through the package manifest; the app-level toolchain is copied from its devDependencies; shiki and @glideapps/glide-data-grid are optional peer deps deduped to the app root.
  • User version control moves to overrides in reflex.lock/package.json. REACT_VERSION/REACT_ROUTER_VERSION are deprecated (warn now, removed in 1.0). react-helmet is only installed when rx.helmet is used.

Tests

  • Vitest unit tests live inside the package (frontend/tests/) and run as a js-unit-tests CI job; state.js becomes unit-testable for the first time. The JS test tooling itself is tracked in #7611.

Pending work

The implementation lives on branch claude/reflex-frontend-js-package-ockox9 (7 commits, +4775/-348 across 99 files). It was validated end to end with reflex compile and reflex export of a scratch app in source mode, bun and npm installs of the packed tarball, and the wheel in a clean venv. No PR has been opened for it yet.

  • Rebase onto current main: the branch is based on #6919 (late August) and predates later changes to the install flow (for example #7235 and the open #7451).
  • Open the PR; re-check the breaking-change fragment (news/+frontend-npm-package.breaking.md) and the docs/wrapping-react/local-packages.md section describing the file:/link: entry and overrides.
  • Confirm the release pipeline builds the tarball into the wheel (verify-frontend-tgz in scripts/release.py) on an actual pre-release.

Related

  • #7611 JS unit tests for the shipped frontend
  • #7562 / #7564 reproducible production builds
Vorherrschende Sprache
Python
Sterne
29k
Forks
1.8k
Ø Merge
2 T. 22 Std.
Gemergte PRs (30 T.)
143

Entwicklungsumgebung

In Codespaces öffnen

Startet den Dev-Container des Projekts im Browser, mit Ihrem eigenen GitHub-Konto.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus reflex-dev/reflex

Alle Issues in reflex-dev/reflex

Ähnliche Issues

Weitere Issues zu Python

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.