1 | import withTrailingSlash from './with-trailing-slash'
|
2 |
|
3 | const DEFAULT_PARSER = /^https?:\/\/([a-zA-Z0-9\-_.]+)()(\/.*)/
|
4 | const PROTOCOL = /(https?):\/\//
|
5 | const SLICE_END = ')'
|
6 | const SLICE_START = '('
|
7 | const SLICE_MARKERS = new RegExp(`[${SLICE_START}${SLICE_END}]`, 'g')
|
8 |
|
9 |
|
10 |
|
11 | const withoutQueryString = s => (/\?/.test(s) ? s.match(/(.*?)\?/)[1] : s)
|
12 |
|
13 | const MANY_APPS = /^(https?:\/\/.+?\/)(https?:\/\/.+)/
|
14 |
|
15 | const appSplit = s => {
|
16 | let currentUri = s
|
17 | let nextUri = false
|
18 |
|
19 | const manyAppsMatch = s.match(MANY_APPS)
|
20 | if (manyAppsMatch) {
|
21 | currentUri = manyAppsMatch[1]
|
22 | nextUri = manyAppsMatch[2]
|
23 | }
|
24 |
|
25 | return {
|
26 | currentUri,
|
27 | nextUri,
|
28 | }
|
29 | }
|
30 |
|
31 | export default function parse(uri, parsers = []) {
|
32 | const apps = {
|
33 | byName: {},
|
34 | items: [],
|
35 | }
|
36 | const routes = {
|
37 | byContext: {},
|
38 | items: [],
|
39 | }
|
40 |
|
41 |
|
42 | const protocol = uri.match(PROTOCOL)[1]
|
43 | let nextUri = withoutQueryString(withTrailingSlash(uri))
|
44 |
|
45 | do {
|
46 | const split = appSplit(nextUri)
|
47 | const currentUri = split.currentUri
|
48 | nextUri = split.nextUri
|
49 |
|
50 | const parser = parsers.find(p => p.test(currentUri)) || DEFAULT_PARSER
|
51 | let [_0, app, _1, path] = currentUri.match(parser)
|
52 |
|
53 | const base = `${protocol}://${app}`
|
54 | const context = routes.items.length > 0
|
55 | ? routes.items[routes.items.length - 1]
|
56 | : ''
|
57 |
|
58 |
|
59 | let pathRoute = []
|
60 | let isVisible = true
|
61 | do {
|
62 | path = path.split('/')
|
63 | const lastBit = path.length > 1 ? path[path.length - 2] : ''
|
64 | path = path.slice(0, path.length - 1).join('/')
|
65 | const hasSliceEndMarkerForRoot = lastBit.indexOf(SLICE_END) === 0
|
66 | const hasSliceEndMarker =
|
67 | !hasSliceEndMarkerForRoot && lastBit.indexOf(SLICE_END) > -1
|
68 | const hasSliceStartMarker = lastBit.indexOf(SLICE_START) > -1
|
69 |
|
70 | if (hasSliceEndMarker || hasSliceStartMarker) {
|
71 | isVisible = false
|
72 | }
|
73 |
|
74 | const panelPath = path.replace(SLICE_MARKERS, '') || '/'
|
75 |
|
76 | const panelId = `${app}${panelPath}`
|
77 |
|
78 | pathRoute.push({
|
79 | app,
|
80 | context: `${context}${base}${withTrailingSlash(path)}`,
|
81 | isVisible,
|
82 | panelId,
|
83 | path: panelPath,
|
84 | })
|
85 |
|
86 | isVisible = hasSliceEndMarkerForRoot
|
87 | ? false
|
88 | : hasSliceStartMarker ? true : isVisible
|
89 | } while (path.length)
|
90 |
|
91 | if (apps.items.indexOf(app) === -1) {
|
92 | apps.items.push(app)
|
93 | apps.byName[app] = {
|
94 | app,
|
95 | panels: [],
|
96 | }
|
97 | }
|
98 |
|
99 | pathRoute.reverse().forEach(route => {
|
100 | routes.byContext[route.context] = route
|
101 | routes.items.push(route.context)
|
102 |
|
103 | if (apps.byName[app].panels.indexOf(route.path) === -1) {
|
104 | apps.byName[app].panels.push(route.path)
|
105 | }
|
106 | })
|
107 | } while (nextUri)
|
108 |
|
109 | return {
|
110 | apps,
|
111 | routes,
|
112 | }
|
113 | }
|