├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── LICENSE ├── README.md ├── package.json └── src ├── components ├── DefaultRoute.svelte ├── Link.svelte ├── Route.svelte └── Router.svelte ├── history.js ├── index.js ├── link.js ├── path.js └── store.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "root": true, 3 | "env": { 4 | "es6": true, 5 | "browser": true, 6 | }, 7 | "plugins": [ 8 | "svelte3", 9 | ], 10 | "parser": "babel-eslint", 11 | "extends": [ 12 | "eslint:recommended", 13 | ], 14 | "rules": { 15 | "space-infix-ops": "error", 16 | "keyword-spacing": "error", 17 | "comma-spacing": "error", 18 | "arrow-spacing": "error", 19 | "newline-before-return": "error", 20 | "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1}], 21 | "space-in-parens": ["error", "never"], 22 | "indent": ["error", 2, {"SwitchCase": 1}], 23 | "no-multi-spaces": "error", 24 | "no-trailing-spaces": "error", 25 | "brace-style": ["error"], 26 | "curly": "error", 27 | "quotes": ["error", "single"], 28 | "key-spacing": [2, { beforeColon: false, afterColon: true }], 29 | "object-curly-spacing": ["error", "always", { "objectsInObjects": false }], 30 | "semi": ["error", "never"], 31 | "comma-dangle": "error", 32 | "array-bracket-spacing": ["error", "never"], 33 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 34 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" 35 | }, 36 | "overrides": [ 37 | { 38 | files: ['**/*.svelte'], 39 | processor: 'svelte3/svelte3' 40 | }, 41 | ], 42 | "parserOptions": { 43 | "sourceType": "module", 44 | "ecmaVersion": 2019, 45 | }, 46 | "globals": { 47 | "NODE_ENV": true, 48 | "API_URL": true, 49 | "__DEV__": true 50 | } 51 | } 52 | module.exports = { 53 | "root": true, 54 | "env": { 55 | "es6": true, 56 | "browser": true, 57 | }, 58 | "plugins": [ 59 | "svelte3", 60 | ], 61 | "parser": "babel-eslint", 62 | "extends": [ 63 | "eslint:recommended", 64 | ], 65 | "rules": { 66 | "space-infix-ops": "error", 67 | "keyword-spacing": "error", 68 | "comma-spacing": "error", 69 | "arrow-spacing": "error", 70 | "newline-before-return": "error", 71 | "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1}], 72 | "space-in-parens": ["error", "never"], 73 | "indent": ["error", 2, {"SwitchCase": 1}], 74 | "no-multi-spaces": "error", 75 | "no-trailing-spaces": "error", 76 | "brace-style": ["error"], 77 | "curly": "error", 78 | "quotes": ["error", "single"], 79 | "key-spacing": [2, { beforeColon: false, afterColon: true }], 80 | "object-curly-spacing": ["error", "always", { "objectsInObjects": false }], 81 | "semi": ["error", "never"], 82 | "comma-dangle": "error", 83 | "array-bracket-spacing": ["error", "never"], 84 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 85 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" 86 | }, 87 | "overrides": [ 88 | { 89 | files: ['**/*.svelte'], 90 | processor: 'svelte3/svelte3' 91 | }, 92 | ], 93 | "parserOptions": { 94 | "sourceType": "module", 95 | "ecmaVersion": 2019, 96 | }, 97 | "globals": { 98 | "NODE_ENV": true, 99 | "API_URL": true, 100 | "__DEV__": true 101 | } 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: '00:00' 8 | commit-message: 9 | prefix: chore 10 | include: scope 11 | versioning-strategy: "widen" 12 | reviewers: 13 | - "AntoineLelaisant" 14 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '45 21 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | yarn.lock 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 svelters 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/svelters-router.svg)](https://badge.fury.io/js/svelters-router) 2 | 3 | # Svelters Router 4 | 5 | ## Installation 6 | 7 | ### npm 8 | 9 | ``` 10 | npm install --save svelters-router 11 | ``` 12 | 13 | ### Yarn 14 | 15 | ``` 16 | yarn add svelters-router 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Declare route with Route component 22 | 23 | ```svelte 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |

Hi!

37 |

Routes can also have child content!

38 |
39 |
40 | ``` 41 | 42 | ### Declare route programmatically 43 | 44 | ```svelte 45 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | :bulb: Note that you can combine the two ways of declaring routes. 69 | 70 | ### Get route parameters in component 71 | 72 | Route parameters are compiled by `svelters-router` and given to defined component through a prop named `routeParams`. 73 | 74 | #### Your Router: 75 | 76 | ```svelte 77 | 81 | 82 | 83 | 84 | 85 | ``` 86 | 87 | #### Your component `Profile.svelte`: 88 | 89 | ```svelte 90 | 95 | 96 | 100 | 101 | ``` 102 | 103 | ### Links 104 | 105 | #### The link action 106 | 107 | ```svelte 108 | 111 | 112 |

I wonder how simple it is to create a link ?

113 | 114 | Just click here! 115 | ``` 116 | 117 | #### The Link component 118 | 119 | ```svelte 120 | 123 | 124 |

I wonder how simple it is to create a link ?

125 | 126 | Just click here! 127 | ``` 128 | 129 | ### To navigate somewhere programmatically 130 | 131 | ```svelte 132 | 142 | 143 |

I'm a rocket man!

144 | 145 | 146 | ``` 147 | 148 | ## Advanced 149 | 150 | ### Default route 151 | 152 | You can display a default component when no any route matches the current location: 153 | 154 | ```svelte 155 | 160 | 161 | 162 | 163 | 164 | 165 | ``` 166 | 167 | ### Named routes 168 | 169 | It's sometime more convenient to rely on route name instead of its path to generate links. 170 | 171 | `svelters-router` provides all the stuff you need! 172 | 173 | #### Route declaration 174 | 175 | ```svelte 176 | 190 | 191 | 192 | 193 | 194 | 195 | ``` 196 | 197 | #### Link to a named route 198 | 199 | ```svelte 200 | 205 | 206 | See the user {userId} 207 | ``` 208 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelters-router", 3 | "version": "0.1.0", 4 | "description": "A lightweight router component for svelte", 5 | "keywords": [ 6 | "svelte", 7 | "router", 8 | "routing" 9 | ], 10 | "main": "src/index.js", 11 | "author": "svelters", 12 | "license": "MIT", 13 | "repository": "github:svelters/svelters-router", 14 | "scripts": { 15 | "lint": "eslint_d src/ --ext .js --ext .svelte" 16 | }, 17 | "peerDependencies": { 18 | "svelte": "^3.12.1" 19 | }, 20 | "dependencies": { 21 | "path-to-regexp": "^3.1.0" 22 | }, 23 | "devDependencies": { 24 | "babel-eslint": "^10.0.3", 25 | "eslint-plugin-svelte3": "^2.7.3", 26 | "eslint_d": "^8.0.0", 27 | "svelte": "^3.12.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/DefaultRoute.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | {#if !matchAny($location.pathname, $routes) } 9 | {#if component !== null } 10 | 11 | {:else} 12 | 13 | {/if} 14 | {/if} 15 | -------------------------------------------------------------------------------- /src/components/Link.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/Route.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | {#if matchPath($location.pathname, path) } 24 | {#if component !== null } 25 | 26 | {:else} 27 | 28 | {/if} 29 | {/if} 30 | -------------------------------------------------------------------------------- /src/components/Router.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#each routes as route} 11 | 16 | {/each} 17 | 18 | -------------------------------------------------------------------------------- /src/history.js: -------------------------------------------------------------------------------- 1 | import { location } from './store' 2 | 3 | export const push = path => { 4 | window.history.pushState(null, null, path) 5 | location.update(() => window.location) 6 | } 7 | 8 | export const replace = path => { 9 | window.history.replaceState(null, null, path) 10 | location.update(() => window.location) 11 | } 12 | 13 | export const goBack = () => { 14 | window.history.back() 15 | location.update(() => window.location) 16 | } 17 | 18 | export const goForward = () => { 19 | window.history.forward() 20 | location.update(() => window.location) 21 | } 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as Router } from './components/Router.svelte' 2 | export { default as Route } from './components/Route.svelte' 3 | export { default as DefaultRoute } from './components/DefaultRoute.svelte' 4 | export { default as Link } from './components/Link.svelte' 5 | export { push, replace, goBack, goForward } from './history' 6 | export { default as link } from './link' 7 | -------------------------------------------------------------------------------- /src/link.js: -------------------------------------------------------------------------------- 1 | import { push } from './history' 2 | 3 | export default function(node) { 4 | node.onclick = e => { 5 | e.preventDefault() 6 | push(e.target.attributes.getNamedItem('href').value) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/path.js: -------------------------------------------------------------------------------- 1 | import pathToRegexp from 'path-to-regexp' 2 | 3 | export const matchPath = (location, path) => { 4 | const regexp = pathToRegexp(path) 5 | const match = regexp.exec(location) 6 | 7 | return match !== null 8 | } 9 | 10 | export const matchAny = (location, routes) => { 11 | for (const route of routes) { 12 | if (route.path !== null && matchPath(location, route.path)) { 13 | return true 14 | } 15 | } 16 | 17 | return false 18 | } 19 | 20 | export const parseParameters = (location, path) => { 21 | const keys = [] 22 | const regexp = pathToRegexp(path, keys) 23 | /* eslint-disable no-unused-vars */ 24 | const [url, ...values] = regexp.exec(location) 25 | 26 | return keys.reduce((memo, key, index) => { 27 | memo[key.name] = values[index] 28 | 29 | return memo 30 | }, {}) 31 | } 32 | 33 | export const compileParameters = (path, parameters) => { 34 | const toPath = pathToRegexp.compile(path) 35 | 36 | return toPath(parameters) 37 | } 38 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store' 2 | 3 | export const location = writable(null) 4 | export const routes = writable([]) 5 | 6 | export const registerRoute = ({ name, path, component }) => 7 | routes.update(routes => [...routes, { 8 | name: name ? name : path, 9 | path, 10 | component 11 | }]) 12 | 13 | export const unregisterRoute = ({ name }) => { 14 | routes.update(routes => { 15 | const index = routes.findIndex(route => { 16 | return route.name === name 17 | }) 18 | routes.splice(index, 1) 19 | 20 | return routes 21 | }) 22 | } 23 | --------------------------------------------------------------------------------