Skip to content

Commit 97a1d3f

Browse files
committed
Merge remote-tracking branch 'router_js/master' into rebase-merge
2 parents 2a5515d + 4e5506a commit 97a1d3f

44 files changed

Lines changed: 12597 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/router_js/.ember-cli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"testPort": 0
3+
}

packages/router_js/.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/

packages/router_js/.eslintrc.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
'use strict';
2+
3+
module.exports = {
4+
root: true,
5+
parserOptions: {
6+
ecmaVersion: 2018,
7+
sourceType: 'module',
8+
},
9+
plugins: ['prettier'],
10+
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
11+
env: {
12+
browser: true,
13+
},
14+
rules: {},
15+
overrides: [
16+
// typescript files
17+
{
18+
files: ['**/*.ts'],
19+
parser: '@typescript-eslint/parser',
20+
parserOptions: {
21+
project: './tsconfig.json',
22+
23+
// allows eslint from any dir
24+
tsconfigRootDir: __dirname,
25+
},
26+
plugins: ['@typescript-eslint'],
27+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
28+
settings: {
29+
node: {
30+
tryExtensions: ['.js', '.json', '.d.ts', '.ts'],
31+
32+
convertPath: [
33+
{
34+
include: ['lib/**/*.ts'],
35+
replace: ['^lib/(.+)\\.ts$', 'dist/$1.js'],
36+
},
37+
],
38+
},
39+
},
40+
rules: {
41+
'@typescript-eslint/no-empty-function': 'off',
42+
'@typescript-eslint/no-unused-vars': [
43+
'error',
44+
{
45+
args: 'none',
46+
},
47+
],
48+
49+
// TODO: stop disabling these rules
50+
'prefer-const': 'off',
51+
'no-prototype-builtins': 'off',
52+
'@typescript-eslint/ban-types': [
53+
'error',
54+
{
55+
extendDefaults: true,
56+
types: {
57+
'{}': false,
58+
},
59+
},
60+
],
61+
'@typescript-eslint/no-non-null-assertion': 'off',
62+
},
63+
},
64+
65+
// tests
66+
{
67+
files: ['tests/**/*.[jt]s'],
68+
env: {
69+
qunit: true,
70+
},
71+
},
72+
73+
// node files
74+
{
75+
files: ['.eslintrc.js', 'ember-cli-build.js', 'testem.js', 'server/**/*.js'],
76+
parserOptions: {
77+
sourceType: 'script',
78+
},
79+
env: {
80+
browser: false,
81+
node: true,
82+
},
83+
},
84+
],
85+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- 'v*' # older version branches
8+
tags:
9+
- '*'
10+
pull_request: {}
11+
schedule:
12+
- cron: '0 6 * * 0' # weekly, on sundays
13+
14+
jobs:
15+
test:
16+
name: Tests
17+
runs-on: 'ubuntu-latest'
18+
19+
strategy:
20+
matrix:
21+
node: ['12', '14', '16', '17']
22+
23+
steps:
24+
- uses: actions/checkout@v1
25+
- uses: volta-cli/action@v1
26+
with:
27+
node-version: ${{ matrix.node }}
28+
- name: install dependencies
29+
run: yarn
30+
- name: lint
31+
run: yarn lint
32+
- name: test
33+
run: yarn test

packages/router_js/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/.bundle
2+
/dist
3+
/tmp
4+
/node_modules
5+
6+
dist/tests
7+
.eslintcache

packages/router_js/.jshintrc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"predef": [
3+
"QUnit"
4+
],
5+
6+
"node" : false,
7+
"browser" : true,
8+
9+
"boss" : true,
10+
"esnext" : true,
11+
"curly": false,
12+
"debug": false,
13+
"devel": false,
14+
"eqeqeq": true,
15+
"evil": true,
16+
"forin": false,
17+
"immed": false,
18+
"laxbreak": false,
19+
"newcap": true,
20+
"noarg": true,
21+
"noempty": false,
22+
"nonew": false,
23+
"nomen": false,
24+
"onevar": false,
25+
"plusplus": false,
26+
"regexp": false,
27+
"undef": true,
28+
"unused": true,
29+
"sub": true,
30+
"strict": false,
31+
"white": false,
32+
"eqnull": true
33+
}

packages/router_js/.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"singleQuote": true,
3+
"trailingComma": "es5",
4+
"printWidth": 100,
5+
"endOfLine": "auto"
6+
}

packages/router_js/ARCHITECTURE.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# [router.js](https://github.com/tildeio/router.js) Architecture
2+
3+
This is a guide to `router.js`'s internals.
4+
5+
`router.js` is a stand-alone microlibrary for client-side routing in JavaScript
6+
applications. It's notably used by the [Ember.js Router][Ember Router].
7+
8+
9+
## Scope of `router.js` and its Dependencies
10+
11+
Ember.js's router consumes `router.js`, which in turn consumes
12+
[route-recognizer](https://github.com/tildeio/route-recognizer).
13+
14+
The division of responsibilities of these three libs is as follows:
15+
16+
### `route-recognizer`
17+
18+
`route-recognizer` is an engine for both parsing/generating URLs into/from
19+
parameters.
20+
21+
It can take a URL like `articles/123/comments` and parse out the parameter
22+
`{ article_id: "123" }`.
23+
24+
It can take `{ article_id: "123" }` and a route descriptor like
25+
`articles/:article_id/comments` and generate `articles/123/comments`.
26+
27+
### `router.js`
28+
29+
`router.js` adds the concept of transitions to `route-recognizer`'s
30+
URL parsing engine.
31+
32+
Transitions can be URL-initiated (via browser navigation) or can be
33+
directly initiated via route name
34+
(e.g. `transitionTo('articles', articleObject)`).
35+
36+
`router.js` resolves all of the model objects that needed to be loaded
37+
in order to enter a route.
38+
39+
e.g. to navigate to `articles/123/comments/2`, a promise for both the
40+
`article` and `comments` routes need to be fulfilled.
41+
42+
### Ember Router
43+
44+
The [Ember Router][] adds a DSL for declaring your app's routes on top of
45+
`router.js`. It defines the API for the `Ember.Route` class that handles
46+
intelligent defaults, rendering templates, and loading data into controllers.
47+
48+
49+
## History
50+
51+
`router.js` has gone through a few iterations between 2013 and 2014:
52+
53+
* July of 2013 – `router.js` adds promise-awareness.
54+
* Jan 2014 – refactored `router.js`'s primitives to handle corner cases.
55+
56+
### Corner Cases
57+
58+
1. Avoid running `model` hooks (responsible for fetching data needed to enter a
59+
route) for shared parent routes.
60+
61+
2. Avoid running model hooks when redirecting in the middle of another transition.
62+
e.g. during a transition to `articles/123/comments/2` you redirect to
63+
`articles/123/comments/3` after resolving Article 123 and you want to
64+
avoid re-running the hooks to load Article 123 again.
65+
66+
3. Handle two different approaches to transitions:
67+
68+
* URL based (where a URL is parsed into route parameters that are used to
69+
load all the data needed to enter a route (e.g. `{ article_id: 123 }`).
70+
71+
* direct named transition-based, where a route name and any context objects
72+
are provided (e.g. `transitionTo('article', articleObject)`), and the
73+
provided context object(s) might be promises that can't be serialized
74+
into URL params until they've fulfilled.
75+
76+
77+
## Classes
78+
79+
### `HandlerInfo`
80+
81+
A `HandlerInfo` is an object that describes the state of a route handler.
82+
83+
For example, the `foo/bar` URL likely breaks down into a hierarchy of two
84+
handlers: the `foo` handler and the `bar` handler. A "handler" is just an
85+
object that defines hooks that `router.js` will call in the course of a
86+
transition (e.g. `model`, `beforeModel`, `setup`, etc.).
87+
88+
In Ember.js, handlers are instances of `Ember.Route`.
89+
90+
A `HandlerInfo` instance contains that handler's model (e.g. `articleObject`),
91+
or the URL parameters associated with the current state of that handler
92+
(e.g. `{ article_id: '123' }`).
93+
94+
Because `router.js` allows you to reuse handlers between different routes and
95+
route hierarchies, we need `HandlerInfo`s to describe the state of each route
96+
hierarchy.
97+
98+
`HandlerInfo` is a top-level class with 3 subclasses:
99+
100+
#### `UnresolvedHandlerInfoByParam`
101+
`UnresolvedHandlerInfoByParam` has the URL params stored on it which it can use
102+
to resolve itself (by calling the handler's `beforeModel`/`model`/`afterModel`
103+
hooks).
104+
105+
#### `UnresolvedHandlerInfoByObject`
106+
`UnresolvedHandlerInfoByObject` has a context object, but no URL params.
107+
It can use the context to resolve itself and serialize into URL params once
108+
the context object is fulfilled.
109+
110+
#### `ResolvedHandlerInfo`
111+
`ResolvedHandlerInfo` has calculated its URL params and resolved context/model
112+
object.
113+
114+
#### Public API
115+
`HandlerInfo` has just a `resolve` method which fires all `model` hooks and
116+
ultimately resolves to a `ResolvedHandlerInfo` object.
117+
118+
The `ResolvedHandlerInfo`'s `resolve` method just returns a promise that
119+
fulfills with itself.
120+
121+
### `TransitionState`
122+
123+
The `TransitionState` object consists of an array of `HandlerInfo`s
124+
(though more might be added to it; not sure yet).
125+
126+
#### Public API
127+
It too has a public API consisting only of a `resolve` method that
128+
will loop through all of its `HandlerInfo`s, swapping unresolved
129+
`HandlerInfo`s with `ResolvedHandlerInfo`s as it goes.
130+
131+
Instances of `Router` and `Transition` contain `TransitionState`
132+
properties, which is useful since, depending on whether or not there is
133+
a currently active transition, the "starting point" of a transition
134+
might be the router's current hierarchy of `ResolvedHandlerInfo`s, or it
135+
might be a transition's hierarchy of `ResolvedHandlerInfo`s mixed with
136+
unresolved HandlerInfos.
137+
138+
### `TransitionIntent`
139+
140+
A `TransitionIntent` describes an attempt to transition.
141+
142+
via URL
143+
or by named transition (via its subclasses `URLTransitionIntent` and
144+
`NamedTransitionIntent`).
145+
146+
#### `URLTransitionIntent`
147+
A `URLTransitionIntent` has a `url` property.
148+
149+
#### `NamedTransitionIntent`
150+
A `NamedTransitionIntent` has a target route `name` and `contexts` array
151+
property.
152+
153+
This class defines only one method `applyToState` which takes an instance of
154+
`TransitionState` and plays this `TransitionIntent` on top of it to generate
155+
and return a new instance of `TransitionState` that contains a combination of
156+
resolved and unresolved `HandlerInfo`s.
157+
158+
`TransitionIntent`s don't care whether the provided state comes from a router
159+
or a currently active transition; whatever you provide it, both subclasses of
160+
`TransitionIntent`s are smart enough to spit out a `TransitionState`
161+
containing `HandlerInfo`s that still need to be resolved in order to complete
162+
a transition.
163+
164+
Much of the messy logic that used to live in `paramsForHandler`/`getMatchPoint`
165+
now live way less messily in the `applyToState` methods.
166+
167+
This makes it easy to detect corner cases like no-op transitions – if the
168+
returned `TransitionState` consists entirely of `ResolvedHandlerInfo`s, there's
169+
no need to fire off a transition. It simplifies things like redirecting into a
170+
child route without winding up in some infinite loop on the parent route hook
171+
that's doing the redirecting.
172+
173+
This simplifies `Transition#retry`; to retry a transition, provide its `intent`
174+
property to the transitioning function used by `transitionTo`, `handleURL`.
175+
`handle` function will make the right choice as to the correct `TransitionState`
176+
to pass to the intent's `applyToState` method.
177+
178+
This approach is used to implement `Router#isActive`. You can determine if a
179+
destination route is active by constructing a `TransitionIntent`, applying it
180+
to the router's current state, and returning `true` if all of the
181+
`HandlerInfo`s are already resolved.
182+
183+
[Ember Router]: http://emberjs.com/guides/routing/

0 commit comments

Comments
 (0)