npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

svelte-steer

v0.0.3

Published

Router with lazy-loading and nested routes for svelte

Downloads

1

Readme

svelte-steer

npm

As there is curently no dedicated nor even suitable router for svelte, and as we needed a powerful and flexible one, we decided to make one and make it a dedicated package.

Just released [2021-12-29], it is still highly to be tested, corrected and receive suggestions/PRs - Beta mode.

Installation / usage

npm i -S svelte-steer
import { Router, Route, Link, link } from "svelte-steer";
const router = <Routing>getContext('router');
----
$:    myLink = router.link('user', {id: 42});
----
$:    myLink = router.link('/user/42');  // Don't laugh - if we are in a nested router, this might become `/en/user/42` or `/de/user/42` depending of the parent router
<Router {routes} >
    ...
    <Link route="user" parms={{id: 42}}>  <!-- named route -->
    ----
    <Link route="/user/42">  <!-- url path -->
    ...
    <Route>404</Route>
    ...
</Router>

Main difference with common routers

The Router object contains only the state of the routing. On the HTML generation level, it just forwards the content. It manages every routing-related activity/elements (Route, Link, getContext('router'), &c.) that happen inside.

The Route element effectively displays the selected route.

Elements

Router

Properties

variableMarker : /^\:/ : Variables in the path are written ":variable-name"

routes : Gives the Route[] tree of routes to serve

history : History mode to use. Two modes are defined by default.

  • H5History uses the Html5 history mode : http://mysite/my/route/path
  • HashHistory uses the hash as history mode : http://mysite/#my/route/path

By default, H5History is used.

Hint: If you don't use a SPA server, for example serving the app from the file system, the hash history is required. Changing to hash happens like this :

<Router history={HashHistory} ...>
...
</Router>
<script lang="ts">
    import { HashHistory } from "svelte-steer";
    ...
</script>

Also, changing the history mode requires nothing else. All the <Link ...> and calls to Routing will act accordingly.

Route

Slot

The slot is displayed if no route is found. The error value can be used to display more information.

Properties

A route can be forced (and hence the router state ignored) if this is specified :

  • route: RouteMatch : Either the path (begins with a '/') or the name of the route to point to
  • params: Record<string, string> : If a route name is provided, this is the dictionary of the properties to give.

State feedbacks :

  • loading: Writable<boolean> : Set to true when waiting a lazy-load
  • error: Writable<Error> : Set (or unset if value is undefined) to the the route-related error. In error state, the slot is displayed. The slot is displayed without error when the route is not found.

Link

Properties

route : Either the path (begins with a '/') or the name of the route to point to

params : If a route name is provided, this is the dictionary of the properties to give.

Annex

Route determination

When a (route: string, params: Record<string, string>) is used, like the attributes of the <Link> element or the parameters to the match function, either the route begins with a '/' - in which case the params part is ignored and the route string is analyzed as a path, either it does not begin with a '/' and is therefore used as a route name.

If two routes have the name "details", one under the route "author" and one under the route "book", the name "details" will raise an ambiguity error. The names "author/details" and "book/details" (where both "author", "book" and "details were given as route names) are valid and non-ambiguous.

Contexts

"router"

Interface to interract with the router - see Routing.

"route"

Give the Readable<RouteMatch> directly contained in this route.

Structures

Routing

When in a router, the context "router" is the following interface :

interface Routing {
    link(path: string | RouteMatch, props?: Record<string, string>): string;
    match(path: string, props?: Record<string, string>): RouteMatch;
    navigate(path: string, props?: Record<string, string>, push: boolean = true);
    replace(path: string, props?: Record<string, string>);
    go(delta: number);
}

Example:

let router = <Routing>getContext('router');

router.navigate('/new/url');

Route definition

interface RouteDesc {
    name?: string;
    path: string;
    component?: Lazy<SvelteComponent>;
    nested?: RouteDesc[];
    async enter?(route: RouteMatch): Promise<boolean | void>;
    async properties?(props: Record<string, string>, route: RouteMatch): Promise<boolean | void>;
    leave?(route: RouteMatch): string | void;
    meta?: any;
}
  • name is only used to refer to this route by its name. Some function can take the name of a route to refer to it.
  • path refer to the whole path of the route. Each part begining with a : defines a parameter (like every router: /user/:id).
  • component is the component to display (lazy-loaded). Optional: if there are nested routes, not specifying a component is equivalent to specify a component containing only <Route /> and hence displaying directly the nested route.
  • nested is an array of nested routes.
  • meta is not used internally and is meant to be used by the user.

Call-backs :

  • enter is called when a route is entered. Explicitely returning false cancels the navigation.
  • leave is called when a route is exited. Returning a string will raise a prompt with that string to ask user's confirmation of leaving.
  • properties is called when properties are changed or just after enter if there are properties. Explicitely returning false cancels the navigation.

Route match

interface RouteMatch {
  spec: RouteSpec;
  parent?: RouteMatch;
  nested?: RouteMatch;
  props: Record<string, string>;
}
  • spec gives all the indication of the generic route (without match). (Note, it inherits from Routes)
  • parent and nested give both the match of the parent and nested route (if any)
  • props contains all the parameters given to the route. The prototype of this object are the parameters given to the parent route (chained)

Nesting

A router routes is defined with an array of Route : <Router {routes}> - Except when it is a nested router. Nesting can be done in two ways, illustrated here : available routes are a, a/c, a/d.

index.svelte

<script>
  import { Router, Route } from "svelte-steer";
  import A from "./a.svelte";
  import C from "./c.svelte";
  import D from "./d.svelte";

  let routes = [{
    path: 'a',
    component: A,
    nested: [{
      path: 'c', component: C
    }, {
      path: 'd', component: D
    }]
  }]
</script>
<Router {routes}><Route/></Router>

a.svelte

<script>
  import { Route } from "svelte-steer";
</script>
a/ ... <Route />

If route a does not define a component, the sub-component (C or D) will be directly used.

TODOs & participation

Fixes:

  • generate a bundled .d.ts
  • test & fix w/ SSR

Functionalities:

  • lazy loading - add a default behaviour for "Loading..." ? Dim old page? youtube-like "progress" on the screen-top?
  • transitions?
  • page preservation/destruction mgt
  • path i18n: "/login"|en, "/connexion"|fr, "/autentificare"|ro, ...
  • multi-parts routes : menu, center, toolbox, ....

Management of "remaining route", either:

  • Plan lazy-loaded nested route
  • Exception when some part of the path is not used (perhaps optionally?)
  • Both