Skip to content

scoville/re-url

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ReUrl

ReUrl is a port of the elm-url library, a powerful Url parser and builder.

Simple example

// We can define our Route module
module Route = {
  open UrlParser

  // It will contain an exhaustive list of possible routes, with arguments
  // Notice that we use `deriving(accessors)` for conveniency, but you don't have to
  @deriving(accessors)
  type t = Home | Blog(int) | Something(string, int) | NotFound

  // We can now define our 3 possible paths
  let topRoute = top->map(home)

  let blogRoute = s("blog")->slash(int())->map(blog)

  // Even complex routes are still easy to read:
  // something / a string / else / an int
  let somethingRoute = s("something")->slash(str())->slash(s("else"))->slash(int())->map(something)

  // This function will take a string and try to decode/parse it using one of the provided parsers
  // If no paths match, then the fallback value will be returned
  let fromString = oneOf([topRoute, blogRoute, somethingRoute])->parseString(~fallback=notFound)
}

// You might use some existing library like the bs-webapi
// Any string value would work anyway
@val @scope(("window", "location"))
external currentUrl: string = "href"

// The core logic of our router,
// it can be used to display some components conditionally in React for example
Js.log(
  switch Route.fromString(currentUrl) {
  | Home => "Home page"
  | Blog(id) => `Blog page with id ${id->Int.toString}`
  | Something(x, y) => `Found this ${x} and that ${y->Int.toString}`
  | NotFound => "Page not found"
  },
)

The test file contains an example for most of the provided functions.

Known current limitations

  • The QueryParser module is not implemented currently
  • Syntaxically, ReScript doesn't allow yet for custom operators. Once it does, we could imagine this syntax to define the somethingRoute from above:
let somethingRoute = (s("something") </> str() </> s("else") </> int())->map(something)
  • If you define a parser and don't use it, or if you use the parser outside the module where the route type t is defined, you will encounter one of these errors (using the same Route module as above):
module Route = {
  let blogRoute = s("blog")->slash(int())
  // Raises a `The type of this module contains type variables that cannot be generalized`
  // compile error, and later: `let blogRoute: Url.UrlParser.parser<int => '_weak1, '_weak1>`
}

// When trying to use the above parser outside the module, while it seems it should solve the weak
// polymorphism issue, it actually introduces an other error:
Route.blogRoute->UrlParser.parseString(~fallback=Route.notFound, url)
// Will raise `The type constructor Route.t would escape its scope` compile error

You can check the OCaml documentation explains the first limitation pretty well.

The limitations can be hard to lift upstream, and should be handled downstream, in your application. It's worth noticing that these type errors won't happen if you follow the canonical example above.

About

An url parser and builder inspired based on elm-url: https://github.com/elm/url

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published