1 | import { AsyncSeriesHook, Tapable } from 'tapable';
|
2 | import _uniq from 'lodash/uniq';
|
3 | import _cloneDeep from 'lodash/cloneDeep';
|
4 | import { matchPath } from 'react-router';
|
5 | import AsyncRouteLoadErrorComponent from '../components/AsyncRouteLoadError';
|
6 | import AsyncRouteLoaderComponent from '../components/AsyncRouteLoader';
|
7 | import NotFoundComponent from '../components/NotFound';
|
8 | import ErrorComponent from '../components/Error';
|
9 | import RouteCompiler from './compiler';
|
10 | import PwaIcon192 from '../resources/images/pwa-icon-192x192.png';
|
11 | import PwaIcon512 from '../resources/images/pwa-icon-512x512.png';
|
12 |
|
13 | export default class RouteHandler extends Tapable {
|
14 | static defaultPwaSchema = {
|
15 | name: 'PawJS',
|
16 | short_name: 'PawJS',
|
17 |
|
18 |
|
19 | dir: 'ltr',
|
20 |
|
21 |
|
22 | lang: 'en-US',
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | orientation: 'any',
|
28 | start_url: '/',
|
29 | background_color: '#fff',
|
30 | theme_color: '#fff',
|
31 | display: 'standalone',
|
32 | description: 'A highly scalable & plug-able, Progressive Web Application foundation with the best Developer Experience.',
|
33 | icons: [
|
34 | {
|
35 | src: PwaIcon192,
|
36 | sizes: '192x192',
|
37 | },
|
38 | {
|
39 | src: PwaIcon512,
|
40 | sizes: '512x512',
|
41 | },
|
42 | ],
|
43 | };
|
44 |
|
45 | static defaultSeoSchema = {
|
46 | title: 'PawJS',
|
47 | description: RouteHandler.defaultPwaSchema.description,
|
48 | keywords: [],
|
49 | image: '',
|
50 | site_name: RouteHandler.defaultPwaSchema.name,
|
51 | twitter: {
|
52 | site: '',
|
53 | creator: '',
|
54 | },
|
55 | facebook: {
|
56 | admins: [],
|
57 | },
|
58 | type: 'article',
|
59 | type_details: {
|
60 | section: '',
|
61 | published_time: '',
|
62 | modified_time: '',
|
63 | },
|
64 | };
|
65 |
|
66 | static computeRootMatch = pathname => ({
|
67 | path: '/', url: '/', params: {}, isExact: pathname === '/',
|
68 | });
|
69 |
|
70 | static matchRoutes = (...args) => {
|
71 | const [routes, pathname] = args;
|
72 | const branch = args.length > 2 && args[2] !== undefined ? args[2] : [];
|
73 |
|
74 | routes.some((route) => {
|
75 | let match;
|
76 |
|
77 | if (route.path) {
|
78 | match = matchPath(pathname, route);
|
79 | } else {
|
80 | match = branch.length ? branch[branch.length - 1].match
|
81 | : RouteHandler.computeRootMatch(pathname);
|
82 | }
|
83 |
|
84 | if (match) {
|
85 | branch.push({ route, match });
|
86 |
|
87 | if (route.routes) {
|
88 | RouteHandler.matchRoutes(route.routes, pathname, branch);
|
89 | }
|
90 | }
|
91 |
|
92 | return match;
|
93 | });
|
94 |
|
95 | return branch;
|
96 | };
|
97 |
|
98 | routes = [];
|
99 |
|
100 | components = {
|
101 | NotFoundComponent,
|
102 | ErrorComponent,
|
103 | };
|
104 |
|
105 | constructor(options) {
|
106 | super();
|
107 |
|
108 | this.routeCompiler = new RouteCompiler({
|
109 | isServer: Boolean(options.isServer),
|
110 | env: options.env,
|
111 | });
|
112 | this.hooks = {
|
113 | initRoutes: new AsyncSeriesHook(),
|
114 | };
|
115 |
|
116 |
|
117 | let loadErrorComponent = AsyncRouteLoadErrorComponent;
|
118 | let loaderComponent = AsyncRouteLoaderComponent;
|
119 | let notFoundComponent = NotFoundComponent;
|
120 | let errorComponent = ErrorComponent;
|
121 | let seoSchema = Object.assign({}, RouteHandler.defaultSeoSchema);
|
122 | let pwaSchema = Object.assign({}, RouteHandler.defaultPwaSchema);
|
123 | pwaSchema.start_url = options.env.appRootUrl ? options.env.appRootUrl : '/';
|
124 | if (!pwaSchema.start_url.endsWith('/')) {
|
125 | pwaSchema.start_url = `${pwaSchema.start_url}/`;
|
126 | }
|
127 |
|
128 | let delay = 300;
|
129 | let timeout = 10000;
|
130 |
|
131 | this.setDefaultSeoSchema = (schema = {}) => {
|
132 | seoSchema = Object.assign(seoSchema, schema);
|
133 | };
|
134 |
|
135 | this.getDefaultSeoSchema = () => Object.assign({}, seoSchema);
|
136 |
|
137 | this.setPwaSchema = (schema = {}) => {
|
138 | pwaSchema = Object.assign(pwaSchema, schema);
|
139 | };
|
140 |
|
141 | this.getPwaSchema = () => Object.assign({}, pwaSchema);
|
142 |
|
143 | this.setDefaultLoadErrorComponent = (component) => {
|
144 | loadErrorComponent = component;
|
145 | return this;
|
146 | };
|
147 |
|
148 | this.getDefaultLoadErrorComponent = () => loadErrorComponent;
|
149 |
|
150 | this.setDefaultLoaderComponent = (component) => {
|
151 | loaderComponent = component;
|
152 | return this;
|
153 | };
|
154 |
|
155 | this.getDefaultLoaderComponent = () => loaderComponent;
|
156 |
|
157 | this.setDefaultAllowedLoadDelay = (allowedDelay) => {
|
158 | delay = allowedDelay;
|
159 | return this;
|
160 | };
|
161 |
|
162 | this.getDefaultAllowedLoadDelay = () => delay;
|
163 |
|
164 | this.setDefaultLoadTimeout = (loadTimeout) => {
|
165 | timeout = loadTimeout;
|
166 | return this;
|
167 | };
|
168 |
|
169 | this.getDefaultLoadTimeout = () => timeout;
|
170 |
|
171 | this.set404Component = (component = () => null) => {
|
172 | notFoundComponent = component;
|
173 | };
|
174 | this.get404Component = () => notFoundComponent;
|
175 |
|
176 | this.setErrorComponent = (component = () => null) => {
|
177 | errorComponent = component;
|
178 | };
|
179 |
|
180 | this.getErrorComponent = () => errorComponent;
|
181 | }
|
182 |
|
183 | addRoute(route) {
|
184 | const compiledRoute = this.routeCompiler.compileRoute(route, this);
|
185 | this.routes.push(compiledRoute);
|
186 | this.routes = _uniq(this.routes);
|
187 | }
|
188 |
|
189 | addRoutes(routes) {
|
190 | const compiledRoutes = this.routeCompiler.compileRoutes(routes, this);
|
191 | this.routes = this.routes.concat(compiledRoutes);
|
192 | this.routes = _uniq(this.routes);
|
193 | }
|
194 |
|
195 | addPlugin(plugin) {
|
196 | try {
|
197 | if (plugin.hooks && Object.keys(plugin.hooks).length) {
|
198 | plugin.hooks.forEach((hookValue, hookName) => {
|
199 | this.hooks[hookName] = hookValue;
|
200 | });
|
201 | }
|
202 | } catch (ex) {
|
203 |
|
204 | console.log(ex);
|
205 | }
|
206 | if (plugin.apply) {
|
207 | plugin.apply(this);
|
208 | }
|
209 | }
|
210 |
|
211 | getRoutes() {
|
212 | const routes = _cloneDeep(this.routes);
|
213 | routes.push({
|
214 | component: this.get404Component(),
|
215 | });
|
216 | return routes;
|
217 | }
|
218 | }
|