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

Commit 7c81de9

Browse files
author
Je
committed
refactor: split app module
1 parent c1ca751 commit 7c81de9

File tree

6 files changed

+122
-116
lines changed

6 files changed

+122
-116
lines changed

app.ts

Lines changed: 3 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import React, { ComponentType, createContext, useCallback, useEffect, useState } from 'https://esm.sh/react'
2-
import { hydrate, render } from 'https://esm.sh/react-dom'
32
import { DataContext } from './data.ts'
43
import { E404Page, E501 } from './error.ts'
54
import events from './events.ts'
65
import route from './route.ts'
76
import { RouterContext } from './router.ts'
8-
import type { AppManifest, RouterURL } from './types.ts'
7+
import type { AppManifest, Module, RouterURL } from './types.ts'
98
import util, { hashShort } from './util.ts'
109

1110
export const AppManifestContext = createContext<AppManifest>({
@@ -15,7 +14,7 @@ export const AppManifestContext = createContext<AppManifest>({
1514
})
1615
AppManifestContext.displayName = 'AppManifestContext'
1716

18-
function ALEPH({ initial }: {
17+
export function ALEPH({ initial }: {
1918
initial: {
2019
manifest: AppManifest
2120
pageModules: Record<string, { moduleId: string, hash: string }>
@@ -168,115 +167,6 @@ function ALEPH({ initial }: {
168167
)
169168
}
170169

171-
export async function redirect(url: string, replace: boolean) {
172-
const { location, document, history } = window as any
173-
174-
if (util.isHttpUrl(url)) {
175-
location.href = url
176-
return
177-
}
178-
179-
url = util.cleanPath(url)
180-
if (location.protocol === 'file:') {
181-
const dataEl = document.getElementById('ssr-data')
182-
if (dataEl) {
183-
const ssrData = JSON.parse(dataEl.innerHTML)
184-
if (ssrData && 'url' in ssrData) {
185-
const { url: { pagePath: initialPagePath } } = ssrData
186-
location.href = location.href.replace(
187-
`/${util.trimPrefix(initialPagePath, '/') || 'index'}.html`,
188-
`/${util.trimPrefix(url, '/') || 'index'}.html`
189-
)
190-
}
191-
}
192-
return
193-
}
194-
195-
if (replace) {
196-
history.replaceState(null, '', url)
197-
} else {
198-
history.pushState(null, '', url)
199-
}
200-
events.emit('popstate', { type: 'popstate' })
201-
}
202-
203-
interface Module {
204-
moduleId: string,
205-
hash: string,
206-
}
207-
208-
export async function bootstrap({
209-
baseUrl,
210-
defaultLocale,
211-
locales,
212-
keyModules,
213-
pageModules
214-
}: AppManifest & {
215-
keyModules: Record<string, Module>
216-
pageModules: Record<string, Module>
217-
}) {
218-
const { document } = window as any
219-
const mainEl = document.querySelector('main')
220-
const dataEl = document.getElementById('ssr-data')
221-
222-
let url: RouterURL
223-
if (dataEl) {
224-
const data = JSON.parse(dataEl.innerHTML)
225-
if (util.isPlainObject(data.url)) {
226-
url = data.url
227-
} else {
228-
throw new Error("invalid ssr-data")
229-
}
230-
} else {
231-
url = route(
232-
baseUrl,
233-
Object.keys(pageModules),
234-
{
235-
defaultLocale,
236-
locales: Object.keys(locales)
237-
}
238-
)
239-
}
240-
241-
const pageModule = pageModules[url.pagePath]!
242-
const [
243-
{ default: data },
244-
{ default: App },
245-
{ default: E404 },
246-
{ default: Page }
247-
] = await Promise.all([
248-
keyModules.data ? import(getModuleImportUrl(baseUrl, keyModules.data)) : Promise.resolve({ default: {} }),
249-
keyModules.app ? import(getModuleImportUrl(baseUrl, keyModules.app)) : Promise.resolve({}),
250-
keyModules['404'] ? import(getModuleImportUrl(baseUrl, keyModules['404'])) : Promise.resolve({}),
251-
pageModule ? import(getModuleImportUrl(baseUrl, pageModule)) : Promise.resolve({}),
252-
])
253-
const el = React.createElement(
254-
ALEPH,
255-
{
256-
initial: {
257-
manifest: { baseUrl, defaultLocale, locales },
258-
pageModules,
259-
url,
260-
data,
261-
components: { E404, App, Page }
262-
}
263-
}
264-
)
265-
if (dataEl) {
266-
hydrate(el, mainEl)
267-
// remove ssr head elements, set a timmer to avoid tab title flash
268-
setTimeout(() => {
269-
Array.from(document.head.children).forEach((el: any) => {
270-
if (el.hasAttribute('ssr')) {
271-
document.head.removeChild(el)
272-
}
273-
})
274-
}, 0)
275-
} else {
276-
render(el, mainEl)
277-
}
278-
}
279-
280-
function getModuleImportUrl(baseUrl: string, { moduleId, hash }: Module) {
170+
export function getModuleImportUrl(baseUrl: string, { moduleId, hash }: Module) {
281171
return util.cleanPath(baseUrl + '/_aleph/' + moduleId.replace(/\.js$/, `.${hash.slice(0, hashShort)}.js`))
282172
}

bootstrap.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from 'https://esm.sh/react'
2+
import { hydrate, render } from 'https://esm.sh/react-dom'
3+
import { ALEPH, getModuleImportUrl } from './app.ts'
4+
import route from './route.ts'
5+
import type { AppManifest, Module, RouterURL } from './types.ts'
6+
import util from './util.ts'
7+
8+
export default async function bootstrap({
9+
baseUrl,
10+
defaultLocale,
11+
locales,
12+
keyModules,
13+
pageModules
14+
}: AppManifest & {
15+
keyModules: Record<string, Module>
16+
pageModules: Record<string, Module>
17+
}) {
18+
const { document } = window as any
19+
const mainEl = document.querySelector('main')
20+
const dataEl = document.getElementById('ssr-data')
21+
22+
let url: RouterURL
23+
if (dataEl) {
24+
const data = JSON.parse(dataEl.innerHTML)
25+
if (util.isPlainObject(data.url)) {
26+
url = data.url
27+
} else {
28+
throw new Error("invalid ssr-data")
29+
}
30+
} else {
31+
url = route(
32+
baseUrl,
33+
Object.keys(pageModules),
34+
{
35+
defaultLocale,
36+
locales: Object.keys(locales)
37+
}
38+
)
39+
}
40+
41+
const pageModule = pageModules[url.pagePath]!
42+
const [
43+
{ default: data },
44+
{ default: App },
45+
{ default: E404 },
46+
{ default: Page }
47+
] = await Promise.all([
48+
keyModules.data ? import(getModuleImportUrl(baseUrl, keyModules.data)) : Promise.resolve({ default: {} }),
49+
keyModules.app ? import(getModuleImportUrl(baseUrl, keyModules.app)) : Promise.resolve({}),
50+
keyModules['404'] ? import(getModuleImportUrl(baseUrl, keyModules['404'])) : Promise.resolve({}),
51+
pageModule ? import(getModuleImportUrl(baseUrl, pageModule)) : Promise.resolve({}),
52+
])
53+
const el = React.createElement(
54+
ALEPH,
55+
{
56+
initial: {
57+
manifest: { baseUrl, defaultLocale, locales },
58+
pageModules,
59+
url,
60+
data,
61+
components: { E404, App, Page }
62+
}
63+
}
64+
)
65+
if (dataEl) {
66+
hydrate(el, mainEl)
67+
// remove ssr head elements, set a timmer to avoid tab title flash
68+
setTimeout(() => {
69+
Array.from(document.head.children).forEach((el: any) => {
70+
if (el.hasAttribute('ssr')) {
71+
document.head.removeChild(el)
72+
}
73+
})
74+
}, 0)
75+
} else {
76+
render(el, mainEl)
77+
}
78+
}

link.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { Children, cloneElement, CSSProperties, isValidElement, MouseEvent, PropsWithChildren, useCallback, useEffect, useMemo, useRef } from 'https://esm.sh/react'
2-
import { redirect } from './app.ts'
3-
import { useRouter } from './router.ts'
2+
import { redirect, useRouter } from './router.ts'
43
import util from './util.ts'
54

65
interface LinkProps {

mod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './app.ts'
1+
export { AppManifestContext } from './app.ts'
22
export * from './data.ts'
33
export { ErrorPage } from './error.ts'
44
export { default as Head } from './head.ts'

router.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React, { ComponentType, createContext, useContext } from 'https://esm.sh/react'
2+
import events from './events.ts'
23
import type { RouterURL } from './types.ts'
4+
import util from './util.ts'
35

46
export const RouterContext = createContext<RouterURL>({
57
locale: 'en',
@@ -21,3 +23,35 @@ export function withRouter(Component: ComponentType<{ url: RouterURL }>) {
2123
export function useRouter() {
2224
return useContext(RouterContext)
2325
}
26+
27+
export async function redirect(url: string, replace: boolean) {
28+
const { location, document, history } = window as any
29+
30+
if (util.isHttpUrl(url)) {
31+
location.href = url
32+
return
33+
}
34+
35+
url = util.cleanPath(url)
36+
if (location.protocol === 'file:') {
37+
const dataEl = document.getElementById('ssr-data')
38+
if (dataEl) {
39+
const ssrData = JSON.parse(dataEl.innerHTML)
40+
if (ssrData && 'url' in ssrData) {
41+
const { url: { pagePath: initialPagePath } } = ssrData
42+
location.href = location.href.replace(
43+
`/${util.trimPrefix(initialPagePath, '/') || 'index'}.html`,
44+
`/${util.trimPrefix(url, '/') || 'index'}.html`
45+
)
46+
}
47+
}
48+
return
49+
}
50+
51+
if (replace) {
52+
history.replaceState(null, '', url)
53+
} else {
54+
history.pushState(null, '', url)
55+
}
56+
events.emit('popstate', { type: 'popstate' })
57+
}

types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,8 @@ export interface APIResponse {
5858
export interface APIHandle {
5959
(req: APIRequest, res: APIResponse): void
6060
}
61+
62+
export interface Module {
63+
moduleId: string,
64+
hash: string,
65+
}

0 commit comments

Comments
 (0)