Move registry calls from component to loader in admin.sports.$id

SIMULATOR_TYPES and getSimulatorInfo were used directly in the component
body to build the simulator type dropdown. This dragged registry.ts —
which imports every simulator implementation — into the client bundle,
causing a TDZ ReferenceError that broke any route co-loaded with it
(observed as "Error loading route module" on admin events pages).

Fix: pre-build simulatorOptions in the loader and pass them as plain
serializable data. The registry imports remain for the action's
validator but are now only reachable from server-only code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-05-11 22:49:00 -07:00
parent 7c94657417
commit 5cc025bfd3

View file

@ -34,8 +34,17 @@ export async function loader({ params }: Route.LoaderArgs) {
const sports = await findAllSports(); const sports = await findAllSports();
const simulatorOptions = [...SIMULATOR_TYPES]
.toSorted((a, b) => {
const nameA = getSimulatorInfo(a)?.name ?? a;
const nameB = getSimulatorInfo(b)?.name ?? b;
return nameA.localeCompare(nameB);
})
.map((type) => ({ value: type, label: getSimulatorInfo(type)?.name ?? type }));
return { return {
sport, sport,
simulatorOptions,
existingIconOptions: sports existingIconOptions: sports
.filter((option) => option.id !== sport.id && Boolean(option.iconUrl)) .filter((option) => option.id !== sport.id && Boolean(option.iconUrl))
.map((option) => ({ id: option.id, name: option.name, iconUrl: option.iconUrl as string })), .map((option) => ({ id: option.id, name: option.name, iconUrl: option.iconUrl as string })),
@ -116,7 +125,7 @@ export async function action({ request, params }: Route.ActionArgs) {
} }
export default function EditSport({ loaderData, actionData }: Route.ComponentProps) { export default function EditSport({ loaderData, actionData }: Route.ComponentProps) {
const { sport, existingIconOptions } = loaderData; const { sport, simulatorOptions, existingIconOptions } = loaderData;
return ( return (
<div className="p-8"> <div className="p-8">
@ -185,17 +194,11 @@ export default function EditSport({ loaderData, actionData }: Route.ComponentPro
className={SELECT_CLASS} className={SELECT_CLASS}
> >
<option value="none">No simulator</option> <option value="none">No simulator</option>
{[...SIMULATOR_TYPES] {simulatorOptions.map((option) => (
.toSorted((a, b) => { <option key={option.value} value={option.value}>
const nameA = getSimulatorInfo(a)?.name ?? a; {option.label}
const nameB = getSimulatorInfo(b)?.name ?? b; </option>
return nameA.localeCompare(nameB); ))}
})
.map((type) => (
<option key={type} value={type}>
{getSimulatorInfo(type)?.name ?? type}
</option>
))}
</select> </select>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Algorithm used when running EV simulations for this sport Algorithm used when running EV simulations for this sport