~cytrogen/blog-public

ref: 88eebf3dfdd8ab819fa1a84e1976a8a75d5af2b6 blog-public/posts/5d57.html -rw-r--r-- 401.3 KiB
88eebf3dCytrogen Deploy 2026-02-19 08:34:27 3 days ago
                                                                                
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Hexo 源代码分析【1】:入口文件 · Cytrogen 的个人博客</title><meta name="description" content="本文深入分析了 Hexo 7.2.0 的源代码,从入口文件 dist/hexo/index.js 入手,详细剖析了核心 Hexo 类的构造函数、属性和关键方法。文章逐一解读了路径管理、配置加载、渲染、路由、插件机制、数据模型等核心模块的实现原理,帮助你理解 Hexo 的底层工作机制,为深度定制或插件开发打下坚实基础。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/5d57.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
  font-family: 'Open Sans';
  src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  size-adjust: 107%;
  ascent-override: 97%;
  descent-override: 25%;
  line-gap-override: 0%;
}
</style><script>(function() {
  try {
    // 优先级:用户选择 > 系统偏好 > 默认浅色
    const saved = localStorage.getItem('theme');
    const theme = saved || 
      (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.style.colorScheme = theme;
  } catch (error) {
    // 失败时使用默认主题,不阻塞渲染
    document.documentElement.setAttribute('data-theme', 'light');
  }
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const theme = document.documentElement.getAttribute('data-theme');
    const pageWrapper = document.getElementById('page-wrapper');
    if (pageWrapper && theme) {
      pageWrapper.setAttribute('data-theme', theme);
    }
  });
})();

