-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add first test * new VirtualRoutes mixin that handles routes. fetch tries to use VirtualRoutes. default config updated * cover all basic use cases * regex matching in routes * covered all virtual routes tests * added hack to fix config test on firefox * removed formatting regex matches into string routes * added support for "next" function * added docs * navigate now supports both hash and history routerModes * waiting for networkidle in navigateToRoute helper * promiseless implementation * remove firefox workaround from catchPluginErrors test, since we no longer use promises * updated docs * updated docs for "alias" as well * minor rephrasing * removed non-legacy code from exact-match; updated navigateToRoute helper to infer router mode from page * moved endsWith from router utils to general utils; added startsWith util; refactored makeExactMatcher to use both * updated docs per feedback * moved navigateToRoute helper into the virtual-routes test file * moved navigateToRoute to top of file * updated docs per pr comments
- Loading branch information
Showing
12 changed files
with
570 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function startsWith(str, prefix) { | ||
return str.indexOf(prefix) === 0; | ||
} | ||
|
||
export function endsWith(str, suffix) { | ||
return str.indexOf(suffix, str.length - suffix.length) !== -1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { startsWith, endsWith } from '../util/str'; | ||
|
||
/** | ||
* Adds beginning of input (^) and end of input ($) assertions if needed into a regex string | ||
* @param {string} matcher the string to match | ||
* @returns {string} | ||
*/ | ||
export function makeExactMatcher(matcher) { | ||
const matcherWithBeginningOfInput = startsWith(matcher, '^') | ||
? matcher | ||
: `^${matcher}`; | ||
|
||
const matcherWithBeginningAndEndOfInput = endsWith( | ||
matcherWithBeginningOfInput, | ||
'$' | ||
) | ||
? matcherWithBeginningOfInput | ||
: `${matcherWithBeginningOfInput}$`; | ||
|
||
return matcherWithBeginningAndEndOfInput; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { makeExactMatcher } from './exact-match'; | ||
import { createNextFunction } from './next'; | ||
|
||
/** @typedef {import('../Docsify').Constructor} Constructor */ | ||
|
||
/** @typedef {Record<string, string | VirtualRouteHandler>} VirtualRoutesMap */ | ||
/** @typedef {(route: string, match: RegExpMatchArray | null) => string | void | Promise<string | void> } VirtualRouteHandler */ | ||
|
||
/** | ||
* @template {!Constructor} T | ||
* @param {T} Base - The class to extend | ||
*/ | ||
export function VirtualRoutes(Base) { | ||
return class VirtualRoutes extends Base { | ||
/** | ||
* Gets the Routes object from the configuration | ||
* @returns {VirtualRoutesMap} | ||
*/ | ||
routes() { | ||
return this.config.routes || {}; | ||
} | ||
|
||
/** | ||
* Attempts to match the given path with a virtual route. | ||
* @param {string} path the path of the route to match | ||
* @returns {Promise<string | null>} resolves to string if route was matched, otherwise null | ||
*/ | ||
matchVirtualRoute(path) { | ||
const virtualRoutes = this.routes(); | ||
const virtualRoutePaths = Object.keys(virtualRoutes); | ||
|
||
let done = () => null; | ||
|
||
/** | ||
* This is a tail recursion that iterates over all the available routes. | ||
* It can result in one of two ways: | ||
* 1. Call itself (essentially reviewing the next route) | ||
* 2. Call the "done" callback with the result (either the contents, or "null" if no match was found) | ||
*/ | ||
function asyncMatchNextRoute() { | ||
const virtualRoutePath = virtualRoutePaths.shift(); | ||
if (!virtualRoutePath) { | ||
return done(null); | ||
} | ||
|
||
const matcher = makeExactMatcher(virtualRoutePath); | ||
const matched = path.match(matcher); | ||
|
||
if (!matched) { | ||
return asyncMatchNextRoute(); | ||
} | ||
|
||
const virtualRouteContentOrFn = virtualRoutes[virtualRoutePath]; | ||
|
||
if (typeof virtualRouteContentOrFn === 'string') { | ||
const contents = virtualRouteContentOrFn; | ||
return done(contents); | ||
} | ||
|
||
if (typeof virtualRouteContentOrFn === 'function') { | ||
const fn = virtualRouteContentOrFn; | ||
|
||
const [next, onNext] = createNextFunction(); | ||
onNext(contents => { | ||
if (typeof contents === 'string') { | ||
return done(contents); | ||
} else if (contents === false) { | ||
return done(null); | ||
} else { | ||
return asyncMatchNextRoute(); | ||
} | ||
}); | ||
|
||
if (fn.length <= 2) { | ||
const returnedValue = fn(path, matched); | ||
return next(returnedValue); | ||
} else { | ||
return fn(path, matched, next); | ||
} | ||
} | ||
|
||
return asyncMatchNextRoute(); | ||
} | ||
|
||
return { | ||
then: function (cb) { | ||
done = cb; | ||
asyncMatchNextRoute(); | ||
}, | ||
}; | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** @typedef {((value: any) => void) => void} OnNext */ | ||
/** @typedef {(value: any) => void} NextFunction */ | ||
|
||
/** | ||
* Creates a pair of a function and an event emitter. | ||
* When the function is called, the event emitter calls the given callback with the value that was passed to the function. | ||
* @returns {[NextFunction, OnNext]} | ||
*/ | ||
export function createNextFunction() { | ||
let storedCb = () => null; | ||
|
||
function next(value) { | ||
storedCb(value); | ||
} | ||
|
||
function onNext(cb) { | ||
storedCb = cb; | ||
} | ||
|
||
return [next, onNext]; | ||
} |
Oops, something went wrong.