1
1
import type { Aleph , LoadInput , LoadOutput , ResolveResult , Plugin } from '../types.d.ts'
2
- import marked from 'https://esm.sh/marked@2 .0.1 '
2
+ import marked from 'https://esm.sh/marked@3 .0.4 '
3
3
import { safeLoadFront } from 'https://esm.sh/[email protected] '
4
4
import util from '../shared/util.ts'
5
5
6
- export const test = / \. ( m d | m a r k d o w n ) $ / i
6
+ const hljsUri = 'https://esm.sh/[email protected] '
7
+ const languages = new Set ( [
8
+ '1c' ,
9
+ 'abnf' ,
10
+ 'accesslog' ,
11
+ 'actionscript' ,
12
+ 'ada' ,
13
+ 'angelscript' ,
14
+ 'apache' ,
15
+ 'applescript' ,
16
+ 'arcade' ,
17
+ 'arduino' ,
18
+ 'armasm' ,
19
+ 'xml' ,
20
+ 'asciidoc' ,
21
+ 'aspectj' ,
22
+ 'autohotkey' ,
23
+ 'autoit' ,
24
+ 'avrasm' ,
25
+ 'awk' ,
26
+ 'axapta' ,
27
+ 'bash' ,
28
+ 'basic' ,
29
+ 'bnf' ,
30
+ 'brainfuck' ,
31
+ 'c' ,
32
+ 'cal' ,
33
+ 'capnproto' ,
34
+ 'ceylon' ,
35
+ 'clean' ,
36
+ 'clojure' ,
37
+ 'clojure-repl' ,
38
+ 'cmake' ,
39
+ 'coffeescript' ,
40
+ 'coq' ,
41
+ 'cos' ,
42
+ 'cpp' ,
43
+ 'crmsh' ,
44
+ 'crystal' ,
45
+ 'csharp' ,
46
+ 'csp' ,
47
+ 'css' ,
48
+ 'd' ,
49
+ 'markdown' ,
50
+ 'dart' ,
51
+ 'delphi' ,
52
+ 'diff' ,
53
+ 'django' ,
54
+ 'dns' ,
55
+ 'dockerfile' ,
56
+ 'dos' ,
57
+ 'dsconfig' ,
58
+ 'dts' ,
59
+ 'dust' ,
60
+ 'ebnf' ,
61
+ 'elixir' ,
62
+ 'elm' ,
63
+ 'ruby' ,
64
+ 'erb' ,
65
+ 'erlang-repl' ,
66
+ 'erlang' ,
67
+ 'excel' ,
68
+ 'fix' ,
69
+ 'flix' ,
70
+ 'fortran' ,
71
+ 'fsharp' ,
72
+ 'gams' ,
73
+ 'gauss' ,
74
+ 'gcode' ,
75
+ 'gherkin' ,
76
+ 'glsl' ,
77
+ 'gml' ,
78
+ 'go' ,
79
+ 'golo' ,
80
+ 'gradle' ,
81
+ 'groovy' ,
82
+ 'haml' ,
83
+ 'handlebars' ,
84
+ 'haskell' ,
85
+ 'haxe' ,
86
+ 'hsp' ,
87
+ 'http' ,
88
+ 'hy' ,
89
+ 'inform7' ,
90
+ 'ini' ,
91
+ 'irpf90' ,
92
+ 'isbl' ,
93
+ 'java' ,
94
+ 'javascript' ,
95
+ 'jboss-cli' ,
96
+ 'json' ,
97
+ 'julia' ,
98
+ 'julia-repl' ,
99
+ 'kotlin' ,
100
+ 'lasso' ,
101
+ 'latex' ,
102
+ 'ldif' ,
103
+ 'leaf' ,
104
+ 'less' ,
105
+ 'lisp' ,
106
+ 'livecodeserver' ,
107
+ 'livescript' ,
108
+ 'llvm' ,
109
+ 'lsl' ,
110
+ 'lua' ,
111
+ 'makefile' ,
112
+ 'mathematica' ,
113
+ 'matlab' ,
114
+ 'maxima' ,
115
+ 'mel' ,
116
+ 'mercury' ,
117
+ 'mipsasm' ,
118
+ 'mizar' ,
119
+ 'perl' ,
120
+ 'mojolicious' ,
121
+ 'monkey' ,
122
+ 'moonscript' ,
123
+ 'n1ql' ,
124
+ 'nestedtext' ,
125
+ 'nginx' ,
126
+ 'nim' ,
127
+ 'nix' ,
128
+ 'node-repl' ,
129
+ 'nsis' ,
130
+ 'objectivec' ,
131
+ 'ocaml' ,
132
+ 'openscad' ,
133
+ 'oxygene' ,
134
+ 'parser3' ,
135
+ 'pf' ,
136
+ 'pgsql' ,
137
+ 'php' ,
138
+ 'php-template' ,
139
+ 'plaintext' ,
140
+ 'pony' ,
141
+ 'powershell' ,
142
+ 'processing' ,
143
+ 'profile' ,
144
+ 'prolog' ,
145
+ 'properties' ,
146
+ 'protobuf' ,
147
+ 'puppet' ,
148
+ 'purebasic' ,
149
+ 'python' ,
150
+ 'python-repl' ,
151
+ 'q' ,
152
+ 'qml' ,
153
+ 'r' ,
154
+ 'reasonml' ,
155
+ 'rib' ,
156
+ 'roboconf' ,
157
+ 'routeros' ,
158
+ 'rsl' ,
159
+ 'ruleslanguage' ,
160
+ 'rust' ,
161
+ 'sas' ,
162
+ 'scala' ,
163
+ 'scheme' ,
164
+ 'scilab' ,
165
+ 'scss' ,
166
+ 'shell' ,
167
+ 'smali' ,
168
+ 'smalltalk' ,
169
+ 'sml' ,
170
+ 'sqf' ,
171
+ 'sql' ,
172
+ 'stan' ,
173
+ 'stata' ,
174
+ 'step21' ,
175
+ 'stylus' ,
176
+ 'subunit' ,
177
+ 'swift' ,
178
+ 'taggerscript' ,
179
+ 'yaml' ,
180
+ 'tap' ,
181
+ 'tcl' ,
182
+ 'thrift' ,
183
+ 'tp' ,
184
+ 'twig' ,
185
+ 'typescript' ,
186
+ 'vala' ,
187
+ 'vbnet' ,
188
+ 'vbscript' ,
189
+ 'vbscript-html' ,
190
+ 'verilog' ,
191
+ 'vhdl' ,
192
+ 'vim' ,
193
+ 'wasm' ,
194
+ 'wren' ,
195
+ 'x86asm' ,
196
+ 'xl' ,
197
+ 'xquery' ,
198
+ 'zephir' ,
199
+ ] )
200
+ const languageAlias = {
201
+ 'js' : 'javascript' ,
202
+ 'jsx' : 'javascript' ,
203
+ 'ts' : 'typescript' ,
204
+ 'tsx' : 'typescript' ,
205
+ 'py' : 'python' ,
206
+ 'make' : 'makefile' ,
207
+ 'md' : 'markdown' ,
208
+ 'ps' : 'powershell' ,
209
+ 'rs' : 'rust' ,
210
+ 'styl' : 'stylus' ,
211
+ }
212
+
213
+ const test = / \. ( m d | m a r k d o w n ) $ / i
214
+ const reCodeLanguage = / < c o d e c l a s s = " l a n g u a g e \- ( [ ^ " ] + ) " / g
7
215
8
216
export const markdownResovler = ( specifier : string ) : ResolveResult => {
9
217
let pagePath = util . trimPrefix ( specifier . replace ( / \. ( m d | m a r k d o w n ) $ / i, '' ) , '/pages' )
@@ -17,43 +225,83 @@ export const markdownResovler = (specifier: string): ResolveResult => {
17
225
return { asPage : { path : pagePath , isIndex } }
18
226
}
19
227
20
- export const markdownLoader = async ( { specifier } : LoadInput , aleph : Aleph ) : Promise < LoadOutput > => {
228
+ export const markdownLoader = async ( { specifier } : LoadInput , aleph : Aleph , { highlight } : Options = { } ) : Promise < LoadOutput > => {
21
229
const { framework } = aleph . config
22
230
const { content } = await aleph . fetchModule ( specifier )
23
231
const { __content, ...meta } = safeLoadFront ( ( new TextDecoder ) . decode ( content ) )
24
232
const html = marked . parse ( __content )
25
233
const props = {
26
234
id : util . isString ( meta . id ) ? meta . id : undefined ,
27
- className : util . isString ( meta . className ) ? meta . className : undefined ,
28
- style : util . isPlainObject ( meta . style ) ? meta . style : undefined ,
235
+ className : util . isString ( meta . className ) ? meta . className . trim ( ) : undefined ,
236
+ style : util . isPlainObject ( meta . style ) ? Object . entries ( meta . style ) . reduce ( ( prev , [ key , value ] ) => {
237
+ prev [ key . replaceAll ( / \- [ a - z ] / g, m => m . slice ( 1 ) . toUpperCase ( ) ) ] = value
238
+ return prev
239
+ } , { } as Record < string , any > ) : undefined ,
240
+ }
241
+ const code = [
242
+ `import { createElement, useEffect, useRef } from 'https://esm.sh/react'` ,
243
+ `import HTMLPage from 'https://deno.land/x/aleph/framework/react/components/HTMLPage.ts'` ,
244
+ `export default function MarkdownPage(props) {` ,
245
+ ` return createElement(HTMLPage, {` ,
246
+ ` ...${ JSON . stringify ( props ) } ,` ,
247
+ ` ...props,` ,
248
+ ` html: ${ JSON . stringify ( html ) } ` ,
249
+ ` })` ,
250
+ `}` ,
251
+ `MarkdownPage.meta = ${ JSON . stringify ( meta ) } ` ,
252
+ ]
253
+ if ( highlight ) {
254
+ const extra : string [ ] = [ `import hljs from '${ hljsUri } /lib/core'` ]
255
+ const activated : Set < string > = new Set ( )
256
+ const hooks = [
257
+ ` const ref = useRef()` ,
258
+ ` useEffect(() => ref.current && ref.current.querySelectorAll('code').forEach(el => hljs.highlightElement(el)), [])`
259
+ ]
260
+ for ( const m of html . matchAll ( reCodeLanguage ) ) {
261
+ let lang = m [ 1 ]
262
+ if ( lang === 'jsx' || lang === 'tsx' ) {
263
+ activated . add ( 'xml' )
264
+ }
265
+ if ( lang in languageAlias ) {
266
+ lang = ( languageAlias as any ) [ lang ]
267
+ }
268
+ if ( languages . has ( lang ) ) {
269
+ activated . add ( lang )
270
+ }
271
+ }
272
+ activated . forEach ( lang => {
273
+ extra . push (
274
+ `import ${ lang } from '${ hljsUri } /lib/languages/${ lang } '` ,
275
+ `hljs.registerLanguage('${ lang } ', ${ lang } )`
276
+ )
277
+ } )
278
+ code . splice ( 6 , 0 , ` ref,` )
279
+ code . splice ( 3 , 0 , ...hooks )
280
+ code . unshift ( ...extra , `import '${ hljsUri } /styles/${ highlight . theme || 'default' } .css'` )
29
281
}
30
282
31
283
if ( framework === 'react' ) {
32
284
return {
33
- code : [
34
- `import { createElement } from 'https://esm.sh/react'` ,
35
- `import HTMLPage from 'https://deno.land/x/aleph/framework/react/components/HTMLPage.ts'` ,
36
- `export default function MarkdownPage(props) {` ,
37
- ` return createElement(HTMLPage, {` ,
38
- ` ...${ JSON . stringify ( props ) } ,` ,
39
- ` ...props,` ,
40
- ` html: ${ JSON . stringify ( html ) } ` ,
41
- ` })` ,
42
- `}` ,
43
- `MarkdownPage.meta = ${ JSON . stringify ( meta ) } ` ,
44
- ] . join ( '\n' )
285
+ code : code . join ( '\n' )
45
286
}
46
287
}
47
288
48
289
throw new Error ( `markdown-loader: don't support framework '${ framework } '` )
49
290
}
50
291
51
- export default ( ) : Plugin => {
292
+ export type Options = {
293
+ highlight ?: {
294
+ provider : 'highlight.js' , // todo: support prism and other libs
295
+ theme ?: string
296
+ }
297
+ }
298
+
299
+ export default ( options ?: Options ) : Plugin => {
52
300
return {
53
301
name : 'markdown-loader' ,
54
302
setup : aleph => {
55
303
aleph . onResolve ( test , markdownResovler )
56
- aleph . onLoad ( test , input => markdownLoader ( input , aleph ) )
304
+ aleph . onLoad ( test , input => markdownLoader ( input , aleph , options ) )
57
305
}
58
306
}
59
307
}
0 commit comments