Skip to content

Fix redirect guard behaviour#33

Open
IzumiSy wants to merge 6 commits intomainfrom
fix_redirect_guard_behaviour
Open

Fix redirect guard behaviour#33
IzumiSy wants to merge 6 commits intomainfrom
fix_redirect_guard_behaviour

Conversation

@IzumiSy
Copy link
Contributor

@IzumiSy IzumiSy commented Feb 27, 2026

See changeset for detail

@IzumiSy IzumiSy self-assigned this Feb 27, 2026
@IzumiSy IzumiSy force-pushed the fix_redirect_guard_behaviour branch from 5c6d4b0 to a6124fb Compare February 27, 2026 08:58
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 27, 2026

Open in StackBlitz

npm i https://pkg.pr.new/tailor-platform/app-shell/@tailor-platform/app-shell@33
npm i https://pkg.pr.new/tailor-platform/app-shell/@tailor-platform/app-shell-vite-plugin@33

commit: 5888a43

@IzumiSy
Copy link
Contributor Author

IzumiSy commented Feb 27, 2026

/review

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by API Design Review for issue #33

// even though the loader always redirects/throws and this component will never render.
Component: () => null,
}
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1/2 — Medium] Loader-only index route semantics unclear

The new behavior creates index routes for loader-only modules/resources when source.loader exists but source.component doesn't. However, looking at the codebase, source.loader is only populated by withGuardsLoader() when guards are present.

Issue: The condition source.loader is being used as a proxy for "has guards", which is implicit and fragile. If a module/resource could have a loader without guards in the future, this would incorrectly create an index route with Component: () => null.

Recommendation: Make the intent explicit by checking for guards directly, or document this coupling clearly. Consider:

: (source.loader && hasGuards) // or add a flag like `source.hasGuards`
  ? {
      index: true,
      loader: source.loader,
      Component: () => null,
    }
  : undefined;

Alternatively, add a JSDoc comment explaining that source.loader only exists when guards are present.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a JSDoc comment explaining that source.loader is currently only set by withGuardsLoader() when guards are present, and that this condition should be revisited if that coupling changes in the future.

Commit: a9d566c


const guardResult = await runGuards(module.guards);
if (guardResult.type !== "pass") return null;
if (guardResult.type === "hidden") return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[2/2 — High] Navigation filtering change breaks API contract

Breaking change: The navigation filtering logic changed from "only show items where guards pass" (!== "pass") to "show everything except explicitly hidden items" (=== "hidden").

API consistency issue: This means modules/resources with redirectTo() guards now appear in navigation even though they never render their component — they always redirect away. This creates a confusing UX where users see a nav item that, when clicked, immediately redirects elsewhere.

Example of problematic behavior:

defineModule({
  path: "old-dashboard",
  guards: [() => redirectTo("/new-dashboard")],
  resources: [/* ... */],
});
// "Old Dashboard" appears in sidebar, but clicking it redirects to "/new-dashboard"

Recommendation: Consider one of these alternatives:

  1. Add a new guard result type like { type: "silent-redirect" } that redirects but hides from navigation
  2. Add an explicit meta.hiddenInNav flag so developers can control visibility independently
  3. Document this new behavior clearly as intentional UX (nav items can be "redirect aliases")

The current approach conflates routing behavior (redirect) with UI visibility (sidebar), which may surprise developers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional — keeping redirectTo() items visible in the sidebar is a deliberate design choice for two reasons:

  1. Redirect-only modules: A module may have no component but should still appear as a sidebar item that redirects elsewhere (e.g. aliasing a legacy path).
  2. Child resource visibility: In auto-generated sidebar mode, hiding a module also hides all of its child resources from navigation. A module that redirects at its own path may still have child resources with real pages that users need to access.

If a developer wants to hide a module/resource from the sidebar entirely, they should use hidden() instead of redirectTo().

Added inline comments in navigation.tsx clarifying this design intent, and updated the changeset with a guard result → nav visibility table documenting the behavior.

Commit: 5888a43

@IzumiSy IzumiSy force-pushed the fix_redirect_guard_behaviour branch from af9f63b to 4e2ecf3 Compare February 27, 2026 14:15
@IzumiSy IzumiSy marked this pull request as ready for review February 27, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant