Skip to content

Commit 2644d95

Browse files
author
Robert Jackson
committed
Make continuation tied to Transition#isAborted explicitly.
1 parent e8bc95f commit 2644d95

File tree

5 files changed

+60
-98
lines changed

5 files changed

+60
-98
lines changed

lib/router/route-info.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ export interface Route {
4040
buildRouteInfoMetadata?(): unknown;
4141
}
4242

43-
export type Continuation = () => boolean;
44-
4543
export interface RouteInfo {
4644
readonly name: string;
4745
readonly parent: RouteInfo | RouteInfoWithAttributes | null;
@@ -222,16 +220,13 @@ export default class InternalRouteInfo<T extends Route> {
222220
return this.params || {};
223221
}
224222

225-
resolve(
226-
shouldContinue: Continuation,
227-
transition: InternalTransition<T>
228-
): Promise<ResolvedRouteInfo<T>> {
223+
resolve(transition: InternalTransition<T>): Promise<ResolvedRouteInfo<T>> {
229224
return Promise.resolve(this.routePromise)
230-
.then((route: Route) => this.checkForAbort(shouldContinue, route))
225+
.then((route: Route) => this.checkForAbort(transition, route))
231226
.then(() => this.runBeforeModelHook(transition))
232-
.then(() => this.checkForAbort(shouldContinue, null))
227+
.then(() => this.checkForAbort(transition, null))
233228
.then(() => this.getModel(transition))
234-
.then((resolvedModel) => this.checkForAbort(shouldContinue, resolvedModel))
229+
.then((resolvedModel) => this.checkForAbort(transition, resolvedModel))
235230
.then((resolvedModel) => this.runAfterModelHook(transition, resolvedModel))
236231
.then((resolvedModel) => this.becomeResolved(transition, resolvedModel));
237232
}
@@ -375,8 +370,10 @@ export default class InternalRouteInfo<T extends Route> {
375370
});
376371
}
377372

378-
private checkForAbort<T>(shouldContinue: Continuation, value: T) {
379-
shouldContinue();
373+
private checkForAbort<U>(transition: InternalTransition<T>, value: U) {
374+
if (transition.isAborted) {
375+
throw new Error('Transition aborted');
376+
}
380377

381378
return value;
382379
}
@@ -427,10 +424,7 @@ export class ResolvedRouteInfo<T extends Route> extends InternalRouteInfo<T> {
427424
this.context = context;
428425
}
429426

430-
resolve(
431-
_shouldContinue: Continuation,
432-
transition: InternalTransition<T>
433-
): Promise<InternalRouteInfo<T>> {
427+
resolve(transition: InternalTransition<T>): Promise<InternalRouteInfo<T>> {
434428
// A ResolvedRouteInfo just resolved with itself.
435429
if (transition && transition.resolvedModels) {
436430
transition.resolvedModels[this.name] = this.context as Dict<unknown>;

lib/router/transition-state.ts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Promise } from 'rsvp';
22
import { Dict } from './core';
3-
import InternalRouteInfo, { Continuation, Route, ResolvedRouteInfo } from './route-info';
3+
import InternalRouteInfo, { Route, ResolvedRouteInfo } from './route-info';
44
import Transition from './transition';
55
import { forEach, promiseLabel } from './utils';
66

@@ -25,7 +25,7 @@ export default class TransitionState<T extends Route> {
2525
return promiseLabel("'" + targetName + "': " + label);
2626
}
2727

28-
resolve(shouldContinue: Continuation, transition: Transition<T>): Promise<TransitionState<T>> {
28+
resolve(transition: Transition<T>): Promise<TransitionState<T>> {
2929
// First, calculate params for this state. This is useful
3030
// information to provide to the various route hooks.
3131
let params = this.params;
@@ -37,7 +37,6 @@ export default class TransitionState<T extends Route> {
3737
transition.resolveIndex = 0;
3838

3939
let currentState = this;
40-
let wasAborted = false;
4140

4241
// The prelude RSVP.resolve() asyncs us into the promise land.
4342
return Promise.resolve(null, this.promiseLabel('Start transition'))
@@ -47,18 +46,6 @@ export default class TransitionState<T extends Route> {
4746
return currentState;
4847
});
4948

50-
function innerShouldContinue() {
51-
if (shouldContinue()) {
52-
return true;
53-
} else {
54-
// We distinguish between errors that occurred
55-
// during resolution (e.g. before"Model/model/afterModel),
56-
// and aborts due to a rejecting promise from shouldContinue().
57-
wasAborted = true;
58-
throw new Error('Transition aborted');
59-
}
60-
}
61-
6249
function handleError(error: Error): never {
6350
// This is the only possible
6451
// reject value of TransitionState#resolve
@@ -68,6 +55,8 @@ export default class TransitionState<T extends Route> {
6855
? routeInfos.length - 1
6956
: transition.resolveIndex;
7057

58+
let wasAborted = transition.isAborted;
59+
7160
throw new TransitionError(
7261
error,
7362
currentState.routeInfos[errorHandlerIndex].route!,
@@ -98,9 +87,11 @@ export default class TransitionState<T extends Route> {
9887

9988
// Proceed after ensuring that the redirect hook
10089
// didn't abort this transition by transitioning elsewhere.
101-
if (innerShouldContinue()) {
102-
return resolveOneRouteInfo();
90+
if (transition.isAborted) {
91+
throw new Error('Transition aborted');
10392
}
93+
94+
return resolveOneRouteInfo();
10495
}
10596

10697
function resolveOneRouteInfo(): void | Promise<void> {
@@ -113,7 +104,7 @@ export default class TransitionState<T extends Route> {
113104
let routeInfo = currentState.routeInfos[transition.resolveIndex];
114105

115106
return routeInfo
116-
.resolve(innerShouldContinue, transition)
107+
.resolve(transition)
117108
.then(proceed, null, currentState.promiseLabel('Proceed'));
118109
}
119110
}

lib/router/transition.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,11 @@ export default class Transition<T extends Route> implements Partial<Promise<T>>
169169
}
170170

171171
this.sequence = router.currentSequence++;
172-
this.promise = state
173-
.resolve(() => !this.isAborted, this)
174-
.catch((result: TransitionError) => {
175-
return Promise.reject(this.router.transitionDidError(result, this));
176-
}, promiseLabel('Handle Abort'));
172+
this.promise = state.resolve(this).catch((result: TransitionError) => {
173+
let error = this.router.transitionDidError(result, this);
174+
175+
throw error;
176+
}, promiseLabel('Handle Abort'));
177177
} else {
178178
this.promise = Promise.resolve(this[STATE_SYMBOL]!);
179179
this[PARAMS_SYMBOL] = {};

tests/route_info_test.ts

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ test('ResolvedRouteInfo resolve to themselves', function (assert) {
1818
let router = new TestRouter();
1919
let routeInfo = new ResolvedRouteInfo(router, 'foo', [], {}, createHandler('empty'));
2020
let intent = new URLTransitionIntent(router, 'foo');
21-
routeInfo
22-
.resolve(() => false, new InternalTransition(router, intent, undefined))
23-
.then(function (resolvedRouteInfo) {
24-
assert.equal(routeInfo, resolvedRouteInfo);
25-
});
21+
22+
let transition = new InternalTransition(router, intent, undefined);
23+
24+
routeInfo.resolve(transition).then(function (resolvedRouteInfo) {
25+
assert.equal(routeInfo, resolvedRouteInfo);
26+
});
2627
});
2728

2829
test('UnresolvedRouteInfoByParam defaults params to {}', function (assert) {
@@ -35,17 +36,14 @@ test('UnresolvedRouteInfoByParam defaults params to {}', function (assert) {
3536
});
3637

3738
test('RouteInfo can be aborted mid-resolve', function (assert) {
38-
assert.expect(2);
39+
assert.expect(1);
3940

4041
let routeInfo = createHandlerInfo('stub');
4142

42-
function abortResolve(): boolean {
43-
assert.ok(true, 'abort was called');
44-
45-
throw new Error('foo');
46-
}
43+
let transition = {} as Transition;
44+
transition.isAborted = true;
4745

48-
routeInfo.resolve(abortResolve, {} as Transition).catch(function (error: Error) {
46+
routeInfo.resolve(transition).catch(function (error: Error) {
4947
assert.equal(error, 'LOL');
5048
});
5149
});
@@ -54,17 +52,15 @@ test('RouteInfo#resolve resolves with a ResolvedRouteInfo', function (assert) {
5452
assert.expect(1);
5553

5654
let routeInfo = createHandlerInfo('stub');
57-
routeInfo
58-
.resolve(() => false, {} as Transition)
59-
.then(function (resolvedRouteInfo: RouteInfo<Route>) {
60-
assert.ok(resolvedRouteInfo instanceof ResolvedRouteInfo);
61-
});
55+
routeInfo.resolve({} as Transition).then(function (resolvedRouteInfo: RouteInfo<Route>) {
56+
assert.ok(resolvedRouteInfo instanceof ResolvedRouteInfo);
57+
});
6258
});
6359

6460
test('RouteInfo#resolve runs beforeModel hook on handler', function (assert) {
6561
assert.expect(1);
6662

67-
let transition = {};
63+
let transition = {} as Transition;
6864

6965
let routeInfo = createHandlerInfo('stub', {
7066
route: createHandler('stub', {
@@ -78,27 +74,27 @@ test('RouteInfo#resolve runs beforeModel hook on handler', function (assert) {
7874
}),
7975
});
8076

81-
routeInfo.resolve(() => true, transition as Transition);
77+
routeInfo.resolve(transition);
8278
});
8379

8480
test('RouteInfo#resolve runs getModel hook', function (assert) {
8581
assert.expect(1);
8682

87-
let transition = {};
83+
let transition = {} as Transition;
8884

8985
let routeInfo = createHandlerInfo('stub', {
9086
getModel(payload: Dict<unknown>) {
9187
assert.equal(payload, transition);
9288
},
9389
});
9490

95-
routeInfo.resolve(() => true, transition as Transition);
91+
routeInfo.resolve(transition);
9692
});
9793

9894
test('RouteInfo#resolve runs afterModel hook on handler', function (assert) {
9995
assert.expect(3);
10096

101-
let transition = {};
97+
let transition = {} as Transition;
10298
let model = {};
10399

104100
let routeInfo = createHandlerInfo('foo', {
@@ -114,18 +110,16 @@ test('RouteInfo#resolve runs afterModel hook on handler', function (assert) {
114110
},
115111
});
116112

117-
routeInfo
118-
.resolve(() => true, transition as Transition)
119-
.then(function (resolvedRouteInfo: RouteInfo<Route>) {
120-
assert.equal(resolvedRouteInfo.context, model, 'RouteInfo resolved with correct model');
121-
});
113+
routeInfo.resolve(transition).then(function (resolvedRouteInfo: RouteInfo<Route>) {
114+
assert.equal(resolvedRouteInfo.context, model, 'RouteInfo resolved with correct model');
115+
});
122116
});
123117

124118
test('UnresolvedRouteInfoByParam gets its model hook called', function (assert) {
125119
assert.expect(2);
126120
let router = new TestRouter();
127121

128-
let transition = {};
122+
let transition = {} as Transition;
129123

130124
let routeInfo = new UnresolvedRouteInfoByParam(
131125
router,
@@ -143,7 +137,7 @@ test('UnresolvedRouteInfoByParam gets its model hook called', function (assert)
143137
})
144138
);
145139

146-
routeInfo.resolve(() => true, transition as Transition);
140+
routeInfo.resolve(transition);
147141
});
148142

149143
test('UnresolvedRouteInfoByObject does NOT get its model hook called', function (assert) {
@@ -163,12 +157,10 @@ test('UnresolvedRouteInfoByObject does NOT get its model hook called', function
163157
resolve({ name: 'dorkletons' })
164158
);
165159

166-
routeInfo
167-
.resolve(() => true, {} as Transition)
168-
.then(function (resolvedRouteInfo: RouteInfo<Route>) {
169-
// @ts-ignore
170-
assert.equal(resolvedRouteInfo.context!.name, 'dorkletons');
171-
});
160+
routeInfo.resolve({} as Transition).then(function (resolvedRouteInfo: RouteInfo<Route>) {
161+
// @ts-ignore
162+
assert.equal(resolvedRouteInfo.context!.name, 'dorkletons');
163+
});
172164
});
173165

174166
test('RouteInfo.find', function (assert) {

tests/transition_state_test.ts

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { Transition } from 'router';
22
import { Dict } from 'router/core';
3-
import {
4-
Continuation,
5-
Route,
6-
UnresolvedRouteInfoByObject,
7-
UnresolvedRouteInfoByParam,
8-
} from 'router/route-info';
3+
import { Route, UnresolvedRouteInfoByObject, UnresolvedRouteInfoByParam } from 'router/route-info';
94
import TransitionState, { TransitionError } from 'router/transition-state';
105
import { Promise, resolve } from 'rsvp';
116
import {
@@ -25,7 +20,7 @@ test('it starts off with default state', function (assert) {
2520
});
2621

2722
test("#resolve delegates to handleInfo objects' resolve()", function (assert) {
28-
assert.expect(7);
23+
assert.expect(3);
2924

3025
let state = new TransitionState();
3126

@@ -35,29 +30,22 @@ test("#resolve delegates to handleInfo objects' resolve()", function (assert) {
3530

3631
state.routeInfos = [
3732
createHandlerInfo('one', {
38-
resolve: function (shouldContinue: Continuation) {
33+
resolve: function () {
3934
++counter;
4035
assert.equal(counter, 1);
41-
shouldContinue();
4236
return resolve(resolvedHandlerInfos[0]);
4337
},
4438
}),
4539
createHandlerInfo('two', {
46-
resolve: function (shouldContinue: Continuation) {
40+
resolve: function () {
4741
++counter;
4842
assert.equal(counter, 2);
49-
shouldContinue();
5043
return resolve(resolvedHandlerInfos[1]);
5144
},
5245
}),
5346
];
5447

55-
function keepGoing() {
56-
assert.ok(true, 'continuation function was called');
57-
return true;
58-
}
59-
60-
state.resolve(keepGoing, {} as Transition).then(function (result: TransitionState<Route>) {
48+
state.resolve({} as Transition).then(function (result: TransitionState<Route>) {
6149
assert.deepEqual(result.routeInfos, resolvedHandlerInfos);
6250
});
6351
});
@@ -69,9 +57,7 @@ test('State resolution can be halted', function (assert) {
6957

7058
state.routeInfos = [
7159
createHandlerInfo('one', {
72-
resolve: function (shouldContinue: Continuation) {
73-
return shouldContinue();
74-
},
60+
resolve: function () {},
7561
}),
7662
createHandlerInfo('two', {
7763
resolve: function () {
@@ -80,11 +66,10 @@ test('State resolution can be halted', function (assert) {
8066
}),
8167
];
8268

83-
function keepGoing() {
84-
return false;
85-
}
69+
let fakeTransition = {} as Transition;
70+
fakeTransition.isAborted = true;
8671

87-
state.resolve(keepGoing, {} as Transition).catch(function (reason: TransitionError) {
72+
state.resolve(fakeTransition).catch(function (reason: TransitionError) {
8873
assert.ok(reason.wasAborted, 'state resolution was correctly marked as aborted');
8974
});
9075

@@ -118,7 +103,7 @@ test('Integration w/ HandlerInfos', function (assert) {
118103
];
119104

120105
state
121-
.resolve(() => true, transition as Transition)
106+
.resolve(transition as Transition)
122107
.then(function (result: TransitionState<Route>) {
123108
let models = [];
124109
for (let i = 0; i < result.routeInfos.length; i++) {

0 commit comments

Comments
 (0)