Xenia.Routes.Discovery (Xenia v0.1.1)

Copy Markdown View Source

Turns paths in the app directory into routes. Pure: paths in, structs out. Only scan/1 reads the filesystem.

Folder names follow the Next.js App Router:

  • runbooks a static URL segment
  • (auth) a route group: groups pages and layouts, adds nothing to
             the URL. It grants nothing by itself; authentication stays
             in Phoenix pipelines and `live_session`.
  • [id] a dynamic segment, :id
  • [...path] a catch-all segment, *path; must be the last folder
  • _name a private folder: never routed, for colocated components

Four special files:

  • page.ex makes its folder a route: the UI for that URL
  • layout.ex frames every page in its folder and below, outermost
                first
  • persistent.ex optional; the persistent part of the layout.ex in the
                same folder: a LiveView that stays alive while the user
                moves between that layout's pages. Requires the
                `layout.ex`, whose frame places it.
  • live.ex optional; one LiveView owns every page in its folder and
                below, so switching between them is a patch that keeps
                the process and its state. The nearest `live.ex` wins,
                so a nested one starts a new owner. Pages without one
                are their own LiveView.

Summary

Functions

Every page.ex, layout.ex, persistent.ex and live.ex under root, relative to it and sorted. Hidden folders are skipped. Xenia.Routes compares this list between compilations to notice added and removed pages.

The page at file, given the app's layout.ex, persistent.ex and live.ex files (all relative paths). Other files in the list are ignored.

All pages in a list of relative paths, in the order Phoenix should match them: static segments before dynamic ones, catch-alls last.

Reads the app folder at root and returns its pages, see pages/1. Paths in the result are relative to root.

Functions

files(root)

@spec files(Path.t()) :: [String.t()]

Every page.ex, layout.ex, persistent.ex and live.ex under root, relative to it and sorted. Hidden folders are skipped. Xenia.Routes compares this list between compilations to notice added and removed pages.

page(file, files)

@spec page(String.t(), [String.t()]) :: Xenia.Routes.Page.t()

The page at file, given the app's layout.ex, persistent.ex and live.ex files (all relative paths). Other files in the list are ignored.

iex> Xenia.Routes.Discovery.page("(auth)/runbooks/[id]/page.ex", [
...>   "layout.ex",
...>   "(auth)/layout.ex",
...>   "(auth)/persistent.ex"
...> ])
%Xenia.Routes.Page{
  file: "(auth)/runbooks/[id]/page.ex",
  path: "/runbooks/:id",
  params: ["id"],
  groups: ["auth"],
  layouts: ["layout.ex", "(auth)/layout.ex"],
  persistent: %{"(auth)/layout.ex" => "(auth)/persistent.ex"},
  live: nil
}

Raises ArgumentError for a file that isn't a page.ex and for invalid folder names.

pages(files, opts \\ [])

@spec pages([String.t()], keyword()) :: [Xenia.Routes.Page.t()]

All pages in a list of relative paths, in the order Phoenix should match them: static segments before dynamic ones, catch-alls last.

Other files, and anything inside a private _folder, are ignored. Raises ArgumentError when two pages resolve to the same path, when a live.ex owns no page, and when a persistent.ex has no layout.ex next to it.

With check_owners: false, a live.ex that owns no page yet is allowed: the generators use it, because an unfinished folder isn't the new file's problem.

scan(root)

@spec scan(Path.t()) :: [Xenia.Routes.Page.t()]

Reads the app folder at root and returns its pages, see pages/1. Paths in the result are relative to root.