-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod.ts
90 lines (77 loc) · 3.16 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// @deno-types="https://deno.land/x/types/react/v16.13.1/react.d.ts"
import React from 'https://dev.jspm.io/react@16.13.1'
// @deno-types="https://deno.land/x/types/react/v16.13.1/react.d.ts"
import { Application, Router } from 'https://deno.land/x/oak@v6.0.1/mod.ts'
import { join, fromFileUrl } from 'https://deno.land/std@0.62.0/path/mod.ts'
import { Renderer, createRenderer } from './renderer.tsx'
import { getQuery } from 'https://deno.land/x/oak@v6.0.1/helpers.ts'
type Option = {
port?: number
}
type Dependencies = {
application?: Application
router?: Router
renderer?: Renderer
}
export class DexrApp {
readonly #application: Application
readonly #router: Router
#renderer: Renderer = createRenderer()
#isStart: boolean = false
#compiledModule: Map<string, string> = new Map()
constructor(dependencies?: Dependencies) {
this.#application = dependencies?.application ?? new Application()
this.#router = dependencies?.router ?? new Router()
}
useRenderer(layout: Renderer): this {
this.#renderer = layout
return this
}
async addPage(route: string, componentPath: string): Promise<void>
async addPage<T extends {}, U extends {}, P extends {}>(route: string, componentPath: string, renderProps?: (params: T, query: U) => Promise<P>): Promise<void>
async addPage<T extends {}, U extends {}, P extends {}>(route: string, componentPath: string, renderProps?: (params: T, query: U) => Promise<P>) {
const fullPath = join(Deno.cwd(), componentPath)
const App = (await import(`file://${ fullPath }`)).default
const [, script] = await Deno.compile(fullPath)
Object.entries(script).forEach(([key, source]) => {
const filePath = fromFileUrl(key).replace(Deno.cwd(), '')
this.#compiledModule.set(filePath, source)
})
this.#router.get(route, async (context) => {
context.response.headers = new Headers({
'content-type': 'text/html; charset=UTF-8',
})
const query = getQuery(context, { mergeParams: false }) as U
const appProps = renderProps ? await renderProps(context.params as T, query) : undefined
context.response.body = this.#renderer.render(App, componentPath, appProps)
})
}
async run(option?: Option) {
if (this.#isStart) throw new Error('Dexr is already run!!!')
// sync deno's import/client import
for (const [key, source] of this.#compiledModule.entries()) {
if (!key.includes('.js.map')) {
this.#router.get(key.replace('.js', '.tsx'), (context) => {
context.response.headers = new Headers({
'content-type': 'text/javascript; charset=UTF-8',
})
context.response.body = source
})
this.#router.get(key.replace('.js', '.ts'), (context) => {
context.response.headers = new Headers({
'content-type': 'text/javascript; charset=UTF-8',
})
context.response.body = source
})
}
}
const {
port = 8000,
} = option || {}
this.#application.use(this.#router.routes())
this.#application.listen({ port })
this.#isStart = true
console.log(`serve: http://localhost:${ port }/`)
}
}
export const createDexr = () => new DexrApp()