</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/5d57.html">永久链接</a><div class="p-summary visually-hidden"><p>使用 Hexo,痛骂 Hexo,理解 Hexo,成为 Hexo。</p>
<p>这篇文章是用来记录我阅读 Hexo 源代码的过程和分析。</p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E4%BB%A3%E7%A0%81%E8%A7%A3%E8%AF%BB/">代码解读</a><a class="p-category" href="../tags/Hexo/">Hexo</a><a class="p-category" href="../tags/JavaScript/">JavaScript</a></div><h1 class="post-title p-name">Hexo 源代码分析【1】:入口文件</h1><div class="post-info"><time class="post-date dt-published" datetime="2024-05-26T05:00:00.000Z">5/26/2024</time><time class="dt-updated visually-hidden" datetime="2026-02-09T17:16:54.713Z"></time></div><div class="post-content e-content"><html><head></head><body><p>使用 Hexo,痛骂 Hexo,理解 Hexo,成为 Hexo。</p>
<p>这篇文章是用来记录我阅读 Hexo 源代码的过程和分析。</p>
<span id="more"></span>
<h1 id="版本号"><a class="markdownIt-Anchor" href="#版本号"></a> 版本号</h1>
<p>文章写的时候,Hexo 的版本为 <strong>7.2.0</strong></p>
<figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line">  <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"hexo"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"7.2.0"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"description"</span><span class="punctuation">:</span> <span class="string">"A fast, simple &amp; powerful blog framework, powered by Node.js."</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"main"</span><span class="punctuation">:</span> <span class="string">"dist/hexo"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"bin"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"hexo"</span><span class="punctuation">:</span> <span class="string">"./bin/hexo"</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"scripts"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"prepublishOnly"</span><span class="punctuation">:</span> <span class="string">"npm install &amp;&amp; npm run clean &amp;&amp; npm run build"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"build"</span><span class="punctuation">:</span> <span class="string">"tsc -b"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"clean"</span><span class="punctuation">:</span> <span class="string">"tsc -b --clean"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"eslint"</span><span class="punctuation">:</span> <span class="string">"eslint lib test"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"pretest"</span><span class="punctuation">:</span> <span class="string">"npm run clean &amp;&amp; npm run build"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"test"</span><span class="punctuation">:</span> <span class="string">"mocha test/scripts/**/*.ts --require ts-node/register"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"test-cov"</span><span class="punctuation">:</span> <span class="string">"c8 --reporter=lcovonly npm test -- --no-parallel"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"prepare"</span><span class="punctuation">:</span> <span class="string">"husky install"</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"files"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">"dist/"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">"bin/"</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"types"</span><span class="punctuation">:</span> <span class="string">"./dist/hexo/index.d.ts"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"repository"</span><span class="punctuation">:</span> <span class="string">"hexojs/hexo"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"homepage"</span><span class="punctuation">:</span> <span class="string">"https://hexo.io/"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"funding"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"opencollective"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"url"</span><span class="punctuation">:</span> <span class="string">"https://opencollective.com/hexo"</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"keywords"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">"website"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">"blog"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">"cms"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">"framework"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">"hexo"</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"author"</span><span class="punctuation">:</span> <span class="string">"Tommy Chen &lt;tommy351@gmail.com&gt; (https://zespia.tw)"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"maintainers"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">"Abner Chou &lt;hi@abnerchou.me&gt; (https://abnerchou.me)"</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"license"</span><span class="punctuation">:</span> <span class="string">"MIT"</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"dependencies"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"abbrev"</span><span class="punctuation">:</span> <span class="string">"^2.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"archy"</span><span class="punctuation">:</span> <span class="string">"^1.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"bluebird"</span><span class="punctuation">:</span> <span class="string">"^3.7.2"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-cli"</span><span class="punctuation">:</span> <span class="string">"^4.3.2"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-front-matter"</span><span class="punctuation">:</span> <span class="string">"^4.2.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-fs"</span><span class="punctuation">:</span> <span class="string">"^4.1.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-i18n"</span><span class="punctuation">:</span> <span class="string">"^2.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-log"</span><span class="punctuation">:</span> <span class="string">"^4.0.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-util"</span><span class="punctuation">:</span> <span class="string">"^3.3.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"js-yaml"</span><span class="punctuation">:</span> <span class="string">"^4.1.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"js-yaml-js-types"</span><span class="punctuation">:</span> <span class="string">"^1.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"micromatch"</span><span class="punctuation">:</span> <span class="string">"^4.0.4"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"moize"</span><span class="punctuation">:</span> <span class="string">"^6.1.6"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"moment"</span><span class="punctuation">:</span> <span class="string">"^2.29.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"moment-timezone"</span><span class="punctuation">:</span> <span class="string">"^0.5.34"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"nunjucks"</span><span class="punctuation">:</span> <span class="string">"^3.2.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"picocolors"</span><span class="punctuation">:</span> <span class="string">"^1.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"pretty-hrtime"</span><span class="punctuation">:</span> <span class="string">"^1.0.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"resolve"</span><span class="punctuation">:</span> <span class="string">"^1.22.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"strip-ansi"</span><span class="punctuation">:</span> <span class="string">"^6.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"text-table"</span><span class="punctuation">:</span> <span class="string">"^0.2.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"tildify"</span><span class="punctuation">:</span> <span class="string">"^2.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"titlecase"</span><span class="punctuation">:</span> <span class="string">"^1.1.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"warehouse"</span><span class="punctuation">:</span> <span class="string">"^5.0.1"</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"devDependencies"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"0x"</span><span class="punctuation">:</span> <span class="string">"^5.1.2"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/abbrev"</span><span class="punctuation">:</span> <span class="string">"^1.1.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/bluebird"</span><span class="punctuation">:</span> <span class="string">"^3.5.37"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/chai"</span><span class="punctuation">:</span> <span class="string">"^4.3.11"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/js-yaml"</span><span class="punctuation">:</span> <span class="string">"^4.0.9"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/mocha"</span><span class="punctuation">:</span> <span class="string">"^10.0.6"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/node"</span><span class="punctuation">:</span> <span class="string">"^18.11.8 &lt;18.19.9"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/nunjucks"</span><span class="punctuation">:</span> <span class="string">"^3.2.2"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/rewire"</span><span class="punctuation">:</span> <span class="string">"^2.5.30"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/sinon"</span><span class="punctuation">:</span> <span class="string">"^17.0.3"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"@types/text-table"</span><span class="punctuation">:</span> <span class="string">"^0.2.4"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"c8"</span><span class="punctuation">:</span> <span class="string">"^9.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"chai"</span><span class="punctuation">:</span> <span class="string">"^4.3.6"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"cheerio"</span><span class="punctuation">:</span> <span class="string">"0.22.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"decache"</span><span class="punctuation">:</span> <span class="string">"^4.6.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"eslint"</span><span class="punctuation">:</span> <span class="string">"^8.48.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"eslint-config-hexo"</span><span class="punctuation">:</span> <span class="string">"^5.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"hexo-renderer-marked"</span><span class="punctuation">:</span> <span class="string">"^6.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"husky"</span><span class="punctuation">:</span> <span class="string">"^8.0.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"lint-staged"</span><span class="punctuation">:</span> <span class="string">"^15.2.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"mocha"</span><span class="punctuation">:</span> <span class="string">"^10.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"rewire"</span><span class="punctuation">:</span> <span class="string">"^7.0.0"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"sinon"</span><span class="punctuation">:</span> <span class="string">"^17.0.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"ts-node"</span><span class="punctuation">:</span> <span class="string">"^10.9.1"</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">"typescript"</span><span class="punctuation">:</span> <span class="string">"^5.3.2"</span></span><br><span class="line">  <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">"engines"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line">    <span class="attr">"node"</span><span class="punctuation">:</span> <span class="string">"&gt;=14"</span></span><br><span class="line">  <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure>
<p>Hexo 的入口文件是 <code>dist/hexo/index.js</code>,我们来看看这个文件:</p>
<div class="danger">
<p>部分代码因为太长了,所以先注释掉,后续会拿出来说。</p>
</div>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">  <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> tildify_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"tildify"</span>));</span><br><span class="line"><span class="keyword">const</span> warehouse_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"warehouse"</span>));</span><br><span class="line"><span class="keyword">const</span> picocolors_1 = <span class="built_in">require</span>(<span class="string">"picocolors"</span>);</span><br><span class="line"><span class="keyword">const</span> events_1 = <span class="built_in">require</span>(<span class="string">"events"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> module_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"module"</span>));</span><br><span class="line"><span class="keyword">const</span> vm_1 = <span class="built_in">require</span>(<span class="string">"vm"</span>);</span><br><span class="line"><span class="keyword">const</span> { version } = <span class="built_in">require</span>(<span class="string">'../../package.json'</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_log_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"hexo-log"</span>));</span><br><span class="line"><span class="keyword">const</span> extend_1 = <span class="built_in">require</span>(<span class="string">"../extend"</span>);</span><br><span class="line"><span class="keyword">const</span> render_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./render"</span>));</span><br><span class="line"><span class="keyword">const</span> register_models_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./register_models"</span>));</span><br><span class="line"><span class="keyword">const</span> post_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./post"</span>));</span><br><span class="line"><span class="keyword">const</span> scaffold_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./scaffold"</span>));</span><br><span class="line"><span class="keyword">const</span> source_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./source"</span>));</span><br><span class="line"><span class="keyword">const</span> router_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./router"</span>));</span><br><span class="line"><span class="keyword">const</span> theme_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"../theme"</span>));</span><br><span class="line"><span class="keyword">const</span> locals_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./locals"</span>));</span><br><span class="line"><span class="keyword">const</span> default_config_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./default_config"</span>));</span><br><span class="line"><span class="keyword">const</span> load_database_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./load_database"</span>));</span><br><span class="line"><span class="keyword">const</span> multi_config_path_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./multi_config_path"</span>));</span><br><span class="line"><span class="keyword">const</span> hexo_util_1 = <span class="built_in">require</span>(<span class="string">"hexo-util"</span>);</span><br><span class="line"><span class="keyword">let</span> resolveSync; <span class="comment">// = require('resolve');</span></span><br><span class="line"><span class="keyword">const</span> libDir = (<span class="number">0</span>, path_1.<span class="property">dirname</span>)(__dirname);</span><br><span class="line"><span class="keyword">const</span> dbVersion = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">stopWatcher</span> = (<span class="params">box</span>) =&gt; { <span class="keyword">if</span> (box.<span class="title function_">isWatching</span>())</span><br><span class="line">  box.<span class="title function_">unwatch</span>(); };</span><br><span class="line"><span class="keyword">const</span> routeCache = <span class="keyword">new</span> <span class="title class_">WeakMap</span>();</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">castArray</span> = (<span class="params">obj</span>) =&gt; { <span class="keyword">return</span> <span class="title class_">Array</span>.<span class="title function_">isArray</span>(obj) ? obj : [obj]; };</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">mergeCtxThemeConfig</span> = (<span class="params">ctx</span>) =&gt; { ... };</span><br><span class="line"><span class="keyword">const</span> createLoadThemeRoute = <span class="keyword">function</span> (<span class="params">generatorResult, locals, ctx</span>) { ... };</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">debounce</span>(<span class="params">func, wait</span>) { ... }</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Hexo</span> <span class="keyword">extends</span> <span class="title class_ inherited__">events_1.EventEmitter</span> { ... }</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property">lib_dir</span> = libDir + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">lib_dir</span> = <span class="title class_">Hexo</span>.<span class="property">lib_dir</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property">core_dir</span> = (<span class="number">0</span>, path_1.<span class="property">dirname</span>)(libDir) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">core_dir</span> = <span class="title class_">Hexo</span>.<span class="property">core_dir</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property">version</span> = version;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">version</span> = <span class="title class_">Hexo</span>.<span class="property">version</span>;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Hexo</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=index.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<h1 id="hexo类"><a class="markdownIt-Anchor" href="#hexo类"></a> <code>Hexo</code></h1>
<p>重点来看 <code>Hexo</code> 类的定义:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Hexo</span> <span class="keyword">extends</span> <span class="title class_ inherited__">events_1.EventEmitter</span> {</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params">base = process.cwd(), args = {}</span>) { ... }</span><br><span class="line">  <span class="title function_">_bindLocals</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">init</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">call</span>(<span class="params">name, args, callback</span>) { ... }</span><br><span class="line">  <span class="title function_">model</span>(<span class="params">name, schema</span>) { ... }</span><br><span class="line">  <span class="title function_">resolvePlugin</span>(<span class="params">name, basedir</span>) { ... }</span><br><span class="line">  <span class="title function_">loadPlugin</span>(<span class="params">path, callback</span>) { ... }</span><br><span class="line">  <span class="title function_">_showDrafts</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">load</span>(<span class="params">callback</span>) { ... }</span><br><span class="line">  <span class="title function_">watch</span>(<span class="params">callback</span>) { ... }</span><br><span class="line">  <span class="title function_">unwatch</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">_generateLocals</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">_runGenerators</span>(<span class="params"></span>) { ... }</span><br><span class="line">  <span class="title function_">_routerRefresh</span>(<span class="params">runningGenerators, useCache</span>) { ... }</span><br><span class="line">  <span class="title function_">_generate</span>(<span class="params">options = {}</span>) { ... }</span><br><span class="line">  <span class="title function_">exit</span>(<span class="params">err</span>) { ... }</span><br><span class="line">  <span class="title function_">execFilter</span>(<span class="params">type, data, options</span>) { ... }</span><br><span class="line">  <span class="title function_">execFilterSync</span>(<span class="params">type, data, options</span>) { ... }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="构造函数"><a class="markdownIt-Anchor" href="#构造函数"></a> 构造函数</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">constructor</span>(<span class="params">base = process.cwd(), args = {}</span>) {</span><br><span class="line">  <span class="variable language_">super</span>();</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">base_dir</span> = base + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">public_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'public'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">source_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'source'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">plugin_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'node_modules'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">script_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'scripts'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">scaffold_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'scaffolds'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">theme_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'themes'</span>, default_config_1.<span class="property">default</span>.<span class="property">theme</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">theme_script_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(<span class="variable language_">this</span>.<span class="property">theme_dir</span>, <span class="string">'scripts'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">env</span> = {</span><br><span class="line">    args,</span><br><span class="line">    <span class="attr">debug</span>: <span class="title class_">Boolean</span>(args.<span class="property">debug</span>),</span><br><span class="line">    <span class="attr">safe</span>: <span class="title class_">Boolean</span>(args.<span class="property">safe</span>),</span><br><span class="line">    <span class="attr">silent</span>: <span class="title class_">Boolean</span>(args.<span class="property">silent</span>),</span><br><span class="line">    <span class="attr">env</span>: process.<span class="property">env</span>.<span class="property">NODE_ENV</span> || <span class="string">'development'</span>,</span><br><span class="line">    version,</span><br><span class="line">    <span class="attr">cmd</span>: args.<span class="property">_</span> ? args.<span class="property">_</span>[<span class="number">0</span>] : <span class="string">''</span>,</span><br><span class="line">    <span class="attr">init</span>: <span class="literal">false</span></span><br><span class="line">  };</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">extend</span> = {</span><br><span class="line">    <span class="attr">console</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Console</span>(),</span><br><span class="line">    <span class="attr">deployer</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Deployer</span>(),</span><br><span class="line">    <span class="attr">filter</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Filter</span>(),</span><br><span class="line">    <span class="attr">generator</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Generator</span>(),</span><br><span class="line">    <span class="attr">helper</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Helper</span>(),</span><br><span class="line">    <span class="attr">highlight</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Highlight</span>(),</span><br><span class="line">    <span class="attr">injector</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Injector</span>(),</span><br><span class="line">    <span class="attr">migrator</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Migrator</span>(),</span><br><span class="line">    <span class="attr">processor</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Processor</span>(),</span><br><span class="line">    <span class="attr">renderer</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Renderer</span>(),</span><br><span class="line">    <span class="attr">tag</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Tag</span>()</span><br><span class="line">  };</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">config</span> = <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, default_config_1.<span class="property">default</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">log</span> = (<span class="number">0</span>, hexo_log_1.<span class="property">default</span>)(<span class="variable language_">this</span>.<span class="property">env</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">render</span> = <span class="keyword">new</span> render_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">route</span> = <span class="keyword">new</span> router_1.<span class="title function_">default</span>();</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">post</span> = <span class="keyword">new</span> post_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">scaffold</span> = <span class="keyword">new</span> scaffold_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">_dbLoaded</span> = <span class="literal">false</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">_isGenerating</span> = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">const</span> dbPath = args.<span class="property">output</span> || base;</span><br><span class="line">  <span class="keyword">if</span> (<span class="regexp">/^(init|new|g|publish|s|deploy|render|migrate)/</span>.<span class="title function_">test</span>(<span class="variable language_">this</span>.<span class="property">env</span>.<span class="property">cmd</span>)) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">d</span>(<span class="string">`Writing database to <span class="subst">${(<span class="number">0</span>, path_1.join)(dbPath, <span class="string">'db.json'</span>)}</span>`</span>);</span><br><span class="line">  }</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">database</span> = <span class="keyword">new</span> warehouse_1.<span class="title function_">default</span>({</span><br><span class="line">    <span class="attr">version</span>: dbVersion,</span><br><span class="line">    <span class="attr">path</span>: (<span class="number">0</span>, path_1.<span class="property">join</span>)(dbPath, <span class="string">'db.json'</span>)</span><br><span class="line">  });</span><br><span class="line">  <span class="keyword">const</span> mcp = (<span class="number">0</span>, multi_config_path_1.<span class="property">default</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">config_path</span> = args.<span class="property">config</span> ? <span class="title function_">mcp</span>(base, args.<span class="property">config</span>, args.<span class="property">output</span>)</span><br><span class="line">    : (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'_config.yml'</span>);</span><br><span class="line">  (<span class="number">0</span>, register_models_1.<span class="property">default</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">source</span> = <span class="keyword">new</span> source_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">theme</span> = <span class="keyword">new</span> theme_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">locals</span> = <span class="keyword">new</span> locals_1.<span class="title function_">default</span>();</span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">_bindLocals</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h4 id="路径"><a class="markdownIt-Anchor" href="#路径"></a> 路径</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">base_dir</span> = base + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">public_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'public'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">source_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'source'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">plugin_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'node_modules'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">script_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'scripts'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">scaffold_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'scaffolds'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">theme_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'themes'</span>, default_config_1.<span class="property">default</span>.<span class="property">theme</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">theme_script_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(<span class="variable language_">this</span>.<span class="property">theme_dir</span>, <span class="string">'scripts'</span>) + path_1.<span class="property">sep</span>;</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>base_dir</code>:项目的基础目录</li>
<li><code>public_dir</code>:项目生成的静态文件存放的目录(<code>./public</code>),当运行 <code>hexo generate</code> 时生成的所有静态文件都会被放在这个目录下</li>
<li><code>source_dir</code>:Markdown 文章(<code>./source</code></li>
<li><code>plugin_dir</code>:插件(<code>./node_modules</code>),Hexo 的插件机制基于 Node.JS 的模块系统</li>
<li><code>script_dir</code>:脚本(<code>./scripts</code>),Hexo 启动时会自动执行这些 JavaScript 文件</li>
<li><code>scaffold_dir</code>:脚手架(<code>./scaffolds</code>)是一种模板,可以用它快速创建新的文章</li>
<li><code>theme_dir</code>:主题(<code>./themes</code><ul>
<li><code>theme_script_dir</code>:类似于脚本(<code>./themes/scripts</code></li>
</ul>
</li>
</ul>
<h4 id="环境信息"><a class="markdownIt-Anchor" href="#环境信息"></a> 环境信息</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">env</span> = {</span><br><span class="line">  args,</span><br><span class="line">  <span class="attr">debug</span>: <span class="title class_">Boolean</span>(args.<span class="property">debug</span>),</span><br><span class="line">  <span class="attr">safe</span>: <span class="title class_">Boolean</span>(args.<span class="property">safe</span>),</span><br><span class="line">  <span class="attr">silent</span>: <span class="title class_">Boolean</span>(args.<span class="property">silent</span>),</span><br><span class="line">  <span class="attr">env</span>: process.<span class="property">env</span>.<span class="property">NODE_ENV</span> || <span class="string">'development'</span>,</span><br><span class="line">  version,</span><br><span class="line">  <span class="attr">cmd</span>: args.<span class="property">_</span> ? args.<span class="property">_</span>[<span class="number">0</span>] : <span class="string">''</span>,</span><br><span class="line">  <span class="attr">init</span>: <span class="literal">false</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>包含了关于 Hexo 运行环境的信息,如调试模式、安全模式、静默模式、环境变量、版本号、命令、是否初始化等。</p>
<h4 id="扩展"><a class="markdownIt-Anchor" href="#扩展"></a> 扩展</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">extend</span> = {</span><br><span class="line">  <span class="attr">console</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Console</span>(),</span><br><span class="line">  <span class="attr">deployer</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Deployer</span>(),</span><br><span class="line">  <span class="attr">filter</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Filter</span>(),</span><br><span class="line">  <span class="attr">generator</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Generator</span>(),</span><br><span class="line">  <span class="attr">helper</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Helper</span>(),</span><br><span class="line">  <span class="attr">highlight</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Highlight</span>(),</span><br><span class="line">  <span class="attr">injector</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Injector</span>(),</span><br><span class="line">  <span class="attr">migrator</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Migrator</span>(),</span><br><span class="line">  <span class="attr">processor</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Processor</span>(),</span><br><span class="line">  <span class="attr">renderer</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Renderer</span>(),</span><br><span class="line">  <span class="attr">tag</span>: <span class="keyword">new</span> extend_1.<span class="title class_">Tag</span>()</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>这些是 Hexo 的核心组件,以后会详细介绍。</p>
<blockquote>
<p>其实可以先行阅读 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/">Hexo 官方文档</a>,虽然初次看可能很难看懂,但是看了总是会在未来的某个时刻头脑一亮埋下伏笔。</p>
</blockquote>
<h4 id="配置"><a class="markdownIt-Anchor" href="#配置"></a> 配置</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">config</span> = <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, default_config_1.<span class="property">default</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>默认配置的内容:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line">    <span class="attr">title</span>: <span class="string">'Hexo'</span>,</span><br><span class="line">    <span class="attr">subtitle</span>: <span class="string">''</span>,</span><br><span class="line">    <span class="attr">description</span>: <span class="string">''</span>,</span><br><span class="line">    <span class="attr">author</span>: <span class="string">'John Doe'</span>,</span><br><span class="line">    <span class="attr">language</span>: <span class="string">'en'</span>,</span><br><span class="line">    <span class="attr">timezone</span>: <span class="string">''</span>,</span><br><span class="line"></span><br><span class="line">    <span class="attr">url</span>: <span class="string">'http://example.com'</span>,</span><br><span class="line">    <span class="attr">root</span>: <span class="string">'/'</span>,</span><br><span class="line">    <span class="attr">permalink</span>: <span class="string">':year/:month/:day/:title/'</span>,</span><br><span class="line">    <span class="attr">permalink_defaults</span>: {},</span><br><span class="line">    <span class="attr">pretty_urls</span>: {</span><br><span class="line">        <span class="attr">trailing_index</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">trailing_html</span>: <span class="literal">true</span></span><br><span class="line">    },</span><br><span class="line"></span><br><span class="line">    <span class="attr">source_dir</span>: <span class="string">'source'</span>,</span><br><span class="line">    <span class="attr">public_dir</span>: <span class="string">'public'</span>,</span><br><span class="line">    <span class="attr">tag_dir</span>: <span class="string">'tags'</span>,</span><br><span class="line">    <span class="attr">archive_dir</span>: <span class="string">'archives'</span>,</span><br><span class="line">    <span class="attr">category_dir</span>: <span class="string">'categories'</span>,</span><br><span class="line">    <span class="attr">code_dir</span>: <span class="string">'downloads/code'</span>,</span><br><span class="line">    <span class="attr">i18n_dir</span>: <span class="string">':lang'</span>,</span><br><span class="line">    <span class="attr">skip_render</span>: [],</span><br><span class="line"></span><br><span class="line">    <span class="attr">new_post_name</span>: <span class="string">':title.md'</span>,</span><br><span class="line">    <span class="attr">default_layout</span>: <span class="string">'post'</span>,</span><br><span class="line">    <span class="attr">titlecase</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">external_link</span>: {</span><br><span class="line">        <span class="attr">enable</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">field</span>: <span class="string">'site'</span>,</span><br><span class="line">        <span class="attr">exclude</span>: <span class="string">''</span></span><br><span class="line">    },</span><br><span class="line">    <span class="attr">filename_case</span>: <span class="number">0</span>,</span><br><span class="line">    <span class="attr">render_drafts</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">post_asset_folder</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">relative_link</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">future</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">syntax_highlighter</span>: <span class="string">'highlight.js'</span>,</span><br><span class="line">    <span class="attr">highlight</span>: {</span><br><span class="line">        <span class="attr">auto_detect</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="attr">line_number</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">tab_replace</span>: <span class="string">''</span>,</span><br><span class="line">        <span class="attr">wrap</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">exclude_languages</span>: [],</span><br><span class="line">        <span class="attr">language_attr</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="attr">hljs</span>: <span class="literal">false</span>,</span><br><span class="line">        <span class="attr">line_threshold</span>: <span class="number">0</span>,</span><br><span class="line">        <span class="attr">first_line_number</span>: <span class="string">'always1'</span>,</span><br><span class="line">        <span class="attr">strip_indent</span>: <span class="literal">true</span></span><br><span class="line">    },</span><br><span class="line">    <span class="attr">prismjs</span>: {</span><br><span class="line">        <span class="attr">preprocess</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">line_number</span>: <span class="literal">true</span>,</span><br><span class="line">        <span class="attr">tab_replace</span>: <span class="string">''</span>,</span><br><span class="line">        <span class="attr">exclude_languages</span>: [],</span><br><span class="line">        <span class="attr">strip_indent</span>: <span class="literal">true</span></span><br><span class="line">    },</span><br><span class="line"></span><br><span class="line">    <span class="attr">default_category</span>: <span class="string">'uncategorized'</span>,</span><br><span class="line">    <span class="attr">category_map</span>: {},</span><br><span class="line">    <span class="attr">tag_map</span>: {},</span><br><span class="line"></span><br><span class="line">    <span class="attr">date_format</span>: <span class="string">'YYYY-MM-DD'</span>,</span><br><span class="line">    <span class="attr">time_format</span>: <span class="string">'HH:mm:ss'</span>,</span><br><span class="line">    <span class="attr">updated_option</span>: <span class="string">'mtime'</span>,</span><br><span class="line"></span><br><span class="line">    <span class="attr">per_page</span>: <span class="number">10</span>,</span><br><span class="line">    <span class="attr">pagination_dir</span>: <span class="string">'page'</span>,</span><br><span class="line"></span><br><span class="line">    <span class="attr">theme</span>: <span class="string">'landscape'</span>,</span><br><span class="line">    <span class="attr">server</span>: {</span><br><span class="line">        <span class="attr">cache</span>: <span class="literal">false</span></span><br><span class="line">    },</span><br><span class="line"></span><br><span class="line">    <span class="attr">deploy</span>: {},</span><br><span class="line"></span><br><span class="line">    <span class="attr">ignore</span>: [],</span><br><span class="line"></span><br><span class="line">    <span class="attr">meta_generator</span>: <span class="literal">true</span></span><br><span class="line">};</span><br><span class="line"><span class="comment">//# sourceMappingURL=default_config.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p>看着很熟悉吧,这些配置项都是我们初始化 Hexo 项目时,出现在根目录的 <code>_config.yml</code> 内的配置项。</p>
<blockquote>
<p>可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/docs/configuration">Hexo 官方文档</a></p>
</blockquote>
<h4 id="日志"><a class="markdownIt-Anchor" href="#日志"></a> 日志</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">log</span> = (<span class="number">0</span>, hexo_log_1.<span class="property">default</span>)(<span class="variable language_">this</span>.<span class="property">env</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>这里的 <code>hexo_log_1</code> 是:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> hexo_log_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"hexo-log"</span>));</span><br></pre></td></tr></tbody></table></figure>
<p><a target="_blank" rel="noopener" href="https://github.com/hexojs/hexo-log"><img src="https://gh-card.dev/repos/hexojs/hexo-log.svg" alt="hexojs/hexo-log - GitHub"></a></p>
<p><code>index.js</code> 中大量的导入变量都是这样命名的。</p>
<h4 id="渲染"><a class="markdownIt-Anchor" href="#渲染"></a> 渲染</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">render</span> = <span class="keyword">new</span> render_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/rendering">Hexo 官方文档</a></p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getExtname</span> = (<span class="params">str</span>) =&gt; {</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> str !== <span class="string">'string'</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">''</span>;</span><br><span class="line">    <span class="keyword">const</span> ext = (<span class="number">0</span>, path_1.<span class="property">extname</span>)(str);</span><br><span class="line">    <span class="keyword">return</span> ext.<span class="title function_">startsWith</span>(<span class="string">'.'</span>) ? ext.<span class="title function_">slice</span>(<span class="number">1</span>) : ext;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">toString</span> = (<span class="params">result, options</span>) =&gt; {</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span>.<span class="title function_">call</span>(options, <span class="string">'toString'</span>) || <span class="keyword">typeof</span> result === <span class="string">'string'</span>)</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">toString</span> === <span class="string">'function'</span>) {</span><br><span class="line">        <span class="keyword">return</span> options.<span class="title function_">toString</span>(result);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> result === <span class="string">'object'</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(result);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (result.<span class="property">toString</span>) {</span><br><span class="line">        <span class="keyword">return</span> result.<span class="title function_">toString</span>();</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Render</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">ctx</span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">context</span> = ctx;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">renderer</span> = ctx.<span class="property">extend</span>.<span class="property">renderer</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">isRenderable</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">renderer</span>.<span class="title function_">isRenderable</span>(path);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">isRenderableSync</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">renderer</span>.<span class="title function_">isRenderableSync</span>(path);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">getOutput</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">renderer</span>.<span class="title function_">getOutput</span>(path);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">getRenderer</span>(<span class="params">ext, sync</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">renderer</span>.<span class="title function_">get</span>(ext, sync);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">getRendererSync</span>(<span class="params">ext</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">getRenderer</span>(ext, <span class="literal">true</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">render</span>(<span class="params">data, options, callback</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!callback &amp;&amp; <span class="keyword">typeof</span> options === <span class="string">'function'</span>) {</span><br><span class="line">            callback = options;</span><br><span class="line">            options = {};</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">let</span> ext = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">let</span> promise;</span><br><span class="line">        <span class="keyword">if</span> (!data)</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'No input file or string!'</span>));</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">text</span> != <span class="literal">null</span>) {</span><br><span class="line">            promise = bluebird_1.<span class="property">default</span>.<span class="title function_">resolve</span>(data.<span class="property">text</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (!data.<span class="property">path</span>) {</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'No input file or string!'</span>));</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            promise = (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(data.<span class="property">path</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> promise.<span class="title function_">then</span>(<span class="function"><span class="params">text</span> =&gt;</span> {</span><br><span class="line">            data.<span class="property">text</span> = text;</span><br><span class="line">            ext = data.<span class="property">engine</span> || <span class="title function_">getExtname</span>(data.<span class="property">path</span>);</span><br><span class="line">            <span class="keyword">if</span> (!ext || !<span class="variable language_">this</span>.<span class="title function_">isRenderable</span>(ext))</span><br><span class="line">                <span class="keyword">return</span> text;</span><br><span class="line">            <span class="keyword">const</span> renderer = <span class="variable language_">this</span>.<span class="title function_">getRenderer</span>(ext);</span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">Reflect</span>.<span class="title function_">apply</span>(renderer, ctx, [data, options]);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> {</span><br><span class="line">            result = <span class="title function_">toString</span>(result, data);</span><br><span class="line">            <span class="keyword">if</span> (data.<span class="property">onRenderEnd</span>) {</span><br><span class="line">                <span class="keyword">return</span> data.<span class="title function_">onRenderEnd</span>(result);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">const</span> output = <span class="variable language_">this</span>.<span class="title function_">getOutput</span>(ext) || ext;</span><br><span class="line">            <span class="keyword">return</span> ctx.<span class="title function_">execFilter</span>(<span class="string">`after_render:<span class="subst">${output}</span>`</span>, result, {</span><br><span class="line">                <span class="attr">context</span>: ctx,</span><br><span class="line">                <span class="attr">args</span>: [data]</span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">renderSync</span>(<span class="params">data, options = {}</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!data)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'No input file or string!'</span>);</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">text</span> == <span class="literal">null</span>) {</span><br><span class="line">            <span class="keyword">if</span> (!data.<span class="property">path</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'No input file or string!'</span>);</span><br><span class="line">            data.<span class="property">text</span> = (<span class="number">0</span>, hexo_fs_1.<span class="property">readFileSync</span>)(data.<span class="property">path</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">text</span> == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'No input file or string!'</span>);</span><br><span class="line">        <span class="keyword">const</span> ext = data.<span class="property">engine</span> || <span class="title function_">getExtname</span>(data.<span class="property">path</span>);</span><br><span class="line">        <span class="keyword">let</span> result;</span><br><span class="line">        <span class="keyword">if</span> (ext &amp;&amp; <span class="variable language_">this</span>.<span class="title function_">isRenderableSync</span>(ext)) {</span><br><span class="line">            <span class="keyword">const</span> renderer = <span class="variable language_">this</span>.<span class="title function_">getRendererSync</span>(ext);</span><br><span class="line">            result = <span class="title class_">Reflect</span>.<span class="title function_">apply</span>(renderer, ctx, [data, options]);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            result = data.<span class="property">text</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">const</span> output = <span class="variable language_">this</span>.<span class="title function_">getOutput</span>(ext) || ext;</span><br><span class="line">        result = <span class="title function_">toString</span>(result, data);</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">onRenderEnd</span>) {</span><br><span class="line">            result = data.<span class="title function_">onRenderEnd</span>(result);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> ctx.<span class="title function_">execFilterSync</span>(<span class="string">`after_render:<span class="subst">${output}</span>`</span>, result, {</span><br><span class="line">            <span class="attr">context</span>: ctx,</span><br><span class="line">            <span class="attr">args</span>: [data]</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Render</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=render.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p><code>bluebird</code> 库是一个流行的 JavaScript Promise 库,它提供了一种更加健壮、高效和优雅的方式来处理异步操作。</p>
<p>在传统的基于回调函数的异步编程中,存在着回调地狱、代码耦合等问题。Promise 的出现解决了这些问题,使着异步代码更加易于理解和维护。而 <code>bluebird</code> 在 Promise 的基础上提供了更多的增强功能和优化。</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://github.com/petkaantonov/bluebird"><img src="https://gh-card.dev/repos/petkaantonov/bluebird.svg" alt="petkaantonov/bluebird - GitHub"></a></p>
<p><code>Render</code> 类包含两个主要方法:<code>render</code><code>renderSync</code></p>
<ol>
<li><code>render</code> 方法是一个异步方法,首先检查文件是否可根据其扩展名进行渲染。如果可以,它会获取相应的渲染器并将其应用于数据。渲染后的结果将通过一个过滤器,并在回调函数中返回。</li>
<li><code>renderSync</code> 方法是 <code>render</code> 的同步版本。工作方式类似,但使用同步文件读取,并使用异常代替回调和 Promise 处理错误。</li>
</ol>
<p>其他部分包括:</p>
<ul>
<li><code>getExtname</code> 是一个辅助函数,用于从文件路径中提取文件扩展名。</li>
<li><code>toString</code> 是一个辅助函数,根据提供的选项将结果对象转换为字符串表示。</li>
<li><code>Render</code> 类的构造函数接收 <code>Hexo</code> 上下文 <code>ctx</code>,并从中初始化 <code>renderer</code> 属性。</li>
<li><code>isRenderable</code><code>isRenderableSync</code> 方法检查给定扩展名的文件是否可渲染。</li>
<li><code>getOutput</code> 方法获取给定输入扩展名的输出扩展名。</li>
<li><code>getRenderer</code><code>getRendererSync</code> 方法获取给定扩展名的相应渲染器。</li>
</ul>
<p>这段代码为 Hexo 提供了一种基于文件扩展名渲染文件的方式,使用每种扩展名对应的渲染器。</p>
<h4 id="路由"><a class="markdownIt-Anchor" href="#路由"></a> 路由</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">route</span> = <span class="keyword">new</span> router_1.<span class="title function_">default</span>();</span><br></pre></td></tr></tbody></table></figure>
<p>这行代码实例化了 <code>Router</code> 类的对象,用于管理路由。在 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/router">Hexo 官方文档</a> 中,路由是 <strong>存储了网站中所用到的所有路径</strong></p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> events_1 = <span class="built_in">require</span>(<span class="string">"events"</span>);</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="keyword">const</span> stream_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"stream"</span>));</span><br><span class="line"><span class="keyword">const</span> { <span class="title class_">Readable</span> } = stream_1.<span class="property">default</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RouteStream</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Readable</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">data</span>) {</span><br><span class="line">        <span class="variable language_">super</span>({ <span class="attr">objectMode</span>: <span class="literal">true</span> });</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">_data</span> = data.<span class="property">data</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">_ended</span> = <span class="literal">false</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">modified</span> = data.<span class="property">modified</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">// Assume we only accept Buffer, plain object, or string</span></span><br><span class="line">    <span class="title function_">_toBuffer</span>(<span class="params">data</span>) {</span><br><span class="line">        <span class="keyword">if</span> (data <span class="keyword">instanceof</span> <span class="title class_">Buffer</span>) {</span><br><span class="line">            <span class="keyword">return</span> data;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> data === <span class="string">'object'</span>) {</span><br><span class="line">            data = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(data);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> data === <span class="string">'string'</span>) {</span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">Buffer</span>.<span class="title function_">from</span>(data); <span class="comment">// Assume string is UTF-8 encoded string</span></span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_read</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">const</span> data = <span class="variable language_">this</span>.<span class="property">_data</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> data !== <span class="string">'function'</span>) {</span><br><span class="line">            <span class="keyword">const</span> bufferData = <span class="variable language_">this</span>.<span class="title function_">_toBuffer</span>(data);</span><br><span class="line">            <span class="keyword">if</span> (bufferData) {</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">push</span>(bufferData);</span><br><span class="line">            }</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">push</span>(<span class="literal">null</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// Don't read it twice!</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">_ended</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">_ended</span> = <span class="literal">true</span>;</span><br><span class="line">        <span class="title function_">data</span>().<span class="title function_">then</span>(<span class="function"><span class="params">data</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (data <span class="keyword">instanceof</span> stream_1.<span class="property">default</span> &amp;&amp; data.<span class="property">readable</span>) {</span><br><span class="line">                data.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function"><span class="params">d</span> =&gt;</span> {</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="title function_">push</span>(d);</span><br><span class="line">                });</span><br><span class="line">                data.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="title function_">push</span>(<span class="literal">null</span>);</span><br><span class="line">                });</span><br><span class="line">                data.<span class="title function_">on</span>(<span class="string">'error'</span>, <span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'error'</span>, err);</span><br><span class="line">                });</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> {</span><br><span class="line">                <span class="keyword">const</span> bufferData = <span class="variable language_">this</span>.<span class="title function_">_toBuffer</span>(data);</span><br><span class="line">                <span class="keyword">if</span> (bufferData) {</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="title function_">push</span>(bufferData);</span><br><span class="line">                }</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">push</span>(<span class="literal">null</span>);</span><br><span class="line">            }</span><br><span class="line">        }).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'error'</span>, err);</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">push</span>(<span class="literal">null</span>);</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">_format</span> = (<span class="params">path</span>) =&gt; {</span><br><span class="line">    path = path || <span class="string">''</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> path !== <span class="string">'string'</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'path must be a string!'</span>);</span><br><span class="line">    path = path</span><br><span class="line">        .<span class="title function_">replace</span>(<span class="regexp">/^\/+/</span>, <span class="string">''</span>) <span class="comment">// Remove prefixed slashes</span></span><br><span class="line">        .<span class="title function_">replace</span>(<span class="regexp">/\\/g</span>, <span class="string">'/'</span>) <span class="comment">// Replaces all backslashes</span></span><br><span class="line">        .<span class="title function_">replace</span>(<span class="regexp">/\?.*$/</span>, <span class="string">''</span>); <span class="comment">// Remove query string</span></span><br><span class="line">    <span class="comment">// Appends `index.html` to the path with trailing slash</span></span><br><span class="line">    <span class="keyword">if</span> (!path || path.<span class="title function_">endsWith</span>(<span class="string">'/'</span>)) {</span><br><span class="line">        path += <span class="string">'index.html'</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> path;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Router</span> <span class="keyword">extends</span> <span class="title class_ inherited__">events_1.EventEmitter</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="variable language_">super</span>();</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">routes</span> = {};</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">list</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">const</span> { routes } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">Object</span>.<span class="title function_">keys</span>(routes).<span class="title function_">filter</span>(<span class="function"><span class="params">key</span> =&gt;</span> routes[key]);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">format</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">_format</span>(path);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">get</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> path !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'path must be a string!'</span>);</span><br><span class="line">        <span class="keyword">const</span> data = <span class="variable language_">this</span>.<span class="property">routes</span>[<span class="variable language_">this</span>.<span class="title function_">format</span>(path)];</span><br><span class="line">        <span class="keyword">if</span> (data == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RouteStream</span>(data);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">isModified</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> path !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'path must be a string!'</span>);</span><br><span class="line">        <span class="keyword">const</span> data = <span class="variable language_">this</span>.<span class="property">routes</span>[<span class="variable language_">this</span>.<span class="title function_">format</span>(path)];</span><br><span class="line">        <span class="keyword">return</span> data ? data.<span class="property">modified</span> : <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">set</span>(<span class="params">path, data</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> path !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'path must be a string!'</span>);</span><br><span class="line">        <span class="keyword">if</span> (data == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'data is required!'</span>);</span><br><span class="line">        <span class="keyword">let</span> obj;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> data === <span class="string">'object'</span> &amp;&amp; data.<span class="property">data</span> != <span class="literal">null</span>) {</span><br><span class="line">            obj = data;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            obj = {</span><br><span class="line">                data,</span><br><span class="line">                <span class="attr">modified</span>: <span class="literal">true</span></span><br><span class="line">            };</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> obj.<span class="property">data</span> === <span class="string">'function'</span>) {</span><br><span class="line">            <span class="keyword">if</span> (obj.<span class="property">data</span>.<span class="property">length</span>) {</span><br><span class="line">                obj.<span class="property">data</span> = bluebird_1.<span class="property">default</span>.<span class="title function_">promisify</span>(obj.<span class="property">data</span>);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> {</span><br><span class="line">                obj.<span class="property">data</span> = bluebird_1.<span class="property">default</span>.<span class="title function_">method</span>(obj.<span class="property">data</span>);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        path = <span class="variable language_">this</span>.<span class="title function_">format</span>(path);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">routes</span>[path] = {</span><br><span class="line">            <span class="attr">data</span>: obj.<span class="property">data</span>,</span><br><span class="line">            <span class="attr">modified</span>: obj.<span class="property">modified</span> == <span class="literal">null</span> ? <span class="literal">true</span> : obj.<span class="property">modified</span></span><br><span class="line">        };</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'update'</span>, path);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">remove</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> path !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'path must be a string!'</span>);</span><br><span class="line">        path = <span class="variable language_">this</span>.<span class="title function_">format</span>(path);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">routes</span>[path] = <span class="literal">null</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'remove'</span>, path);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Router</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=router.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p>这段代码定义了一个名为 <code>Router</code> 的类和一个名为 <code>RouteStream</code> 的类。它们在 Hexo 框架中用于管理路由和从路由读取数据流。</p>
<p><code>RouteStream</code> 类:</p>
<ul>
<li>继承自 Node.JS 内置的 <code>stream.Readable</code> 类,因此它是一个可读流。</li>
<li>构造函数接收一个 <code>data</code> 对象,该对象包含 <code>data</code> 属性和 <code>modified</code> 属性。</li>
<li><code>_read()</code> 方法是可读流的实现。如果 <code>data</code> 是一个函数,它会尝试从该函数中获取数据。否则,它会将 <code>data</code> 推送到流中。</li>
<li><code>_toBuffer()</code> 是一个内部方法,用于将数据转换为 <code>Buffer</code></li>
</ul>
<p><code>Router</code> 类:</p>
<ul>
<li>继承自 <code>EventEmitter</code>,因此可以发射和监听事件。</li>
<li><code>routes</code> 属性是一个对象,用于存储路径及其对应的数据。</li>
<li><code>list()</code> 方法返回所有已注册路径的列表。</li>
<li><code>format(path)</code> 方法用于格式化路径,移除前导斜杠、替换反斜杠,并为以斜杠结尾的路径添加 <code>index.html</code></li>
<li><code>get(path)</code> 方法用于获取给定路径的数据流。如果路径不存在或数据为 <code>null</code>,则返回 <code>undefined</code></li>
<li><code>isModified(path)</code> 方法用于检查给定路径的数据是否已修改。</li>
<li><code>set(path, data)</code> 方法用于设置给定路径的数据。如果 <code>data</code> 是一个函数,它会使用 <code>Bluebird</code> 库将其转换为 Promise。该方法还会触发 <code>update</code> 事件。</li>
<li><code>remove(path)</code> 方法用于移除给定路径的数据。它会将路径对应的值设置为 <code>null</code>,并触发 <code>remove</code> 事件。</li>
</ul>
<blockquote>
<p>Node.JS 的 <code>EventEmitter</code> 是一个模块,提供基于事件驱动的编程方式,是 Node.JS 很多核心模块和第三方模块的基础,也是 Node.JS 中非常重要的一个概念和设计模式。</p>
<p><code>EventEmitter</code> 是一个构造函数,可以创建一个新的事件发射器对象。这个对象可以发射命名事件,并且可以通过添加事件监听器来监听特定事件。当事件被发射时,所有监听该事件的函数回调都会被同步地调用。</p>
<p><code>EventEmitter</code> 对象有以下常用方法:</p>
<ol>
<li><code>emitter.on(eventName, listener)</code> 为指定事件注册一个监听器。</li>
<li><code>emitter.once(eventName, listener)</code> 为指定事件注册一个一次性的监听器,触发后就会被移除。</li>
<li><code>emitter.off(eventName, listener)</code> 移除指定事件的监听器。</li>
<li><code>emitter.emit(eventName, [...args])</code> 发射指定事件,传递参数给监听器回调函数。</li>
</ol>
<p><a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/events">Hexo 官方文档</a> 中也提到了 <code>EventEmitter</code></p>
</blockquote>
<h4 id="博客文章"><a class="markdownIt-Anchor" href="#博客文章"></a> 博客文章</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">post</span> = <span class="keyword">new</span> post_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/posts">Hexo 官方文档</a></p>
</blockquote>
<div class="danger">
<p><code>post.js</code> 很长,因此将部分代码分开讲解。</p>
</div>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> assert_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"assert"</span>));</span><br><span class="line"><span class="keyword">const</span> moment_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"moment"</span>));</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> picocolors_1 = <span class="built_in">require</span>(<span class="string">"picocolors"</span>);</span><br><span class="line"><span class="keyword">const</span> js_yaml_1 = <span class="built_in">require</span>(<span class="string">"js-yaml"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_util_1 = <span class="built_in">require</span>(<span class="string">"hexo-util"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_front_matter_1 = <span class="built_in">require</span>(<span class="string">"hexo-front-matter"</span>);</span><br><span class="line"><span class="keyword">const</span> preservedKeys = [<span class="string">'title'</span>, <span class="string">'slug'</span>, <span class="string">'path'</span>, <span class="string">'layout'</span>, <span class="string">'date'</span>, <span class="string">'content'</span>];</span><br><span class="line"><span class="keyword">const</span> rHexoPostRenderEscape = <span class="regexp">/&lt;hexoPostRenderCodeBlock&gt;([\s\S]+?)&lt;\/hexoPostRenderCodeBlock&gt;/g</span>;</span><br><span class="line"><span class="keyword">const</span> rSwigPlaceHolder = <span class="regexp">/(?:&lt;|&amp;lt;)!--swig\uFFFC(\d+)--(?:&gt;|&amp;gt;)/g</span>;</span><br><span class="line"><span class="keyword">const</span> rCodeBlockPlaceHolder = <span class="regexp">/(?:&lt;|&amp;lt;)!--code\uFFFC(\d+)--(?:&gt;|&amp;gt;)/g</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">STATE_PLAINTEXT</span> = <span class="title class_">Symbol</span>(<span class="string">'plaintext'</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">STATE_SWIG_VAR</span> = <span class="title class_">Symbol</span>(<span class="string">'swig_var'</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">STATE_SWIG_COMMENT</span> = <span class="title class_">Symbol</span>(<span class="string">'swig_comment'</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">STATE_SWIG_TAG</span> = <span class="title class_">Symbol</span>(<span class="string">'swig_tag'</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">STATE_SWIG_FULL_TAG</span> = <span class="title class_">Symbol</span>(<span class="string">'swig_full_tag'</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="title function_">isNonWhiteSpaceChar</span> = (<span class="params">char</span>) =&gt; char !== <span class="string">'\r'</span></span><br><span class="line">    &amp;&amp; char !== <span class="string">'\n'</span></span><br><span class="line">    &amp;&amp; char !== <span class="string">'\t'</span></span><br><span class="line">    &amp;&amp; char !== <span class="string">'\f'</span></span><br><span class="line">    &amp;&amp; char !== <span class="string">'\v'</span></span><br><span class="line">    &amp;&amp; char !== <span class="string">' '</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>PostRenderEscape</code> 类用于处理 Swig / Nunjucks 标签和代码块的转义和还原:</p>
<blockquote>
<p>Swig 和 Nunjucks 是两种流行的模板引擎。</p>
<p>主要语法包括:</p>
<ul>
<li><code>{% ... %}</code>:用于执行语句,如条件语句和循环语句。</li>
<li><code>{{ ... }}</code>:用于输出变量值。</li>
</ul>
<p>在渲染文章内容时,Hexo 需要先对 Swig / Nunjucks 标签和代码块进行转义,防止被误解析,然后再使用相应的渲染器(如 Markdown)进行渲染。渲染完成后,需要将转义的内容还原回来。</p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PostRenderEscape</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">stored</span> = [];</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">escapeContent</span>(<span class="params">cache, flag, str</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="string">`&lt;!--<span class="subst">${flag}</span>\uFFFC<span class="subst">${cache.push(str) - <span class="number">1</span>}</span>--&gt;`</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">static</span> <span class="title function_">restoreContent</span>(<span class="params">cache</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="function">(<span class="params">_, index</span>) =&gt;</span> {</span><br><span class="line">            (<span class="number">0</span>, assert_1.<span class="property">default</span>)(cache[index]);</span><br><span class="line">            <span class="keyword">const</span> value = cache[index];</span><br><span class="line">            cache[index] = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">return</span> value;</span><br><span class="line">        };</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">restoreAllSwigTags</span>(<span class="params">str</span>) {</span><br><span class="line">        <span class="keyword">const</span> restored = str.<span class="title function_">replace</span>(rSwigPlaceHolder, <span class="title class_">PostRenderEscape</span>.<span class="title function_">restoreContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>));</span><br><span class="line">        <span class="keyword">return</span> restored;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">restoreCodeBlocks</span>(<span class="params">str</span>) {</span><br><span class="line">        <span class="keyword">return</span> str.<span class="title function_">replace</span>(rCodeBlockPlaceHolder, <span class="title class_">PostRenderEscape</span>.<span class="title function_">restoreContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>));</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">escapeCodeBlocks</span>(<span class="params">str</span>) {</span><br><span class="line">        <span class="keyword">return</span> str.<span class="title function_">replace</span>(rHexoPostRenderEscape, <span class="function">(<span class="params">_, content</span>) =&gt;</span> <span class="title class_">PostRenderEscape</span>.<span class="title function_">escapeContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>, <span class="string">'code'</span>, content));</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> {<span class="type">string</span>} <span class="variable">str</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@returns</span> <span class="variable">string</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="title function_">escapeAllSwigTags</span>(<span class="params">str</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!<span class="regexp">/(\{\{.+?\}\})|(\{#.+?#\})|(\{%.+?%\})/</span>s.<span class="title function_">test</span>(str)) {</span><br><span class="line">            <span class="keyword">return</span> str;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">let</span> state = <span class="variable constant_">STATE_PLAINTEXT</span>;</span><br><span class="line">        <span class="keyword">let</span> buffer = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">let</span> output = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">let</span> swig_tag_name_begin = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">let</span> swig_tag_name_end = <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">let</span> swig_tag_name = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">let</span> swig_full_tag_start_buffer = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">const</span> { length } = str;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> idx = <span class="number">0</span>; idx &lt; length; idx++) {</span><br><span class="line">            <span class="keyword">const</span> char = str[idx];</span><br><span class="line">            <span class="keyword">const</span> next_char = str[idx + <span class="number">1</span>];</span><br><span class="line">            <span class="keyword">if</span> (state === <span class="variable constant_">STATE_PLAINTEXT</span>) { <span class="comment">// From plain text to swig</span></span><br><span class="line">                <span class="keyword">if</span> (char === <span class="string">'{'</span>) {</span><br><span class="line">                    <span class="comment">// check if it is a complete tag {{ }}</span></span><br><span class="line">                    <span class="keyword">if</span> (next_char === <span class="string">'{'</span>) {</span><br><span class="line">                        state = <span class="variable constant_">STATE_SWIG_VAR</span>;</span><br><span class="line">                        idx++;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (next_char === <span class="string">'#'</span>) {</span><br><span class="line">                        state = <span class="variable constant_">STATE_SWIG_COMMENT</span>;</span><br><span class="line">                        idx++;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (next_char === <span class="string">'%'</span>) {</span><br><span class="line">                        state = <span class="variable constant_">STATE_SWIG_TAG</span>;</span><br><span class="line">                        idx++;</span><br><span class="line">                        swig_tag_name = <span class="string">''</span>;</span><br><span class="line">                        swig_full_tag_start_buffer = <span class="string">''</span>;</span><br><span class="line">                        swig_tag_name_begin = <span class="literal">false</span>; <span class="comment">// Mark if it is the first non white space char in the swig tag</span></span><br><span class="line">                        swig_tag_name_end = <span class="literal">false</span>;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> {</span><br><span class="line">                        output += char;</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> {</span><br><span class="line">                    output += char;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (state === <span class="variable constant_">STATE_SWIG_TAG</span>) {</span><br><span class="line">                <span class="keyword">if</span> (char === <span class="string">'%'</span> &amp;&amp; next_char === <span class="string">'}'</span>) { <span class="comment">// From swig back to plain text</span></span><br><span class="line">                    idx++;</span><br><span class="line">                    <span class="keyword">if</span> (swig_tag_name !== <span class="string">''</span> &amp;&amp; str.<span class="title function_">includes</span>(<span class="string">`end<span class="subst">${swig_tag_name}</span>`</span>)) {</span><br><span class="line">                        state = <span class="variable constant_">STATE_SWIG_FULL_TAG</span>;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> {</span><br><span class="line">                        swig_tag_name = <span class="string">''</span>;</span><br><span class="line">                        state = <span class="variable constant_">STATE_PLAINTEXT</span>;</span><br><span class="line">                        output += <span class="title class_">PostRenderEscape</span>.<span class="title function_">escapeContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>, <span class="string">'swig'</span>, <span class="string">`{%<span class="subst">${buffer}</span>%}`</span>);</span><br><span class="line">                    }</span><br><span class="line">                    buffer = <span class="string">''</span>;</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> {</span><br><span class="line">                    buffer = buffer + char;</span><br><span class="line">                    swig_full_tag_start_buffer = swig_full_tag_start_buffer + char;</span><br><span class="line">                    <span class="keyword">if</span> (<span class="title function_">isNonWhiteSpaceChar</span>(char)) {</span><br><span class="line">                        <span class="keyword">if</span> (!swig_tag_name_begin &amp;&amp; !swig_tag_name_end) {</span><br><span class="line">                            swig_tag_name_begin = <span class="literal">true</span>;</span><br><span class="line">                        }</span><br><span class="line">                        <span class="keyword">if</span> (swig_tag_name_begin) {</span><br><span class="line">                            swig_tag_name += char;</span><br><span class="line">                        }</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> {</span><br><span class="line">                        <span class="keyword">if</span> (swig_tag_name_begin === <span class="literal">true</span>) {</span><br><span class="line">                            swig_tag_name_begin = <span class="literal">false</span>;</span><br><span class="line">                            swig_tag_name_end = <span class="literal">true</span>;</span><br><span class="line">                        }</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (state === <span class="variable constant_">STATE_SWIG_VAR</span>) {</span><br><span class="line">                <span class="keyword">if</span> (char === <span class="string">'}'</span> &amp;&amp; next_char === <span class="string">'}'</span>) {</span><br><span class="line">                    idx++;</span><br><span class="line">                    state = <span class="variable constant_">STATE_PLAINTEXT</span>;</span><br><span class="line">                    output += <span class="title class_">PostRenderEscape</span>.<span class="title function_">escapeContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>, <span class="string">'swig'</span>, <span class="string">`{{<span class="subst">${buffer}</span>}}`</span>);</span><br><span class="line">                    buffer = <span class="string">''</span>;</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> {</span><br><span class="line">                    buffer = buffer + char;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (state === <span class="variable constant_">STATE_SWIG_COMMENT</span>) { <span class="comment">// From swig back to plain text</span></span><br><span class="line">                <span class="keyword">if</span> (char === <span class="string">'#'</span> &amp;&amp; next_char === <span class="string">'}'</span>) {</span><br><span class="line">                    idx++;</span><br><span class="line">                    state = <span class="variable constant_">STATE_PLAINTEXT</span>;</span><br><span class="line">                    buffer = <span class="string">''</span>;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (state === <span class="variable constant_">STATE_SWIG_FULL_TAG</span>) {</span><br><span class="line">                <span class="keyword">if</span> (char === <span class="string">'{'</span> &amp;&amp; next_char === <span class="string">'%'</span>) {</span><br><span class="line">                    <span class="keyword">let</span> swig_full_tag_end_buffer = <span class="string">''</span>;</span><br><span class="line">                    <span class="keyword">let</span> _idx = idx + <span class="number">2</span>;</span><br><span class="line">                    <span class="keyword">for</span> (; _idx &lt; length; _idx++) {</span><br><span class="line">                        <span class="keyword">const</span> _char = str[_idx];</span><br><span class="line">                        <span class="keyword">const</span> _next_char = str[_idx + <span class="number">1</span>];</span><br><span class="line">                        <span class="keyword">if</span> (_char === <span class="string">'%'</span> &amp;&amp; _next_char === <span class="string">'}'</span>) {</span><br><span class="line">                            _idx++;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        }</span><br><span class="line">                        swig_full_tag_end_buffer = swig_full_tag_end_buffer + _char;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">if</span> (swig_full_tag_end_buffer.<span class="title function_">includes</span>(<span class="string">`end<span class="subst">${swig_tag_name}</span>`</span>)) {</span><br><span class="line">                        state = <span class="variable constant_">STATE_PLAINTEXT</span>;</span><br><span class="line">                        output += <span class="title class_">PostRenderEscape</span>.<span class="title function_">escapeContent</span>(<span class="variable language_">this</span>.<span class="property">stored</span>, <span class="string">'swig'</span>, <span class="string">`{%<span class="subst">${swig_full_tag_start_buffer}</span>%}<span class="subst">${buffer}</span>{%<span class="subst">${swig_full_tag_end_buffer}</span>%}`</span>);</span><br><span class="line">                        idx = _idx;</span><br><span class="line">                        swig_full_tag_start_buffer = <span class="string">''</span>;</span><br><span class="line">                        swig_full_tag_end_buffer = <span class="string">''</span>;</span><br><span class="line">                        buffer = <span class="string">''</span>;</span><br><span class="line">                    }</span><br><span class="line">                    <span class="keyword">else</span> {</span><br><span class="line">                        buffer += char;</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">                <span class="keyword">else</span> {</span><br><span class="line">                    buffer += char;</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> output;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>escapeContent</code>:用于将给定的字符串转义为占位符,并存储在缓存数组中。
<ul>
<li>转义后的占位符格式为:<code><!--flag\uFFFC${index}--></code></li>
</ul>
</li>
<li><code>restoreContent</code>:用于将占位符还原为原始字符串。</li>
<li><code>restoreAllSwigTags</code>:使用正则表达式替换所有 Swig 标签占位符,调用 <code>restoreContent</code> 方法还原原始内容。</li>
<li><code>restoreCodeBlocks</code>:使用正则表达式替换所有代码块占位符,调用 <code>restoreContent</code> 方法还原原始内容。</li>
<li><code>escapeCodeBlocks</code>:使用正则表达式匹配所有 Markdown 代码块,调用 <code>escapeContent</code> 方法将它们转义为占位符。</li>
<li><code>escapeAllSwigTags</code>:用于转义 Swig 模板标签。首先检查输入字符串是否包含 Swig 标签,然后使用有限状态机的方式遍历每个字符,识别出 Swig 标签的类型和内容,并调用 <code>escapeContent</code> 进行转义。</li>
</ul>
<blockquote>
<p>有限状态机(Finite State Machine;FSM)是一种数学计算模型,用于描述具有有限个状态以及基于事件进行状态转移的系统。</p>
<p>一个有限状态机包含以下几个基本组成部分:</p>
<ul>
<li>有限状态集合(States)。</li>
<li>输入事件/符号集合(Input Events/Symbols)。</li>
<li>一个初始状态(Initial State)。</li>
<li>状态转移函数(State Transition Function)。</li>
<li>终止状态集合(Final States)。</li>
</ul>
<p>有限状态机的工作原理:</p>
<ol>
<li>机器初始处于一个确定的初始状态。</li>
<li>机器接收一个输入事件 / 符号。</li>
<li>根据当前状态和输入事件 / 符号,机器按照状态转移函数进行状态转移。</li>
<li>机器进入新的状态,等待下一个输入事件 / 符号。</li>
<li>重复以上过程,直到进入某个终止状态或发生无法处理的输入事件 / 符号。</li>
</ol>
<p><code>escapeAllSwigTags</code> 使用了一个有限状态机来精确识别和处理各种 Swig 标签。它定义了五种状态,通过遍历输入字符串,根据当前状态和字符进行状态转移,从而正确处理嵌套、注释等复杂情况。</p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">prepareFrontMatter</span> = (<span class="params">data, jsonMode</span>) =&gt; {</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> [key, item] <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">entries</span>(data)) {</span><br><span class="line">        <span class="keyword">if</span> (moment_1.<span class="property">default</span>.<span class="title function_">isMoment</span>(item)) {</span><br><span class="line">            data[key] = item.<span class="title function_">utc</span>().<span class="title function_">format</span>(<span class="string">'YYYY-MM-DD HH:mm:ss'</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (moment_1.<span class="property">default</span>.<span class="title function_">isDate</span>(item)) {</span><br><span class="line">            data[key] = moment_1.<span class="property">default</span>.<span class="title function_">utc</span>(item).<span class="title function_">format</span>(<span class="string">'YYYY-MM-DD HH:mm:ss'</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> item === <span class="string">'string'</span>) {</span><br><span class="line">            <span class="keyword">if</span> (jsonMode || item.<span class="title function_">includes</span>(<span class="string">':'</span>) || item.<span class="title function_">startsWith</span>(<span class="string">'#'</span>) || item.<span class="title function_">startsWith</span>(<span class="string">'!!'</span>)</span><br><span class="line">                || item.<span class="title function_">includes</span>(<span class="string">'{'</span>) || item.<span class="title function_">includes</span>(<span class="string">'}'</span>) || item.<span class="title function_">includes</span>(<span class="string">'['</span>) || item.<span class="title function_">includes</span>(<span class="string">']'</span>)</span><br><span class="line">                || item.<span class="title function_">includes</span>(<span class="string">'\''</span>) || item.<span class="title function_">includes</span>(<span class="string">'"'</span>))</span><br><span class="line">                data[key] = <span class="string">`"<span class="subst">${item.replace(/<span class="string">"/g, '\\"</span><span class="string">')}"`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">        }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    return data;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">};</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">const removeExtname = (str) =&gt; {</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    return str.substring(0, str.length - (0, path_1.extname)(str).length);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">};</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">const createAssetFolder = (path, assetFolder) =&gt; {</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    if (!assetFolder)</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">        return bluebird_1.default.resolve();</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    const target = removeExtname(path);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    if ((0, path_1.basename)(target) === '</span>index<span class="string">')</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">        return bluebird_1.default.resolve();</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    return (0, hexo_fs_1.exists)(target).then(exist =&gt; {</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">        if (!exist)</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">            return (0, hexo_fs_1.mkdirs)(target);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">    });</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">};</span></span></span></span><br></pre></td></tr></tbody></table></figure>
<ul>
<li>
<p><code>prepareFrontMatter</code> 方法用于处理文章的 Front Matter 元数据。</p>
<blockquote>
<p>Front Matter 是指在 Markdown 或其他 markup 文件的头部添加的一组元数据。它通常被包裹在两组连续的三短横线之间,例如:</p>
<figure class="highlight markdown"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: My Blog Post</span><br><span class="line">date: 2023-05-26</span><br><span class="line"><span class="section">tags: [blog, coding]</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"></span><br><span class="line"><span class="section"># My Blog Post</span></span><br><span class="line"></span><br><span class="line">This is the content of the blog post...</span><br></pre></td></tr></tbody></table></figure>
<p>Front Matter 通常采用 YAML 或 JSON 格式来表示键值对的元数据。Front Matter 中的数据可以在渲染 markdown 文件时被解析和使用,例如在博客系统中用于生成文章的元数据、URL 等。不同的静态站点生成器和 markdown 渲染器支持不同的 Front Matter 语法和元数据项。</p>
</blockquote>
<p>它遍历 <code>data</code> 对象的每个键值对。如果值是 <code>moment</code> 对象或 <code>Date</code> 对象,则将其转换为 UTC 时区的 <code>YYYY-MM-DD HH:mm:ss</code> 格式的字符串。如果值是字符串,并且符合某些条件(包含特殊字符或需要转义),则将其用双引号包裹起来,并对双引号进行转义。</p>
<p>这个函数主要用于在生成文章时正确处理 Front Matter 中的日期和特殊字符。</p>
</li>
<li>
<p><code>removeExtname</code> 方法用于从文件路径中移除扩展名。它使用 <code>path.extname</code> 获取扩展名,然后从原始路径中截取并返回不包含扩展名的部分。</p>
</li>
<li>
<p><code>createAssetFolder</code> 方法用于在生成文章时创建与文章相关联的资源文件夹(如果配置启用了该选项)。</p>
<ul>
<li>如果 <code>assetFolder</code><code>false</code>,则直接返回一个已解决的 Promise。否则它会调用 <code>removeExtname</code> 来获取不包含扩展名的文件路径。</li>
<li>如果这个路径的基础名称是 <code>index</code>,则直接返回一个已解决的 Promise(因为 <code>index</code> 通常是默认文件名,不需要创建文件夹)。否则,它会检查该路径是否存在,如果不存在,则创建该路径作为文件夹。</li>
</ul>
</li>
</ul>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Post</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">context</span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">context</span> = context;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">create</span>(<span class="params">data, replace, callback</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!callback &amp;&amp; <span class="keyword">typeof</span> replace === <span class="string">'function'</span>) {</span><br><span class="line">            callback = replace;</span><br><span class="line">            replace = <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">const</span> { config } = ctx;</span><br><span class="line">        data.<span class="property">slug</span> = (<span class="number">0</span>, hexo_util_1.<span class="property">slugize</span>)((data.<span class="property">slug</span> || data.<span class="property">title</span>).<span class="title function_">toString</span>(), { <span class="attr">transform</span>: config.<span class="property">filename_case</span> });</span><br><span class="line">        data.<span class="property">layout</span> = (data.<span class="property">layout</span> || config.<span class="property">default_layout</span>).<span class="title function_">toLowerCase</span>();</span><br><span class="line">        data.<span class="property">date</span> = data.<span class="property">date</span> ? (<span class="number">0</span>, moment_1.<span class="property">default</span>)(data.<span class="property">date</span>) : (<span class="number">0</span>, moment_1.<span class="property">default</span>)();</span><br><span class="line">        <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">all</span>([</span><br><span class="line">            <span class="comment">// Get the post path</span></span><br><span class="line">            ctx.<span class="title function_">execFilter</span>(<span class="string">'new_post_path'</span>, data, {</span><br><span class="line">                <span class="attr">args</span>: [replace],</span><br><span class="line">                <span class="attr">context</span>: ctx</span><br><span class="line">            }),</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">_renderScaffold</span>(data)</span><br><span class="line">        ]).<span class="title function_">spread</span>(<span class="function">(<span class="params">path, content</span>) =&gt;</span> {</span><br><span class="line">            <span class="keyword">const</span> result = { path, content };</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">all</span>([</span><br><span class="line">                <span class="comment">// Write content to file</span></span><br><span class="line">                (<span class="number">0</span>, hexo_fs_1.<span class="property">writeFile</span>)(path, content),</span><br><span class="line">                <span class="comment">// Create asset folder</span></span><br><span class="line">                <span class="title function_">createAssetFolder</span>(path, config.<span class="property">post_asset_folder</span>)</span><br><span class="line">            ]).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">                ctx.<span class="title function_">emit</span>(<span class="string">'new'</span>, result);</span><br><span class="line">                <span class="keyword">return</span> result;</span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_getScaffold</span>(<span class="params">layout</span>) {</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">return</span> ctx.<span class="property">scaffold</span>.<span class="title function_">get</span>(layout).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (result != <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">return</span> result;</span><br><span class="line">            <span class="keyword">return</span> ctx.<span class="property">scaffold</span>.<span class="title function_">get</span>(<span class="string">'normal'</span>);</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_renderScaffold</span>(<span class="params">data</span>) {</span><br><span class="line">        <span class="keyword">const</span> { tag } = <span class="variable language_">this</span>.<span class="property">context</span>.<span class="property">extend</span>;</span><br><span class="line">        <span class="keyword">let</span> splitted;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_getScaffold</span>(data.<span class="property">layout</span>).<span class="title function_">then</span>(<span class="function"><span class="params">scaffold</span> =&gt;</span> {</span><br><span class="line">            splitted = (<span class="number">0</span>, hexo_front_matter_1.<span class="property">split</span>)(scaffold);</span><br><span class="line">            <span class="keyword">const</span> jsonMode = splitted.<span class="property">separator</span>.<span class="title function_">startsWith</span>(<span class="string">';'</span>);</span><br><span class="line">            <span class="keyword">const</span> frontMatter = <span class="title function_">prepareFrontMatter</span>(<span class="title class_">Object</span>.<span class="title function_">assign</span>({}, data), jsonMode);</span><br><span class="line">            <span class="keyword">return</span> tag.<span class="title function_">render</span>(splitted.<span class="property">data</span>, frontMatter);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">frontMatter</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">const</span> { separator } = splitted;</span><br><span class="line">            <span class="keyword">const</span> jsonMode = separator.<span class="title function_">startsWith</span>(<span class="string">';'</span>);</span><br><span class="line">            <span class="comment">// Parse front-matter</span></span><br><span class="line">            <span class="keyword">const</span> obj = jsonMode ? <span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="string">`{<span class="subst">${frontMatter}</span>}`</span>) : (<span class="number">0</span>, js_yaml_1.<span class="property">load</span>)(frontMatter);</span><br><span class="line">            <span class="title class_">Object</span>.<span class="title function_">keys</span>(data)</span><br><span class="line">                .<span class="title function_">filter</span>(<span class="function"><span class="params">key</span> =&gt;</span> !preservedKeys.<span class="title function_">includes</span>(key) &amp;&amp; obj[key] == <span class="literal">null</span>)</span><br><span class="line">                .<span class="title function_">forEach</span>(<span class="function"><span class="params">key</span> =&gt;</span> {</span><br><span class="line">                    obj[key] = data[key];</span><br><span class="line">                });</span><br><span class="line">            <span class="keyword">let</span> content = <span class="string">''</span>;</span><br><span class="line">            <span class="comment">// Prepend the separator</span></span><br><span class="line">            <span class="keyword">if</span> (splitted.<span class="property">prefixSeparator</span>)</span><br><span class="line">                content += <span class="string">`<span class="subst">${separator}</span>\n`</span>;</span><br><span class="line">            content += (<span class="number">0</span>, hexo_front_matter_1.<span class="property">stringify</span>)(obj, {</span><br><span class="line">                <span class="attr">mode</span>: jsonMode ? <span class="string">'json'</span> : <span class="string">''</span></span><br><span class="line">            });</span><br><span class="line">            <span class="comment">// Concat content</span></span><br><span class="line">            content += splitted.<span class="property">content</span>;</span><br><span class="line">            <span class="keyword">if</span> (data.<span class="property">content</span>) {</span><br><span class="line">                content += <span class="string">`\n<span class="subst">${data.content}</span>`</span>;</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">return</span> content;</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">publish</span>(<span class="params">data, replace, callback</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!callback &amp;&amp; <span class="keyword">typeof</span> replace === <span class="string">'function'</span>) {</span><br><span class="line">            callback = replace;</span><br><span class="line">            replace = <span class="literal">false</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">layout</span> === <span class="string">'draft'</span>)</span><br><span class="line">            data.<span class="property">layout</span> = <span class="string">'post'</span>;</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">const</span> { config } = ctx;</span><br><span class="line">        <span class="keyword">const</span> draftDir = (<span class="number">0</span>, path_1.<span class="property">join</span>)(ctx.<span class="property">source_dir</span>, <span class="string">'_drafts'</span>);</span><br><span class="line">        <span class="keyword">const</span> slug = (<span class="number">0</span>, hexo_util_1.<span class="property">slugize</span>)(data.<span class="property">slug</span>.<span class="title function_">toString</span>(), { <span class="attr">transform</span>: config.<span class="property">filename_case</span> });</span><br><span class="line">        data.<span class="property">slug</span> = slug;</span><br><span class="line">        <span class="keyword">const</span> regex = <span class="keyword">new</span> <span class="title class_">RegExp</span>(<span class="string">`^<span class="subst">${(<span class="number">0</span>, hexo_util_1.escapeRegExp)(slug)}</span>(?:[^\\/\\\\]+)`</span>);</span><br><span class="line">        <span class="keyword">let</span> src = <span class="string">''</span>;</span><br><span class="line">        <span class="keyword">const</span> result = {};</span><br><span class="line">        data.<span class="property">layout</span> = (data.<span class="property">layout</span> || config.<span class="property">default_layout</span>).<span class="title function_">toLowerCase</span>();</span><br><span class="line">        <span class="comment">// Find the draft</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">listDir</span>)(draftDir).<span class="title function_">then</span>(<span class="function"><span class="params">list</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">const</span> item = list.<span class="title function_">find</span>(<span class="function"><span class="params">item</span> =&gt;</span> regex.<span class="title function_">test</span>(item));</span><br><span class="line">            <span class="keyword">if</span> (!item)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`Draft "<span class="subst">${slug}</span>" does not exist.`</span>);</span><br><span class="line">            <span class="comment">// Read the content</span></span><br><span class="line">            src = (<span class="number">0</span>, path_1.<span class="property">join</span>)(draftDir, item);</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(src);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">content</span> =&gt;</span> {</span><br><span class="line">            <span class="comment">// Create post</span></span><br><span class="line">            <span class="title class_">Object</span>.<span class="title function_">assign</span>(data, (<span class="number">0</span>, hexo_front_matter_1.<span class="property">parse</span>)(content));</span><br><span class="line">            data.<span class="property">content</span> = data.<span class="property">_content</span>;</span><br><span class="line">            data.<span class="property">_content</span> = <span class="literal">undefined</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">create</span>(data, replace);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">post</span> =&gt;</span> {</span><br><span class="line">            result.<span class="property">path</span> = post.<span class="property">path</span>;</span><br><span class="line">            result.<span class="property">content</span> = post.<span class="property">content</span>;</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">unlink</span>)(src);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (!config.<span class="property">post_asset_folder</span>)</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            <span class="comment">// Copy assets</span></span><br><span class="line">            <span class="keyword">const</span> assetSrc = <span class="title function_">removeExtname</span>(src);</span><br><span class="line">            <span class="keyword">const</span> assetDest = <span class="title function_">removeExtname</span>(result.<span class="property">path</span>);</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">exists</span>)(assetSrc).<span class="title function_">then</span>(<span class="function"><span class="params">exist</span> =&gt;</span> {</span><br><span class="line">                <span class="keyword">if</span> (!exist)</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">copyDir</span>)(assetSrc, assetDest).<span class="title function_">then</span>(<span class="function">() =&gt;</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">rmdir</span>)(assetSrc));</span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">thenReturn</span>(result).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">render</span>(<span class="params">source, data = {}, callback</span>) {</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">const</span> { config } = ctx;</span><br><span class="line">        <span class="keyword">const</span> { tag } = ctx.<span class="property">extend</span>;</span><br><span class="line">        <span class="keyword">const</span> ext = data.<span class="property">engine</span> || (source ? (<span class="number">0</span>, path_1.<span class="property">extname</span>)(source) : <span class="string">''</span>);</span><br><span class="line">        <span class="keyword">let</span> promise;</span><br><span class="line">        <span class="keyword">if</span> (data.<span class="property">content</span> != <span class="literal">null</span>) {</span><br><span class="line">            promise = bluebird_1.<span class="property">default</span>.<span class="title function_">resolve</span>(data.<span class="property">content</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (source) {</span><br><span class="line">            <span class="comment">// Read content from files</span></span><br><span class="line">            promise = (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(source);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'No input file or string!'</span>)).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// Files like js and css are also processed by this function, but they do not require preprocessing like markdown</span></span><br><span class="line">        <span class="comment">// data.source does not exist when tag plugins call the markdown renderer</span></span><br><span class="line">        <span class="keyword">const</span> isPost = !data.<span class="property">source</span> || [<span class="string">'html'</span>, <span class="string">'htm'</span>].<span class="title function_">includes</span>(ctx.<span class="property">render</span>.<span class="title function_">getOutput</span>(data.<span class="property">source</span>));</span><br><span class="line">        <span class="keyword">if</span> (!isPost) {</span><br><span class="line">            <span class="keyword">return</span> promise.<span class="title function_">then</span>(<span class="function"><span class="params">content</span> =&gt;</span> {</span><br><span class="line">                data.<span class="property">content</span> = content;</span><br><span class="line">                ctx.<span class="property">log</span>.<span class="title function_">debug</span>(<span class="string">'Rendering file: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(source));</span><br><span class="line">                <span class="keyword">return</span> ctx.<span class="property">render</span>.<span class="title function_">render</span>({</span><br><span class="line">                    <span class="attr">text</span>: data.<span class="property">content</span>,</span><br><span class="line">                    <span class="attr">path</span>: source,</span><br><span class="line">                    <span class="attr">engine</span>: data.<span class="property">engine</span>,</span><br><span class="line">                    <span class="attr">toString</span>: <span class="literal">true</span></span><br><span class="line">                });</span><br><span class="line">            }).<span class="title function_">then</span>(<span class="function"><span class="params">content</span> =&gt;</span> {</span><br><span class="line">                data.<span class="property">content</span> = content;</span><br><span class="line">                <span class="keyword">return</span> data;</span><br><span class="line">            }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// disable Nunjucks when the renderer specify that.</span></span><br><span class="line">        <span class="keyword">let</span> disableNunjucks = ext &amp;&amp; ctx.<span class="property">render</span>.<span class="property">renderer</span>.<span class="title function_">get</span>(ext) &amp;&amp; !!ctx.<span class="property">render</span>.<span class="property">renderer</span>.<span class="title function_">get</span>(ext).<span class="property">disableNunjucks</span>;</span><br><span class="line">        <span class="comment">// front-matter overrides renderer's option</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> data.<span class="property">disableNunjucks</span> === <span class="string">'boolean'</span>)</span><br><span class="line">            disableNunjucks = data.<span class="property">disableNunjucks</span>;</span><br><span class="line">        <span class="keyword">const</span> cacheObj = <span class="keyword">new</span> <span class="title class_">PostRenderEscape</span>();</span><br><span class="line">        <span class="keyword">return</span> promise.<span class="title function_">then</span>(<span class="function"><span class="params">content</span> =&gt;</span> {</span><br><span class="line">            data.<span class="property">content</span> = content;</span><br><span class="line">            <span class="comment">// Run "before_post_render" filters</span></span><br><span class="line">            <span class="keyword">return</span> ctx.<span class="title function_">execFilter</span>(<span class="string">'before_post_render'</span>, data, { <span class="attr">context</span>: ctx });</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">            data.<span class="property">content</span> = cacheObj.<span class="title function_">escapeCodeBlocks</span>(data.<span class="property">content</span>);</span><br><span class="line">            <span class="comment">// Escape all Nunjucks/Swig tags</span></span><br><span class="line">            <span class="keyword">if</span> (disableNunjucks === <span class="literal">false</span>) {</span><br><span class="line">                data.<span class="property">content</span> = cacheObj.<span class="title function_">escapeAllSwigTags</span>(data.<span class="property">content</span>);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">const</span> options = data.<span class="property">markdown</span> || {};</span><br><span class="line">            <span class="keyword">if</span> (!config.<span class="property">syntax_highlighter</span>)</span><br><span class="line">                options.<span class="property">highlight</span> = <span class="literal">null</span>;</span><br><span class="line">            ctx.<span class="property">log</span>.<span class="title function_">debug</span>(<span class="string">'Rendering post: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(source));</span><br><span class="line">            <span class="comment">// Render with markdown or other renderer</span></span><br><span class="line">            <span class="keyword">return</span> ctx.<span class="property">render</span>.<span class="title function_">render</span>({</span><br><span class="line">                <span class="attr">text</span>: data.<span class="property">content</span>,</span><br><span class="line">                <span class="attr">path</span>: source,</span><br><span class="line">                <span class="attr">engine</span>: data.<span class="property">engine</span>,</span><br><span class="line">                <span class="attr">toString</span>: <span class="literal">true</span>,</span><br><span class="line">                <span class="title function_">onRenderEnd</span>(<span class="params">content</span>) {</span><br><span class="line">                    <span class="comment">// Replace cache data with real contents</span></span><br><span class="line">                    data.<span class="property">content</span> = cacheObj.<span class="title function_">restoreAllSwigTags</span>(content);</span><br><span class="line">                    <span class="comment">// Return content after replace the placeholders</span></span><br><span class="line">                    <span class="keyword">if</span> (disableNunjucks)</span><br><span class="line">                        <span class="keyword">return</span> data.<span class="property">content</span>;</span><br><span class="line">                    <span class="comment">// Render with Nunjucks</span></span><br><span class="line">                    <span class="keyword">return</span> tag.<span class="title function_">render</span>(data.<span class="property">content</span>, data);</span><br><span class="line">                }</span><br><span class="line">            }, options);</span><br><span class="line">        }).<span class="title function_">then</span>(<span class="function"><span class="params">content</span> =&gt;</span> {</span><br><span class="line">            data.<span class="property">content</span> = cacheObj.<span class="title function_">restoreCodeBlocks</span>(content);</span><br><span class="line">            <span class="comment">// Run "after_post_render" filters</span></span><br><span class="line">            <span class="keyword">return</span> ctx.<span class="title function_">execFilter</span>(<span class="string">'after_post_render'</span>, data, { <span class="attr">context</span>: ctx });</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Post</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=post.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Post</code> 类包含了在 Hexo 框架中创建、发布和渲染文章的和新方法。</p>
<ul>
<li><code>create(data, replace, callback)</code>:创建一篇新文章。
<ol>
<li>首先根据配置和传入的数据准备文章的 slug、布局和日期。</li>
<li>然后通过执行 <code>new_post_path</code> 过滤器获取文章路径,并调用 <code>_renderScaffold</code> 方法渲染文章内容。</li>
<li>最后将渲染后的内容写入文件,并根据配置创建相关的资源文件夹。</li>
</ol>
</li>
<li><code>_getScaffold(layout)</code><code>_renderScaffold(data)</code><ul>
<li><code>_getScaffold</code> 方法用于获取指定布局的脚手架。</li>
<li><code>_renderScaffold</code> 方法用于渲染脚手架,并将传入的数据合并到模板中。
<ul>
<li>先解析 Front Matter,然后使用标签插件渲染 Front Matter。最后将渲染后的 Front Matter 和原始内容合并,返回最终的文章内容。</li>
</ul>
</li>
</ul>
</li>
<li><code>publish(data, replace, callback)</code>:将草稿文章发布为正式文章。
<ol>
<li>首先从 <code>_drafts</code> 目录查找对应的草稿文件,读取其内容。</li>
<li>然后将草稿内容与传入的数据合并,调用 <code>create</code> 方法创建正式文章。</li>
<li>最后删除原始的草稿文件,并根据配置复制相关的资源文件夹。</li>
</ol>
</li>
<li><code>render(source, data, callback)</code>:渲染文章内容。
<ol>
<li>首先读取源文件或使用传入的内容。</li>
<li>然后执行 <code>before_post_render</code> 过滤器。</li>
<li>接着对代码块和 Swig / Nunjucks 标签进行转义,使用 Markdown 或其他渲染器渲染文章内容。</li>
<li>渲染完成后,将转义的内容还原,并执行 <code>after_post_render</code> 过滤器。</li>
<li>如果禁用了 Nunjucks,则返回渲染后的内容;否则使用标签插件渲染一次。</li>
</ol>
</li>
</ul>
<h4 id="脚手架"><a class="markdownIt-Anchor" href="#脚手架"></a> 脚手架</h4>
<p>脚手架文件是创建新文章或页面时使用的模板文件。</p>
<blockquote>
<p>可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/scaffolds">Hexo 官方文档</a></p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">scaffold</span> = <span class="keyword">new</span> scaffold_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Scaffold</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">context</span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">context</span> = context;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">scaffoldDir</span> = context.<span class="property">scaffold_dir</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">defaults</span> = {</span><br><span class="line">            <span class="attr">normal</span>: [</span><br><span class="line">                <span class="string">'---'</span>,</span><br><span class="line">                <span class="string">'layout: {{ layout }}'</span>,</span><br><span class="line">                <span class="string">'title: {{ title }}'</span>,</span><br><span class="line">                <span class="string">'date: {{ date }}'</span>,</span><br><span class="line">                <span class="string">'tags:'</span>,</span><br><span class="line">                <span class="string">'---'</span></span><br><span class="line">            ].<span class="title function_">join</span>(<span class="string">'\n'</span>)</span><br><span class="line">        };</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_listDir</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">const</span> { scaffoldDir } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">exists</span>)(scaffoldDir).<span class="title function_">then</span>(<span class="function"><span class="params">exist</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (!exist)</span><br><span class="line">                <span class="keyword">return</span> [];</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">listDir</span>)(scaffoldDir, {</span><br><span class="line">                <span class="attr">ignorePattern</span>: <span class="regexp">/^_|\/_/</span></span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> ({</span><br><span class="line">            <span class="attr">name</span>: item.<span class="title function_">substring</span>(<span class="number">0</span>, item.<span class="property">length</span> - (<span class="number">0</span>, path_1.<span class="property">extname</span>)(item).<span class="property">length</span>),</span><br><span class="line">            <span class="attr">path</span>: (<span class="number">0</span>, path_1.<span class="property">join</span>)(scaffoldDir, item)</span><br><span class="line">        }));</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_getScaffold</span>(<span class="params">name</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_listDir</span>().<span class="title function_">then</span>(<span class="function"><span class="params">list</span> =&gt;</span> list.<span class="title function_">find</span>(<span class="function"><span class="params">item</span> =&gt;</span> item.<span class="property">name</span> === name));</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">get</span>(<span class="params">name, callback</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_getScaffold</span>(name).<span class="title function_">then</span>(<span class="function"><span class="params">item</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (item) {</span><br><span class="line">                <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(item.<span class="property">path</span>);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">defaults</span>[name];</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">set</span>(<span class="params">name, content, callback</span>) {</span><br><span class="line">        <span class="keyword">const</span> { scaffoldDir } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_getScaffold</span>(name).<span class="title function_">then</span>(<span class="function"><span class="params">item</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">let</span> path = item ? item.<span class="property">path</span> : (<span class="number">0</span>, path_1.<span class="property">join</span>)(scaffoldDir, name);</span><br><span class="line">            <span class="keyword">if</span> (!(<span class="number">0</span>, path_1.<span class="property">extname</span>)(path))</span><br><span class="line">                path += <span class="string">'.md'</span>;</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">writeFile</span>)(path, content);</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">remove</span>(<span class="params">name, callback</span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_getScaffold</span>(name).<span class="title function_">then</span>(<span class="function"><span class="params">item</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (!item)</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">unlink</span>)(item.<span class="property">path</span>);</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Scaffold</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=scaffold.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Scaffold</code> 类在 Hexo 框架中用于管理 Scaffold 模板文件,也就是脚手架文件。脚手架是用于生成新文章或页面的默认模板。</p>
<ul>
<li><code>constructor(context)</code>:初始化了 <code>scaffoldDir</code> 属性,指定脚手架文件所在的目录。它还定义了一个 <code>defaults</code> 对象,包含了默认的脚手架内容。</li>
<li><code>_listDir()</code>:用于列出 <code>scaffoldDir</code> 目录下的所有脚手架文件。它会忽略以下划线开头的文件或目录。</li>
<li><code>_getScaffold(name)</code>:根据给定的名称获取对应的脚手架文件信息。
<ul>
<li>先调用 <code>_listDir()</code> 方法获取所有脚手架文件,然后查找名称匹配的文件。</li>
</ul>
</li>
<li><code>get(name, callback)</code>:用于获取指定名称的脚手架内容。
<ul>
<li>如果找到对应的文件,它会读取并返回文件内容。如果没有找到,它会返回 <code>defaults</code> 对象中对应的默认脚手架内容。</li>
</ul>
</li>
<li><code>set(name, content, callback)</code>:用于设置或创建一个新的脚手架文件。
<ul>
<li>首先检查是否已经存在同名的文件,如果存在就使用原有路径,否则就在 <code>scaffoldDir</code> 目录下创建新文件(文件扩展名默认为 <code>.md</code>)。</li>
</ul>
</li>
<li><code>remove(name, callback)</code>:用于删除一个脚手架文件。它会查找对应名称的文件,如果存在则将其删除。</li>
</ul>
<h4 id="数据库"><a class="markdownIt-Anchor" href="#数据库"></a> 数据库</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">_dbLoaded</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">_isGenerating</span> = <span class="literal">false</span>;</span><br><span class="line"><span class="comment">// If `output` is provided, use that as the</span></span><br><span class="line"><span class="comment">// root for saving the db. Otherwise default to `base`.</span></span><br><span class="line"><span class="keyword">const</span> dbPath = args.<span class="property">output</span> || base;</span><br><span class="line"><span class="keyword">if</span> (<span class="regexp">/^(init|new|g|publish|s|deploy|render|migrate)/</span>.<span class="title function_">test</span>(<span class="variable language_">this</span>.<span class="property">env</span>.<span class="property">cmd</span>)) {</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">d</span>(<span class="string">`Writing database to <span class="subst">${(<span class="number">0</span>, path_1.join)(dbPath, <span class="string">'db.json'</span>)}</span>`</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li>初始化两个内部状态标志,用于跟踪数据库是否已经加载(<code>_dbLoaded</code>)和网站是否正在生成(<code>_isGenerating</code>)。</li>
<li>确定数据库文件将要被保存在的路径。如果执行 Hexo 命令时提供了 <code>output</code> 参数,那么数据库就会被保存在该输出路径下;不然就保存在项目根目录下。</li>
<li>接着检查当前执行的 Hexo 命令是否属于 <code>init</code><code>new</code><code>g</code><code>publish</code><code>deploy</code><code>render</code> 或者 <code>migrate</code>。如果是,就打印说数据库文件被写入到哪里去了。</li>
</ol>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">database</span> = <span class="keyword">new</span> warehouse_1.<span class="title function_">default</span>({</span><br><span class="line">  <span class="attr">version</span>: dbVersion,</span><br><span class="line">  <span class="attr">path</span>: (<span class="number">0</span>, path_1.<span class="property">join</span>)(dbPath, <span class="string">'db.json'</span>)</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure>
<p>Hexo 初始化和配置数据库,实例化过程中传入了一个配置对象:</p>
<ul>
<li><code>version</code>:数据库的版本号。在 <code>index.js</code> 的最上方能找到:<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> dbVersion = <span class="number">1</span>;</span><br></pre></td></tr></tbody></table></figure>
</li>
<li><code>path</code>:数据库文件的保存路径,由先前定义的 <code>dbPath</code> 和文件名 <code>db.json</code> 拼接而成。</li>
</ul>
<p>此处实例化用到了 <code>warehouse</code> 库。这是一个轻量级的数据存储库,主要用于 Node.JS 应用程序中处理 JSON 数据。它提供了一种简单的方式来定义、存储和检索模型数据,类似于一个迷你数据库或 ORM(对象关系映射)。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/dundalek/warehouse"><img src="https://gh-card.dev/repos/dundalek/warehouse.svg" alt="dundalek/warehouse - GitHub"></a></p>
<h4 id="多配置文件路径管理"><a class="markdownIt-Anchor" href="#多配置文件路径管理"></a> 多配置文件路径管理</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> mcp = (<span class="number">0</span>, multi_config_path_1.<span class="property">default</span>)(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> js_yaml_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"js-yaml"</span>));</span><br><span class="line"><span class="keyword">const</span> hexo_util_1 = <span class="built_in">require</span>(<span class="string">"hexo-util"</span>);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function">(<span class="params">ctx</span>) =&gt;</span> <span class="keyword">function</span> <span class="title function_">multiConfigPath</span>(<span class="params">base, configPaths, outputDir</span>) {</span><br><span class="line">    <span class="keyword">const</span> { log } = ctx;</span><br><span class="line">    <span class="keyword">const</span> defaultPath = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'_config.yml'</span>);</span><br><span class="line">    <span class="keyword">if</span> (!configPaths) {</span><br><span class="line">        log.<span class="title function_">w</span>(<span class="string">'No config file entered.'</span>);</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'_config.yml'</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">let</span> paths;</span><br><span class="line">    <span class="comment">// determine if comma or space separated</span></span><br><span class="line">    <span class="keyword">if</span> (configPaths.<span class="title function_">includes</span>(<span class="string">','</span>)) {</span><br><span class="line">        paths = configPaths.<span class="title function_">replace</span>(<span class="string">' '</span>, <span class="string">''</span>).<span class="title function_">split</span>(<span class="string">','</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// only one config</span></span><br><span class="line">        <span class="keyword">let</span> configPath = (<span class="number">0</span>, path_1.<span class="property">isAbsolute</span>)(configPaths) ? configPaths : (<span class="number">0</span>, path_1.<span class="property">resolve</span>)(base, configPaths);</span><br><span class="line">        <span class="keyword">if</span> (!(<span class="number">0</span>, hexo_fs_1.<span class="property">existsSync</span>)(configPath)) {</span><br><span class="line">            log.<span class="title function_">w</span>(<span class="string">`Config file <span class="subst">${configPaths}</span> not found, using default.`</span>);</span><br><span class="line">            configPath = defaultPath;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> configPath;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">const</span> numPaths = paths.<span class="property">length</span>;</span><br><span class="line">    <span class="comment">// combine files</span></span><br><span class="line">    <span class="keyword">let</span> combinedConfig = {};</span><br><span class="line">    <span class="keyword">let</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; numPaths; i++) {</span><br><span class="line">        <span class="keyword">const</span> configPath = (<span class="number">0</span>, path_1.<span class="property">isAbsolute</span>)(paths[i]) ? paths[i] : (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, paths[i]);</span><br><span class="line">        <span class="keyword">if</span> (!(<span class="number">0</span>, hexo_fs_1.<span class="property">existsSync</span>)(configPath)) {</span><br><span class="line">            log.<span class="title function_">w</span>(<span class="string">`Config file <span class="subst">${paths[i]}</span> not found.`</span>);</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="comment">// files read synchronously to ensure proper overwrite order</span></span><br><span class="line">        <span class="keyword">const</span> file = (<span class="number">0</span>, hexo_fs_1.<span class="property">readFileSync</span>)(configPath);</span><br><span class="line">        <span class="keyword">const</span> ext = (<span class="number">0</span>, path_1.<span class="property">extname</span>)(paths[i]).<span class="title function_">toLowerCase</span>();</span><br><span class="line">        <span class="keyword">if</span> (ext === <span class="string">'.yml'</span>) {</span><br><span class="line">            combinedConfig = (<span class="number">0</span>, hexo_util_1.<span class="property">deepMerge</span>)(combinedConfig, js_yaml_1.<span class="property">default</span>.<span class="title function_">load</span>(file));</span><br><span class="line">            count++;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (ext === <span class="string">'.json'</span>) {</span><br><span class="line">            combinedConfig = (<span class="number">0</span>, hexo_util_1.<span class="property">deepMerge</span>)(combinedConfig, js_yaml_1.<span class="property">default</span>.<span class="title function_">load</span>(file, { <span class="attr">json</span>: <span class="literal">true</span> }));</span><br><span class="line">            count++;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            log.<span class="title function_">w</span>(<span class="string">`Config file <span class="subst">${paths[i]}</span> not supported type.`</span>);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">if</span> (count === <span class="number">0</span>) {</span><br><span class="line">        log.<span class="title function_">e</span>(<span class="string">'No config files found. Using _config.yml.'</span>);</span><br><span class="line">        <span class="keyword">return</span> defaultPath;</span><br><span class="line">    }</span><br><span class="line">    log.<span class="title function_">i</span>(<span class="string">'Config based on'</span>, count.<span class="title function_">toString</span>(), <span class="string">'files'</span>);</span><br><span class="line">    <span class="keyword">const</span> multiconfigRoot = outputDir || base;</span><br><span class="line">    <span class="keyword">const</span> outputPath = (<span class="number">0</span>, path_1.<span class="property">join</span>)(multiconfigRoot, <span class="string">'_multiconfig.yml'</span>);</span><br><span class="line">    log.<span class="title function_">d</span>(<span class="string">`Writing _multiconfig.yml to <span class="subst">${outputPath}</span>`</span>);</span><br><span class="line">    (<span class="number">0</span>, hexo_fs_1.<span class="property">writeFileSync</span>)(outputPath, js_yaml_1.<span class="property">default</span>.<span class="title function_">dump</span>(combinedConfig));</span><br><span class="line">    <span class="comment">// write file and return path</span></span><br><span class="line">    <span class="keyword">return</span> outputPath;</span><br><span class="line">};</span><br><span class="line"><span class="comment">//# sourceMappingURL=multi_config_path.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>multiConfigPath</code> 函数用于处理 Hexo 配置文件。该函数会根据传入的配置文件路径来合并多个配置文件并生成一个新的配置文件 <code>_multiconfig.yml</code></p>
<p>顺带一个读取配置文件路径的参数:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">config_path</span> = args.<span class="property">config</span> ? <span class="title function_">mcp</span>(base, args.<span class="property">config</span>, args.<span class="property">output</span>)</span><br><span class="line">  : (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, <span class="string">'_config.yml'</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>根据用户的命令行输入和 Hexo 项目的配置,决定要读取的配置文件的路径。</p>
<h4 id="注册模型"><a class="markdownIt-Anchor" href="#注册模型"></a> 注册模型</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="number">0</span>, register_models_1.<span class="property">default</span>)(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __createBinding = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__createBinding</span>) || (<span class="title class_">Object</span>.<span class="property">create</span> ? (<span class="keyword">function</span>(<span class="params">o, m, k, k2</span>) {</span><br><span class="line">    <span class="keyword">if</span> (k2 === <span class="literal">undefined</span>) k2 = k;</span><br><span class="line">    <span class="keyword">var</span> desc = <span class="title class_">Object</span>.<span class="title function_">getOwnPropertyDescriptor</span>(m, k);</span><br><span class="line">    <span class="keyword">if</span> (!desc || (<span class="string">"get"</span> <span class="keyword">in</span> desc ? !m.<span class="property">__esModule</span> : desc.<span class="property">writable</span> || desc.<span class="property">configurable</span>)) {</span><br><span class="line">        desc = { <span class="attr">enumerable</span>: <span class="literal">true</span>, <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>) { <span class="keyword">return</span> m[k]; } };</span><br><span class="line">    }</span><br><span class="line">    <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(o, k2, desc);</span><br><span class="line">}) : (<span class="keyword">function</span>(<span class="params">o, m, k, k2</span>) {</span><br><span class="line">    <span class="keyword">if</span> (k2 === <span class="literal">undefined</span>) k2 = k;</span><br><span class="line">    o[k2] = m[k];</span><br><span class="line">}));</span><br><span class="line"><span class="keyword">var</span> __setModuleDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__setModuleDefault</span>) || (<span class="title class_">Object</span>.<span class="property">create</span> ? (<span class="keyword">function</span>(<span class="params">o, v</span>) {</span><br><span class="line">    <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(o, <span class="string">"default"</span>, { <span class="attr">enumerable</span>: <span class="literal">true</span>, <span class="attr">value</span>: v });</span><br><span class="line">}) : <span class="keyword">function</span>(<span class="params">o, v</span>) {</span><br><span class="line">    o[<span class="string">"default"</span>] = v;</span><br><span class="line">});</span><br><span class="line"><span class="keyword">var</span> __importStar = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importStar</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">if</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) <span class="keyword">return</span> mod;</span><br><span class="line">    <span class="keyword">var</span> result = {};</span><br><span class="line">    <span class="keyword">if</span> (mod != <span class="literal">null</span>) <span class="keyword">for</span> (<span class="keyword">var</span> k <span class="keyword">in</span> mod) <span class="keyword">if</span> (k !== <span class="string">"default"</span> &amp;&amp; <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">hasOwnProperty</span>.<span class="title function_">call</span>(mod, k)) <span class="title function_">__createBinding</span>(result, mod, k);</span><br><span class="line">    <span class="title function_">__setModuleDefault</span>(result, mod);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> models = <span class="title function_">__importStar</span>(<span class="built_in">require</span>(<span class="string">"../models"</span>));</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function">(<span class="params">ctx</span>) =&gt;</span> {</span><br><span class="line">    <span class="keyword">const</span> db = ctx.<span class="property">database</span>;</span><br><span class="line">    <span class="keyword">const</span> keys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(models);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = keys.<span class="property">length</span>; i &lt; len; i++) {</span><br><span class="line">        <span class="keyword">const</span> key = keys[i];</span><br><span class="line">        db.<span class="title function_">model</span>(key, models[key](ctx));</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"><span class="comment">//# sourceMappingURL=register_models.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p>上三个函数是 TypeScript 编译器在编译时生成的,用于处理 ES 模块的导入和默认导出行为。它们确保在使用 CommonJS 模块时,能正确处理 ES 模块的导入。</p>
<p>接下来导入的 <code>models</code> 模块是 <code>hexo/dist/models</code> 目录,这里使用了 <code>__importStar</code> 函数导入 <code>models</code> 模块中的所有导出,作为 <code>models</code> 对象的属性。</p>
<blockquote>
<p><code>models</code> 模块内容很多,后续会仔细讲解。</p>
</blockquote>
<p><code>register_models</code> 模块导出一个函数,该函数接收一个 <code>ctx</code> 参数,并从 <code>ctx</code> 对象中获取数据库实例 <code>db</code>、获取 <code>models</code> 对象的所有键,遍历这些键,为每个模型在数据库中注册一个模型。</p>
<p>主要功能是导入所有的模型,并将这些模型注册到数据库中。</p>
<h4 id="源文件管理"><a class="markdownIt-Anchor" href="#源文件管理"></a> 源文件管理</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">source</span> = <span class="keyword">new</span> source_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> box_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"../box"</span>));</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Source</span> <span class="keyword">extends</span> <span class="title class_ inherited__">box_1.default</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">ctx</span>) {</span><br><span class="line">        <span class="variable language_">super</span>(ctx, ctx.<span class="property">source_dir</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">processors</span> = ctx.<span class="property">extend</span>.<span class="property">processor</span>.<span class="title function_">list</span>();</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Source</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=source.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Source</code> 类继承自 <code>Box</code> 类,所以先看一眼 <code>Box</code> 类:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __awaiter = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__awaiter</span>) || <span class="keyword">function</span> (<span class="params">thisArg, _arguments, P, generator</span>) {</span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">adopt</span>(<span class="params">value</span>) { <span class="keyword">return</span> value <span class="keyword">instanceof</span> P ? value : <span class="keyword">new</span> <span class="title function_">P</span>(<span class="keyword">function</span> (<span class="params">resolve</span>) { <span class="title function_">resolve</span>(value); }); }</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">new</span> (P || (P = <span class="title class_">Promise</span>))(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) {</span><br><span class="line">        <span class="keyword">function</span> <span class="title function_">fulfilled</span>(<span class="params">value</span>) { <span class="keyword">try</span> { <span class="title function_">step</span>(generator.<span class="title function_">next</span>(value)); } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } }</span><br><span class="line">        <span class="keyword">function</span> <span class="title function_">rejected</span>(<span class="params">value</span>) { <span class="keyword">try</span> { <span class="title function_">step</span>(generator[<span class="string">"throw"</span>](value)); } <span class="keyword">catch</span> (e) { <span class="title function_">reject</span>(e); } }</span><br><span class="line">        <span class="keyword">function</span> <span class="title function_">step</span>(<span class="params">result</span>) { result.<span class="property">done</span> ? <span class="title function_">resolve</span>(result.<span class="property">value</span>) : <span class="title function_">adopt</span>(result.<span class="property">value</span>).<span class="title function_">then</span>(fulfilled, rejected); }</span><br><span class="line">        <span class="title function_">step</span>((generator = generator.<span class="title function_">apply</span>(thisArg, _arguments || [])).<span class="title function_">next</span>());</span><br><span class="line">    });</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="built_in">exports</span>, <span class="string">"__esModule"</span>, { <span class="attr">value</span>: <span class="literal">true</span> });</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="keyword">const</span> file_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./file"</span>));</span><br><span class="line"><span class="keyword">const</span> hexo_util_1 = <span class="built_in">require</span>(<span class="string">"hexo-util"</span>);</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> picocolors_1 = <span class="built_in">require</span>(<span class="string">"picocolors"</span>);</span><br><span class="line"><span class="keyword">const</span> events_1 = <span class="built_in">require</span>(<span class="string">"events"</span>);</span><br><span class="line"><span class="keyword">const</span> micromatch_1 = <span class="built_in">require</span>(<span class="string">"micromatch"</span>);</span><br><span class="line"><span class="keyword">const</span> defaultPattern = <span class="keyword">new</span> hexo_util_1.<span class="title class_">Pattern</span>(<span class="function">() =&gt;</span> ({}));</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Box</span> <span class="keyword">extends</span> <span class="title class_ inherited__">events_1.EventEmitter</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">ctx, base, options</span>) {</span><br><span class="line">        <span class="variable language_">super</span>();</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">options</span> = <span class="title class_">Object</span>.<span class="title function_">assign</span>({</span><br><span class="line">            <span class="attr">persistent</span>: <span class="literal">true</span>,</span><br><span class="line">            <span class="attr">awaitWriteFinish</span>: {</span><br><span class="line">                <span class="attr">stabilityThreshold</span>: <span class="number">200</span></span><br><span class="line">            }</span><br><span class="line">        }, options);</span><br><span class="line">        <span class="keyword">if</span> (!base.<span class="title function_">endsWith</span>(path_1.<span class="property">sep</span>)) {</span><br><span class="line">            base += path_1.<span class="property">sep</span>;</span><br><span class="line">        }</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">context</span> = ctx;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">base</span> = base;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">processors</span> = [];</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">_processingFiles</span> = {};</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">watcher</span> = <span class="literal">null</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">Cache</span> = ctx.<span class="title function_">model</span>(<span class="string">'Cache'</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">File</span> = <span class="variable language_">this</span>.<span class="title function_">_createFileClass</span>();</span><br><span class="line">        <span class="keyword">let</span> targets = <span class="variable language_">this</span>.<span class="property">options</span>.<span class="property">ignored</span> || [];</span><br><span class="line">        <span class="keyword">if</span> (ctx.<span class="property">config</span>.<span class="property">ignore</span> &amp;&amp; ctx.<span class="property">config</span>.<span class="property">ignore</span>.<span class="property">length</span>) {</span><br><span class="line">            targets = targets.<span class="title function_">concat</span>(ctx.<span class="property">config</span>.<span class="property">ignore</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">ignore</span> = targets;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">options</span>.<span class="property">ignored</span> = targets.<span class="title function_">map</span>(<span class="function"><span class="params">s</span> =&gt;</span> <span class="title function_">toRegExp</span>(ctx, s)).<span class="title function_">filter</span>(<span class="function"><span class="params">x</span> =&gt;</span> x);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_createFileClass</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">const</span> ctx = <span class="variable language_">this</span>.<span class="property">context</span>;</span><br><span class="line">        <span class="keyword">class</span> <span class="title class_">_File</span> <span class="keyword">extends</span> <span class="title class_ inherited__">file_1.default</span> {</span><br><span class="line">            <span class="title function_">render</span>(<span class="params">options</span>) {</span><br><span class="line">                <span class="keyword">return</span> ctx.<span class="property">render</span>.<span class="title function_">render</span>({</span><br><span class="line">                    <span class="attr">path</span>: <span class="variable language_">this</span>.<span class="property">source</span></span><br><span class="line">                }, options);</span><br><span class="line">            }</span><br><span class="line">            <span class="title function_">renderSync</span>(<span class="params">options</span>) {</span><br><span class="line">                <span class="keyword">return</span> ctx.<span class="property">render</span>.<span class="title function_">renderSync</span>({</span><br><span class="line">                    <span class="attr">path</span>: <span class="variable language_">this</span>.<span class="property">source</span></span><br><span class="line">                }, options);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        _File.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">box</span> = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">return</span> _File;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">addProcessor</span>(<span class="params">pattern, fn</span>) {</span><br><span class="line">        <span class="keyword">if</span> (!fn &amp;&amp; <span class="keyword">typeof</span> pattern === <span class="string">'function'</span>) {</span><br><span class="line">            fn = pattern;</span><br><span class="line">            pattern = defaultPattern;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> fn !== <span class="string">'function'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'fn must be a function'</span>);</span><br><span class="line">        <span class="keyword">if</span> (!(pattern <span class="keyword">instanceof</span> hexo_util_1.<span class="property">Pattern</span>))</span><br><span class="line">            pattern = <span class="keyword">new</span> hexo_util_1.<span class="title class_">Pattern</span>(pattern);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">processors</span>.<span class="title function_">push</span>({</span><br><span class="line">            pattern,</span><br><span class="line">            <span class="attr">process</span>: fn</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_readDir</span>(<span class="params">base, prefix = <span class="string">''</span></span>) {</span><br><span class="line">        <span class="keyword">const</span> { <span class="attr">context</span>: ctx } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">const</span> results = [];</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">readDirWalker</span>(ctx, base, results, <span class="variable language_">this</span>.<span class="property">ignore</span>, prefix)</span><br><span class="line">            .<span class="keyword">return</span>(results)</span><br><span class="line">            .<span class="title function_">map</span>(<span class="function"><span class="params">path</span> =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">_checkFileStatus</span>(path))</span><br><span class="line">            .<span class="title function_">map</span>(<span class="function"><span class="params">file</span> =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">_processFile</span>(file.<span class="property">type</span>, file.<span class="property">path</span>).<span class="keyword">return</span>(file.<span class="property">path</span>));</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_checkFileStatus</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">const</span> { <span class="title class_">Cache</span>, <span class="attr">context</span>: ctx } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">const</span> src = (<span class="number">0</span>, path_1.<span class="property">join</span>)(<span class="variable language_">this</span>.<span class="property">base</span>, path);</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">Cache</span>.<span class="title function_">compareFile</span>(src.<span class="title function_">substring</span>(ctx.<span class="property">base_dir</span>.<span class="property">length</span>), <span class="function">() =&gt;</span> <span class="title function_">getHash</span>(src), <span class="function">() =&gt;</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">stat</span>)(src)).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> ({</span><br><span class="line">            <span class="attr">type</span>: result.<span class="property">type</span>,</span><br><span class="line">            path</span><br><span class="line">        }));</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">process</span>(<span class="params">callback</span>) {</span><br><span class="line">        <span class="keyword">const</span> { base, <span class="title class_">Cache</span>, <span class="attr">context</span>: ctx } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">stat</span>)(base).<span class="title function_">then</span>(<span class="function"><span class="params">stats</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (!stats.<span class="title function_">isDirectory</span>())</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            <span class="comment">// Check existing files in cache</span></span><br><span class="line">            <span class="keyword">const</span> relativeBase = base.<span class="title function_">substring</span>(ctx.<span class="property">base_dir</span>.<span class="property">length</span>);</span><br><span class="line">            <span class="keyword">const</span> cacheFiles = <span class="title class_">Cache</span>.<span class="title function_">filter</span>(<span class="function"><span class="params">item</span> =&gt;</span> item.<span class="property">_id</span>.<span class="title function_">startsWith</span>(relativeBase)).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =&gt;</span> item.<span class="property">_id</span>.<span class="title function_">substring</span>(relativeBase.<span class="property">length</span>));</span><br><span class="line">            <span class="comment">// Handle deleted files</span></span><br><span class="line">            <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_readDir</span>(base)</span><br><span class="line">                .<span class="title function_">then</span>(<span class="function">(<span class="params">files</span>) =&gt;</span> cacheFiles.<span class="title function_">filter</span>(<span class="function">(<span class="params">path</span>) =&gt;</span> !files.<span class="title function_">includes</span>(path)))</span><br><span class="line">                .<span class="title function_">map</span>(<span class="function">(<span class="params">path</span>) =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">_processFile</span>(file_1.<span class="property">default</span>.<span class="property">TYPE_DELETE</span>, path));</span><br><span class="line">        }).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (err &amp;&amp; err.<span class="property">code</span> !== <span class="string">'ENOENT'</span>)</span><br><span class="line">                <span class="keyword">throw</span> err;</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">_processFile</span>(<span class="params">type, path</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">_processingFiles</span>[path]) {</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">resolve</span>();</span><br><span class="line">        }</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">_processingFiles</span>[path] = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">const</span> { base, <span class="title class_">File</span>, <span class="attr">context</span>: ctx } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'processBefore'</span>, {</span><br><span class="line">            type,</span><br><span class="line">            path</span><br><span class="line">        });</span><br><span class="line">        <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reduce</span>(<span class="variable language_">this</span>.<span class="property">processors</span>, <span class="function">(<span class="params">count, processor</span>) =&gt;</span> {</span><br><span class="line">            <span class="comment">// patten supports *nix style path only, replace backslashes on Windows</span></span><br><span class="line">            <span class="keyword">const</span> params = processor.<span class="property">pattern</span>.<span class="title function_">match</span>(<span class="title function_">escapeBackslash</span>(path));</span><br><span class="line">            <span class="keyword">if</span> (!params)</span><br><span class="line">                <span class="keyword">return</span> count;</span><br><span class="line">            <span class="keyword">const</span> file = <span class="keyword">new</span> <span class="title class_">File</span>({</span><br><span class="line">                <span class="comment">// source is used for file system path, keep backslashes on Windows</span></span><br><span class="line">                <span class="attr">source</span>: (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, path),</span><br><span class="line">                <span class="comment">// path is used for URL path, replace backslashes on Windows</span></span><br><span class="line">                <span class="attr">path</span>: <span class="title function_">escapeBackslash</span>(path),</span><br><span class="line">                params,</span><br><span class="line">                type</span><br><span class="line">            });</span><br><span class="line">            <span class="keyword">return</span> <span class="title class_">Reflect</span>.<span class="title function_">apply</span>(bluebird_1.<span class="property">default</span>.<span class="title function_">method</span>(processor.<span class="property">process</span>), ctx, [file])</span><br><span class="line">                .<span class="title function_">thenReturn</span>(count + <span class="number">1</span>);</span><br><span class="line">        }, <span class="number">0</span>).<span class="title function_">then</span>(<span class="function"><span class="params">count</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (count) {</span><br><span class="line">                ctx.<span class="property">log</span>.<span class="title function_">debug</span>(<span class="string">'Processed: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(path));</span><br><span class="line">            }</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'processAfter'</span>, {</span><br><span class="line">                type,</span><br><span class="line">                path</span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">            ctx.<span class="property">log</span>.<span class="title function_">error</span>({ err }, <span class="string">'Process failed: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(path));</span><br><span class="line">        }).<span class="title function_">finally</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">_processingFiles</span>[path] = <span class="literal">false</span>;</span><br><span class="line">        }).<span class="title function_">thenReturn</span>(path);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">watch</span>(<span class="params">callback</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="title function_">isWatching</span>()) {</span><br><span class="line">            <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'Watcher has already started.'</span>)).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">const</span> { base } = <span class="variable language_">this</span>;</span><br><span class="line">        <span class="keyword">function</span> <span class="title function_">getPath</span>(<span class="params">path</span>) {</span><br><span class="line">            <span class="keyword">return</span> path.<span class="title function_">substring</span>(base.<span class="property">length</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">process</span>().<span class="title function_">then</span>(<span class="function">() =&gt;</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">watch</span>)(base, <span class="variable language_">this</span>.<span class="property">options</span>)).<span class="title function_">then</span>(<span class="function"><span class="params">watcher</span> =&gt;</span> {</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">watcher</span> = watcher;</span><br><span class="line">            watcher.<span class="title function_">on</span>(<span class="string">'add'</span>, <span class="function"><span class="params">path</span> =&gt;</span> {</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">_processFile</span>(file_1.<span class="property">default</span>.<span class="property">TYPE_CREATE</span>, <span class="title function_">getPath</span>(path));</span><br><span class="line">            });</span><br><span class="line">            watcher.<span class="title function_">on</span>(<span class="string">'change'</span>, <span class="function"><span class="params">path</span> =&gt;</span> {</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">_processFile</span>(file_1.<span class="property">default</span>.<span class="property">TYPE_UPDATE</span>, <span class="title function_">getPath</span>(path));</span><br><span class="line">            });</span><br><span class="line">            watcher.<span class="title function_">on</span>(<span class="string">'unlink'</span>, <span class="function"><span class="params">path</span> =&gt;</span> {</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">_processFile</span>(file_1.<span class="property">default</span>.<span class="property">TYPE_DELETE</span>, <span class="title function_">getPath</span>(path));</span><br><span class="line">            });</span><br><span class="line">            watcher.<span class="title function_">on</span>(<span class="string">'addDir'</span>, <span class="function"><span class="params">path</span> =&gt;</span> {</span><br><span class="line">                <span class="keyword">let</span> prefix = <span class="title function_">getPath</span>(path);</span><br><span class="line">                <span class="keyword">if</span> (prefix)</span><br><span class="line">                    prefix += path_1.<span class="property">sep</span>;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">_readDir</span>(path, prefix);</span><br><span class="line">            });</span><br><span class="line">        }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">unwatch</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="title function_">isWatching</span>())</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">watcher</span>.<span class="title function_">close</span>();</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">watcher</span> = <span class="literal">null</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">isWatching</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">Boolean</span>(<span class="variable language_">this</span>.<span class="property">watcher</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">escapeBackslash</span>(<span class="params">path</span>) {</span><br><span class="line">    <span class="comment">// Replace backslashes on Windows</span></span><br><span class="line">    <span class="keyword">return</span> path.<span class="title function_">replace</span>(<span class="regexp">/\\/g</span>, <span class="string">'/'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getHash</span>(<span class="params">path</span>) {</span><br><span class="line">    <span class="keyword">const</span> src = (<span class="number">0</span>, hexo_fs_1.<span class="property">createReadStream</span>)(path);</span><br><span class="line">    <span class="keyword">const</span> hasher = (<span class="number">0</span>, hexo_util_1.<span class="property">createSha1Hash</span>)();</span><br><span class="line">    <span class="keyword">const</span> finishedPromise = <span class="keyword">new</span> bluebird_1.<span class="title function_">default</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> {</span><br><span class="line">        src.<span class="title function_">once</span>(<span class="string">'error'</span>, reject);</span><br><span class="line">        src.<span class="title function_">once</span>(<span class="string">'end'</span>, resolve);</span><br><span class="line">    });</span><br><span class="line">    src.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function"><span class="params">chunk</span> =&gt;</span> { hasher.<span class="title function_">update</span>(chunk); });</span><br><span class="line">    <span class="keyword">return</span> finishedPromise.<span class="title function_">then</span>(<span class="function">() =&gt;</span> hasher.<span class="title function_">digest</span>(<span class="string">'hex'</span>));</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">toRegExp</span>(<span class="params">ctx, arg</span>) {</span><br><span class="line">    <span class="keyword">if</span> (!arg)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> arg !== <span class="string">'string'</span>) {</span><br><span class="line">        ctx.<span class="property">log</span>.<span class="title function_">warn</span>(<span class="string">'A value of "ignore:" section in "_config.yml" is not invalid (not a string)'</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">const</span> result = (<span class="number">0</span>, micromatch_1.<span class="property">makeRe</span>)(arg);</span><br><span class="line">    <span class="keyword">if</span> (!result) {</span><br><span class="line">        ctx.<span class="property">log</span>.<span class="title function_">warn</span>(<span class="string">'A value of "ignore:" section in "_config.yml" can not be converted to RegExp:'</span> + arg);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">isIgnoreMatch</span>(<span class="params">path, ignore</span>) {</span><br><span class="line">    <span class="keyword">return</span> path &amp;&amp; ignore &amp;&amp; ignore.<span class="property">length</span> &amp;&amp; (<span class="number">0</span>, micromatch_1.<span class="property">isMatch</span>)(path, ignore);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">readDirWalker</span>(<span class="params">ctx, base, results, ignore, prefix</span>) {</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_">isIgnoreMatch</span>(base, ignore))</span><br><span class="line">        <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">resolve</span>();</span><br><span class="line">    <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">map</span>((<span class="number">0</span>, hexo_fs_1.<span class="property">readdir</span>)(base).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">        ctx.<span class="property">log</span>.<span class="title function_">error</span>({ err }, <span class="string">'Failed to read directory: %s'</span>, base);</span><br><span class="line">        <span class="keyword">if</span> (err &amp;&amp; err.<span class="property">code</span> === <span class="string">'ENOENT'</span>)</span><br><span class="line">            <span class="keyword">return</span> [];</span><br><span class="line">        <span class="keyword">throw</span> err;</span><br><span class="line">    }), <span class="function">(<span class="params">path</span>) =&gt;</span> <span class="title function_">__awaiter</span>(<span class="variable language_">this</span>, <span class="keyword">void</span> <span class="number">0</span>, <span class="keyword">void</span> <span class="number">0</span>, <span class="keyword">function</span>* () {</span><br><span class="line">        <span class="keyword">const</span> fullpath = (<span class="number">0</span>, path_1.<span class="property">join</span>)(base, path);</span><br><span class="line">        <span class="keyword">const</span> stats = <span class="title function_">yield</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">stat</span>)(fullpath).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">            ctx.<span class="property">log</span>.<span class="title function_">error</span>({ err }, <span class="string">'Failed to stat file: %s'</span>, fullpath);</span><br><span class="line">            <span class="keyword">if</span> (err &amp;&amp; err.<span class="property">code</span> === <span class="string">'ENOENT'</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">throw</span> err;</span><br><span class="line">        });</span><br><span class="line">        <span class="keyword">const</span> prefixPath = <span class="string">`<span class="subst">${prefix}</span><span class="subst">${path}</span>`</span>;</span><br><span class="line">        <span class="keyword">if</span> (stats) {</span><br><span class="line">            <span class="keyword">if</span> (stats.<span class="title function_">isDirectory</span>()) {</span><br><span class="line">                <span class="keyword">return</span> <span class="title function_">readDirWalker</span>(ctx, fullpath, results, ignore, prefixPath + path_1.<span class="property">sep</span>);</span><br><span class="line">            }</span><br><span class="line">            <span class="keyword">if</span> (!<span class="title function_">isIgnoreMatch</span>(fullpath, ignore)) {</span><br><span class="line">                results.<span class="title function_">push</span>(prefixPath);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }));</span><br><span class="line">}</span><br><span class="line"><span class="built_in">exports</span>.<span class="property">default</span> = <span class="title class_">Box</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=index.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p>部分辅助函数:</p>
<ul>
<li><code>__awaiter</code>:用于处理异步函数和生成器函数,使得可以使用 <code>async/await</code> 语法。</li>
<li><code>__importDefault</code>:用于处理默认导入。</li>
<li><code>escapeBackslash</code>:将路径中的反斜杠替换为斜杠。</li>
<li><code>getHash</code>:生成文件的 SHA-1 哈希值。</li>
<li><code>toRegExp</code>:将字符串转换为正则表达式。</li>
<li><code>isIgnoreMatch</code>:检查路径是否与忽略模式匹配。</li>
<li><code>readDirWalker</code>:递归读取目录,并收集所有符合条件的文件路径。</li>
</ul>
<p><code>box/index.js</code> 也导入了一个 <code>file.js</code> 文件,该文件定义了一个 <code>File</code> 类,用来表示文件对象,并提供了一些方法来读取和获取文件状态:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">File</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">{ source, path, params, type }</span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">source</span> = source;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">path</span> = path;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">params</span> = params;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">type</span> = type;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">read</span>(<span class="params">options</span>) {</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(<span class="variable language_">this</span>.<span class="property">source</span>, options);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">readSync</span>(<span class="params">options</span>) {</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">readFileSync</span>)(<span class="variable language_">this</span>.<span class="property">source</span>, options);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">stat</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">stat</span>)(<span class="variable language_">this</span>.<span class="property">source</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">statSync</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">statSync</span>)(<span class="variable language_">this</span>.<span class="property">source</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="title class_">File</span>.<span class="property">TYPE_CREATE</span> = <span class="string">'create'</span>;</span><br><span class="line"><span class="title class_">File</span>.<span class="property">TYPE_UPDATE</span> = <span class="string">'update'</span>;</span><br><span class="line"><span class="title class_">File</span>.<span class="property">TYPE_SKIP</span> = <span class="string">'skip'</span>;</span><br><span class="line"><span class="title class_">File</span>.<span class="property">TYPE_DELETE</span> = <span class="string">'delete'</span>;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">File</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=file.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<ul>
<li><code>read(options)</code>:异步读取文件内容,返回一个 Promise。</li>
<li><code>readSync(options)</code>:同步读取文件内容,返回文件内容。</li>
<li><code>stat()</code>:异步获取文件状态,返回一个 Promise。</li>
<li><code>statSync()</code>:同步获取文件状态,返回文件状态。</li>
<li>文件类型常量:明确表示文件的操作类型。</li>
</ul>
<p><code>Box</code> 类也是继承自 <code>EventEmitter</code></p>
<p>主要的属性和方法:</p>
<ul>
<li>构造函数:初始化 Box 类的实例,设置:
<ul>
<li><code>options</code>:默认选项和用户传入的选项合并。</li>
<li><code>context</code>:上下文对象。</li>
<li><code>base</code>:基础路径。</li>
<li><code>processors</code>:处理器列表。</li>
<li><code>_processingFiles</code>:正在处理的文件。</li>
<li><code>watcher</code>:文件监视器。</li>
<li><code>Cache</code>:缓存模型。</li>
<li><code>File</code>:文件类。</li>
<li><code>ignore</code>:忽略模式。</li>
</ul>
</li>
<li><code>_createFileClass</code>:创建一个文件类,用于处理文件的渲染操作。</li>
<li><code>addProcessor</code>:添加一个处理器,用于处理特定模式的文件。</li>
<li><code>_readDir</code>:读取目录,并递归处理所有文件。</li>
<li><code>_checkFileStatus</code>:检查文件状态,比较缓存文件和实际文件。</li>
<li><code>process</code>:处理目录中的所有文件。</li>
<li><code>_processFile</code>:处理单个文件,根据文件类型执行处理器。</li>
<li><code>watch</code>:监视目录中的文件变化,并在文件添加、修改或删除时触发相应的处理。</li>
<li><code>unwatch</code>:停止监视目录。</li>
<li><code>isWatching</code>:检查是否正在监视目录。</li>
</ul>
<p><code>Box</code> 类的更多详情可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/box">Hexo 官方文档</a></p>
<p>现在再来看 <code>Source</code> 类。它继承自 <code>Box</code> 类,拥有着 <code>Box</code> 类的所有属性和方法。<code>Box</code> 类提供了文件读取、监视、处理等能力。而 <code>Source</code> 类在此基础上,专注于处理特定于源文件的逻辑。</p>
<h4 id="主题"><a class="markdownIt-Anchor" href="#主题"></a> 主题</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">theme</span> = <span class="keyword">new</span> theme_1.<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br></pre></td></tr></tbody></table></figure>
<div class="danger">
<p><code>Theme</code> 类被定义在 <code>hexo/dist/theme</code> 中,未来会详细说。目前只讲 <code>index.js</code> 的大致逻辑。</p>
</div>
<p><code>Theme</code> 类是 Hexo 中负责管理主题的核心模块。</p>
<blockquote>
<p>可见 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/themes">Hexo 官方文档</a></p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">    <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> path_1 = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="keyword">const</span> box_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"../box"</span>));</span><br><span class="line"><span class="keyword">const</span> view_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"./view"</span>));</span><br><span class="line"><span class="keyword">const</span> hexo_i18n_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"hexo-i18n"</span>));</span><br><span class="line"><span class="keyword">const</span> config_1 = <span class="built_in">require</span>(<span class="string">"./processors/config"</span>);</span><br><span class="line"><span class="keyword">const</span> i18n_1 = <span class="built_in">require</span>(<span class="string">"./processors/i18n"</span>);</span><br><span class="line"><span class="keyword">const</span> source_1 = <span class="built_in">require</span>(<span class="string">"./processors/source"</span>);</span><br><span class="line"><span class="keyword">const</span> view_2 = <span class="built_in">require</span>(<span class="string">"./processors/view"</span>);</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Theme</span> <span class="keyword">extends</span> <span class="title class_ inherited__">box_1.default</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">ctx, options</span>) {</span><br><span class="line">        <span class="variable language_">super</span>(ctx, ctx.<span class="property">theme_dir</span>, options);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">config</span> = {};</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">views</span> = {};</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">processors</span> = [</span><br><span class="line">            config_1.<span class="property">config</span>,</span><br><span class="line">            i18n_1.<span class="property">i18n</span>,</span><br><span class="line">            source_1.<span class="property">source</span>,</span><br><span class="line">            view_2.<span class="property">view</span></span><br><span class="line">        ];</span><br><span class="line">        <span class="keyword">let</span> languages = ctx.<span class="property">config</span>.<span class="property">language</span>;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title class_">Array</span>.<span class="title function_">isArray</span>(languages))</span><br><span class="line">            languages = [languages];</span><br><span class="line">        languages.<span class="title function_">push</span>(<span class="string">'default'</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">i18n</span> = <span class="keyword">new</span> hexo_i18n_1.<span class="title function_">default</span>({</span><br><span class="line">            <span class="attr">languages</span>: [...<span class="keyword">new</span> <span class="title class_">Set</span>(languages.<span class="title function_">filter</span>(<span class="title class_">Boolean</span>))]</span><br><span class="line">        });</span><br><span class="line">        <span class="keyword">class</span> <span class="title class_">_View</span> <span class="keyword">extends</span> <span class="title class_ inherited__">view_1.default</span> {</span><br><span class="line">        }</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">View</span> = _View;</span><br><span class="line">        _View.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">_theme</span> = <span class="variable language_">this</span>;</span><br><span class="line">        _View.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">_render</span> = ctx.<span class="property">render</span>;</span><br><span class="line">        _View.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">_helper</span> = ctx.<span class="property">extend</span>.<span class="property">helper</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">getView</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="comment">// Replace backslashes on Windows</span></span><br><span class="line">        path = path.<span class="title function_">replace</span>(<span class="regexp">/\\/g</span>, <span class="string">'/'</span>);</span><br><span class="line">        <span class="keyword">const</span> ext = (<span class="number">0</span>, path_1.<span class="property">extname</span>)(path);</span><br><span class="line">        <span class="keyword">const</span> name = path.<span class="title function_">substring</span>(<span class="number">0</span>, path.<span class="property">length</span> - ext.<span class="property">length</span>);</span><br><span class="line">        <span class="keyword">const</span> views = <span class="variable language_">this</span>.<span class="property">views</span>[name];</span><br><span class="line">        <span class="keyword">if</span> (!views)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span> (ext) {</span><br><span class="line">            <span class="keyword">return</span> views[ext];</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> views[<span class="title class_">Object</span>.<span class="title function_">keys</span>(views)[<span class="number">0</span>]];</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">setView</span>(<span class="params">path, data</span>) {</span><br><span class="line">        <span class="keyword">const</span> ext = (<span class="number">0</span>, path_1.<span class="property">extname</span>)(path);</span><br><span class="line">        <span class="keyword">const</span> name = path.<span class="title function_">substring</span>(<span class="number">0</span>, path.<span class="property">length</span> - ext.<span class="property">length</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">views</span>[name] = <span class="variable language_">this</span>.<span class="property">views</span>[name] || {};</span><br><span class="line">        <span class="keyword">const</span> views = <span class="variable language_">this</span>.<span class="property">views</span>[name];</span><br><span class="line">        views[ext] = <span class="keyword">new</span> <span class="variable language_">this</span>.<span class="title class_">View</span>(path, data);</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">removeView</span>(<span class="params">path</span>) {</span><br><span class="line">        <span class="keyword">const</span> ext = (<span class="number">0</span>, path_1.<span class="property">extname</span>)(path);</span><br><span class="line">        <span class="keyword">const</span> name = path.<span class="title function_">substring</span>(<span class="number">0</span>, path.<span class="property">length</span> - ext.<span class="property">length</span>);</span><br><span class="line">        <span class="keyword">const</span> views = <span class="variable language_">this</span>.<span class="property">views</span>[name];</span><br><span class="line">        <span class="keyword">if</span> (!views)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        views[ext] = <span class="literal">undefined</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Theme</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=index.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Theme</code> 类同样继承自 <code>Box</code> 类:</p>
<ul>
<li><code>constructor(ctx, options)</code>:构造函数接受上下文对象 <code>ctx</code> 和选项 <code>options</code>,调用父类 <code>box_1.default</code> 的构造函数。</li>
<li>初始化 <code>config</code><code>views</code> 属性为空对象。</li>
<li><code>processors</code>:设置处理器数组,包括配置、国际化、源文件和视图处理器。</li>
<li>初始化多语言支持,确保 <code>languages</code> 是数组,并添加 <code>default</code> 语言。使用 <code>hexo-i18n</code> 库创建 <code>i18n</code> 对象。</li>
<li>定义一个新的 <code>_View</code> 类,继承自 <code>view_1.default</code>,并在其原型上添加 <code>_theme</code><code>_render</code><code>_helper</code> 属性。</li>
</ul>
<p>撇开本地文件导入,这里的 <code>hexo-i18n</code> 是 Hexo 自己的 i18n 模块。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/hexojs/hexo-i18n"><img src="https://gh-card.dev/repos/hexojs/hexo-i18n.svg" alt="hexojs/hexo-i18n - GitHub"></a></p>
<blockquote>
<p>i18n 的全程是 <strong>Internationalization</strong>(国际化),是指在设计软件、将软件与特定语言及地区脱钩的过程。由于英文单字长度过长,所以常被简称为 i18n(18 意味着在 Internationalization 这个单字中,i 和 n 之间有 18 个字母。</p>
</blockquote>
<p><code>Theme</code> 类也有以下方法:</p>
<ul>
<li><code>getView(path)</code>:根据给定路径获取视图。</li>
<li><code>setView(path, data)</code> 设置指定路径的 <code>View</code> 内容。</li>
<li><code>removeView(path)</code> 移除指定路径的 <code>View</code></li>
</ul>
<p>通过实例化 <code>Theme</code> 类,Hexo 可以获得一个用于操作当前主题的对象,从而正确地渲染和生成静态页面。</p>
<blockquote>
<p>视图(<code>View</code>)在 Hexo 中是用于渲染页面内容的模板文件。通常使用模板引擎语法编写,如 Swig、Pug 等,允许在模板中嵌入动态数据和逻辑。</p>
<p>视图主要有以下几种类型:</p>
<ol>
<li>布局(Layout):定义了页面的基本结构,如 HTML 头部、导航栏、页脚等通用部分。所有其他视图都将被渲染到布局视图中的特定位置。</li>
<li>包含(Partial):可重用的模板片段,通常用于渲染页面的某一部分,如文章列表、评论区等。它们可以在其他视图中被引入和渲染。</li>
<li>页面(Page):用于渲染特定的页面内容,如文章、分类、标签等。它们通常会引入布局视图和其他所需的包含视图。</li>
<li>助手(Helper):一些辅助函数,用于在模板中执行特定的逻辑或操作,如格式化日期、生成链接等。</li>
</ol>
</blockquote>
<h4 id="本地数据"><a class="markdownIt-Anchor" href="#本地数据"></a> 本地数据</h4>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">locals</span> = <span class="keyword">new</span> locals_1.<span class="title function_">default</span>();</span><br></pre></td></tr></tbody></table></figure>
<p><code>Locals</code> 类用于管理和缓存局部变量:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">const</span> hexo_util_1 = <span class="built_in">require</span>(<span class="string">"hexo-util"</span>);</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Locals</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">cache</span> = <span class="keyword">new</span> hexo_util_1.<span class="title class_">Cache</span>();</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">getters</span> = {};</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">get</span>(<span class="params">name</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> name !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'name must be a string!'</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">cache</span>.<span class="title function_">apply</span>(name, <span class="function">() =&gt;</span> {</span><br><span class="line">            <span class="keyword">const</span> getter = <span class="variable language_">this</span>.<span class="property">getters</span>[name];</span><br><span class="line">            <span class="keyword">if</span> (!getter)</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="title function_">getter</span>();</span><br><span class="line">        });</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">set</span>(<span class="params">name, value</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> name !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'name must be a string!'</span>);</span><br><span class="line">        <span class="keyword">if</span> (value == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'value is required!'</span>);</span><br><span class="line">        <span class="keyword">const</span> getter = <span class="keyword">typeof</span> value === <span class="string">'function'</span> ? value : <span class="function">() =&gt;</span> value;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">getters</span>[name] = getter;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">cache</span>.<span class="title function_">del</span>(name);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">remove</span>(<span class="params">name</span>) {</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> name !== <span class="string">'string'</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">TypeError</span>(<span class="string">'name must be a string!'</span>);</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">getters</span>[name] = <span class="literal">null</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">cache</span>.<span class="title function_">del</span>(name);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">invalidate</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">cache</span>.<span class="title function_">flush</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">this</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">toObject</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="keyword">const</span> result = {};</span><br><span class="line">        <span class="keyword">const</span> keys = <span class="title class_">Object</span>.<span class="title function_">keys</span>(<span class="variable language_">this</span>.<span class="property">getters</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = keys.<span class="property">length</span>; i &lt; len; i++) {</span><br><span class="line">            <span class="keyword">const</span> key = keys[i];</span><br><span class="line">            <span class="keyword">const</span> item = <span class="variable language_">this</span>.<span class="title function_">get</span>(key);</span><br><span class="line">            <span class="keyword">if</span> (item != <span class="literal">null</span>)</span><br><span class="line">                result[key] = item;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Locals</span>;</span><br><span class="line"><span class="comment">//# sourceMappingURL=locals.js.map</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Locals</code> 类有两个属性:</p>
<ul>
<li><code>cache</code>:使用 <code>hexo-util</code> 模块的 <code>Cache</code> 类实例化缓存对象。</li>
<li><code>getters</code>:初始化一个空对象,用于存储 <code>getter</code> 函数。</li>
</ul>
<p><code>Locals</code> 类也有以下方法:</p>
<ul>
<li><code>get(name)</code> 获取指定名称的本地数据。</li>
<li><code>set(name, value)</code> 设置指定名称的本地数据。</li>
<li><code>remove(name)</code> 移除指定名称的本地数据。</li>
<li><code>invalidate()</code> 清空缓存。</li>
<li><code>toObject()</code> 将本地数据转换为普通对象。</li>
</ul>
<p><code>Locals</code> 类是 Hexo 中用于管理本地数据的模块。本地数据在 Hexo 插件开发中意外重要,后续会详细讲解。</p>
<blockquote>
<p>暂且,请看 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/locals">Hexo 官方文档</a></p>
</blockquote>
<h2 id="本地数据绑定"><a class="markdownIt-Anchor" href="#本地数据绑定"></a> 本地数据绑定</h2>
<p>构造函数的最后一行是用来绑定本地数据的:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="title function_">_bindLocals</span>();</span><br></pre></td></tr></tbody></table></figure>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_bindLocals</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="keyword">const</span> db = <span class="variable language_">this</span>.<span class="property">database</span>;</span><br><span class="line">  <span class="keyword">const</span> { locals } = <span class="variable language_">this</span>;</span><br><span class="line">  locals.<span class="title function_">set</span>(<span class="string">'posts'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="keyword">const</span> query = {};</span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">future</span>) {</span><br><span class="line">      query.<span class="property">date</span> = { <span class="attr">$lte</span>: <span class="title class_">Date</span>.<span class="title function_">now</span>() };</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="title function_">_showDrafts</span>()) {</span><br><span class="line">      query.<span class="property">published</span> = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> db.<span class="title function_">model</span>(<span class="string">'Post'</span>).<span class="title function_">find</span>(query);</span><br><span class="line">  });</span><br><span class="line">  locals.<span class="title function_">set</span>(<span class="string">'pages'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="keyword">const</span> query = {};</span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">future</span>) {</span><br><span class="line">      query.<span class="property">date</span> = { <span class="attr">$lte</span>: <span class="title class_">Date</span>.<span class="title function_">now</span>() };</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> db.<span class="title function_">model</span>(<span class="string">'Page'</span>).<span class="title function_">find</span>(query);</span><br><span class="line">  });</span><br><span class="line">  locals.<span class="title function_">set</span>(<span class="string">'categories'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="comment">// Ignore categories with zero posts</span></span><br><span class="line">    <span class="keyword">return</span> db.<span class="title function_">model</span>(<span class="string">'Category'</span>).<span class="title function_">filter</span>(<span class="function"><span class="params">category</span> =&gt;</span> category.<span class="property">length</span>);</span><br><span class="line">  });</span><br><span class="line">  locals.<span class="title function_">set</span>(<span class="string">'tags'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="comment">// Ignore tags with zero posts</span></span><br><span class="line">    <span class="keyword">return</span> db.<span class="title function_">model</span>(<span class="string">'Tag'</span>).<span class="title function_">filter</span>(<span class="function"><span class="params">tag</span> =&gt;</span> tag.<span class="property">length</span>);</span><br><span class="line">  });</span><br><span class="line">  locals.<span class="title function_">set</span>(<span class="string">'data'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="keyword">const</span> obj = {};</span><br><span class="line">    db.<span class="title function_">model</span>(<span class="string">'Data'</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">data</span> =&gt;</span> {</span><br><span class="line">      obj[data.<span class="property">_id</span>] = data.<span class="property">data</span>;</span><br><span class="line">    });</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">  });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>首先获取 <code>Hexo</code> 实例的数据库对象 <code>this.database</code>,接着使用对象解构赋值的语法从 <code>this</code> 对象中提取 <code>locals</code> 属性的值。<code>this</code> 指向的是当前执行上下文中的对象实例,在这里,<code>this</code><code>Hexo</code> 实例本身。</p>
<p><code>const { locals } = this;</code> 相当于这行代码:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> locals = <span class="variable language_">this</span>.<span class="property">locals</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>通过调用 <code>locals.set()</code> 方法,为不同的本地数据设置了获取器函数。当其他地方访问这些本地数据时,相应的获取器函数将被执行,从数据库中查询并返回所需的数据。</p>
<p>具体来说,这个方法设置了以下几个本地数据及其获取器:</p>
<ul>
<li><code>posts</code>:获取所有已发布且不是未来日期的文章。如果 <code>config.future</code> 设置为 <code>true</code>,则也包括未来日期的文章。如果 <code>_showDrafts()</code> 返回 <code>true</code>,则也包括草稿文章。</li>
<li><code>pages</code>:获取所有已发布且不是未来日期的页面。如果 <code>config.future</code> 设置为 <code>true</code>,则也包括未来日期的页面。</li>
<li><code>categories</code>:获取所有包含文章的分类。</li>
<li><code>tags</code>:获取所有包含文章的标签。</li>
<li><code>data</code>:获取所有自定义数据。</li>
</ul>
<h2 id="初始化"><a class="markdownIt-Anchor" href="#初始化"></a> 初始化</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">init</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">debug</span>(<span class="string">'Hexo version: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(<span class="variable language_">this</span>.<span class="property">version</span>));</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">debug</span>(<span class="string">'Working directory: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)((<span class="number">0</span>, tildify_1.<span class="property">default</span>)(<span class="variable language_">this</span>.<span class="property">base_dir</span>)));</span><br><span class="line">  <span class="comment">// Load internal plugins</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/console'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/filter'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/generator'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/helper'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/highlight'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/injector'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/processor'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/renderer'</span>)(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'../plugins/tag'</span>).<span class="title function_">default</span>(<span class="variable language_">this</span>);</span><br><span class="line">  <span class="comment">// Load config</span></span><br><span class="line">  <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">each</span>([</span><br><span class="line">    <span class="string">'update_package'</span>, <span class="comment">// Update package.json</span></span><br><span class="line">    <span class="string">'load_config'</span>, <span class="comment">// Load config</span></span><br><span class="line">    <span class="string">'load_theme_config'</span>, <span class="comment">// Load alternate theme config</span></span><br><span class="line">    <span class="string">'load_plugins'</span> <span class="comment">// Load external plugins &amp; scripts</span></span><br><span class="line">  ], <span class="function"><span class="params">name</span> =&gt;</span> <span class="built_in">require</span>(<span class="string">`./<span class="subst">${name}</span>`</span>)(<span class="variable language_">this</span>)).<span class="title function_">then</span>(<span class="function">() =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">execFilter</span>(<span class="string">'after_init'</span>, <span class="literal">null</span>, { <span class="attr">context</span>: <span class="variable language_">this</span> })).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="comment">// Ready to go!</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'ready'</span>);</span><br><span class="line">  });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>init</code> 方法负责进行初始化工作:</p>
<ol>
<li>打印 Hexo 版本和工作目录信息到控制台,使用 <code>picocolors</code> 库为版本和目录路径上色。</li>
<li>加载内部插件。</li>
<li>执行一系列初始化步骤后,使用了 <code>bluebird.each</code> 方法按顺序执行以下步骤:
<ul>
<li><code>update_package</code>:更新 <code>package.json</code> 文件。</li>
<li><code>load_config</code>:加载配置文件。</li>
<li><code>load_theme_config</code>:加载主题配置文件。</li>
<li><code>load_plugins</code>:加载外部插件和脚本。</li>
</ul>
</li>
<li>在所有初始化步骤完成后,执行 <code>after_init</code> 过滤器,允许插件和主题在初始化后进行一些额外的操作。</li>
<li>发射 <code>ready</code> 事件,表示 Hexo 已经准备就绪,可以执行其他操作了。</li>
</ol>
<h2 id="调用控制台命令"><a class="markdownIt-Anchor" href="#调用控制台命令"></a> 调用控制台命令</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">call</span>(<span class="params">name, args, callback</span>) {</span><br><span class="line">  <span class="keyword">if</span> (!callback &amp;&amp; <span class="keyword">typeof</span> args === <span class="string">'function'</span>) {</span><br><span class="line">    callback = args;</span><br><span class="line">    args = {};</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">const</span> c = <span class="variable language_">this</span>.<span class="property">extend</span>.<span class="property">console</span>.<span class="title function_">get</span>(name);</span><br><span class="line">  <span class="keyword">if</span> (c)</span><br><span class="line">    <span class="keyword">return</span> <span class="title class_">Reflect</span>.<span class="title function_">apply</span>(c, <span class="variable language_">this</span>, [args]).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">  <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">`Console \`<span class="subst">${name}</span>\` has not been registered yet!`</span>));</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这段代码定义了 <code>call</code> 方法,用于调用已注册的控制台命令。</p>
<p>先是检查了是否传入回调函数。如果没有,但是第二个参数是一个函数的话,就将第二个参数视为回调函数,同时将 <code>args</code> 设置为一个空对象。这是为了兼容只传入回调函数的情况。</p>
<p><code>this.extend.console</code> 对象中获取名为 <code>name</code> 的控制台命令。如果找到了对应的控制台命令对象 <code>c</code>,便执行以下步骤:</p>
<ol>
<li>使用 <code>Reflect</code> 对象的 <code>apply</code> 方法调用控制台命令 <code>c</code></li>
<li><code>.asCallback(callback)</code> 将上一步的调用结果转换为一个 Promise 对象,并将该 Promise 与回调函数 <code>callback</code> 关联。这样就可以在 Promise 完成时自动调用回调函数。</li>
</ol>
<p>如果未能找到对应的控制台命令对象,就返回一个被拒绝的 Promise,其中包含一个表示该命令尚未注册的错误消息。</p>
<h2 id="定义数据库模型"><a class="markdownIt-Anchor" href="#定义数据库模型"></a> 定义数据库模型</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">model</span>(<span class="params">name, schema</span>) {</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">database</span>.<span class="title function_">model</span>(name, schema);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>回到 <a href="####%E6%95%B0%E6%8D%AE%E5%BA%93">数据库</a><code>Database</code> 类,能看出 <code>model</code> 方法是用来创建一个模型的。</p>
<h2 id="解析插件"><a class="markdownIt-Anchor" href="#解析插件"></a> 解析插件</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">resolvePlugin</span>(<span class="params">name, basedir</span>) {</span><br><span class="line">  <span class="keyword">try</span> {</span><br><span class="line">    <span class="comment">// Try to resolve the plugin with the Node.js's built-in require.resolve.</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">require</span>.<span class="title function_">resolve</span>(name, { <span class="attr">paths</span>: [basedir] });</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">catch</span> (err) {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      <span class="comment">// There was an error (likely the node_modules is corrupt or from early version of npm)</span></span><br><span class="line">      <span class="comment">// Use Hexo prior 6.0.0's behavior (resolve.sync) to resolve the plugin.</span></span><br><span class="line">      resolveSync = resolveSync || <span class="built_in">require</span>(<span class="string">'resolve'</span>).<span class="property">sync</span>;</span><br><span class="line">      <span class="keyword">return</span> <span class="title function_">resolveSync</span>(name, { basedir });</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">catch</span> (err) {</span><br><span class="line">      <span class="comment">// There was an error (likely the plugin wasn't found), so return a possibly</span></span><br><span class="line">      <span class="comment">// non-existing path that a later part of the resolution process will check.</span></span><br><span class="line">      <span class="keyword">return</span> (<span class="number">0</span>, path_1.<span class="property">join</span>)(basedir, <span class="string">'node_modules'</span>, name);</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>先是使用 <code>require.resolve</code> 方法用于解析模块。如果解析失败了,就继续尝试使用 <code>resolve.sync</code> 方法。最后如果依然失败,则返回一个可能不存在的路径以供后续处理。</p>
<blockquote>
<p><code>require.resolve</code> 方法是 Node.JS 内置的,用于解析模块的路径。 如果 <code>require.resolve</code> 失败,通常是由于 <code>node_modules</code> 目录损坏或其他原因。</p>
<p><code>require('resolve').sync</code> 方法是一个更兼容旧版本 npm 的解决方案,类似于早期 Hexo 版本的插件解析方式。<code>resolve.sync</code> 方法如果也失败,很有可能是因为插件未找到,这时候便返回一个大概并不存在的路径。</p>
</blockquote>
<h2 id="加载插件"><a class="markdownIt-Anchor" href="#加载插件"></a> 加载插件</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">loadPlugin</span>(<span class="params">path, callback</span>) {</span><br><span class="line">  <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">readFile</span>)(path).<span class="title function_">then</span>(<span class="function"><span class="params">script</span> =&gt;</span> {</span><br><span class="line">    <span class="comment">// Based on: https://github.com/nodejs/node-v0.x-archive/blob/v0.10.33/src/node.js#L516</span></span><br><span class="line">    <span class="keyword">const</span> <span class="variable language_">module</span> = <span class="keyword">new</span> module_1.<span class="title function_">default</span>(path);</span><br><span class="line">    <span class="variable language_">module</span>.<span class="property">filename</span> = path;</span><br><span class="line">    <span class="variable language_">module</span>.<span class="property">paths</span> = module_1.<span class="property">default</span>.<span class="title function_">_nodeModulePaths</span>(path);</span><br><span class="line">    <span class="keyword">function</span> <span class="title function_">req</span>(<span class="params">path</span>) {</span><br><span class="line">      <span class="keyword">return</span> <span class="variable language_">module</span>.<span class="built_in">require</span>(path);</span><br><span class="line">    }</span><br><span class="line">    req.<span class="property">resolve</span> = <span class="function">(<span class="params">request</span>) =&gt;</span> module_1.<span class="property">default</span>.<span class="title function_">_resolveFilename</span>(request, <span class="variable language_">module</span>);</span><br><span class="line">    req.<span class="property">main</span> = <span class="built_in">require</span>.<span class="property">main</span>;</span><br><span class="line">    req.<span class="property">extensions</span> = module_1.<span class="property">default</span>.<span class="property">_extensions</span>;</span><br><span class="line">    req.<span class="property">cache</span> = module_1.<span class="property">default</span>.<span class="property">_cache</span>;</span><br><span class="line">    script = <span class="string">`(async function(exports, require, module, __filename, __dirname, hexo){<span class="subst">${script}</span>\n});`</span>;</span><br><span class="line">    <span class="keyword">const</span> fn = (<span class="number">0</span>, vm_1.<span class="property">runInThisContext</span>)(script, path);</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_">fn</span>(<span class="variable language_">module</span>.<span class="property">exports</span>, req, <span class="variable language_">module</span>, path, (<span class="number">0</span>, path_1.<span class="property">dirname</span>)(path), <span class="variable language_">this</span>);</span><br><span class="line">  }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li>
<p>使用 <code>hexo-fs</code> 库的 <code>readFile</code> 异步读取插件文件的内容,返回一个 Promise 对象。</p>
<blockquote>
<p><code>hexo-fs</code> 库的源代码未来再说,暂且只需要知道是 Hexo 的文件系统模块。</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://github.com/hexojs/hexo-fs"><img src="https://gh-card.dev/repos/hexojs/hexo-fs.svg" alt="hexojs/hexo-fs - GitHub"></a></p>
</li>
<li>
<p>接着创建模块环境,并设置 <code>filename</code><code>paths</code> 属性。这里的 <code>module_1</code><code>module</code> 模块,定义了多种与模块加载、解析和处理相关的类型和接口。<code>filename</code> 属性被赋值插件的路径 <code>path</code><code>paths</code> 被赋值模块的搜索路径。</p>
</li>
<li>
<p><code>req</code> 方法用于模块加载。这个方法实际上模拟了 <code>require</code> 函数。之后这段代码分别定义了 <code>req.resolve</code> 方法来解析模块的绝对路径,设置 <code>req.main</code> 属性来指向 Node.JS 主模块、<code>req.extensions</code> 属性来处理模块文件的加载、<code>req.cache</code> 属性来缓存加载的模块。</p>
</li>
<li>
<p>插件的内容被包装在一个异步函数中,并传入了必要的参数、赋值给变量 <code>script</code></p>
</li>
<li>
<p>使用 <code>vm_1.runInThisContext</code> 方法将包装后的插件内容编译为可执行函数。</p>
<ul>
<li><code>vm_1</code><code>vm</code> 模块,提供了一组 API 用于在 JavaScript 中创建虚拟机和运行沙盒化的代码。</li>
</ul>
</li>
<li>
<p>执行编译后的函数,传入需要的参数。</p>
</li>
<li>
<p>最终将 Promise 对象转换为回调函数形式,并进行执行。</p>
</li>
</ol>
<h2 id="显示草稿内容"><a class="markdownIt-Anchor" href="#显示草稿内容"></a> 显示草稿内容</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_showDrafts</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="keyword">const</span> { args } = <span class="variable language_">this</span>.<span class="property">env</span>;</span><br><span class="line">  <span class="keyword">return</span> args.<span class="property">draft</span> || args.<span class="property">drafts</span> || <span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">render_drafts</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>根据用户的输入和 Hexo 的配置,确定是否应该显示草稿内容。</p>
<h2 id="加载"><a class="markdownIt-Anchor" href="#加载"></a> 加载</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">load</span>(<span class="params">callback</span>) {</span><br><span class="line">  <span class="keyword">return</span> (<span class="number">0</span>, load_database_1.<span class="property">default</span>)(<span class="variable language_">this</span>).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">info</span>(<span class="string">'Start processing'</span>);</span><br><span class="line">    <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">all</span>([</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">source</span>.<span class="title function_">process</span>(),</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">theme</span>.<span class="title function_">process</span>()</span><br><span class="line">    ]);</span><br><span class="line">  }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="title function_">mergeCtxThemeConfig</span>(<span class="variable language_">this</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_generate</span>({ <span class="attr">cache</span>: <span class="literal">false</span> });</span><br><span class="line">  }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p><code>load_database</code> 模块检查数据库文件是否存在,存在则尝试加载数据库。如果加载失败,则删除数据库文件。它使用了 <code>hexo-fs</code> 模块来处理文件系统操作,并使用 <code>bluebird</code> 提供的 Promise 来管理异步操作。通过 <code>ctx</code> 上下文对象来传递数据库和日志对象,以及跟踪数据库加载状态。</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="keyword">var</span> __importDefault = (<span class="variable language_">this</span> &amp;&amp; <span class="variable language_">this</span>.<span class="property">__importDefault</span>) || <span class="keyword">function</span> (<span class="params">mod</span>) {</span><br><span class="line">  <span class="keyword">return</span> (mod &amp;&amp; mod.<span class="property">__esModule</span>) ? mod : { <span class="string">"default"</span>: mod };</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> hexo_fs_1 = <span class="built_in">require</span>(<span class="string">"hexo-fs"</span>);</span><br><span class="line"><span class="keyword">const</span> bluebird_1 = <span class="title function_">__importDefault</span>(<span class="built_in">require</span>(<span class="string">"bluebird"</span>));</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function">(<span class="params">ctx</span>) =&gt;</span> {</span><br><span class="line">  <span class="keyword">if</span> (ctx.<span class="property">_dbLoaded</span>)</span><br><span class="line">    <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">resolve</span>();</span><br><span class="line">  <span class="keyword">const</span> db = ctx.<span class="property">database</span>;</span><br><span class="line">  <span class="keyword">const</span> { path } = db.<span class="property">options</span>;</span><br><span class="line">  <span class="keyword">const</span> { log } = ctx;</span><br><span class="line">  <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">exists</span>)(path).<span class="title function_">then</span>(<span class="function"><span class="params">exist</span> =&gt;</span> {</span><br><span class="line">    <span class="keyword">if</span> (!exist)</span><br><span class="line">      <span class="keyword">return</span>;</span><br><span class="line">    log.<span class="title function_">debug</span>(<span class="string">'Loading database.'</span>);</span><br><span class="line">    <span class="keyword">return</span> db.<span class="title function_">load</span>();</span><br><span class="line">  }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    ctx.<span class="property">_dbLoaded</span> = <span class="literal">true</span>;</span><br><span class="line">  }).<span class="title function_">catch</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    log.<span class="title function_">error</span>(<span class="string">'Database load failed. Deleting database.'</span>);</span><br><span class="line">    <span class="keyword">return</span> (<span class="number">0</span>, hexo_fs_1.<span class="property">unlink</span>)(path);</span><br><span class="line">  });</span><br><span class="line">};</span><br><span class="line"><span class="comment">//# sourceMappingURL=load_database.js.map</span></span><br></pre></td></tr></tbody></table></figure>
</blockquote>
<p>在数据库加载完成后,使用 <code>then</code> 方法处理返回的 Promise。在这个处理函数中,首先记录了日志信息 <code>Start processing</code>,然后使用 <code>bluebird_1.default.all</code> 方法并行处理两个任务:</p>
<ul>
<li><code>this.source.process()</code> 处理源文件。</li>
<li><code>this.theme.process()</code> 处理主题。</li>
</ul>
<p>处理完源文件和主题后,再次使用了 <code>then</code> 方法处理返回的 Promise。调用 <code>mergeCtxThemeConfig</code> 函数,将 <code>Hexo</code> 实例的上下文与主题配置合并,然后调用了 <code>this._generate({ cache: false })</code> 方法,生成静态文件。</p>
<p><code>mergeCtxThemeConfig</code> 函数在 <code>index.js</code> 文件的很上方:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">mergeCtxThemeConfig</span> = (<span class="params">ctx</span>) =&gt; {</span><br><span class="line">  <span class="keyword">if</span> (ctx.<span class="property">config</span>.<span class="property">theme_config</span>) {</span><br><span class="line">    ctx.<span class="property">theme</span>.<span class="property">config</span> = (<span class="number">0</span>, hexo_util_1.<span class="property">deepMerge</span>)(ctx.<span class="property">theme</span>.<span class="property">config</span>, ctx.<span class="property">config</span>.<span class="property">theme_config</span>);</span><br><span class="line">  }</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>该函数用于将 <code>ctx.config.theme_config</code>(主题配置)合并到 <code>ctx.theme.config</code>(博客目录)中。这里还使用了 <code>hexo-util</code> 库的深度合并。<code>hexo-util</code> 库也是未来会讲的。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/hexojs/hexo-util"><img src="https://gh-card.dev/repos/hexojs/hexo-util.svg" alt="hexojs/hexo-util - GitHub"></a></p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_generate</span>(<span class="params">options = {}</span>) {</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">_isGenerating</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">const</span> useCache = options.<span class="property">cache</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">_isGenerating</span> = <span class="literal">true</span>;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'generateBefore'</span>);</span><br><span class="line">  <span class="comment">// Run before_generate filters</span></span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">execFilter</span>(<span class="string">'before_generate'</span>, <span class="literal">null</span>, { <span class="attr">context</span>: <span class="variable language_">this</span> })</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function">() =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">_routerRefresh</span>(<span class="variable language_">this</span>.<span class="title function_">_runGenerators</span>(), useCache)).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'generateAfter'</span>);</span><br><span class="line">      <span class="comment">// Run after_generate filters</span></span><br><span class="line">      <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">execFilter</span>(<span class="string">'after_generate'</span>, <span class="literal">null</span>, { <span class="attr">context</span>: <span class="variable language_">this</span> });</span><br><span class="line">    }).<span class="title function_">finally</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">_isGenerating</span> = <span class="literal">false</span>;</span><br><span class="line">    });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>为了防止重复生成,<code>_generate</code> 方法先是判断了 <code>_isGenerating</code> 变量,再进行接下来的操作。从 <code>options</code> 参数中提取 <code>cache</code> 选项,是为了决定是否在生成过程中使用缓存。</p>
<p>设置 <code>this._isGenerating</code><code>true</code> 后,便开始生成。<code>generateBefore</code> 事件被触发,接着执行 <code>before_generate</code> 过滤器。</p>
<blockquote>
<p>过滤器(Filter)后续会讲解。目前先延申 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/filter">Hexo 官方文档</a> 中的说明:</p>
<blockquote>
<p>过滤器用于修改特定文件,Hexo 将这些文件依序传给过滤器,而过滤器可以针对文件进行修改。</p>
</blockquote>
</blockquote>
<blockquote>
<p>其中的 <code>execFilter</code> 方法在 <code>Hexo</code> 类的最下方被定义:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">execFilter</span>(<span class="params">type, data, options</span>) {</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">exec</span>(type, data, options);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p><code>Hexo</code> 类也有一个 <code>execFilterSync</code> 方法:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">execFilterSync</span>(<span class="params">type, data, options</span>) {</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">extend</span>.<span class="property">filter</span>.<span class="title function_">execSync</span>(type, data, options);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
</blockquote>
</blockquote>
<p><code>before_generate</code> 过滤器执行完毕后,调用 <code>_routerRefresh</code> 方法:</p>
<blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_routerRefresh</span>(<span class="params">runningGenerators, useCache</span>) {</span><br><span class="line">  <span class="keyword">const</span> { route } = <span class="variable language_">this</span>;</span><br><span class="line">  <span class="keyword">const</span> routeList = route.<span class="title function_">list</span>();</span><br><span class="line">  <span class="keyword">const</span> <span class="title class_">Locals</span> = <span class="variable language_">this</span>.<span class="title function_">_generateLocals</span>();</span><br><span class="line">  <span class="title class_">Locals</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">cache</span> = useCache;</span><br><span class="line">  <span class="keyword">return</span> runningGenerators.<span class="title function_">map</span>(<span class="function">(<span class="params">generatorResult</span>) =&gt;</span> {</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> generatorResult !== <span class="string">'object'</span> || generatorResult.<span class="property">path</span> == <span class="literal">null</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">undefined</span>;</span><br><span class="line">    <span class="keyword">const</span> path = route.<span class="title function_">format</span>(generatorResult.<span class="property">path</span>);</span><br><span class="line">    <span class="keyword">const</span> { data, layout } = generatorResult;</span><br><span class="line">    <span class="keyword">if</span> (!layout) {</span><br><span class="line">      route.<span class="title function_">set</span>(path, data);</span><br><span class="line">      <span class="keyword">return</span> path;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">execFilter</span>(<span class="string">'template_locals'</span>, <span class="keyword">new</span> <span class="title class_">Locals</span>(path, data), { <span class="attr">context</span>: <span class="variable language_">this</span> })</span><br><span class="line">      .<span class="title function_">then</span>(<span class="function"><span class="params">locals</span> =&gt;</span> { route.<span class="title function_">set</span>(path, <span class="title function_">createLoadThemeRoute</span>(generatorResult, locals, <span class="variable language_">this</span>)); })</span><br><span class="line">      .<span class="title function_">thenReturn</span>(path);</span><br><span class="line">  }).<span class="title function_">then</span>(<span class="function"><span class="params">newRouteList</span> =&gt;</span> {</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = routeList.<span class="property">length</span>; i &lt; len; i++) {</span><br><span class="line">      <span class="keyword">const</span> item = routeList[i];</span><br><span class="line">      <span class="keyword">if</span> (!newRouteList.<span class="title function_">includes</span>(item)) {</span><br><span class="line">        route.<span class="title function_">remove</span>(item);</span><br><span class="line">      }</span><br><span class="line">    }</span><br><span class="line">  });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li>初始化和获取路由列表:从 <code>this</code> 中获取 <code>route</code> 对象,并调用其 <code>list</code> 方法获取当前路由列表;生成局部变量 <code>Locals</code>,并将 <code>useCache</code> 选项赋给 <code>Locals.prototype.cache</code>,决定是否使用缓存。</li>
<li>处理生成器结果:遍历 <code>runningGenerators</code> 数组,对于每个生成器结果进行处理。如果 <code>generatorResult</code> 不是对象或 <code>path</code> 为空,则返回 <code>undefined</code></li>
<li>格式化路径并处理数据:调用 <code>route.format</code> 方法格式化路径;解构 <code>generatorResult</code>,获取 <code>data</code><code>layout</code>。如果 <code>layout</code> 不存在,直接将 <code>path</code><code>data</code> 设置到路由中,并返回 <code>path</code></li>
<li>执行 <code>template_locals</code> 过滤器并设置路由:调用 <code>execFilter</code> 方法执行 <code>template_locals</code> 过滤器,传入新的 <code>Locals</code> 实例。在过滤器执行完毕后,将路径和创建的主题路由设置到路由中,并返回 <code>path</code><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> createLoadThemeRoute = <span class="keyword">function</span> (<span class="params">generatorResult, locals, ctx</span>) {</span><br><span class="line">  <span class="keyword">const</span> { log, theme } = ctx;</span><br><span class="line">  <span class="keyword">const</span> { path, <span class="attr">cache</span>: useCache } = locals;</span><br><span class="line">  <span class="keyword">const</span> layout = [...<span class="keyword">new</span> <span class="title class_">Set</span>(<span class="title function_">castArray</span>(generatorResult.<span class="property">layout</span>))];</span><br><span class="line">  <span class="keyword">const</span> layoutLength = layout.<span class="property">length</span>;</span><br><span class="line">  locals.<span class="property">cache</span> = <span class="literal">true</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="keyword">if</span> (useCache &amp;&amp; routeCache.<span class="title function_">has</span>(generatorResult))</span><br><span class="line">      <span class="keyword">return</span> routeCache.<span class="title function_">get</span>(generatorResult);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; layoutLength; i++) {</span><br><span class="line">      <span class="keyword">const</span> name = layout[i];</span><br><span class="line">      <span class="keyword">const</span> view = theme.<span class="title function_">getView</span>(name);</span><br><span class="line">      <span class="keyword">if</span> (view) {</span><br><span class="line">        log.<span class="title function_">debug</span>(<span class="string">`Rendering HTML <span class="subst">${name}</span>: <span class="subst">${(<span class="number">0</span>, picocolors_1.magenta)(path)}</span>`</span>);</span><br><span class="line">        <span class="keyword">return</span> view.<span class="title function_">render</span>(locals)</span><br><span class="line">          .<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> ctx.<span class="property">extend</span>.<span class="property">injector</span>.<span class="title function_">exec</span>(result, locals))</span><br><span class="line">          .<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> ctx.<span class="title function_">execFilter</span>(<span class="string">'_after_html_render'</span>, result, {</span><br><span class="line">            <span class="attr">context</span>: ctx,</span><br><span class="line">            <span class="attr">args</span>: [locals]</span><br><span class="line">          }))</span><br><span class="line">          .<span class="title function_">tap</span>(<span class="function"><span class="params">result</span> =&gt;</span> {</span><br><span class="line">            <span class="keyword">if</span> (useCache) {</span><br><span class="line">              routeCache.<span class="title function_">set</span>(generatorResult, result);</span><br><span class="line">            }</span><br><span class="line">          }).<span class="title function_">tapCatch</span>(<span class="function"><span class="params">err</span> =&gt;</span> {</span><br><span class="line">            log.<span class="title function_">error</span>({ err }, <span class="string">`Render HTML failed: <span class="subst">${(<span class="number">0</span>, picocolors_1.magenta)(path)}</span>`</span>);</span><br><span class="line">          });</span><br><span class="line">      }</span><br><span class="line">    }</span><br><span class="line">    log.<span class="title function_">warn</span>(<span class="string">`No layout: <span class="subst">${(<span class="number">0</span>, picocolors_1.magenta)(path)}</span>`</span>);</span><br><span class="line">  };</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<code>createLoadThemeRoute</code> 方法创建加载主题路由的闭包。<br>
首先从 <code>ctx</code> 中获取日志记录器 <code>log</code> 和主题对象 <code>theme</code>、从 <code>locals</code> 中获取路由的路径 <code>path</code> 和缓存标志 <code>useCache</code>。然后将 <code>generatorResult.layout</code> 转换为一个唯一的布局数组,记录布局的长度,同时将 <code>locals.cache</code> 设置为 <code>true</code>,以确保视图在渲染期间使用缓存。<br>
返回一个闭包。闭包指的是函数和函数内部引用的外部变量的组合。该闭包在调用时会渲染 HTML 视图。<br>
如果 <code>useCache</code><code>true</code>,并且缓存中存在 <code>generatorResult</code>,直接返回缓存的结果。否则遍历布局数组,并尝试从主题中获取对应的视图。<br>
如果找到了视图,就使用该视图渲染 <code>locals</code>,并执行注入器和过滤器,最后返回渲染结果。否则记录警告并返回。</li>
</ol>
<blockquote>
<p>注入器(Injector)是 Hexo 扩展之一,在 <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/injector">Hexo 官方文档</a> 中被声明为 <strong>用于将静态代码片段注入生成的 HTML 的 <code>&lt;head&gt;</code> 和 / 或 <code>&lt;body&gt;</code></strong>,且注入必须在 <code>after_render:html</code> 过滤器之前完成。</p>
</blockquote>
<ol start="5">
<li>移除旧路由:获取新生成的 <code>newRouteList</code>;遍历旧路由列表 <code>routeList</code>,如果某个路由不在 <code>newRouteList</code> 中,则将其移除。</li>
</ol>
<p><code>_routerRefresh</code> 方法确保了生成器结果正确应用到路由中,同时清理不再需要的旧路由,保持路由的最新状态。</p>
</blockquote>
<p>路由刷新完成后,<code>_generate</code> 方法触发了 <code>generateAfter</code> 事件,并执行 <code>after_generate</code> 过滤器。</p>
<p>最后使用 <code>finally</code> 方法确保无论生成过程成功与否,都会将 <code>this._isGenerating</code> 重置为 <code>false</code>,允许后续生成操作进行。</p>
<p>通过执行前后过滤器和触发事件,<code>_generate</code> 方法确保生成过程的各个阶段都可以被插件或自定义代码扩展和修改。</p>
<p><code>load</code> 方法在调用 <code>_generate</code>、生成静态文件后,将整个异步操作的结果传递给回调函数 <code>callback</code><code>asCallback</code> 方法将 <code>Promise</code> 转换为传统的回调形式,以兼容老式回调风格的代码。</p>
<h2 id="监视文件变化"><a class="markdownIt-Anchor" href="#监视文件变化"></a> 监视文件变化</h2>
<p><code>watch</code> 函数的目的是在监视文件变化时重新生成内容,并在需要时启用缓存。</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">watch</span>(<span class="params">callback</span>) {</span><br><span class="line">  <span class="keyword">let</span> useCache = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">const</span> { cache } = <span class="title class_">Object</span>.<span class="title function_">assign</span>({</span><br><span class="line">    <span class="attr">cache</span>: <span class="literal">false</span></span><br><span class="line">  }, <span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">server</span>);</span><br><span class="line">  <span class="keyword">const</span> { alias } = <span class="variable language_">this</span>.<span class="property">extend</span>.<span class="property">console</span>;</span><br><span class="line">  <span class="keyword">if</span> (alias[<span class="variable language_">this</span>.<span class="property">env</span>.<span class="property">cmd</span>] === <span class="string">'server'</span> &amp;&amp; cache) {</span><br><span class="line">    <span class="comment">// enable cache when run hexo server</span></span><br><span class="line">    useCache = <span class="literal">true</span>;</span><br><span class="line">  }</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">_watchBox</span> = <span class="title function_">debounce</span>(<span class="function">() =&gt;</span> <span class="variable language_">this</span>.<span class="title function_">_generate</span>({ <span class="attr">cache</span>: useCache }), <span class="number">100</span>);</span><br><span class="line">  <span class="keyword">return</span> (<span class="number">0</span>, load_database_1.<span class="property">default</span>)(<span class="variable language_">this</span>).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">info</span>(<span class="string">'Start processing'</span>);</span><br><span class="line">    <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">all</span>([</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">source</span>.<span class="title function_">watch</span>(),</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">theme</span>.<span class="title function_">watch</span>()</span><br><span class="line">    ]);</span><br><span class="line">  }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="title function_">mergeCtxThemeConfig</span>(<span class="variable language_">this</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">source</span>.<span class="title function_">on</span>(<span class="string">'processAfter'</span>, <span class="variable language_">this</span>.<span class="property">_watchBox</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">theme</span>.<span class="title function_">on</span>(<span class="string">'processAfter'</span>, <span class="function">() =&gt;</span> {</span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">_watchBox</span>();</span><br><span class="line">      <span class="title function_">mergeCtxThemeConfig</span>(<span class="variable language_">this</span>);</span><br><span class="line">    });</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">_generate</span>({ <span class="attr">cache</span>: useCache });</span><br><span class="line">  }).<span class="title function_">asCallback</span>(callback);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li><code>watch</code> 函数会先从 <code>this.config.server</code> 中读取 <code>cache</code> 配置,默认值为 <code>false</code>。如果当前命令为 <code>server</code>,并且配置了 <code>cache</code>,就会启用缓存。</li>
<li>接着使用 <code>debounce</code> 函数防抖 <code>_generate</code> 方法,间隔设置为 100 毫秒:<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">debounce</span>(<span class="params">func, wait</span>) {</span><br><span class="line">    <span class="keyword">let</span> timeout;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line">        <span class="built_in">clearTimeout</span>(timeout);</span><br><span class="line">        timeout = <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">            func.<span class="title function_">apply</span>(<span class="variable language_">this</span>);</span><br><span class="line">        }, wait);</span><br><span class="line">    };</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<code>debounce</code> 函数的目的是限制某个函数在一段时间内的调用次数,当该函数被连续调用时,只有在停止调用后等待指定的时间才会真正执行。这样便可以优化性能。</li>
<li>调用 <code>load_database_1.default</code> 方法加载数据库,打印日志 <code>Start processing</code>,同时监视 <code>source</code><code>theme</code> 的变化,使用 <code>bluebird_1.default.all</code> 以并行方式执行。</li>
<li>合并主题配置并设置监控回调:这里的 <code>mergeCtxThemeConfig</code> 方法之前已经说过了。当 <code>source</code> 处理完成后,调用 <code>_watchBox</code>;当 <code>theme</code> 处理完成后,调用 <code>_watchBox</code> 并再次合并主题配置。</li>
<li>调用 <code>_generate</code> 方法,初次生成内容,并根据 <code>useCache</code> 决定是否使用缓存。</li>
<li>最后使用 <code>bluebird</code> 库的 <code>asCallback</code> 方法,将结果作为回调传递给 <code>callback</code></li>
</ol>
<h2 id="停止监控"><a class="markdownIt-Anchor" href="#停止监控"></a> 停止监控</h2>
<p>既然有了监视用的方法,那么也有取消监视的方法。</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">unwatch</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">_watchBox</span> != <span class="literal">null</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">source</span>.<span class="title function_">removeListener</span>(<span class="string">'processAfter'</span>, <span class="variable language_">this</span>.<span class="property">_watchBox</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">theme</span>.<span class="title function_">removeListener</span>(<span class="string">'processAfter'</span>, <span class="variable language_">this</span>.<span class="property">_watchBox</span>);</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">_watchBox</span> = <span class="literal">null</span>;</span><br><span class="line">  }</span><br><span class="line">  <span class="title function_">stopWatcher</span>(<span class="variable language_">this</span>.<span class="property">source</span>);</span><br><span class="line">  <span class="title function_">stopWatcher</span>(<span class="variable language_">this</span>.<span class="property">theme</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>unwatch</code> 方法检查并清除了 <code>_watchBox</code>。根据上面 <code>watch</code> 方法的内容,<code>this._watchBox</code> 是一个防抖函数,用来处理文件变动后的生成操作。如果它存在,就需要将其从 <code>source</code><code>theme</code> 对象的 <code>processAfter</code> 事件监听器中移除。</p>
<p><code>stopWatcher</code> 函数被定义在 <code>index.js</code> 的最上方:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">stopWatcher</span> = (<span class="params">box</span>) =&gt; { <span class="keyword">if</span> (box.<span class="title function_">isWatching</span>())</span><br><span class="line">  box.<span class="title function_">unwatch</span>(); };</span><br></pre></td></tr></tbody></table></figure>
<p>它用于停止对给定对象的监控。逻辑很简单:该对象是否正在被监控,如果是,就调用 <code>unwatch</code> 方法停止监控。</p>
<h2 id="生成本地环境"><a class="markdownIt-Anchor" href="#生成本地环境"></a> 生成本地环境</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_generateLocals</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="keyword">const</span> { config, env, theme, theme_dir } = <span class="variable language_">this</span>;</span><br><span class="line">  <span class="keyword">const</span> ctx = { <span class="attr">config</span>: { <span class="attr">url</span>: <span class="variable language_">this</span>.<span class="property">config</span>.<span class="property">url</span> } };</span><br><span class="line">  <span class="keyword">const</span> localsObj = <span class="variable language_">this</span>.<span class="property">locals</span>.<span class="title function_">toObject</span>();</span><br><span class="line">  <span class="keyword">class</span> <span class="title class_">Locals</span> {</span><br><span class="line">    <span class="title function_">constructor</span>(<span class="params">path, locals</span>) {</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">page</span> = <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, locals);</span><br><span class="line">      <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">page</span>.<span class="property">path</span> == <span class="literal">null</span>)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">page</span>.<span class="property">path</span> = path;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">path</span> = path;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">url</span> = hexo_util_1.<span class="property">full_url_for</span>.<span class="title function_">call</span>(ctx, path);</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">config</span> = config;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">theme</span> = theme.<span class="property">config</span>;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">layout</span> = <span class="string">'layout'</span>;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">env</span> = env;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">view_dir</span> = (<span class="number">0</span>, path_1.<span class="property">join</span>)(theme_dir, <span class="string">'layout'</span>) + path_1.<span class="property">sep</span>;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">site</span> = localsObj;</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">return</span> <span class="title class_">Locals</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><code>Locals</code> 类中,许多环境相关的属性,如 <code>url</code><code>config</code> 等属性都被设置。该方法生成了本地环境的信息对象,以便在模块渲染过程中使用。</p>
<h2 id="运行生成器"><a class="markdownIt-Anchor" href="#运行生成器"></a> 运行生成器</h2>
<blockquote>
<p>同样,生成器(Generator)后续也会讲解。</p>
<p><a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/api/generator">Hexo 官方文档</a> 中,生成器被解释为 <strong>会根据处理后的原始文件建立路由</strong></p>
</blockquote>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">_runGenerators</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">locals</span>.<span class="title function_">invalidate</span>();</span><br><span class="line">  <span class="keyword">const</span> siteLocals = <span class="variable language_">this</span>.<span class="property">locals</span>.<span class="title function_">toObject</span>();</span><br><span class="line">  <span class="keyword">const</span> generators = <span class="variable language_">this</span>.<span class="property">extend</span>.<span class="property">generator</span>.<span class="title function_">list</span>();</span><br><span class="line">  <span class="keyword">const</span> { log } = <span class="variable language_">this</span>;</span><br><span class="line">  <span class="comment">// Run generators</span></span><br><span class="line">  <span class="keyword">return</span> bluebird_1.<span class="property">default</span>.<span class="title function_">map</span>(<span class="title class_">Object</span>.<span class="title function_">keys</span>(generators), <span class="function"><span class="params">key</span> =&gt;</span> {</span><br><span class="line">    <span class="keyword">const</span> generator = generators[key];</span><br><span class="line">    log.<span class="title function_">debug</span>(<span class="string">'Generator: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">magenta</span>)(key));</span><br><span class="line">    <span class="keyword">return</span> <span class="title class_">Reflect</span>.<span class="title function_">apply</span>(generator, <span class="variable language_">this</span>, [siteLocals]);</span><br><span class="line">  }).<span class="title function_">reduce</span>(<span class="function">(<span class="params">result, data</span>) =&gt;</span> {</span><br><span class="line">    <span class="keyword">return</span> data ? result.<span class="title function_">concat</span>(data) : result;</span><br><span class="line">  }, []);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<ol>
<li><code>invalidate</code> 方法使 <code>Hexo</code> 实例的本地数据无效,以便在生成器运行之前重新加载。</li>
<li>提取当前 <code>Hexo</code> 实例的本地数据对象,将其赋值给 <code>siteLocals</code> 变量。</li>
<li>获取 <code>Hexo</code> 实例中已注册的所有生成器,将它们保存在 <code>generators</code> 变量中。</li>
<li>运行生成器之前,记录了一条调试信息,指示将要运行哪个生成器。</li>
<li>使用 <code>bluebird</code> 库的 <code>map</code> 方法对生成器进行并行处理。每个生成器都被调用 <code>Reflect.apply(generator, this, [siteLocals])</code>,将生成器函数应用到当前的 <code>Hexo</code> 实例上。</li>
<li>使用 <code>bluebird</code> 库的 <code>reduce</code> 方法将生成器返回的数据收集到一个数组中,并返回这个数组。</li>
</ol>
<p><code>_runGenerators</code> 方法运行 <code>Hexo</code> 实例中所有注册的生成器,并将它们返回的数据收集到一个数组中。</p>
<h2 id="退出进程"><a class="markdownIt-Anchor" href="#退出进程"></a> 退出进程</h2>
<p><code>exit</code> 方法用于退出 Hexo 进程。</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">exit</span>(<span class="params">err</span>) {</span><br><span class="line">  <span class="keyword">if</span> (err) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">log</span>.<span class="title function_">fatal</span>({ err }, <span class="string">'Something\'s wrong. Maybe you can find the solution here: %s'</span>, (<span class="number">0</span>, picocolors_1.<span class="property">underline</span>)(<span class="string">'https://hexo.io/docs/troubleshooting.html'</span>));</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">execFilter</span>(<span class="string">'before_exit'</span>, <span class="literal">null</span>, { <span class="attr">context</span>: <span class="variable language_">this</span> }).<span class="title function_">then</span>(<span class="function">() =&gt;</span> {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">emit</span>(<span class="string">'exit'</span>, err);</span><br><span class="line">  });</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这里值得说的是,<code>exit</code> 方法会触发 <code>execFilter</code> 方法、触发 <code>before_exit</code> 过滤器。以及最终它会触发 <code>exit</code> 事件。</p>
<h2 id="定义属性和导出"><a class="markdownIt-Anchor" href="#定义属性和导出"></a> 定义属性和导出</h2>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Hexo</span>.<span class="property">lib_dir</span> = libDir + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">lib_dir</span> = <span class="title class_">Hexo</span>.<span class="property">lib_dir</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property">core_dir</span> = (<span class="number">0</span>, path_1.<span class="property">dirname</span>)(libDir) + path_1.<span class="property">sep</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">core_dir</span> = <span class="title class_">Hexo</span>.<span class="property">core_dir</span>;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property">version</span> = version;</span><br><span class="line"><span class="title class_">Hexo</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">version</span> = <span class="title class_">Hexo</span>.<span class="property">version</span>;</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">Hexo</span>;</span><br></pre></td></tr></tbody></table></figure>
<p><code>Hexo</code> 类本身终于讲完了,在 <code>Hexo</code> 类的下方,它的部分属性和原型属性被定义,并最终被导出为模块的默认输出。</p>
<ul>
<li><code>LibDir</code><code>Hexo</code> 库目录的路径。</li>
<li><code>core_dir</code>:核心目录的路径。</li>
<li><code>version</code>:版本号。</li>
</ul>
</body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="407.html">上一篇</a><a class="next" href="224a.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section webmention-empty" data-page-url="posts/5d57.html" data-full-url="https://cytrogen.icu/posts/5d57.html" data-mode="static">
              <h3 class="webmention-title">Webmentions (<span class="webmention-count">0</span>)</h3>
              <div class="webmention-list"></div>
              <span>暂无 Webmentions</span>
            </div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
  const codeBlocks = document.querySelectorAll('figure.highlight');
  
  codeBlocks.forEach(block => {
    let caption = block.querySelector('figcaption');
    if (!caption) {
      caption = document.createElement('figcaption');
      block.insertBefore(caption, block.firstChild);
    }

    const info = document.createElement('div');
    info.className = 'info';
    
    const filename = caption.querySelector('span');
    if (filename) {
      filename.className = 'filename';
      info.appendChild(filename);
    }
    
    const lang = block.className.split(' ')[1];
    if (lang) {
      const langSpan = document.createElement('span');
      langSpan.className = 'lang-name';
      langSpan.textContent = lang;
      info.appendChild(langSpan);
    }

    const sourceLink = caption.querySelector('a');
    if (sourceLink) {
      sourceLink.className = 'source-link';
      info.appendChild(sourceLink);
    }

    const actions = document.createElement('div');
    actions.className = 'actions';

    const codeHeight = block.scrollHeight;
    const threshold = 300;

    if (codeHeight > threshold) {
      block.classList.add('folded');
      
      const toggleBtn = document.createElement('button');
      toggleBtn.textContent = '展开';
      toggleBtn.addEventListener('click', () => {
        block.classList.toggle('folded');
        toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
      });
      actions.appendChild(toggleBtn);
    }

    const copyBtn = document.createElement('button');
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', async () => {
      const codeLines = block.querySelectorAll('.code .line');
      const code = Array.from(codeLines)
        .map(line => line.textContent)
        .join('\n')
        .replace(/\n\n/g, '\n');
      
      try {
        await navigator.clipboard.writeText(code);
        copyBtn.textContent = '已复制';
        copyBtn.classList.add('copied');
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
          copyBtn.classList.remove('copied');
        }, 3000);
      } catch (err) {
        console.error('复制失败:', err);
        copyBtn.textContent = '复制失败';
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
        }, 3000);
      }
    });
    actions.appendChild(copyBtn);

    caption.innerHTML = '';
    caption.appendChild(info);
    caption.appendChild(actions);

    const markedLines = block.getAttribute('data-marked-lines');
    if (markedLines) {
      const lines = markedLines.split(',');
      lines.forEach(range => {
        if (range.includes('-')) {
          const [start, end] = range.split('-').map(Number);
          for (let i = start; i <= end; i++) {
            const line = block.querySelector(`.line-${i}`);
            if (line) line.classList.add('marked');
          }
        } else {
          const line = block.querySelector(`.line-${range}`);
          if (line) line.classList.add('marked');
        }
      });
    }
  });
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.querySelector('.theme-toggle');
    
    if (!themeToggle) return;
    
    const getCurrentTheme = () => {
      return document.documentElement.getAttribute('data-theme') || 'light';
    };
    
    const updateUI = (theme) => {
      const isDark = theme === 'dark';
      themeToggle.setAttribute('aria-pressed', isDark.toString());
    };
    
    const setTheme = (theme) => {
      document.documentElement.setAttribute('data-theme', theme);
      document.documentElement.style.colorScheme = theme;
      
      const pageWrapper = document.getElementById('page-wrapper');
      if (pageWrapper) {
        pageWrapper.setAttribute('data-theme', theme);
      }
      
      // Find and remove the temporary anti-flicker style tag if it exists.
      // This ensures the main stylesheet takes full control after the initial load.
      const antiFlickerStyle = document.getElementById('anti-flicker-style');
      if (antiFlickerStyle) {
        antiFlickerStyle.remove();
      }
      
      localStorage.setItem('theme', theme);
      updateUI(theme);
    };
    
    const toggleTheme = () => {
      const current = getCurrentTheme();
      const newTheme = current === 'light' ? 'dark' : 'light';
      setTheme(newTheme);
    };
    
    updateUI(getCurrentTheme());
    
    themeToggle.addEventListener('click', toggleTheme);
    
    if (window.matchMedia) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      mediaQuery.addEventListener('change', function(e) {
        if (!localStorage.getItem('theme')) {
          const theme = e.matches ? 'dark' : 'light';
          setTheme(theme);
        }
      });
    }
  });
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const backToTopBtn = document.querySelector('.back-to-top');
    
    if (!backToTopBtn) return;
    
    const toggleButtonVisibility = () => {
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const shouldShow = scrollTop > 200;
      
      if (shouldShow) {
        backToTopBtn.classList.add('is-visible');
      } else {
        backToTopBtn.classList.remove('is-visible');
      }
    };
    
    let ticking = false;
    const handleScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          toggleButtonVisibility();
          ticking = false;
        });
        ticking = true;
      }
    };
    
    const scrollToTop = (event) => {
      event.preventDefault();
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };
    
    window.addEventListener('scroll', handleScroll);
    backToTopBtn.addEventListener('click', scrollToTop);
    
    toggleButtonVisibility();
  });
})();</script></body></html>