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

Commit 799254f

Browse files
author
Je
committed
feat: add markdown page renderer
1 parent 8ced27c commit 799254f

File tree

5 files changed

+76
-71
lines changed

5 files changed

+76
-71
lines changed

app.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import util, { hashShort, reModuleExt } from './util.ts'
99
export function ALEPH({ initial }: {
1010
initial: {
1111
manifest: AppManifest
12-
pageModules: Record<string, Module>
12+
routing: Record<string, Module>
1313
url: RouterURL
14-
data: Record<string, any>
14+
staticData: Record<string, any>
1515
components: Record<string, ComponentType<any>>
1616
}
1717
}) {
1818
const [manifest, setManifest] = useState(() => initial.manifest)
19-
const [data, setData] = useState(() => initial.data)
20-
const [pageModules, setPageModules] = useState(() => initial.pageModules)
19+
const [staticData, setStaticData] = useState(() => initial.staticData)
20+
const [routing, setRouting] = useState(() => initial.routing)
2121
const [e404, setE404] = useState(() => {
2222
const { E404 } = initial.components
2323
return {
@@ -42,14 +42,14 @@ export function ALEPH({ initial }: {
4242
const { baseUrl, defaultLocale, locales } = manifest
4343
const url = createRouter(
4444
baseUrl,
45-
Object.keys(pageModules),
45+
Object.keys(routing),
4646
{
4747
defaultLocale,
4848
locales: Object.keys(locales)
4949
}
5050
)
51-
if (url.pagePath && url.pagePath in pageModules) {
52-
const mod = pageModules[url.pagePath]!
51+
if (url.pagePath && url.pagePath in routing) {
52+
const mod = routing[url.pagePath]!
5353
const { default: Component } = await import(getModuleImportUrl(baseUrl, mod))
5454
await Promise.all(mod.asyncDeps?.map(dep => {
5555
return import(util.cleanPath(`${baseUrl}/_aleph/${dep.url.replace(reModuleExt, '')}.${dep.hash.slice(0, hashShort)}.js`))
@@ -65,7 +65,7 @@ export function ALEPH({ initial }: {
6565
} else {
6666
setPage({ url, Component: null })
6767
}
68-
}, [manifest, pageModules])
68+
}, [manifest, routing])
6969

7070
useEffect(() => {
7171
window.addEventListener('popstate', onpopstate)
@@ -81,7 +81,7 @@ export function ALEPH({ initial }: {
8181
const { baseUrl } = manifest
8282
const onUpdateData = (data: any) => {
8383
console.log('[DATA]', data)
84-
setData(data)
84+
setStaticData(data)
8585
}
8686
const onAddModule = async (mod: Module) => {
8787
if (mod.id === '/404.js') {
@@ -104,10 +104,10 @@ export function ALEPH({ initial }: {
104104
} else if (mod.id === '/data.js' || mod.id === '/data/index.js') {
105105
const { default: data } = await import(getModuleImportUrl(baseUrl, mod) + '?t=' + Date.now())
106106
console.log('[DATA]', data)
107-
setData(data)
107+
setStaticData(data)
108108
} else if (mod.id.startsWith('/pages/')) {
109109
const pagePath = util.trimSuffix(mod.id, '.js').replace(/\s+/g, '-').replace(/\/?index$/i, '/')
110-
setPageModules(pageModules => ({
110+
setRouting(pageModules => ({
111111
...pageModules,
112112
[pagePath]: mod
113113
}))
@@ -120,9 +120,9 @@ export function ALEPH({ initial }: {
120120
setApp({ Component: null })
121121
} else if (moduleId === '/data.js' || moduleId === '/data/index.js') {
122122
console.log('[DATA]', {})
123-
setData({})
123+
setStaticData({})
124124
} else if (moduleId.startsWith('/pages/')) {
125-
setPageModules(pageModules => {
125+
setRouting(pageModules => {
126126
const newPageModules: Record<string, Module> = {}
127127
for (const pagePath in pageModules) {
128128
const mod = pageModules[pagePath]
@@ -151,7 +151,7 @@ export function ALEPH({ initial }: {
151151
{ value: manifest },
152152
React.createElement(
153153
DataContext.Provider,
154-
{ value: data },
154+
{ value: staticData },
155155
React.createElement(
156156
RouterContext.Provider,
157157
{ value: page.url },

bootstrap.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ export default async function bootstrap({
1010
baseUrl,
1111
defaultLocale,
1212
locales,
13-
coreModules,
14-
pageModules
13+
staticDataModule,
14+
customAppModule,
15+
custom404Module,
16+
routing
1517
}: AppManifest & {
16-
coreModules: Record<string, Module>
17-
pageModules: Record<string, Module>
18+
staticDataModule?: Module
19+
customAppModule?: Module
20+
custom404Module?: Module
21+
routing: Record<string, Module>
1822
}) {
1923
const { document } = window as any
2024
const mainEl = document.querySelector('main')
@@ -31,28 +35,28 @@ export default async function bootstrap({
3135
} else {
3236
url = createRouter(
3337
baseUrl,
34-
Object.keys(pageModules),
38+
Object.keys(routing),
3539
{
3640
defaultLocale,
3741
locales: Object.keys(locales)
3842
}
3943
)
4044
}
4145

42-
const pageModule = pageModules[url.pagePath]
46+
const pageModule = routing[url.pagePath]
4347
if (!pageModule) {
4448
throw new Error('page module not found')
4549
}
4650

4751
const [
48-
{ default: data },
52+
{ default: staticData },
4953
{ default: App },
5054
{ default: E404 },
5155
{ default: Page }
5256
] = await Promise.all([
53-
coreModules.data ? import(getModuleImportUrl(baseUrl, coreModules.data)) : Promise.resolve({ default: {} }),
54-
coreModules.app ? import(getModuleImportUrl(baseUrl, coreModules.app)) : Promise.resolve({}),
55-
coreModules['404'] ? import(getModuleImportUrl(baseUrl, coreModules['404'])) : Promise.resolve({}),
57+
staticDataModule ? import(getModuleImportUrl(baseUrl, staticDataModule)) : Promise.resolve({ default: {} }),
58+
customAppModule ? import(getModuleImportUrl(baseUrl, customAppModule)) : Promise.resolve({}),
59+
custom404Module ? import(getModuleImportUrl(baseUrl, custom404Module)) : Promise.resolve({}),
5660
pageModule ? import(getModuleImportUrl(baseUrl, pageModule)) : Promise.resolve({}),
5761
])
5862
const el = React.createElement(
@@ -63,9 +67,9 @@ export default async function bootstrap({
6367
{
6468
initial: {
6569
manifest: { baseUrl, defaultLocale, locales },
66-
pageModules,
70+
routing,
6771
url,
68-
data,
72+
staticData,
6973
components: { E404, App, Page }
7074
}
7175
}
@@ -74,10 +78,8 @@ export default async function bootstrap({
7478

7579
// import async sytle dependencies
7680
const asyncDeps: { url: string, hash: string }[] = []
77-
for (const key in coreModules) {
78-
const mod = coreModules[key]
79-
mod.asyncDeps?.forEach(deps => asyncDeps.push(deps))
80-
}
81+
customAppModule?.asyncDeps?.forEach(deps => asyncDeps.push(deps))
82+
custom404Module?.asyncDeps?.forEach(deps => asyncDeps.push(deps))
8183
pageModule.asyncDeps?.forEach(deps => asyncDeps.push(deps))
8284
await Promise.all(asyncDeps.map(dep => {
8385
return import(util.cleanPath(`${baseUrl}/_aleph/${dep.url.replace(reModuleExt, '')}.${dep.hash.slice(0, hashShort)}.js`))

head.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import util, { hashShort } from './util.ts'
55
const serverHeadElements: Array<{ type: string, props: Record<string, any> }> = []
66
const serverStyles: Map<string, { css: string, asLink: boolean }> = new Map()
77

8-
export async function renderHead(styleModules?: { url: string, hash: string, async?: boolean }[]) {
8+
export async function renderHead(styles?: { url: string, hash: string, async?: boolean }[]) {
99
const { build: { appRoot, buildID } } = (window as any).ALEPH_ENV as AlephEnv
1010
const tags: string[] = []
1111
serverHeadElements.forEach(({ type, props }) => {
@@ -29,10 +29,10 @@ export async function renderHead(styleModules?: { url: string, hash: string, asy
2929
}
3030
}
3131
})
32-
await Promise.all(styleModules?.filter(({ async }) => !!async).map(({ url, hash }) => {
32+
await Promise.all(styles?.filter(({ async }) => !!async).map(({ url, hash }) => {
3333
return import('file://' + util.cleanPath(`${appRoot}/.aleph/build-${buildID}/${url}.${hash.slice(0, hashShort)}.js`))
3434
}) || [])
35-
styleModules?.forEach(({ url }) => {
35+
styles?.forEach(({ url }) => {
3636
if (serverStyles.has(url)) {
3737
const { css, asLink } = serverStyles.get(url)!
3838
if (asLink) {

project.ts

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import marked from 'https://esm.sh/marked'
12
import { minify } from 'https://esm.sh/terser'
3+
import { loadFront } from 'https://esm.sh/yaml-front-matter'
24
import { EventEmitter } from './events.ts'
35
import { createHtml } from './html.ts'
46
import log from './log.ts'
@@ -130,14 +132,15 @@ export default class Project {
130132
if (!reStyleModuleExt.test(modId)) {
131133
modId = modId + '.js'
132134
}
133-
} else {
135+
} else if (modId.endsWith('.js')) {
136+
let id = modId.slice(0, modId.length - 3)
134137
if (reHashJs.test(modId)) {
135-
const id = modId.slice(0, modId.length - (hashShort + 4))
136-
if (reStyleModuleExt.test(id)) {
137-
modId = id
138-
} else {
139-
modId = id + '.js'
140-
}
138+
id = modId.slice(0, modId.length - (1 + hashShort + 3))
139+
}
140+
if (reMDExt.test(id) || reStyleModuleExt.test(id)) {
141+
modId = id
142+
} else {
143+
modId = id + '.js'
141144
}
142145
}
143146
if (!this.#modules.has(modId)) {
@@ -434,7 +437,7 @@ export default class Project {
434437

435438
for await (const { path: p } of walk(pagesDir, { ...walkOptions, exts: [...walkOptions.exts, '.jsx', '.tsx', '.md', '.mdx'] })) {
436439
const rp = util.trimPrefix(p, pagesDir)
437-
const pagePath = rp.replace(reModuleExt, '').replace(/\s+/g, '-').replace(/\/index$/i, '') || '/'
440+
const pagePath = rp.replace(reModuleExt, '').replace(reMDExt, '').replace(/\s+/g, '-').replace(/\/index$/i, '/')
438441
const mod = await this._compile('/pages' + rp)
439442
this.#pageModules.set(pagePath, {
440443
moduleID: mod.id,
@@ -520,7 +523,7 @@ export default class Project {
520523
if (this.#pageModules.has(moduleID)) {
521524
this._clearPageRenderCache(moduleID)
522525
} else {
523-
const pagePath = util.trimPrefix(moduleID, '/pages').replace(reModuleExt, '').replace(/\s+/g, '-').replace(/\/index$/i, '') || '/'
526+
const pagePath = util.trimPrefix(moduleID, '/pages').replace(reModuleExt, '').replace(reMDExt, '').replace(/\s+/g, '-').replace(/\/index$/i, '/')
524527
this.#pageModules.set(pagePath, { moduleID, rendered: new Map() })
525528
}
526529
}
@@ -618,8 +621,7 @@ export default class Project {
618621
baseUrl,
619622
defaultLocale,
620623
locales: {},
621-
coreModules: {},
622-
pageModules: {}
624+
routing: {}
623625
}
624626
const module = this._moduleFromURL('/main.js')
625627
const deps = [
@@ -631,35 +633,29 @@ export default class Project {
631633
}))
632634
if (this.#modules.has('/data.js')) {
633635
const { id, url, hash } = this.#modules.get('/data.js')!
634-
config.coreModules.data = { id, hash }
636+
const asyncDeps = this._lookupStyleDeps(id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
637+
config.staticDataModule = { id, hash, asyncDeps }
635638
deps.push({ url, hash })
636639
}
637640
if (this.#modules.has('/app.js')) {
638-
const { url, hash } = this.#modules.get('/app.js')!
639-
config.coreModules.app = { id: '/app.js', hash }
641+
const { id, url, hash } = this.#modules.get('/app.js')!
642+
const asyncDeps = this._lookupStyleDeps(id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
643+
config.customAppModule = { id, hash, asyncDeps }
640644
deps.push({ url, hash })
641645
}
642646
if (this.#modules.has('/404.js')) {
643-
const { url, hash } = this.#modules.get('/404.js')!
644-
config.coreModules['404'] = { id: '/404.js', hash }
647+
const { id, url, hash } = this.#modules.get('/404.js')!
648+
const asyncDeps = this._lookupStyleDeps(id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
649+
config.custom404Module = { id: '/404.js', hash, asyncDeps }
645650
deps.push({ url, hash })
646651
}
647652
this.#pageModules.forEach(({ moduleID }, pagePath) => {
648-
const { url, hash } = this.#modules.get(moduleID)!
649-
config.pageModules[pagePath] = { id: moduleID, hash }
653+
const { id, url, hash } = this.#modules.get(moduleID)!
654+
const asyncDeps = this._lookupStyleDeps(id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
655+
config.routing[pagePath] = { id, hash, asyncDeps }
650656
deps.push({ url, hash })
651657
})
652658

653-
for (const key in config.coreModules) {
654-
const m = config.coreModules[key]
655-
m.asyncDeps = this._lookupStyleDeps(m.id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
656-
}
657-
658-
for (const key in config.pageModules) {
659-
const m = config.pageModules[key]
660-
m.asyncDeps = this._lookupStyleDeps(m.id).filter(({ async }) => !!async).map(({ url, hash }) => ({ url, hash }))
661-
}
662-
663659
module.jsContent = [
664660
this.isDev && 'import "./-/deno.land/x/aleph/hmr.js";',
665661
'import bootstrap from "./-/deno.land/x/aleph/bootstrap.js";',
@@ -857,11 +853,18 @@ export default class Project {
857853
mod.hash = hash
858854
} else if (mod.sourceType === 'sass' || mod.sourceType === 'scss') {
859855
// todo: support sass
860-
} else if (mod.sourceType === 'md' || mod.sourceType === 'mdx') {
861-
mod.jsContent = `export default function MD() { return React.createElement('pre', null, ${JSON.stringify(sourceContent)})}`
862-
mod.jsSourceMap = ''
863-
mod.hash = mod.sourceHash
864856
} else {
857+
if (mod.sourceType === 'md' || mod.sourceType === 'mdx') {
858+
const { __content, ...props } = loadFront(sourceContent)
859+
const html = marked.parse(__content)
860+
sourceContent = [
861+
`import React from 'https://esm.sh/react';`,
862+
`export const pageProps = ${JSON.stringify(props, undefined, 4)};`,
863+
`export default function Marked() {`,
864+
` return React.createElement('div', ${JSON.stringify({ className: 'marked', dangerouslySetInnerHTML: { __html: html } }, undefined, 4)});`,
865+
`}`
866+
].join('\n')
867+
}
865868
const compileOptions = {
866869
target: this.config.buildTarget,
867870
mode: this.mode,
@@ -870,12 +873,12 @@ export default class Project {
870873
}
871874
const { diagnostics, outputText, sourceMapText } = compile(mod.sourceFilePath, sourceContent, compileOptions)
872875
if (diagnostics && diagnostics.length > 0) {
873-
throw new Error(`compile ${url}: ${diagnostics.map(d => d.messageText).join(' ')}`)
876+
throw new Error(`compile ${url}: ${diagnostics.map(d => d.messageText).join('\n')}`)
874877
}
875-
const jsContent = outputText.replace(/import([^'"]*)("|')tslib("|')(\)|;)?/g, 'import$1' + JSON.stringify(relativePath(
878+
const jsContent = outputText.replace(/import\s*{([^}]+)}\s*from\s*("|')tslib("|');?/g, 'import {$1} from ' + JSON.stringify(relativePath(
876879
path.dirname(mod.sourceFilePath),
877880
'/-/deno.land/x/aleph/tsc/tslib.js'
878-
)) + '$4')
881+
)) + ';')
879882
if (this.isDev) {
880883
mod.jsContent = jsContent
881884
mod.jsSourceMap = sourceMapText!
@@ -1114,7 +1117,7 @@ export default class Project {
11141117
return a
11151118
}
11161119
s.add(moduleID)
1117-
a.push(...mod.deps.filter(({ url }) => reStyleModuleExt.test(url)))
1120+
a.push(...mod.deps.filter(({ url }) => reStyleModuleExt.test(url) && a.findIndex(i => i.url === url) === -1))
11181121
mod.deps.forEach(({ url }) => {
11191122
if (reModuleExt.test(url) && !reHttp.test(url)) {
11201123
this._lookupStyleDeps(url.replace(reModuleExt, '.js'), a, s)
@@ -1138,7 +1141,7 @@ export function injectHmr({ id, sourceFilePath, jsContent }: Module): string {
11381141
`import { createHotContext, RefreshRuntime, performReactRefresh } from ${JSON.stringify(hmrImportPath)};`,
11391142
`import.meta.hot = createHotContext(${JSON.stringify(id)});`
11401143
]
1141-
const reactRefresh = id.endsWith('.js')
1144+
const reactRefresh = id.endsWith('.js') || id.endsWith('.md') || id.endsWith('.mdx')
11421145
if (reactRefresh) {
11431146
text.push('')
11441147
text.push(

server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export async function start(appDir: string, port: number, isDev = false) {
116116
}
117117

118118
let body = ''
119-
if (mod.id === '/data.js' || mod.id === '/data/index.js') {
119+
if (mod.id === '/data.js') {
120120
const data = await project.getData()
121121
if (project.isDev) {
122122
body = [

0 commit comments

Comments
 (0)