1 | import './chunk/constants.js';
|
2 | import './chunk/utils.js';
|
3 | import { useState, useEffect } from './core.js';
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | function getPathname() {
|
9 | return location.pathname;
|
10 | }
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | function redirect(pathname) {
|
16 | if (pathname == getPathname()) return;
|
17 | history.pushState({}, "history", pathname);
|
18 | window.dispatchEvent(new PopStateEvent("popstate"));
|
19 | }
|
20 |
|
21 | function subscribe(handler) {
|
22 | window.addEventListener("popstate", handler);
|
23 | return () => window.removeEventListener("popstate", handler);
|
24 | }
|
25 |
|
26 | const FOLDERS = /([^\/]+)/g;
|
27 | const FOLDER = "[^\\/]";
|
28 | const SPLIT = "(?:\\/){0,1}";
|
29 | const PARAM = /^(:)(\w+)(\?|(\.){3}){0,1}/;
|
30 | const PARAMS_EMPTY = {};
|
31 | const MEMO = {};
|
32 |
|
33 | function format(path) {
|
34 | return path.replace(/(\/){2,}/g, "/").replace(/([^\/]+)\/$/, "$1");
|
35 | }
|
36 |
|
37 | function parse(string) {
|
38 | let folders = string.match(FOLDERS) || [""];
|
39 | let params = [];
|
40 | let regexp = new RegExp(
|
41 | "^" +
|
42 | folders
|
43 | .map(folder => {
|
44 | let [string, param, field, type] =
|
45 | folder.match(PARAM) || [];
|
46 | if (param) {
|
47 | params.push(field);
|
48 | if (type === "...") {
|
49 | return `(.*)`;
|
50 | } else if (type === "?") {
|
51 | return `${SPLIT}(${FOLDER}*)`;
|
52 | } else {
|
53 | return `\\/(${FOLDER}+)`;
|
54 | }
|
55 | } else {
|
56 | return `\\/(?:${folder
|
57 | .replace(/(\.|\-)/g, "\\$1")
|
58 | .replace(/\*/g, FOLDER + "+")
|
59 | .replace(/\((?!\?\:)/g, "(?:")})`;
|
60 | }
|
61 | })
|
62 | .join("") +
|
63 | "$",
|
64 | "i"
|
65 | );
|
66 |
|
67 | return { regexp, params, logs: {} };
|
68 | }
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | function match(path, value) {
|
76 | path = format(path);
|
77 | value = format(value);
|
78 | if (!MEMO[path]) {
|
79 | MEMO[path] = parse(path);
|
80 | }
|
81 | let { regexp, params, logs } = MEMO[path];
|
82 | if (logs[value]) {
|
83 | return logs[value];
|
84 | }
|
85 | let vs = value.match(regexp);
|
86 | return (logs[value] = [
|
87 | vs ? true : false,
|
88 | vs
|
89 | ? vs.slice(1).reduce((next, value, index) => {
|
90 | next[params[index] || index] = value;
|
91 | return next;
|
92 | }, {})
|
93 | : PARAMS_EMPTY
|
94 | ]);
|
95 | }
|
96 |
|
97 | let cachePathCallback = {};
|
98 |
|
99 | function useHistory() {
|
100 | let pathname = getPathname();
|
101 | let [state, setState] = useState({ pathname });
|
102 |
|
103 | useEffect(() => {
|
104 | function handler() {
|
105 | let pathname = getPathname();
|
106 | setState(state =>
|
107 | state.pathname != pathname ? { pathname } : state
|
108 | );
|
109 | }
|
110 | return subscribe(handler);
|
111 | }, []);
|
112 | return [pathname, redirect];
|
113 | }
|
114 |
|
115 | function useMatchRoute(path) {
|
116 | return match(path, getPathname());
|
117 | }
|
118 |
|
119 | function useRoute(path) {
|
120 | useHistory();
|
121 | return useMatchRoute(path);
|
122 | }
|
123 |
|
124 | function useRedirect(path) {
|
125 | return (cachePathCallback[path] =
|
126 | cachePathCallback[path] || (() => redirect(path)));
|
127 | }
|
128 |
|
129 | function useRouter(cases) {
|
130 | let def = "default";
|
131 | let [pathname] = useHistory();
|
132 | for (let key in cases) {
|
133 | if (key != def) {
|
134 | let [status, params] = match(key, pathname);
|
135 | if (status) return cases[key](params);
|
136 | }
|
137 | }
|
138 | return cases[def]();
|
139 | }
|
140 |
|
141 | export { useHistory, useMatchRoute, useRedirect, useRoute, useRouter };
|
142 |
|