Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 74e5b99

Browse files
committed
refactor: improve routing
1 parent 7636b14 commit 74e5b99

File tree

7 files changed

+105
-46
lines changed

7 files changed

+105
-46
lines changed

framework/core/hmr.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ const { location } = window as any
4949
const { protocol, host } = location
5050
const modules: Map<string, Module> = new Map()
5151
const messageQueue: any[] = []
52-
const socket = new WebSocket((protocol === 'https:' ? 'wss' : 'ws') + '://' + host + '/_hmr')
52+
const url = (protocol === 'https:' ? 'wss' : 'ws') + '://' + host + '/_hmr'
53+
const socket = new WebSocket(url)
5354

5455
socket.addEventListener('open', () => {
5556
messageQueue.forEach(msg => socket.send(JSON.stringify(msg)))
@@ -61,13 +62,25 @@ socket.addEventListener('close', () => {
6162
location.reload()
6263
})
6364

64-
socket.addEventListener('message', ({ data: rawData }: { data?: string }) => {
65-
if (rawData) {
65+
socket.addEventListener('message', ({ data }: { data?: string }) => {
66+
if (data) {
6667
try {
67-
const { type, url, asyncDeps, updateUrl } = JSON.parse(rawData)
68+
const {
69+
type,
70+
url,
71+
updateUrl,
72+
pagePath,
73+
isIndexModule,
74+
useDeno,
75+
} = JSON.parse(data)
6876
switch (type) {
6977
case 'add':
70-
events.emit('add-module', { url, asyncDeps })
78+
events.emit('add-module', {
79+
url,
80+
pagePath,
81+
isIndexModule,
82+
useDeno,
83+
})
7184
break
7285
case 'update':
7386
const mod = modules.get(url)

framework/core/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function toPagePath(url: string): string {
88
pathname = util.trimPrefix(pathname, '/pages')
99
}
1010
if (pathname.endsWith('/index')) {
11-
pathname = util.trimSuffix(pathname, 'index')
11+
pathname = util.trimSuffix(pathname, '/index')
1212
}
1313
if (pathname === '') {
1414
pathname = '/'

framework/core/routing.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import util from '../../shared/util.ts'
22
import type { RouterURL } from '../../types.ts'
33
import events from './events.ts'
4-
import { toPagePath } from './module.ts'
54

65
const ghostRoute: Route = { path: '', module: { url: '' } }
76

@@ -17,11 +16,11 @@ export type RouteModule = {
1716
}
1817

1918
export type RoutingOptions = {
20-
routes?: Route[]
21-
rewrites?: Record<string, string>
2219
baseURL?: string
2320
defaultLocale?: string
2421
locales?: string[]
22+
routes?: Route[]
23+
rewrites?: Record<string, string>
2524
}
2625

2726
export class Routing {
@@ -66,17 +65,21 @@ export class Routing {
6665
})
6766
}
6867

69-
update(module: RouteModule) {
70-
const newRoute: Route = { path: toPagePath(module.url), module: module }
68+
update(path: string, moduleUrl: string, options: { isIndexModule?: boolean, useDeno?: boolean } = {}) {
69+
const { isIndexModule, ...rest } = options
70+
const newRoute: Route = {
71+
path: path === '/' ? path : path + (options.isIndexModule ? '/' : ''),
72+
module: { url: moduleUrl, ...rest }
73+
}
7174
const dirtyRoutes: Set<Route[]> = new Set()
7275
let exists = false
7376
let targetRoutes = this._routes
7477
this._lookup(routePath => {
7578
const path = routePath.map(r => r.path).join('')
7679
const route = routePath[routePath.length - 1]
7780
const parentRoute = routePath[routePath.length - 2]
78-
if (route.module.url === module.url) {
79-
Object.assign(route.module, module)
81+
if (route.module.url === newRoute.module.url) {
82+
Object.assign(route.module, newRoute.module)
8083
exists = true
8184
return false
8285
}

framework/core/routing_test.ts

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,62 @@ Deno.test(`routing`, () => {
99
'/你好世界': '/zh-CN/hello-world',
1010
}
1111
})
12-
routing.update({ url: '/pages/index.tsx' })
13-
routing.update({ url: '/pages/hello-world.tsx' })
14-
routing.update({ url: '/pages/blog/index.tsx' })
15-
routing.update({ url: '/pages/blog/[slug].tsx' })
16-
routing.update({ url: '/pages/user/index.tsx' })
17-
routing.update({ url: '/pages/user/[...all].tsx' })
18-
routing.update({ url: '/pages/blog.tsx' })
19-
routing.update({ url: '/pages/user.tsx' })
20-
routing.update({ url: '/pages/blog/[slug]/subpage.tsx' })
21-
routing.update({ url: '/pages/docs.tsx' })
22-
routing.update({ url: '/pages/docs/get-started.tsx' })
23-
routing.update({ url: '/pages/docs/installation.tsx' })
24-
routing.update({ url: '/pages/index.tsx' })
12+
13+
routing.update(
14+
'/',
15+
'/pages/index.tsx'
16+
)
17+
routing.update(
18+
'/hello-world',
19+
'/pages/hello-world.tsx'
20+
)
21+
routing.update(
22+
'/blog',
23+
'/pages/blog/index.tsx',
24+
{ isIndexModule: true }
25+
)
26+
routing.update(
27+
'/blog/[slug]',
28+
'/pages/blog/[slug].tsx'
29+
)
30+
routing.update(
31+
'/user',
32+
'/pages/user/index.tsx',
33+
{ isIndexModule: true }
34+
)
35+
routing.update(
36+
'/user/[...all]',
37+
'/pages/user/[...all].tsx'
38+
)
39+
routing.update(
40+
'/blog',
41+
'/pages/blog.tsx'
42+
)
43+
routing.update(
44+
'/user',
45+
'/pages/user.tsx'
46+
)
47+
routing.update(
48+
'/blog/[slug]/subpage',
49+
'/pages/blog/[slug]/subpage.tsx'
50+
)
51+
routing.update(
52+
'/docs',
53+
'/pages/docs.tsx'
54+
)
55+
routing.update(
56+
'/docs/get-started',
57+
'/pages/docs/get-started.tsx'
58+
)
59+
routing.update(
60+
'/docs/installation',
61+
'/pages/docs/installation.tsx'
62+
)
63+
routing.update(
64+
'/',
65+
'/pages/index.tsx',
66+
{ isIndexModule: true }
67+
)
2568

2669
assertEquals(routing.paths, [
2770
'/',
@@ -41,94 +84,94 @@ Deno.test(`routing`, () => {
4184
assertEquals(router.locale, 'en')
4285
assertEquals(router.pathname, '/')
4386
assertEquals(router.pagePath, '/')
44-
assertEquals(nestedModules, [{ url: '/pages/index.tsx' }])
87+
assertEquals(nestedModules.map(m => m.url), ['/pages/index.tsx'])
4588
}
4689

4790
{
4891
const [router, nestedModules] = routing.createRouter({ pathname: '/zh-CN' })
4992
assertEquals(router.locale, 'zh-CN')
5093
assertEquals(router.pathname, '/')
5194
assertEquals(router.pagePath, '/')
52-
assertEquals(nestedModules, [{ url: '/pages/index.tsx' }])
95+
assertEquals(nestedModules.map(m => m.url), ['/pages/index.tsx'])
5396
}
5497

5598
{
5699
const [router, nestedModules] = routing.createRouter({ pathname: '/Hello World' })
57100
assertEquals(router.locale, 'en')
58101
assertEquals(router.pathname, '/hello-world')
59102
assertEquals(router.pagePath, '/hello-world')
60-
assertEquals(nestedModules, [{ url: '/pages/hello-world.tsx' }])
103+
assertEquals(nestedModules.map(m => m.url), ['/pages/hello-world.tsx'])
61104
}
62105

63106
{
64107
const [router, nestedModules] = routing.createRouter({ pathname: '/你好世界' })
65108
assertEquals(router.locale, 'zh-CN')
66109
assertEquals(router.pathname, '/hello-world')
67110
assertEquals(router.pagePath, '/hello-world')
68-
assertEquals(nestedModules, [{ url: '/pages/hello-world.tsx' }])
111+
assertEquals(nestedModules.map(m => m.url), ['/pages/hello-world.tsx'])
69112
}
70113

71114
{
72115
const [router, nestedModules] = routing.createRouter({ pathname: '/blog' })
73116
assertEquals(router.locale, 'en')
74117
assertEquals(router.pathname, '/blog')
75118
assertEquals(router.pagePath, '/blog')
76-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/blog.tsx', '/pages/blog/index.tsx'])
119+
assertEquals(nestedModules.map(m => m.url), ['/pages/blog.tsx', '/pages/blog/index.tsx'])
77120
}
78121

79122
{
80123
const [router, nestedModules] = routing.createRouter({ pathname: '/zh-CN/blog' })
81124
assertEquals(router.locale, 'zh-CN')
82125
assertEquals(router.pathname, '/blog')
83126
assertEquals(router.pagePath, '/blog')
84-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/blog.tsx', '/pages/blog/index.tsx'])
127+
assertEquals(nestedModules.map(m => m.url), ['/pages/blog.tsx', '/pages/blog/index.tsx'])
85128
}
86129

87130
{
88131
const [router, nestedModules] = routing.createRouter({ pathname: '/blog/hello-world' })
89132
assertEquals(router.pathname, '/blog/hello-world')
90133
assertEquals(router.pagePath, '/blog/[slug]')
91134
assertEquals(router.params, { slug: 'hello-world' })
92-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/blog.tsx', '/pages/blog/[slug].tsx'])
135+
assertEquals(nestedModules.map(m => m.url), ['/pages/blog.tsx', '/pages/blog/[slug].tsx'])
93136
}
94137

95138
{
96139
const [router, nestedModules] = routing.createRouter({ pathname: '/user' })
97140
assertEquals(router.pathname, '/user')
98141
assertEquals(router.pagePath, '/user')
99142
assertEquals(router.params, {})
100-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/user.tsx', '/pages/user/index.tsx'])
143+
assertEquals(nestedModules.map(m => m.url), ['/pages/user.tsx', '/pages/user/index.tsx'])
101144
}
102145

103146
{
104147
const [router, nestedModules] = routing.createRouter({ pathname: '/user/projects' })
105148
assertEquals(router.pathname, '/user/projects')
106149
assertEquals(router.pagePath, '/user/[...all]')
107150
assertEquals(router.params, { all: 'projects' })
108-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
151+
assertEquals(nestedModules.map(m => m.url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
109152
}
110153

111154
{
112155
const [router, nestedModules] = routing.createRouter({ pathname: '/user/settings/profile' })
113156
assertEquals(router.pathname, '/user/settings/profile')
114157
assertEquals(router.pagePath, '/user/[...all]')
115158
assertEquals(router.params, { all: 'settings/profile' })
116-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
159+
assertEquals(nestedModules.map(m => m.url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
117160
}
118161

119162
{
120163
const [router, nestedModules] = routing.createRouter({ pathname: '/user/settings/security' })
121164
assertEquals(router.pathname, '/user/settings/security')
122165
assertEquals(router.pagePath, '/user/[...all]')
123166
assertEquals(router.params, { all: 'settings/security' })
124-
assertEquals(nestedModules.map(({ url }) => url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
167+
assertEquals(nestedModules.map(m => m.url), ['/pages/user.tsx', '/pages/user/[...all].tsx'])
125168
}
126169

127170
{
128171
const [router, nestedModules] = routing.createRouter({ pathname: '/null' })
129172
assertEquals(router.pathname, '/null')
130173
assertEquals(router.pagePath, '')
131174
assertEquals(router.params, {})
132-
assertEquals(nestedModules, [])
175+
assertEquals(nestedModules.map(m => m.url), [])
133176
}
134177
})

framework/react/pageprops.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ComponentType } from 'https://esm.sh/react'
22
import type { RouterURL } from '../../types.ts'
3-
import { toPagePath } from '../core/module.ts'
43
import { E400MissingComponent } from './error.ts'
54
import { isLikelyReactComponent } from './helper.ts'
65

@@ -41,7 +40,7 @@ function createPagePropsSegment(seg: { url: string, Component?: ComponentType<an
4140
pageProps.Page = seg.Component
4241
} else {
4342
pageProps.Page = E400MissingComponent
44-
pageProps.pageProps = { name: 'Page: ' + toPagePath(seg.url) }
43+
pageProps.pageProps = { name: 'Page Component: ' + seg.url }
4544
}
4645
}
4746
return pageProps

framework/react/router.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
useState,
77
} from 'https://esm.sh/react'
88
import events from '../core/events.ts'
9-
import { importModule } from '../core/module.ts'
9+
import { importModule, toPagePath } from '../core/module.ts'
1010
import { RouteModule, Routing } from '../core/routing.ts'
1111
import { RouterContext } from './context.ts'
1212
import { E400MissingComponent, E404Page, ErrorBoundary } from './error.ts'
@@ -82,7 +82,7 @@ export default function Router({
8282
useEffect(() => {
8383
const isDev = !('__ALEPH' in window)
8484
const { baseURL } = routing
85-
const onAddModule = async (mod: RouteModule) => {
85+
const onAddModule = async (mod: RouteModule & { pagePath?: string, isIndexModule?: boolean }) => {
8686
switch (mod.url) {
8787
case '/404.js': {
8888
const { default: Component } = await importModule(baseURL, mod.url, true)
@@ -103,8 +103,9 @@ export default function Router({
103103
break
104104
}
105105
default: {
106-
if (mod.url.startsWith('/pages/')) {
107-
routing.update(mod)
106+
const { pagePath, url, ...rest } = mod
107+
if (pagePath) {
108+
routing.update(pagePath, url, rest)
108109
events.emit('popstate', { type: 'popstate', forceRefetch: true })
109110
}
110111
break

server/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { join } from 'https://deno.land/[email protected]/path/mod.ts'
22
import { serve as stdServe, serveTLS } from 'https://deno.land/[email protected]/http/server.ts'
33
import { acceptWebSocket, isWebSocketCloseEvent } from 'https://deno.land/[email protected]/ws/mod.ts'
44
import { trimModuleExt } from '../framework/core/module.ts'
5-
import { rewriteURL, RouteModule } from '../framework/core/routing.ts'
5+
import { rewriteURL } from '../framework/core/routing.ts'
66
import { existsFileSync } from '../shared/fs.ts'
77
import log from '../shared/log.ts'
88
import util from '../shared/util.ts'
@@ -47,7 +47,7 @@ export class Server {
4747
const { conn, r: bufReader, w: bufWriter, headers } = r
4848
const socket = await acceptWebSocket({ conn, bufReader, bufWriter, headers })
4949
const watcher = app.createFSWatcher()
50-
watcher.on('add', (mod: RouteModule) => socket.send(JSON.stringify({ ...mod, type: 'add' })))
50+
watcher.on('add', (mod: any) => socket.send(JSON.stringify({ ...mod, type: 'add' })))
5151
watcher.on('remove', (url: string) => {
5252
watcher.removeAllListeners('modify-' + url)
5353
socket.send(JSON.stringify({ type: 'remove', url }))

0 commit comments

Comments
 (0)