UNPKG

5.8 kBJavaScriptView Raw
1import { AsyncSeriesHook, Tapable } from 'tapable';
2import _uniq from 'lodash/uniq';
3import _cloneDeep from 'lodash/cloneDeep';
4import { matchPath } from 'react-router';
5import AsyncRouteLoadErrorComponent from '../components/AsyncRouteLoadError';
6import AsyncRouteLoaderComponent from '../components/AsyncRouteLoader';
7import NotFoundComponent from '../components/NotFound';
8import ErrorComponent from '../components/Error';
9import RouteCompiler from './compiler';
10import PwaIcon192 from '../resources/images/pwa-icon-192x192.png';
11import PwaIcon512 from '../resources/images/pwa-icon-512x512.png';
12
13export default class RouteHandler extends Tapable {
14 static defaultPwaSchema = {
15 name: 'PawJS',
16 short_name: 'PawJS',
17
18 // Possible values ltr(left to right)/rtl(right to left)
19 dir: 'ltr',
20
21 // language: Default en-US
22 lang: 'en-US',
23
24 // Orientation of web-app possible:
25 // any, natural, landscape, landscape-primary, landscape-secondary,
26 // portrait, portrait-primary, portrait-secondary
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', // article/product/music/video
59 type_details: {
60 section: '', // Lifestyle/sports/news
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 // use parent 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 // Private methods
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 // eslint-disable-next-line
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}