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-simple-router

v1.5.0

Published

Svelte 3 History Based Single Page Router

Downloads

4

Readme

svelte-simple-router

Svelte 3 History Based Single Page Router With Server Side Rendering (SSR) support

Installtion

with npm

npm i svelte-simple-router

Usage Note

Ensure your local server is configured in SPA mode. In a default Svelte installation you need to edit your package.json and add --single to sirv public.

"start": "sirv public --single"

Integration into your project

Step 1 : Create Routes File

create routes.js file into your project directory

import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Dashboard from './views/pages/Dashboard.svelte';
import MembersList from './views/pages/MembersList.svelte';
import Page404 from './views/pages/404.svelte';

const routes = {
	groupGuard: [
		{
			url: [/^members/],
			with: async function (routerData, route) {
				return true;
			},
			redirectOnFail: function (routerData, route) {
				return '/dashboard';
			}
		}

	],
	routes: [
		{
			name: "dashboard",
			url: [/^dashboard$/, /^\s*$/],
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: Dashboard
		},
		{
			name: "members",
			url: [/^members/],
			urlPathMapKeys: ['', 'key1', 'key2'],
			searchFilter: async function (routerData, route) {
				return true;
			},
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: MembersList
		},
		{
			name: "404",
			url: [/^404$/],
			layout: PublicLayout,
			component: Page404
		}
	]
};

export { routes }

Step 2 : Import the routes.js and Router from svelte-simple-router into your main app component

<script>
	import { Router } from 'svelete-simple-router';
	import { routes } from './routes.js';
</script>

<Router routes={routes} />

Step 3 : Create Layout File

for example : import AdminLayout from './views/layouts/Admin.svelte';

we created Admin.svelte file

<script>
	import {Route} from 'svelete-simple-router';
	export let currentRoute = {};
</script>
<div>
	<h1>Admin Layout</h1>
	<a href="/dashboard">Dashboard</a>
	<a href="/members">Member List</a>
	<a href="/404">404</a>
	<Route {currentRoute} />
</div>

Step 4 : Create Component/Page File

for example : import Dashboard from './views/pages/Dashboard.svelte';

we created Dashboard.svelte file

<script>
	export let currentRoute = {};
</script>
<h1>Dashboard</h1>

Structure of routes object

import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Dashboard from './views/pages/Dashboard.svelte';
import MembersList from './views/pages/MembersList.svelte';
import Page404 from './views/pages/404.svelte';

const routes = {
	groupGuard: [
		{
			url: [/^members/],
			with: async function (routerData, route) {
				return true;
			},
			redirectOnFail: function (routerData, route) {
				return '/dashboard';
			}
		},
		{
			url: [/^dashboard/],
			with: async function (routerData, route) {
				return true;
			},
			redirectOnFail:"/login"
		}

	],
	routes: [
		{
			name: "dashboard",
			url: [/^dashboard$/, /^\s*$/],
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: Dashboard
		},
		{
			name: "members",
			url: [/^members/],
			urlPathMapKeys: ['', 'key1', 'key2'],
			searchFilter: async function (routerData, route) {
				return true;
			},
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: MembersList
		},
		{
			name: "404",
			url: [/^404$/],
			layout: PublicLayout,
			component: Page404
		}
	]
};

export { routes }

1) groupGuard

groupGuard: [
	{
		url: [/^members/],
		with: async function (routerData, route) {
			return true;
		},
		redirectOnFail: function (routerData, route) {
			return '/dashboard';
		}
	}

]

Using group guard you can guard the multiple routes using matching starting path or any regular expression that you want to check.

Fields | Description -------|------------ groupGuard: | array of object hold all your group routes for your application groupGuard:url: | array of regular expression that you want to match for that particular group route groupGuard:with: | a callback function for checking and guarding particular route. must return either true or false groupGuard:redirectOnFail: | a function or string url for redirect if guard is fail. if it is a function then must return valid url

groupGuard always check and run after specific url guard is completed.

2) routes

routes: [
	{
		name: "dashboard",
		url: [/^dashboard$/, /^\s*$/],
		searchFilter: async function (routerData, route) {
			return true;
		},
		guard: {
			with: async function (routerData, route) {
				return true;
			},
			redirectOnFail: '/404'
		},
		layout: AdminLayout,
		component: Dashboard
	},
	{
		name: "404",
		url: [/^404$/],
		layout: PublicLayout,
		component: Page404
	}
]

Fields | Description -------|------------ routes: | array of object hold all your routes for your application routes:name: | you need to provide unique name for each of your routes. routes:url: | array of regular expression that you want to match for that particular route routes:urlPathMapKeys: | array of keyname that assign to each path item. routes:searchFilter: | a callback function for extra custom matching function if you required other than regular expression routes:guard: | if you want to guard that particular route for some conditional check. routes:guard:with: | a callback function for checking and guarding particular route. must return either true or false routes:guard:redirectOnFail: | a function or string url for redirect if guard is fail. if it is a function then must return valid url routes:layout: | layout component that you want to load, if you dont provide then only component is loaded. routes:component: | page component that you want to load under the layout or without layout.

Mandatory routes

{
	name: "404",
	url: [/^404$/],
	layout: PublicLayout,
	component: Page404
}

404 named route is required to display the component if no component or layout is found.

currentRoute object

every layout and component passed currentRoute props to easily available some router related information.

for example if you visit this url

http://localhost:5000/members/name/rajdeep?quervar1=value1

{
  "routePosition": 1,
  "routeName": "members",
  "pathName": "/members/name/rajdeep",
  "pageName": "members",
  "singleParams": [
    "members",
    "name",
    "rajdeep"
  ],
  "namedParams": {
    "name": "rajdeep"
  },
  "queryParams": {
    "quervar1": "value1"
  },
  "layout": {
    "viewed": false
  },
  "component": {
    "viewed": false
  }
}

searchFilter, group:with, group:redirectOnFail, groupGuard:with, groupGuard:redirectOnFail

you can define all above callback function in Synchronous or Asynchronous manner as per your application need.

all above function in your routes object passed two parameter

1) routerData

routerData contains

for example

"pathName": "/members/name/rajdeep",
"pageName": "members",
"singleParams": [
"members",
"name",
"rajdeep"
],
"namedParams": {
"name": "rajdeep"
},
"queryParams": {
"quervar1": "value1"
}

2) route

route contains whole object of your route that you defined in routes object

for example

{
	name: "dashboard",
	url: [/^dashboard$/, /^\s*$/],
	searchFilter: async function (routerData, route) {
		return true;
	},
	guard: {
		with: async function (routerData, route) {
			return true;
		},
		redirectOnFail: '/404'
	},
	layout: AdminLayout,
	component: Dashboard
}

<a href=""> in router

By default router will capture all the <a href=""> onclick event and based on the href it will match the route

if it's not same domain link then it will just do the regular redirect

if it's same domain link then it will start the matching process and load the matched route

<a href="" class="no-follow">

if you dont want capture some of your <a> link in router by default.

then just add the class="no-follow" and those click event will not goes into matching.

Manually / Code level redirect

import {RouterRedirect} from 'svelte-simple-router';

will provide you redirect functionality at code level.

usage example

RouterRedirect('/some-other-url');
RouterRedirect('https://someother.url');

noFollow you can also pass no noFollow true or false in second parameter to tell router whether you bypass the matching or not.

By default noFollow is set to false

usage example

RouterRedirect('/some-other-url',true); //will not goes into route matching and directly do regular page redirect

Settings For Server Side Rendering (SSR)

there is example in examples/ssr folder on github.

rollup.config.js

import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';

const production = !process.env.ROLLUP_WATCH;

export default [
	//browser bundel
	{

		input: 'src/main.js',
		output: {
			sourcemap: true,
			format: 'iife',
			name: 'app',
			file: 'public/build/bundle.js'
		},
		plugins: [
			svelte({
				// enable run-time checks when not in production
				dev: !production,
				// we'll extract any component CSS out into
				// a separate file - better for performance
				css: css => {
					css.write('public/build/bundle.css');
				},
				hydratable: true
			}),

			// If you have external dependencies installed from
			// npm, you'll most likely need these plugins. In
			// some cases you'll need additional configuration -
			// consult the documentation for details:
			// https://github.com/rollup/plugins/tree/master/packages/commonjs
			resolve({
				browser: true,
				dedupe: ['svelte']
			}),
			commonjs(),

			// In dev mode, call `npm run start` once
			// the bundle has been generated
			!production && serve(),

			// Watch the `public` directory and refresh the
			// browser on changes when not in production
			!production && livereload('public'),

			// If we're building for production (npm run build
			// instead of npm run dev), minify
			production && terser()
		],
		watch: {
			clearScreen: false
		}
	},
	//server ssr bundel
	{
		input: "src/App.svelte",
		output: {
			sourcemap: false,
			format: "cjs",
			name: "app",
			file: "public/App.js"
		},
		plugins: [
			svelte({
				generate: "ssr"
			}),
			resolve(),
			commonjs(),
			production && terser()
		]
	}

];

function serve() {
	let started = false;

	return {
		writeBundle() {
			if (!started) {
				started = true;

				require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
					stdio: ['ignore', 'inherit', 'inherit'],
					shell: true
				});
			}
		}
	};
}

App.svelte

<script>
	import { Router } from 'svelte-simple-router';
	import { routes } from './routes.js';

	// Used for SSR. A falsy value is ignored by the Router.
	export let url = "";
</script>

<Router routes={routes} url="{url}" />

main.js (entry point of your browser bundel app)

import App from './App.svelte';

const app = new App({
	target: document.body,
	hydrate: true
});

export default app;

node.js server

you can use any server of your choice

const http = require('http');
const App = require('./public/App.js');
const port = 3000

const requestHandler = (request, response) => {
	let url = 'http://' + request.headers.host + request.url;
	const { head, html, css } = App.render({ url: url });
	let output = `<!DOCTYPE html>
	<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<link rel='stylesheet' href='http://localhost:5000/global.css'>
		<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
		<script defer src='http://localhost:5000/build/bundle.js'></script>
	</head>
	<body>${html}</body>
	</html>`;
	response.end(output);
}

const server = http.createServer(requestHandler)

server.listen(port, (err) => {
	if (err) {
		return console.log('something bad happened', err)
	}
	console.log(`server is listening on ${port}`)
})

Settings For Code Splitting

there is example in examples/code-splitting folder on github.

rollup.config.js

import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';

const production = !process.env.ROLLUP_WATCH;

export default [
	//browser bundel for code splitting
	{

		input: 'src/main.js',
		output: {
			sourcemap: true,
			format: 'es',
			dir: 'public/module/'
		},
		plugins: [
			svelte({
				// enable run-time checks when not in production
				dev: !production,
				// we'll extract any component CSS out into
				// a separate file - better for performance
				css: css => {
					css.write('public/build/bundle.css');
				},
				hydratable: true
			}),

			// If you have external dependencies installed from
			// npm, you'll most likely need these plugins. In
			// some cases you'll need additional configuration -
			// consult the documentation for details:
			// https://github.com/rollup/plugins/tree/master/packages/commonjs
			resolve({
				browser: true,
				dedupe: ['svelte']
			}),
			commonjs(),

			// In dev mode, call `npm run start` once
			// the bundle has been generated
			!production && serve(),

			// Watch the `public` directory and refresh the
			// browser on changes when not in production
			!production && livereload('public'),

			// If we're building for production (npm run build
			// instead of npm run dev), minify
			production && terser()
		],
		watch: {
			clearScreen: false
		}
	}

];

function serve() {
	let started = false;

	return {
		writeBundle() {
			if (!started) {
				started = true;

				require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
					stdio: ['ignore', 'inherit', 'inherit'],
					shell: true
				});
			}
		}
	};
}

routes.js (which hold all your route in json object)

import AdminLayout from './views/layouts/Admin.svelte';
import PublicLayout from './views/layouts/Public.svelte';
import Page404 from './views/pages/404.svelte';

const routes = {
	groupGuard: [
		{
			url: [/^members/],
			with: async function (routerData, route) {
				return true;
			},
			redirectOnFail: function (routerData, route) {
				return '/dashboard';
			}
		}

	],
	routes: [
		{
			name: "dashboard",
			url: [/^dashboard$/, /^\s*$/],
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: async function () {
				let com = await import('./views/pages/Dashboard.svelte');
				return com.default;
			}
		},
		{
			name: "members",
			url: [/^members/],
			searchFilter: async function (routerData, route) {
				return true;
			},
			guard: {
				with: async function (routerData, route) {
					return true;
				},
				redirectOnFail: '/404'
			},
			layout: AdminLayout,
			component: async function () {
				let com = await import('./views/pages/MembersList.svelte');
				return com.default;
			}
		},
		{
			name: "404",
			url: [/^404$/],
			layout: PublicLayout,
			component: Page404
		}
	]
};

export { routes }

As you can see the difference in above example

instead of import required component from the start and direct assign into component

we are loading it dynamically using function, only when that route is called

without code splitting its look like this

import Dashboard from './views/pages/Dashboard.svelte';

{
	component: Dashboard
}

with code splitting its like this

{
	component: async function () {
				let com = await import('./views/pages/Dashboard.svelte');
				return com.default;
			}
}

public/index.html

as you notice instead of loading the bundel.js

now we are loading main.js which is main entry point of application which load other chunk dynamically.

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset='utf-8'>
	<meta name='viewport' content='width=device-width,initial-scale=1'>
	<title>Svelte app</title>
	<link rel='icon' type='image/png' href='http://localhost:5000/favicon.png'>
	<link rel='stylesheet' href='http://localhost:5000/global.css'>
	<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
	<script type="module" src='http://localhost:5000/module/main.js'></script>
</head>

<body>
</body>

</html>

Settings For Server Side Rendering (SSR) + Code Splitting

If you are using Code Splitting + SSR, then you also need to change the rollup.config.js for SSR part

All other setting for SSR and Code Splitting remain as it is

rollup.conig.js

as you can see now instead of output single file now we also change to directory public/ssr/ which hold the App.js file and chunk files

//server ssr bundel
{
	input: "src/App.svelte",
	output: {
		sourcemap: false,
		format: "cjs",
		name: "app",
		dir: "public/ssr/"
	},
	plugins: [
		svelte({
			generate: "ssr"
		}),
		resolve(),
		commonjs(),
		production && terser()
	]
}

node.js server

you can use any server of your choice

onst http = require('http');
const App = require('./public/ssr/App.js');
const port = 3000

const requestHandler = (request, response) => {
	let url = 'http://' + request.headers.host + request.url;
	const { head, html, css } = App.render({ url: url });
	let output = `<!DOCTYPE html>
	<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<link rel='stylesheet' href='http://localhost:5000/global.css'>
		<link rel='stylesheet' href='http://localhost:5000/build/bundle.css'>
		<script type="module" src='http://localhost:5000/module/main.js'></script>
	</head>
	<body>${html}</body>
	</html>`;
	response.end(output);
}

const server = http.createServer(requestHandler)

server.listen(port, (err) => {
	if (err) {
		return console.log('something bad happened', err)
	}
	console.log(`server is listening on ${port}`)
})