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

Commit e0038c2

Browse files
author
Je
committed
feat: add useDeno Hook (remove app data context)
1 parent c5381da commit e0038c2

File tree

12 files changed

+248
-233
lines changed

12 files changed

+248
-233
lines changed

aleph.ts

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { ComponentType, useCallback, useEffect, useRef, useState } from 'https://esm.sh/react'
2-
import { DataContext, RouterContext } from './context.ts'
2+
import { RouterContext } from './context.ts'
33
import { E400MissingDefaultExportAsComponent, E404Page, ErrorBoundary } from './error.ts'
44
import events from './events.ts'
55
import { createPageProps, RouteModule, Routing } from './routing.ts'
@@ -10,13 +10,11 @@ export function ALEPH({ initial }: {
1010
initial: {
1111
routing: Routing
1212
url: RouterURL
13-
staticData: Record<string, any>
1413
components: Record<string, ComponentType<any>>
1514
pageComponentTree: { id: string, Component?: any }[]
1615
}
1716
}) {
1817
const ref = useRef({ routing: initial.routing })
19-
const [staticData, setStaticData] = useState(() => initial.staticData)
2018
const [e404, setE404] = useState<{ Component: ComponentType<any>, props?: Record<string, any> }>(() => {
2119
const { E404 } = initial.components
2220
if (E404) {
@@ -83,10 +81,6 @@ export function ALEPH({ initial }: {
8381
useEffect(() => {
8482
const { routing } = ref.current
8583
const { baseUrl } = routing
86-
const onUpdateData = (data: any) => {
87-
console.log('[DATA]', data)
88-
setStaticData(data)
89-
}
9084
const onAddModule = async (mod: RouteModule) => {
9185
switch (mod.id) {
9286
case '/404.js': {
@@ -107,12 +101,6 @@ export function ALEPH({ initial }: {
107101
}
108102
break
109103
}
110-
case '/data.js': {
111-
const { default: data } = await import(getModuleImportUrl(baseUrl, mod, true))
112-
console.log('[DATA]', data)
113-
setStaticData(data)
114-
break
115-
}
116104
default: {
117105
if (mod.id.startsWith('/pages/')) {
118106
const { routing } = ref.current
@@ -131,10 +119,6 @@ export function ALEPH({ initial }: {
131119
case '/app.js':
132120
setApp({ Component: null })
133121
break
134-
case '/data.js':
135-
console.log('[DATA]', {})
136-
setStaticData({})
137-
break
138122
default:
139123
if (moduleId.startsWith('/pages/')) {
140124
const { routing } = ref.current
@@ -160,13 +144,11 @@ export function ALEPH({ initial }: {
160144
}
161145
}
162146

163-
events.on('update-data', onUpdateData)
164147
events.on('add-module', onAddModule)
165148
events.on('remove-module', onRemoveModule)
166149
events.on('fetch-page-module', onFetchPageModule)
167150

168151
return () => {
169-
events.off('update-data', onUpdateData)
170152
events.off('add-module', onAddModule)
171153
events.off('remove-module', onRemoveModule)
172154
events.off('fetch-page-module', onFetchPageModule)
@@ -178,17 +160,13 @@ export function ALEPH({ initial }: {
178160
ErrorBoundary,
179161
null,
180162
React.createElement(
181-
DataContext.Provider,
182-
{ value: staticData },
183-
React.createElement(
184-
RouterContext.Provider,
185-
{ value: route.url },
186-
...[
187-
(route.Page && app.Component) && React.createElement(app.Component, Object.assign({}, app.props, { Page: route.Page, pageProps: route.pageProps })),
188-
(route.Page && !app.Component) && React.createElement(route.Page, route.pageProps),
189-
!route.Page && React.createElement(e404.Component, e404.props)
190-
].filter(Boolean),
191-
)
163+
RouterContext.Provider,
164+
{ value: route.url },
165+
...[
166+
(route.Page && app.Component) && React.createElement(app.Component, Object.assign({}, app.props, { Page: route.Page, pageProps: route.pageProps })),
167+
(route.Page && !app.Component) && React.createElement(route.Page, route.pageProps),
168+
!route.Page && React.createElement(e404.Component, e404.props)
169+
].filter(Boolean),
192170
)
193171
)
194172
)

bootstrap.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,20 @@ export default async function bootstrap({
1919
}) {
2020
const { document } = window as any
2121
const mainEl = document.querySelector('main')
22-
const staticData: Record<string, any> = {}
22+
const ssrDataEl = document.querySelector('#ssr-data')
2323
const components: Record<string, ComponentType> = {}
2424
const routing = new Routing(routes, baseUrl, defaultLocale, locales)
2525
const [url, pageModuleTree] = routing.createRouter()
2626
const pageComponentTree: { id: string, Component?: ComponentType }[] = pageModuleTree.map(({ id }) => ({ id }))
2727
const imports = [...preloadModules, ...pageModuleTree].map(async mod => {
28-
const { default: C } = await import(getModuleImportUrl(baseUrl, mod))
28+
const { default: C, __pageProps } = await import(getModuleImportUrl(baseUrl, mod))
2929
if (mod.asyncDeps) {
3030
// import async dependencies
3131
for (const dep of mod.asyncDeps) {
3232
await import(getModuleImportUrl(baseUrl, { id: dep.url.replace(reModuleExt, '.js'), hash: dep.hash }))
3333
}
3434
}
3535
switch (mod.id) {
36-
case '/data.js':
37-
Object.assign(staticData, C)
38-
break
3936
case '/app.js':
4037
components['App'] = C
4138
break
@@ -52,19 +49,22 @@ export default async function bootstrap({
5249
})
5350
await Promise.all(imports)
5451

52+
const ssrData = JSON.parse(ssrDataEl.innerText)
53+
for (const key in ssrData) {
54+
Object.assign(window, { [`useDeno://${url.pathname}?${url.query.toString()}#${key}`]: ssrData[key] })
55+
}
56+
5557
const el = React.createElement(
5658
ALEPH,
5759
{
5860
initial: {
5961
routing,
6062
url,
61-
staticData,
6263
components,
6364
pageComponentTree,
6465
}
6566
}
6667
)
67-
6868
if (mainEl.childElementCount > 0) {
6969
hydrate(el, mainEl)
7070
} else {

cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ async function main() {
9797
log.setLevel(l)
9898
}
9999

100-
if (!hasCommand) {
100+
if (!hasCommand && !args[0]) {
101101
const walkOptions = { includeDirs: false, exts: ['.js', '.jsx', '.mjs', '.ts', '.tsx'], skip: [/\.d\.ts$/i], dep: 1 }
102-
const pagesDir = path.join(path.resolve(args[0] || '.'), 'pages')
102+
const pagesDir = path.join(path.resolve('.'), 'pages')
103103
let hasIndexPage = false
104104
if (existsDirSync(pagesDir)) {
105105
for await (const { path: p } of walk(pagesDir, walkOptions)) {

context.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
import { createContext } from 'https://esm.sh/react'
22
import type { RouterURL } from './types.ts'
33

4-
export const DataContext = createNamedContext<Record<string, any>>(
5-
'DataContext',
6-
{}
7-
)
8-
9-
export const RouterContext = createNamedContext<RouterURL>(
10-
'RouterContext',
11-
{
12-
locale: 'en',
13-
pagePath: '/',
14-
pathname: '/',
15-
params: {},
16-
query: new URLSearchParams(),
17-
}
18-
)
19-
20-
function createNamedContext<T>(name: string, defaultValue: T) {
21-
const c = createContext(defaultValue)
22-
c.displayName = name
23-
return c
24-
}
4+
export const RouterContext = createContext<RouterURL>({
5+
locale: 'en',
6+
pagePath: '/',
7+
pathname: '/',
8+
params: {},
9+
query: new URLSearchParams(),
10+
})
11+
RouterContext.displayName = 'RouterContext'

error.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ export class ErrorBoundary extends React.Component {
1111

1212
static getDerivedStateFromError(error: Error) {
1313
// Update state so the next render will show the fallback UI.
14-
return { stack: error.stack }
14+
return { stack: error?.stack || null }
1515
}
1616

1717
componentDidCatch(error: any, errorInfo: any) {
18-
this.state = { stack: error.stack }
18+
this.state = { stack: error?.stack || null }
1919
}
2020

2121
render() {
@@ -36,26 +36,26 @@ export class ErrorBoundary extends React.Component {
3636

3737
export function E404Page() {
3838
return React.createElement(
39-
Error,
39+
StatusError,
4040
{
4141
status: 404,
42-
message: 'page not found'
42+
message: 'Page not found'
4343
}
4444
)
4545
}
4646

4747
export function E400MissingDefaultExportAsComponent({ name }: { name: string }) {
4848
return React.createElement(
49-
Error,
49+
StatusError,
5050
{
5151
status: 400,
52-
message: `"${name}" should export a React Component as default`,
53-
refreshButton: true
52+
message: `Module "${name}" should export a React Component as default`,
53+
showRefreshButton: true
5454
}
5555
)
5656
}
5757

58-
export function Error({ status, message, refreshButton }: { status: number, message: string, refreshButton?: boolean }) {
58+
export function StatusError({ status, message, showRefreshButton }: { status: number, message: string, showRefreshButton?: boolean }) {
5959
return (
6060
React.createElement(
6161
React.Fragment,
@@ -93,7 +93,7 @@ export function Error({ status, message, refreshButton }: { status: number, mess
9393
message
9494
)
9595
),
96-
refreshButton && React.createElement(
96+
showRefreshButton && React.createElement(
9797
'p',
9898
null,
9999
React.createElement(
@@ -110,3 +110,5 @@ export function Error({ status, message, refreshButton }: { status: number, mess
110110
)
111111
)
112112
}
113+
114+
export class AsyncUseDenoError extends Error { }

hooks.ts

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,53 @@
1-
import React, { ComponentType, useContext } from 'https://esm.sh/react'
2-
import { DataContext, RouterContext } from './context.ts'
3-
import type { RouterURL } from './types.ts'
4-
5-
export function useData(key: string) {
6-
const data = useContext(DataContext)
7-
return data[key]
8-
}
1+
import { DependencyList, useContext, useEffect, useState } from 'https://esm.sh/react'
2+
import { RouterContext } from './context.ts'
3+
import { AsyncUseDenoError } from './error.ts'
4+
import events from './events.ts'
95

106
export function useRouter() {
117
return useContext(RouterContext)
128
}
139

14-
export function withRouter(Component: ComponentType<{ url: RouterURL }>) {
15-
function WithRouter(props: any) {
16-
const url = useRouter()
17-
return React.createElement(Component, Object.assign({}, props, { url }))
18-
}
19-
return WithRouter
20-
}
10+
export function useDeno<T = any>(callback: () => (T | Promise<T>), browser?: boolean, deps?: DependencyList) {
11+
const id = arguments[3] // generated by compiler
12+
const { pathname, query } = useRouter()
13+
const [data, setDate] = useState<T>(() => {
14+
const global = window as any
15+
const { _useDenoAsyncData: asyncData } = global
16+
const useDenoUrl = `useDeno://${pathname}?${query.toString()}`
17+
const key = `${useDenoUrl}#${id}`
18+
if (asyncData && key in asyncData) {
19+
return asyncData[key]
20+
} else if (typeof Deno !== 'undefined' && Deno.version.deno) {
21+
const ret = callback()
22+
if (ret instanceof Promise) {
23+
events.emit(useDenoUrl, id, ret.then(data => {
24+
if (asyncData) {
25+
asyncData[key] = data
26+
}
27+
events.emit(useDenoUrl, id, data)
28+
}), true)
29+
throw new AsyncUseDenoError('async useDeno')
30+
} else {
31+
if (asyncData) {
32+
asyncData[key] = ret
33+
}
34+
events.emit(useDenoUrl, id, ret)
35+
return ret
36+
}
37+
}
38+
return global[key] || null
39+
})
40+
41+
useEffect(() => {
42+
if (browser) {
43+
const ret = callback()
44+
if (ret instanceof Promise) {
45+
ret.then(setDate)
46+
} else {
47+
setDate(ret)
48+
}
49+
}
50+
}, deps)
51+
52+
return data
53+
}

0 commit comments

Comments
 (0)