由于Hexo中使用了
bluebird
这个Promise库,会导致代码较难理解
本文会省略一些和issue无关的代码
最近看到了Hexo的issue #4976,其中提到了大文件的CSS在生成的过程中可能会丢失部分代码。本人感觉这个问题非常有意思,于是自己也尝试了一下大文件的CSS,没想到也成功复现了这个问题。
先说结论:问题应该出现在post.js
中的escapeAllSwigTags()
函数。由于压缩过的CSS中可能出现诸如{#main 这样的语句,而这样的语句会在这个函数中被当成swig模板进行处理,导致了代码的丢失。
解决方法:在_config.yml
的skip_render
中添加CSS的相对路径
以下为排查的过程和部分源码的分析:
hexo-cli
hexo中所输入的命令实际运行的是hexo/bin/hexo
文件:
1 |
|
hexo文件中直接导入hexo-cli
模块,查看hexo-cli
入口点:
1 | { |
即入口点为hexo/node_modules/hexo-cli/lib/hexo.js
,其模块导出:
1 | module.exports = entry; |
查看entry()
函数:
其输入两个参数cwd
和args
。在该函数中调用了loadModule()
函数,并将其返回结果赋值给hexo
,接着调用其init()
函数
1 | function entry(cwd = process.cwd(), args) { |
查看loadModule()
函数,在该函数中创建Hexo对象并返回:
1 | function loadModule(path, args) { |
总结:hexo-cli中创建Hexo对象,并调用其init()函数
hexo初始化
查看hexo
入口点:
1 | { |
即入口点为hexo/lib/hexo/index.js
,其模块导出:
1 | module.exports = Hexo; |
先查看Hexo类的构造函数,在该函数中主要为属性赋值,初始化配置文件;同时初始化数据库,绑定查询方法
1 | constructor(base = process.cwd(), args = {}) { |
由于在hexo-cli
调用了Hexo类中的init()
函数,查看该函数:
1 | init() { |
至此,Hexo初始化完成,可以开始执行用户输入的指令
generate
hexo/lib/plugins/console
用于处理用户输入的指令hexo/lib/plugins/console/index.js
是该模块的入口,该模块用于向对应的extend中注册模块,以下以generate命令为例:
1 | module.exports = function(ctx) { |
查看同目录下的generate.js
模块:
其创建了Generater对象,并调用了this.load()
函数,由于this就是Hexo对象,所以相当于调用了Hexo对象中的load()
函数
1 | function generateConsole(args = {}) { |
查看Hexo类中的load()
函数:
该函数首先调用load_database.js
中的loadDatabase
模块,先检查是否在根目录下存在db.json
数据库文件,如果有则进行读取,否则直接返回
由于hexo将需要处理的文件分成了source
(\source目录下的文件)和theme
(\themes目录下的文件),所以分别需要对这两个部分执行process()
函数进行预处理
在异步调用结束后,需要生成的文件已经被存入了hexo对象中的database
属性中,等待被生成。此时执行mergeCtxThemeConfig()
函数进行配置的融合,并调用_generate()
函数用于执行生成前和生成后的过滤器(filter)
由于CSS文件位于\source
目录下,所以CSS文件会在this.source.process()
中被处理
1 | load(callback) { |
由于issue中所提到的CSS文件属于soruce,所以只需要研究this.source.process()
processor
查看hexo/lib/box/index.js
中的process()
函数:
重点在于最后的return语句,通过_readDir()
函数读取文件到数据库中,再使用过滤器处理被删除的文件。而issue中提到的问题正是在将文件读取到数据库中发生的
1 | process(callback) { |
查看同文件下_readDir()
函数:
函数比较简单,即递归读取特定目录下所有文件,检查其状态并使用_processFile()
函数进行处理
读取文件本身不存在问题,问题出在对读取出来数据的处理上
1 | _readDir(base, prefix = '') { |
查看同文件下_processFile
函数:bluebird
的使用使得代码较难理解,大意就是对于每个path
,判断其是否匹配processor
中的pattern
。如果匹配,则执行processor
中的process()
函数,并将结果返回
1 | _processFile(type, path) { |
注意,这里的this
是hexo对象中的source
而非theme
,所以查看hexo/lib/hexo/source.js
:
1 | class Source extends Box { |
继续查看hexo/lib/extend/processor.js
:
可以知道最终processors中的处理器就是那些初始化时被注册的处理器(和console一样)
1 | class Processor { |
继续查看hexo/lib/plugins/processor/index.js
:
可以知道asset
、data
和post
三个处理器被成功注册,而CSS文件是归属于asset
进行处理的
1 | module.exports = ctx => { |
继续查看hexo/lib/plugins/processor/asset.js
:
CSS文件的renderable
是true
,所以会进入processPage()
函数中
1 | module.exports = ctx => { |
继续查看processPage()
函数:
在该函数中,主要是读取对应文件,进行一定的处理后将结果存入数据库的Page
模型中
1 | function processPage(ctx, file) { |
filter
process()
函数已经完成,此时回到load()
函数中:
开始执行_generate()
函数
1 | load(callback) { |
查看同目录下的_generate()
函数:
基本上就是先运行before_generate
过滤器,接着运行_runGenerators
调用生成器进行生成,最后运行after_generate
过滤器
1 | _generate(options = {}) { |
问题出现在before_generate
过滤器之中,查看hexo/lib/plugins/filter/before_generate/render_post.js
:
在该过滤器中,对于Post
模型和Page
模型分别调用render()
函数对post进行处理(如转义)
1 | function renderPostFilter(data) { |
查看hexo/lib/hexo/post.js
中的render()
函数:
在该函数中,首先运行before_post_render
过滤器,接着在对文件进行转义操作后使用渲染器对markdown等进行渲染,最后运行after_post_render
过滤器
问题就出在escapeAllSwigTags()
函数中。由于压缩过的CSS中可能出现诸如{#main这样的语句,而这样的语句会在这个函数中被当成swig模板进行处理,导致文件丢失部分代码
1 | render(source, data = {}, callback) { |