Skip to content

Phase 1 — Low-risk multi-framework fixes (package managers, routing, host binding, env) #46

Description

@pelazas

Phase 1 — Low-risk multi-framework fixes (package managers, routing, host binding, env)

Part of the multi-framework compatibility effort (PR #45, single PR, gated per phase). Builds on Phase 0 (#42, the mock-writer registry). Phase 1 is the batch of low-risk, high-value correctness fixes organized into three file-disjoint lanes so they implement in parallel without conflicts.

Non-goal: server-side data mocking for SvelteKit/Astro/Remix — that's Phase 2. Lane A only does the host/allowedHosts patch, not data mocks.

File-disjointness (safe parallel execution)

Lane Source owned Tests owned
A stack.py, config.py, discovery.py, mock_server.py, Dockerfile test_stack/config/discovery/mock_server
B routing.py, routes.py test_routing/routes
C env_inject.py test_env_inject

No lane edits main.py, visual.py, or renderpr_config.py.


LANE A — Launch/boot robustness

  • A1 Yarn Berry flag (W4): build_install_command (stack.py:89) emits --frozen-lockfile for all yarn; Berry v2+ needs --immutable. Parse the packageManager field (yarn@4.5.0→berry, yarn@1.22→classic; .yarnrc.yml as secondary signal) and branch the flag. Thread yarn_major through build_launch_profile.
  • A2 Dockerfile corepack (W4): corepack prepare yarn@stable (Dockerfile:23-27) force-pins Yarn 4 globally. Switch to corepack enable + let the repo's packageManager drive the version; pin a classic fallback (yarn@1.22.x) for repos with no field.
  • A3 pnpm workspace root (W5): find_workspace_root (discovery.py:53-74) only reads package.json workspaces; also recognize pnpm-workspace.yaml as a root marker.
  • A4 npm-shrinkwrap (W5): add "npm-shrinkwrap.json": "npm" to LOCKFILES (config.py:40) → restores npm ci + caching.
  • A5 allowedHosts via Phase-0 registry (W2): add SvelteKitMockWriter/AstroMockWriter/RemixMockWriter in mock_server.py whose write_dev_origin_allowlist patches Vite allowed-hosts — SvelteKit/Remix → vite.config.* (reuse write_vite_allowed_hosts), Astro → new write_astro_allowed_hosts patching astro.config.* vite.server.allowedHosts. Register them; shrink the bare no-op loop to ("cra","spa"). Deliberately replaces the Phase-0 lock test_sveltekit_astro_allowlist_parity_noop (this behavior change is intended, not test-weakening). test_stub_writers_generate_nothing stays green (doesn't call the allowlist).
  • A6 Remix host binding (W3): add remix to _HOST_FLAG_FRAMEWORKS (stack.py:22) so --host is appended; set FRAMEWORK_DEFAULT_PORTS["remix"]=5173 (config.py:54; 3000 stays a candidate).
  • A7/A8 robustness (W8): DEV_SERVER_START_TIMEOUT 60→120 (config.py:27); add 5174,4322 to candidate ports (config.py:62); Dockerfile Node 20→22 (setup_22.x); pin pnpm + bun versions.

Lane A commits: (1) yarn berry flag, (2) config: shrinkwrap+remix-port+ports/timeout, (3) pnpm-workspace root, (4) remix host bind, (5) allowedHosts writers + replace parity test, (6) Dockerfile.

LANE B — Routing inference correctness

  • B1 Next App Router (BLOCKER): file_to_route (routing.py:91) doesn't strip route groups (folder), @slot parallel routes, or (.)/(..)/(...)intercepting markers, and doesn't recognize route.ts as API. Add _normalize_next_segments (drop groups/slots, return None for intercepting + route.* files); reuse in is_layout_file.
  • B2 Remix flat-routes (BLOCKER): rewrite RemixStrategy.file_to_route (routing.py:250) — protect [.] escapes before splitting on .; strip trailing _ (layout opt-out blog_.post→/blog/post); drop leading-_ pathless layouts and (optional) segments; _index/; $param/splat→None.
  • B3 BFS extensions (Astro/SvelteKit): _find_importers (routes.py:113) only scans .ts/.tsx/.js/.jsx, never reaching .astro/.svelte pages. Add source_extensions to each strategy (Astro +.astro, SvelteKit +.svelte), thread into _find_importers/_bfs_to_pages; anchor stem matching ([./]{stem}(["'/.])) so index doesn't match reindex and directory imports resolve.
  • B4 SvelteKit coverage: broaden _PAGE_RE/discovery to +page.{svelte,ts,js,server.ts,server.js,md}; optional [[param]]/rest [...] → drop the segment and yield the concrete URL (only required [param]None).
  • B5 Hybrid Next: _resolve_next_variant (routing.py:293) picks app when both app/+pages/ exist. Add NextHybridStrategy composing app+pages (union routes); register next-hybrid.

Lane B commits: (1) Next groups/slots/intercepting/route.ts [BLOCKER], (2) Remix flat-routes [BLOCKER], (3) per-strategy source_extensions + SvelteKit coverage, (4) BFS importer extensions + anchored stem, (5) hybrid Next union.

LANE C — Env-var prefix awareness

  • W7: build_injected_env/write_env_local (env_inject.py:69-118) write a flat .env.local with no awareness of client-exposure prefixes. Add FRAMEWORK_PUBLIC_PREFIXES (next→NEXT_PUBLIC_, vite→VITE_, sveltekit/astro→PUBLIC_, cra→REACT_APP_, remix/spa→None) local to env_inject.py. Extend build_injected_env(..., framework=None) to WARN when an injected key lacks the framework's public prefix; Remix gets an info log that .env.local isn't auto-loaded (process-env path already delivers it). Default framework=None → no warning (back-compat). Warning only — never auto-rewrite vars.

Lane C commits: (1) framework client-prefix warnings + tests.


Test rules (all lanes)

Failing-test-first per AGENTS.md; full suite green after every commit; coverage ≥80%. The only existing test intentionally changed is Lane A's test_sveltekit_allowlist_parity_noop (A5). Run: .venv/bin/python -m pytest.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions