├── .eslintignore
├── .gitignore
├── .npmignore
├── example
├── .gitignore
├── src
│ ├── index.js
│ ├── pages
│ │ ├── home.html
│ │ ├── other.html
│ │ ├── subthing.html
│ │ ├── things.html
│ │ └── thing.html
│ ├── app.html
│ └── routes.js
├── public
│ ├── index.html
│ └── global.css
├── package.json
└── webpack.config.js
├── .editorconfig
├── .prettierrc
├── .eslintrc
├── NestedRoute.html
├── .vscode
├── launch.json
└── settings.json
├── package.json
├── helpers.js
├── Router.html
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | dist/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /example/
2 | /.*
3 | /yarn.lock
4 | /package-lock.json
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | public/bundle.*
4 | package-lock.json
5 | yarn.lock
--------------------------------------------------------------------------------
/example/src/index.js:
--------------------------------------------------------------------------------
1 | import App from "./app.html";
2 |
3 | const app = new App({
4 | target : document.body,
5 | });
6 |
7 | export default app;
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = false
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "printWidth": 80,
4 | "trailingComma": "all",
5 | "bracketSpacing": true,
6 | "jsxBracketSameLine": false,
7 | "singleQuote": true
8 | }
9 |
--------------------------------------------------------------------------------
/example/src/pages/home.html:
--------------------------------------------------------------------------------
1 |
Home
2 |
3 |
14 |
--------------------------------------------------------------------------------
/example/src/pages/other.html:
--------------------------------------------------------------------------------
1 | Other
2 |
3 |
14 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Svelte app
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/src/pages/subthing.html:
--------------------------------------------------------------------------------
1 | Subthing {params.thing}!
2 |
3 |
18 |
--------------------------------------------------------------------------------
/example/src/app.html:
--------------------------------------------------------------------------------
1 | APP
2 |
3 | Home | Things | Other
4 |
5 |
6 |
7 |
21 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["standard", "prettier", "prettier/standard"],
4 | "plugins": [
5 | "standard", "prettier", "html"
6 | ],
7 | "parser": "babel-eslint",
8 | "settings": {
9 | "html/html-extensions": [".html", ".svelte"],
10 | "html/indent": "+2",
11 | "html/report-bad-indent": "warn"
12 | },
13 | "env": {
14 | "browser": true
15 | },
16 | "parserOptions": {
17 | "ecmaFeatures": {
18 | "modules": true
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/NestedRoute.html:
--------------------------------------------------------------------------------
1 | {#if page}
2 |
9 | {:else}
10 |
11 | {/if}
12 |
13 |
27 |
--------------------------------------------------------------------------------
/example/src/pages/things.html:
--------------------------------------------------------------------------------
1 | Things!
2 |
3 | foo: {foo}
4 |
5 | Thing 1 |
6 | Thing 2
7 |
8 |
23 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-app",
3 | "version": "1.0.0",
4 | "devDependencies": {
5 | "cross-env": "^5.2.0",
6 | "css-loader": "^1.0.0",
7 | "mini-css-extract-plugin": "^0.4.2",
8 | "serve": "^10.0.0",
9 | "style-loader": "^0.23.0",
10 | "svelte": "^2.13.2",
11 | "svelte-loader": "2.11.0",
12 | "webpack": "^4.17.1",
13 | "webpack-cli": "^3.1.0",
14 | "webpack-serve": "^2.0.2"
15 | },
16 | "scripts": {
17 | "build": "cross-env NODE_ENV=production webpack",
18 | "dev": "webpack-serve --content public"
19 | },
20 | "dependencies": {}
21 | }
22 |
--------------------------------------------------------------------------------
/example/src/pages/thing.html:
--------------------------------------------------------------------------------
1 | Thing {params.thing}!
2 |
3 | Thing | Subthing
4 |
5 |
6 |
7 |
25 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "runtimeExecutable": "/usr/bin/chromium",
13 | "webRoot": "${workspaceFolder}/example/src"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/example/src/routes.js:
--------------------------------------------------------------------------------
1 | import home from './pages/home.html'
2 |
3 | export default {
4 | '/': {
5 | component: home,
6 | preload: ['/other', '/things'],
7 | },
8 | '/other': {
9 | loader: () => import('./pages/other.html'),
10 | },
11 | '/things': {
12 | loader: () => import('./pages/things.html'),
13 | data: { foo: 'This is a foo value' },
14 | },
15 | '/things/:thing*': {
16 | loader: () => import('./pages/thing.html'),
17 | preload: ['/things/:thing/subthing/'],
18 | },
19 | '/things/:thing/subthing/': {
20 | loader: () => import('./pages/subthing.html'),
21 | },
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.trimAutoWhitespace": true,
3 |
4 | "files.trimTrailingWhitespace": true,
5 |
6 | "prettier.eslintIntegration": true,
7 | "prettier.stylelintIntegration": true,
8 |
9 | "javascript.validate.enable": false,
10 | "html.validate.scripts": false,
11 | "html.validate.styles": true,
12 |
13 | "svelte.plugin.typescript.diagnostics.enable": false,
14 |
15 | "eslint.autoFixOnSave": true,
16 | "eslint.validate": [
17 | "javascript",
18 | "javascriptreact",
19 | { "language": "html", "autoFix": true },
20 | { "language": "svelte", "autoFix": true }
21 | ],
22 |
23 | "debug.allowBreakpointsEverywhere": true
24 | }
--------------------------------------------------------------------------------
/example/public/global.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | body {
8 | color: #333;
9 | margin: 0;
10 | padding: 8px;
11 | box-sizing: border-box;
12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13 | }
14 |
15 | a {
16 | color: rgb(0,100,200);
17 | text-decoration: none;
18 | }
19 |
20 | a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | a:visited {
25 | color: rgb(0,80,160);
26 | }
27 |
28 | label {
29 | display: block;
30 | }
31 |
32 | input, button, select, textarea {
33 | font-family: inherit;
34 | font-size: inherit;
35 | padding: 0.4em;
36 | margin: 0 0 0.5em 0;
37 | box-sizing: border-box;
38 | border: 1px solid #ccc;
39 | border-radius: 2px;
40 | }
41 |
42 | input:disabled {
43 | color: #ccc;
44 | }
45 |
46 | input[type="range"] {
47 | height: 0;
48 | }
49 |
50 | button {
51 | background-color: #f4f4f4;
52 | outline: none;
53 | }
54 |
55 | button:active {
56 | background-color: #ddd;
57 | }
58 |
59 | button:focus {
60 | border-color: #666;
61 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-page",
3 | "version": "2.2.1",
4 | "license": "MIT",
5 | "description": "Why not another option for routing in svelte?",
6 | "author": "Christian Kaisermann ",
7 | "repository": "https://github.com/kaisermann/svelte-router",
8 | "main": "./Router.html",
9 | "svelter": "./Router.html",
10 | "keywords": [
11 | "svelte",
12 | "router"
13 | ],
14 | "scripts": {
15 | "lint": "eslint \"*.html\"",
16 | "format": "eslint --fix \"*.html\"",
17 | "prepublishOnly": "npm run format"
18 | },
19 | "devDependencies": {
20 | "babel-eslint": "^10.0.1",
21 | "eslint": "^5.9.0",
22 | "eslint-config-prettier": "^3.3.0",
23 | "eslint-config-standard": "^12.0.0",
24 | "eslint-plugin-html": "^5.0.0",
25 | "eslint-plugin-import": "^2.14.0",
26 | "eslint-plugin-node": "^8.0.0",
27 | "eslint-plugin-prettier": "^3.0.0",
28 | "eslint-plugin-promise": "^4.0.1",
29 | "eslint-plugin-standard": "^4.0.0",
30 | "prettier": "^1.15.2",
31 | "svelte": "^2.15.3"
32 | },
33 | "peerDependencies": {
34 | "svelte": "^2.15.3"
35 | },
36 | "dependencies": {
37 | "page": "^1.11.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3 | const { resolve } = require('path');
4 |
5 | const mode = process.env.NODE_ENV || 'development';
6 | const prod = mode === 'production';
7 |
8 | module.exports = {
9 | entry: {
10 | bundle: ['./src/index.js']
11 | },
12 | resolve: {
13 | extensions: ['.js', '.html'],
14 | alias: {
15 | svelte: resolve(__dirname, 'node_modules', 'svelte')
16 | }
17 | },
18 | output: {
19 | path: resolve(__dirname, 'public'),
20 | filename: '[name].js',
21 | chunkFilename: '[name].[id].js'
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.html$/,
27 | exclude: /node_modules/,
28 | use: {
29 | loader: 'svelte-loader',
30 | options: {
31 | skipIntroByDefault: true,
32 | nestedTransitions: true,
33 | emitCss: true,
34 | hotReload: true
35 | }
36 | }
37 | },
38 | {
39 | test: /\.css$/,
40 | use: [
41 | /**
42 | * MiniCssExtractPlugin doesn't support HMR.
43 | * For developing, use 'style-loader' instead.
44 | * */
45 | prod ? MiniCssExtractPlugin.loader : 'style-loader',
46 | 'css-loader'
47 | ]
48 | }
49 | ]
50 | },
51 | mode,
52 | plugins: [
53 | new MiniCssExtractPlugin({
54 | filename: '[name].css'
55 | })
56 | ],
57 | devtool: prod ? false: 'source-map'
58 | };
59 |
--------------------------------------------------------------------------------
/helpers.js:
--------------------------------------------------------------------------------
1 | /** Creates a route callback with the specified data */
2 | export const getRouteMiddleware = (routePath, routes) => {
3 | if (routes[routePath].constructor !== Object) {
4 | routes[routePath] = { component: routes[routePath] };
5 | }
6 |
7 | const { data = {}, loader } = routes[routePath];
8 | let hasPreloaded = false;
9 |
10 | const loadPreloads = () => {
11 | const { preload } = routes[routePath];
12 | setTimeout(() =>
13 | preload.forEach(preloadRoute => {
14 | const loader = routes[preloadRoute].loader;
15 | if (typeof loader === 'function') {
16 | loader().then(({ default: component }) => {
17 | routes[preloadRoute].component = component;
18 | });
19 | }
20 | }),
21 | );
22 | hasPreloaded = true;
23 | };
24 |
25 | return (ctx, next) => {
26 | const {
27 | components,
28 | params,
29 | state: { path, ...state },
30 | } = ctx;
31 |
32 | const { component, preload } = routes[routePath];
33 |
34 | /** Remove pathregexp path match */
35 | delete params[0];
36 | const routeData = Object.assign({}, data, state, { params });
37 |
38 | /** If no component present and */
39 | if (!component) {
40 | /** If there's a loader function, load it */
41 | if (typeof loader === 'function') {
42 | loader().then(({ default: component }) => {
43 | components.push({ component, data: routeData });
44 | if (preload && !hasPreloaded) {
45 | loadPreloads();
46 | }
47 | next();
48 | });
49 | } else {
50 | throw new Error(
51 | `[svelte-router] No component for route "${routePath}"`,
52 | );
53 | }
54 | return;
55 | }
56 |
57 | components.push({ component, data: routeData });
58 | if (preload && !hasPreloaded) {
59 | loadPreloads();
60 | }
61 | next();
62 | };
63 | };
64 |
65 | export const getSveltedHierarchy = Router => ctx => {
66 | const { components } = ctx;
67 | const props = {
68 | page: null,
69 | context: ctx,
70 | path: ctx.path,
71 | };
72 |
73 | /** Data needs to always be an object or else nesting won't work */
74 | components.reduce((prev, { component, data = {} }) => {
75 | data.page = null;
76 | prev.page = {
77 | child: component,
78 | props: data,
79 | };
80 |
81 | return prev.page.props;
82 | }, props);
83 |
84 | if(Router.store){
85 | Router.store.fire('router:beforeNavigation', ctx);
86 | }
87 |
88 | Router.set(props);
89 |
90 | if(Router.store) {
91 | Router.store.fire('router:navigation', ctx);
92 | }
93 | };
94 |
--------------------------------------------------------------------------------
/Router.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # svelte-page
2 |
3 | > Nested routing with [`page.js`](https://github.com/visionmedia/page.js) for svelte. For svelte v2 only.
4 |
5 | ## How to use
6 |
7 | 1. Build a object with all your routes:
8 |
9 | ```js
10 | export default {
11 | '/': home,
12 | '/other': other,
13 | '/things': {
14 | component: things,
15 | data: { foo: 'A custom property' },
16 | },
17 | '/things/:thing*': thing,
18 | '/things/:thing/subthing/': subthing,
19 | }
20 | ```
21 |
22 | 2. Pass it to the ``
23 |
24 | ```html
25 |
26 |
27 |
38 | ```
39 |
40 | Every page may receive the following properties:
41 |
42 | - Any property passed to the page programatically with the `Router.go(path, { ...props })` or in the `routes.js` definition
43 | - `params` - An object representing all the parameters from the URL
44 | - `page`
45 | - `child` - The nested route component that should be rendered;
46 | - `props` - The props that should be passed to the nested component
47 |
48 | 3. For nested routes, use the ``
49 |
50 | ```html
51 | Here is my sub-page:
52 |
53 |
54 |
55 |
62 | ```
63 |
64 | `` can fire events to allow communication between parent and child pages.
65 |
66 | Available events:
67 |
68 | - `action`
69 | - `response`
70 | - `event`
71 |
72 | **Example:**
73 |
74 | ```html
75 |
76 | ```
77 |
78 | ## Props
79 |
80 | ```html
81 |
86 | ```
87 |
88 | - `routes` - The routes object `(default: undefined)`
89 | - `strict` - If false, match trailling slash `(default: true)`
90 | - `hashbang` - Add `#!` before urls `(default: true)`
91 |
92 | **Dynamic**:
93 |
94 | - `context` - The `page.js` context object
95 | - `path` - The current router path
96 |
97 | ## Slots
98 |
99 | Both `` and `` have the optional `default` slot which is only rendered when the current route isn't associated with any component.
100 |
101 | ```html
102 |
103 | 404 - Route not found
104 |
105 |
106 |
107 | Nested route not found
108 |
109 | ```
110 |
111 | ## Events
112 |
113 | ### Component events
114 |
115 | #### `notFound`
116 |
117 | When a route isn't found, both the `` and `` fire a `notFound` event.
118 |
119 | ```html
120 |
121 |
122 | ```
123 |
124 | #### `change`
125 |
126 | When a route change occurs, the `` component fires a `change` event:
127 |
128 | ```html
129 |
130 | ```
131 |
132 | This event is also fired at the `root` component with the name of `router:change`:
133 |
134 | ```js
135 | export default {
136 | oncreate() {
137 | this.root.on('router:change', context => {
138 | console.log('New path:', context.path)
139 | })
140 | },
141 | }
142 | ```
143 |
144 | ---
145 |
146 | The `` adds itself to the `root` component as a `router` property, so it's also possible to get the context by observing its lifecycle:
147 |
148 | ```html
149 |
150 |
159 | ```
160 |
161 | ## Static Methods
162 |
163 | ```html
164 |
165 |
179 | ```
180 |
181 | ## Example
182 |
183 | **App.html**
184 |
185 | ```html
186 |
187 |
188 |
218 | ```
219 |
220 | **AboutPersonTemplate.html**
221 |
222 | ```html
223 |
224 |
225 |
232 | ```
233 |
234 | For more examples, please check the [`example/src/routes.js`](https://github.com/kaisermann/svelte-router/blob/master/example/src/routes.js) file.
235 |
236 | ## Credits and inspirations
237 |
238 | - [tivac/svelte-routing](https://github.com/tivac/svelte-routing/) :grin:
239 |
--------------------------------------------------------------------------------