safa-router 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Link.js +1 -1
- package/src/RouteMatcher.js +3 -3
- package/src/SafaRouter.js +18 -41
- package/src/constants.js +1 -1
package/package.json
CHANGED
package/src/Link.js
CHANGED
|
@@ -73,7 +73,7 @@ export class Link {
|
|
|
73
73
|
const cur = this._router.pathname
|
|
74
74
|
const active =
|
|
75
75
|
cur === this._href ||
|
|
76
|
-
(this._href !== '/' && cur.startsWith(this._href))
|
|
76
|
+
(this._href !== '/' && (cur.startsWith(this._href + '/') || cur === this._href))
|
|
77
77
|
this._el.classList.toggle(this._activeClass, active)
|
|
78
78
|
}
|
|
79
79
|
|
package/src/RouteMatcher.js
CHANGED
|
@@ -100,9 +100,9 @@ class RoutePattern {
|
|
|
100
100
|
let path = this.raw
|
|
101
101
|
for (const [key, val] of Object.entries(params)) {
|
|
102
102
|
const v = Array.isArray(val) ? val.join('/') : String(val)
|
|
103
|
-
path = path.
|
|
104
|
-
path = path.
|
|
105
|
-
path = path.
|
|
103
|
+
path = path.replaceAll(`[[...${key}]]`, v)
|
|
104
|
+
path = path.replaceAll(`[...${key}]`, v)
|
|
105
|
+
path = path.replaceAll(`[${key}]`, v)
|
|
106
106
|
}
|
|
107
107
|
return normalizePath(path)
|
|
108
108
|
}
|
package/src/SafaRouter.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RouteMatcher } from './RouteMatcher.js'
|
|
2
1
|
import { RouteTree } from './RouteTree.js'
|
|
3
2
|
import { HistoryManager } from './HistoryManager.js'
|
|
4
3
|
import { MiddlewareChain } from './MiddlewareChain.js'
|
|
@@ -8,8 +7,8 @@ import { EVENTS, DEFAULT_CONFIG } from './constants.js'
|
|
|
8
7
|
import { RouteLoadError, SafaError } from './errors.js'
|
|
9
8
|
|
|
10
9
|
export class SafaRouter {
|
|
11
|
-
static version = '1.1.
|
|
12
|
-
static VERSION = '1.1.
|
|
10
|
+
static version = '1.1.2'
|
|
11
|
+
static VERSION = '1.1.2'
|
|
13
12
|
|
|
14
13
|
constructor(options = {}) {
|
|
15
14
|
this.config = { ...DEFAULT_CONFIG, ...options }
|
|
@@ -20,7 +19,6 @@ export class SafaRouter {
|
|
|
20
19
|
this._events = {}
|
|
21
20
|
for (const key of Object.values(EVENTS)) this._events[key] = []
|
|
22
21
|
|
|
23
|
-
this._matcher = new RouteMatcher()
|
|
24
22
|
this._history = new HistoryManager({
|
|
25
23
|
useHash: this.config.useHash,
|
|
26
24
|
basePath: this.config.basePath,
|
|
@@ -36,6 +34,7 @@ export class SafaRouter {
|
|
|
36
34
|
this._isLoading = false
|
|
37
35
|
this._started = false
|
|
38
36
|
this._targetEl = null
|
|
37
|
+
this._navId = 0
|
|
39
38
|
|
|
40
39
|
this._globalNotFound = this.config.notFound || null
|
|
41
40
|
this._globalError = this.config.error || null
|
|
@@ -62,7 +61,6 @@ export class SafaRouter {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
this._routeTree = new RouteTree(this.config.routes || {})
|
|
65
|
-
this._seedMatcher()
|
|
66
64
|
this._history.init()
|
|
67
65
|
this._unsubHistory = this._history.onChange(this._boundNav)
|
|
68
66
|
await this._resolve(this._history.path, 'replace')
|
|
@@ -145,23 +143,6 @@ export class SafaRouter {
|
|
|
145
143
|
|
|
146
144
|
clearCache() { this._cache.clear() }
|
|
147
145
|
|
|
148
|
-
_seedMatcher() {
|
|
149
|
-
const walk = (routes, base) => {
|
|
150
|
-
if (!routes || typeof routes !== 'object') return
|
|
151
|
-
for (const [key, val] of Object.entries(routes)) {
|
|
152
|
-
const isGroup = key.startsWith('(') && key.endsWith(')')
|
|
153
|
-
const fp = isGroup ? base : base === '/' ? `/${key}` : `${base}/${key}`
|
|
154
|
-
if (typeof val === 'object' && val !== null) {
|
|
155
|
-
if (val.page) this._matcher.add(fp)
|
|
156
|
-
if (val.children) walk(val.children, fp)
|
|
157
|
-
} else if (typeof val === 'function') {
|
|
158
|
-
this._matcher.add(fp)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
walk(this.config.routes || {}, '/')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
146
|
_resolvePagePath(path) {
|
|
166
147
|
const dir = (this.config.pagesDir || '').replace(/\/+$/, '')
|
|
167
148
|
if (!dir) return null
|
|
@@ -236,12 +217,14 @@ export class SafaRouter {
|
|
|
236
217
|
return html.replace(/\{\s*children\s*\}/gi, children || '')
|
|
237
218
|
}
|
|
238
219
|
|
|
239
|
-
async _navigate(path, method, query = {}, state = {}) {
|
|
240
|
-
if (
|
|
241
|
-
|
|
220
|
+
async _navigate(path, method, query = {}, state = {}, depth = 0) {
|
|
221
|
+
if (depth > 10) { console.error('[SafaRouter] Redirect loop detected'); return }
|
|
222
|
+
if (path === this._pathname && depth === 0) return
|
|
223
|
+
await this._resolve(path, method, query, state, depth)
|
|
242
224
|
}
|
|
243
225
|
|
|
244
|
-
async _resolve(path, method, query = {}, state = {}) {
|
|
226
|
+
async _resolve(path, method, query = {}, state = {}, depth = 0) {
|
|
227
|
+
const navId = ++this._navId
|
|
245
228
|
this._isLoading = true
|
|
246
229
|
this._customTitle = null
|
|
247
230
|
emit(this._events, EVENTS.LOADING, { path, loading: true })
|
|
@@ -250,7 +233,8 @@ export class SafaRouter {
|
|
|
250
233
|
try {
|
|
251
234
|
const ctx = { path, method, query, cancelled: false, redirect: null }
|
|
252
235
|
await this._middleware.run(ctx)
|
|
253
|
-
if (
|
|
236
|
+
if (this._navId !== navId) return
|
|
237
|
+
if (ctx.redirect) return this._navigate(ctx.redirect, 'replace', {}, {}, depth + 1)
|
|
254
238
|
if (ctx.cancelled) { this._isLoading = false; return }
|
|
255
239
|
|
|
256
240
|
const routeMatch = this._hasRoutes() ? this._routeTree.resolve(path) : null
|
|
@@ -315,6 +299,8 @@ export class SafaRouter {
|
|
|
315
299
|
return
|
|
316
300
|
}
|
|
317
301
|
|
|
302
|
+
if (this._navId !== navId) return
|
|
303
|
+
|
|
318
304
|
this._saveScroll()
|
|
319
305
|
|
|
320
306
|
if (method === 'push') this._history.push(path, state)
|
|
@@ -327,14 +313,14 @@ export class SafaRouter {
|
|
|
327
313
|
|
|
328
314
|
this._render(pageContent, layoutFns)
|
|
329
315
|
|
|
316
|
+
this._restoreScroll()
|
|
317
|
+
this._updateTitle()
|
|
318
|
+
this._focus()
|
|
319
|
+
|
|
330
320
|
emit(this._events, EVENTS.ROUTE_CHANGE, {
|
|
331
321
|
pathname: path, params: this._params, query: this._query,
|
|
332
322
|
})
|
|
333
323
|
emit(this._events, EVENTS.AFTER_NAVIGATE, { pathname: path })
|
|
334
|
-
|
|
335
|
-
this._updateTitle()
|
|
336
|
-
this._restoreScroll()
|
|
337
|
-
this._focus()
|
|
338
324
|
} catch (err) {
|
|
339
325
|
this._isLoading = false
|
|
340
326
|
await this._handleError(path, err)
|
|
@@ -442,15 +428,7 @@ export class SafaRouter {
|
|
|
442
428
|
this._extractTitle(text)
|
|
443
429
|
return text
|
|
444
430
|
}
|
|
445
|
-
if (typeof mod === 'function')
|
|
446
|
-
let result
|
|
447
|
-
try { result = mod() } catch { return mod }
|
|
448
|
-
if (result && typeof result.then === 'function') {
|
|
449
|
-
const resolved = await result
|
|
450
|
-
return resolved && resolved.default ? resolved.default : resolved
|
|
451
|
-
}
|
|
452
|
-
return result !== undefined ? result : mod
|
|
453
|
-
}
|
|
431
|
+
if (typeof mod === 'function') return mod
|
|
454
432
|
return mod
|
|
455
433
|
} catch (e) {
|
|
456
434
|
throw new RouteLoadError(
|
|
@@ -538,7 +516,6 @@ export class SafaRouter {
|
|
|
538
516
|
}
|
|
539
517
|
|
|
540
518
|
get currentRoute() { return this._routeData }
|
|
541
|
-
get matchedRoute() { return this._matcher.match(this._pathname) }
|
|
542
519
|
|
|
543
520
|
getRoute(path) { return this._routeTree.resolve(normalizePath(path)) }
|
|
544
521
|
|
package/src/constants.js
CHANGED
|
@@ -23,7 +23,7 @@ export const SEGMENT_TYPES = {
|
|
|
23
23
|
export const PARAM_PATTERNS = {
|
|
24
24
|
DYNAMIC: /^\[([^\]]+)\]$/,
|
|
25
25
|
CATCH_ALL: /^\[\.\.\.([^\]]+)\]$/,
|
|
26
|
-
OPTIONAL_CATCH_ALL: /^\[\[\.\.\.[^\]]
|
|
26
|
+
OPTIONAL_CATCH_ALL: /^\[\[\.\.\.([^\]]+)\]\]$/,
|
|
27
27
|
GROUP: /^\(([^)]+)\)$/,
|
|
28
28
|
}
|
|
29
29
|
|