1
- import { markdown } from './src/util/remark/markdown.js'
2
- import {
3
- compileCss ,
4
- convertToUSA ,
5
- generateSingleFile ,
6
- handleUTC ,
7
- parseYaml ,
8
- replaceBody ,
9
- replaceHead ,
10
- startServer ,
11
- } from './src/util/utils.js'
12
- import {
13
- getRss ,
14
- giscus ,
15
- templateArticle ,
16
- templateBox ,
17
- templateProcess ,
18
- } from './src/util/template.js'
19
- import { copy , ensureDir , ensureFile , exists , existsSync } from 'fs'
1
+ import { assertPlugin } from './src/plugins/asserts.js'
2
+ import { Core } from './src/plugins/core.js'
3
+ import { feedPlugin } from './src/plugins/feed.js'
4
+ import { pagesPlugin } from './src/plugins/pages.js'
5
+ import { postPlugin } from './src/plugins/posts.js'
6
+ import { existsSync } from 'fs'
7
+ import { startServer } from './src/util/utils.js'
20
8
import 'https://deno.land/[email protected] /dotenv/load.ts'
21
9
22
- /**
23
- * @typedef {Object } MetaData
24
- * @property {string } MetaData.date
25
- * @property {string } MetaData.title
26
- * @property {string } MetaData.summary
27
- * @property {string[] } MetaData.tags
28
- */
29
-
30
- /**@type {MetaData[] }*/
31
- const metaData = [ ]
32
- const dist = import . meta. resolve ( './dist/' )
33
- const src = import . meta. resolve ( './src/' )
34
- const randomNumber = Math . floor ( Math . random ( ) * 1000000 )
35
-
36
- // global config
37
- const WEBSITE = Deno . env . get ( 'WEBSITE' )
38
- const author = Deno . env . get ( 'AUTHOR' )
39
- const port = Deno . env . get ( 'PORT' )
40
- const header = await Deno . readTextFile ( new URL ( './util/header.html' , src ) )
41
- const footer = await Deno . readTextFile ( new URL ( './util/footer.html' , src ) )
42
-
43
- const getPosts = ( title , content , isPosts = false ) =>
44
- `${ templateArticle ( { title, content, giscus : isPosts ? giscus : '' } ) } `
45
-
46
- const getTags = ( title , tags ) =>
47
- tags . reduce (
48
- ( acc , tag ) =>
49
- acc + `<a class="tag" href="/./${ title } /${ tag } /">
50
- <i class="fa-solid fa-tag"></i> ${ tag }
51
- </a>` ,
52
- '' ,
53
- )
54
-
55
- /**
56
- * @param {string } title
57
- * @param {MetaData[] } iters
58
- */
59
- function getArchive ( title , iters ) {
60
- const content = iters . reduce (
61
- ( acc , { date, summary } ) => {
62
- const place = `/./posts/${ handleUTC ( date ) } /`
63
- return acc +
64
- `<p><a class="decoration-line" href=${ place } target="_blank"> ${ summary } ··· ${ convertToUSA ( date )
65
- } </a></p>`
66
- } ,
67
- '' ,
68
- )
69
-
70
- return templateArticle ( { title, content } )
71
- }
72
- /**
73
- * @param {Map<string,MetaData[]> } map
74
- * @param {string } url
75
- * @param {string } dest
76
- */
77
- async function completeTask ( map , url , dest ) {
78
- for ( const k of map . keys ( ) ) {
79
- const tagsUrl = new URL ( `./${ k } /index.html` , url )
80
- const task = await generatePage ( tagsUrl , k , `${ author } ~ ${ k } ` , k )
81
- await task ( getArchive , map . get ( k ) )
10
+ async function createConfig ( ) {
11
+ const baseConfig = {
12
+ dist : import . meta. resolve ( './dist/' ) ,
13
+ src : import . meta. resolve ( './src/' ) ,
14
+ website : Deno . env . get ( 'WEBSITE' ) ,
15
+ author : Deno . env . get ( 'AUTHOR' ) ,
16
+ port : Deno . env . get ( 'PORT' ) ,
17
+ version : Math . floor ( Math . random ( ) * 1000000 ) ,
82
18
}
83
19
84
- const task = await generatePage (
85
- new URL ( './index .html' , url ) ,
86
- [ ... map . keys ( ) ] . join ( ', ' ) ,
87
- ` ${ author } ~ ${ dest } ` ,
88
- dest ,
20
+ const header = await Deno . readTextFile (
21
+ new URL ( './util/header .html' , baseConfig . src ) ,
22
+ )
23
+ const footer = await Deno . readTextFile (
24
+ new URL ( './util/footer.html' , baseConfig . src ) ,
89
25
)
90
- await task ( getPosts , getTags ( dest , [ ...map . keys ( ) ] ) )
91
- }
92
-
93
- async function generatePage (
94
- /**@type {string }*/ dist ,
95
- /**@type {string }*/ keywords ,
96
- /**@type {string }*/ description ,
97
- /**@type {string }*/ title ,
98
- ) {
99
- if ( ! await exists ( dist ) ) await ensureFile ( dist )
100
- const head = await replaceHead ( keywords , description , title , randomNumber )
101
-
102
- return async ( fn , ...params ) => {
103
- const content = fn ( title , ...params )
104
- const index = `${ head } ${ header } ${ content } ${ footer } `
105
-
106
- await Deno . writeTextFile ( dist , index )
107
- }
108
- }
109
-
110
- // Handle the meta data
111
- async function handlePosts ( ) {
112
- const posts = import . meta. resolve ( './src/posts/' )
113
- const iter = Deno . readDir ( new URL ( posts ) ) [ Symbol . asyncIterator ] ( )
114
- while ( true ) {
115
- const { value, done } = await iter . next ( )
116
- if ( done ) break
117
- const file = await Deno . readTextFile ( new URL ( value . name , posts ) )
118
- const [ { date, title, summary, tags } , md ] = parseYaml ( file )
119
-
120
- // Handle the posts
121
- {
122
- const timeDir = new URL ( `./posts/${ handleUTC ( date ) } /index.html` , dist )
123
- const task = await generatePage ( timeDir , tags . join ( ', ' ) , summary , title )
124
- await task ( getPosts , await markdown ( md ) , true )
125
- }
126
-
127
- metaData . push ( { date, title, summary, tags } )
128
- }
129
- metaData . sort ( ( a , b ) => new Date ( b . date ) - new Date ( a . date ) )
130
- }
131
-
132
- // Handle the others source
133
- async function Others ( ) {
134
- await ensureDir ( new URL ( './public/' , dist ) )
135
- for await ( const entry of Deno . readDir ( new URL ( '../public/' , src ) ) ) {
136
- if ( entry . name === 'css' ) continue
137
- const __src_p = new URL ( `../public/${ entry . name } ` , src )
138
- const __dist_p = new URL ( `./public/${ entry . name } ` , dist )
139
- if ( entry . name === 'JavaScript' ) {
140
- await ensureFile (
141
- new URL ( `./${ entry . name } /index.${ randomNumber } .js` , __dist_p ) ,
142
- )
143
- await copy (
144
- new URL ( `./${ entry . name } /index.js` , __src_p ) ,
145
- new URL ( `./${ entry . name } /index.${ randomNumber } .js` , __dist_p ) ,
146
- { overwrite : true } ,
147
- )
148
- continue
149
- }
150
- await copy ( __src_p , __dist_p , { overwrite : true } )
151
- }
152
-
153
- // compile the css
154
- const __css_d = new URL ( './public/css/' , dist )
155
- await ensureDir ( __css_d )
156
- for await ( const entry of Deno . readDir ( new URL ( '../public/css/' , src ) ) ) {
157
- const path = new URL ( `../public/css/${ entry . name } ` , src )
158
- const code = await compileCss ( path )
159
- const fileName = entry . name . split ( '.' ) . join ( `.${ randomNumber } .` )
160
- await Deno . writeFile ( new URL ( fileName , __css_d ) , code )
161
- }
162
-
163
- // Handle the CNAME
164
- const cname = new URL ( './CNAME' , dist )
165
-
166
- // Handle the RSS
167
- const rss = new URL ( './feed.xml' , dist )
168
- const itemsRss = metaData . reduce ( ( acc , { date, title, summary } ) => {
169
- const url = `${ WEBSITE } posts/${ handleUTC ( date ) } /`
170
- return acc + `<item>
171
- <title>${ title } </title>
172
- <link>${ url } </link>
173
- <description>${ summary } </description>
174
- <pubDate>${ new Date ( date ) . toUTCString ( ) } </pubDate>
175
- </item>`
176
- } , '' )
177
-
178
- // sitemap
179
- const sitemap = new URL ( './sitemap.xml' , dist )
180
- const itemsSitemap = `<?xml version="1.0" encoding="UTF-8"?>
181
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
182
- ${ metaData . reduce ( ( acc , { date } ) =>
183
- `${ acc } <url><loc>${ WEBSITE } posts/${ handleUTC ( date ) } /</loc></url>` , '' )
184
- }
185
- </urlset>`
186
-
187
- // robots
188
- const robots = new URL ( './robots.txt' , dist )
189
- const robotsContent = `User-agent: *
190
- Allow: /
191
- Sitemap: ${ WEBSITE } sitemap.xml`
192
-
193
- const files = [
194
- [ rss , getRss ( author , WEBSITE , itemsRss ) ] ,
195
- [ sitemap , itemsSitemap ] ,
196
- [ robots , robotsContent ] ,
197
- ]
198
- const g = generateSingleFile ( cname , WEBSITE . slice ( 8 , - 1 ) )
199
- g . next ( )
200
- files . forEach ( ( file ) => g . next ( file ) )
201
- }
202
-
203
- // Home page
204
- async function Home ( ) {
205
- const homeDest = new URL ( './home/' , dist )
206
- let indexPage = await Deno . readTextFile ( new URL ( '../index.html' , src ) )
207
- indexPage = replaceBody ( indexPage , header , footer , randomNumber )
208
-
209
- const mLength = metaData . length
210
- const lastPage = Math . ceil ( mLength / 8 )
211
- let content = ''
212
-
213
- for ( let index = 0 ; index < mLength ; index ++ ) {
214
- const { date, title, summary, tags } = metaData [ index ]
215
- const aTags = getTags ( 'tags' , tags )
216
- content += templateBox ( {
217
- place : `/./posts/${ handleUTC ( date ) } /` ,
218
- title,
219
- summary,
220
- time : convertToUSA ( date ) ,
221
- tags : aTags ,
222
- } )
223
-
224
- if ( ( index + 1 ) % 8 === 0 || index + 1 === mLength ) {
225
- const cur = index + 1 === mLength ? lastPage : Math . floor ( ( index + 1 ) / 8 )
226
- const process = templateProcess ( {
227
- before : cur > 2 ? `/./home/${ cur - 1 } /` : '/' ,
228
- page : `${ cur } / ${ lastPage } ` ,
229
- after : index === mLength ? '#' : `/./home/${ cur + 1 } /` ,
230
- } )
231
- const home = indexPage . replace ( '<!-- Template -->' , content + process )
232
-
233
- // Reset the content
234
- content = ''
235
-
236
- // Generate the home dir
237
- if ( cur !== 1 ) await ensureDir ( new URL ( `${ cur } /` , homeDest ) )
238
- const url = cur === 1
239
- ? new URL ( './index.html' , dist )
240
- : new URL ( `${ cur } /index.html` , homeDest )
241
-
242
- await Deno . writeTextFile ( url , home )
243
- }
244
- }
245
- }
246
-
247
- // Archive page
248
- async function Archive ( ) {
249
- const archiveDest = new URL ( './archive/' , dist )
250
- const map = new Map ( )
251
- for ( const meta of metaData ) {
252
- const year = new Date ( meta . date ) . getFullYear ( )
253
- map . has ( year ) ? map . get ( year ) . push ( meta ) : map . set ( year , [ meta ] )
254
- }
255
- await completeTask ( map , archiveDest , 'archive' )
256
- }
257
26
258
- // Tags page
259
- async function Tags ( ) {
260
- const tagsDest = new URL ( './tags/' , dist )
261
- const map = new Map ( )
262
- for ( const meta of metaData ) {
263
- meta . tags . forEach ( ( tag ) => {
264
- map . has ( tag ) ? map . get ( tag ) . push ( meta ) : map . set ( tag , [ meta ] )
265
- } )
27
+ return {
28
+ ...baseConfig ,
29
+ header,
30
+ footer,
266
31
}
267
- await completeTask ( map , tagsDest , 'tags' )
268
- }
269
-
270
- async function About ( ) {
271
- const __dist_about = new URL ( './about/index.html' , dist )
272
- if ( ! await exists ( __dist_about ) ) await ensureFile ( __dist_about )
273
- const __src_about = new URL ( './about/about.html' , src )
274
-
275
- const about = ( await Deno . readTextFile ( __src_about ) )
276
- . replace ( '<!-- base.css -->' , `/public/css/base.${ randomNumber } .css` )
277
- . replace ( '<!-- index.css -->' , `/public/css/index.${ randomNumber } .css` )
278
- . replace ( '<!-- index.js -->' , `/public/JavaScript/index.${ randomNumber } .js` )
279
-
280
- await Deno . writeTextFile ( __dist_about , about )
281
32
}
282
33
34
+ const config = await createConfig ( )
283
35
async function main ( ) {
284
- if ( existsSync ( new URL ( dist ) ) ) {
285
- Deno . removeSync ( new URL ( dist ) , { recursive : true } )
36
+ if ( existsSync ( new URL ( config . dist ) ) ) {
37
+ Deno . removeSync ( new URL ( config . dist ) , { recursive : true } )
286
38
}
287
- await handlePosts ( )
288
- Promise . all ( [ Home ( ) , Archive ( ) , Tags ( ) , Others ( ) , About ( ) ] )
39
+ const core = new Core ( )
40
+ core . use ( postPlugin )
41
+ core . use ( assertPlugin )
42
+ core . use ( feedPlugin )
43
+ core . use ( pagesPlugin )
44
+ await core . runHook ( 'beforeBuild' , config )
45
+ await core . runHook ( 'afterBuild' , config )
289
46
}
290
47
291
48
// Handle the http server
@@ -295,5 +52,5 @@ if (mode === 'DEV' || mode === 'PRO') {
295
52
}
296
53
297
54
if ( mode === 'DEV' || mode === 'PRE' ) {
298
- startServer ( port )
55
+ startServer ( config . port )
299
56
}
0 commit comments