All files route-task.ts

96.92% Statements 63/65
96.87% Branches 31/32
100% Functions 4/4
96.92% Lines 63/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 1062x           2x     2x         2x 697x         697x 637x 637x   697x 9859x 24x 24x 9835x 9859x 697x                           719x 719x   719x 4954x 2x 2x   4952x 4952x 4952x 4954x 16x 16x   4954x 22x 22x   4914x 3894x 659x 659x 659x 3190x 10x 10x 6x 6x 6x 6x 6x 6x 3187x     6x 6x 6x 6x 6x 6x 6x 4x 4x   2x 2x 2x 2x 2x 2x 2x 2x 2x              
import {
    RouteNavigationAbortedError,
    RouteSelfRedirectionError,
    RouteTaskCancelledError,
    RouteTaskExecutionError
} from './error';
import { Route } from './route';
import type { Router } from './router';
import type { RouteConfirmHook, RouteConfirmHookResult } from './types';
import { isUrlEqual, isValidConfirmHookResult } from './util';
 
/**
 * Controls the execution and cancellation of a route task.
 */
export class RouteTaskController {
    private _aborted = false;
 
    /**
     * Aborts the current task.
     */
    abort(): void {
        this._aborted = true;
    }
 
    shouldCancel(name: string): boolean {
        if (this._aborted) {
            return true;
        }
        return false;
    }
}
 
export interface RouteTaskOptions {
    to: Route;
    from: Route | null;
    tasks: RouteTask[];
    router: Router;
    /**
     * Optional route task controller.
     * Used to control the execution and cancellation of the task.
     */
    controller?: RouteTaskController;
}
 
export async function createRouteTask(opts: RouteTaskOptions): Promise<Route> {
    const { to, from, tasks, controller, router } = opts;
 
    for (const task of tasks) {
        if (controller?.shouldCancel(task.name)) {
            throw new RouteTaskCancelledError(task.name, to, from);
        }
 
        let result: RouteConfirmHookResult | null = null;
        try {
            result = await task.task(to, from, router);
        } catch (e) {
            throw new RouteTaskExecutionError(task.name, to, from, e);
        }
 
        if (controller?.shouldCancel(task.name)) {
            throw new RouteTaskCancelledError(task.name, to, from);
        }
 
        if (!isValidConfirmHookResult(result)) continue;
        if (typeof result === 'function') {
            to.handle = result;
            return to;
        }
        if (result === false) {
            throw new RouteNavigationAbortedError(task.name, to, from);
        }
        const nextTo = new Route({
            options: router.parsedOptions,
            toType: to.type,
            toInput: result,
            from: to.url
        });
        if (isUrlEqual(nextTo.url, to.url)) {
            throw new RouteSelfRedirectionError(to.fullPath, to, from);
        }
        return createRouteTask({
            ...opts,
            to: nextTo,
            from: to,
            controller
        });
    }
    return to;
}
 
export enum RouteTaskType {
    fallback = 'fallback',
    override = 'override',
    asyncComponent = 'asyncComponent',
    beforeEach = 'beforeEach',
    beforeEnter = 'beforeEnter',
    beforeUpdate = 'beforeUpdate',
    beforeLeave = 'beforeLeave',
    confirm = 'confirm'
}
 
export interface RouteTask {
    name: string;
    task: RouteConfirmHook;
}