# Preface

Hexo is an excellent static blog generator, I read its source code recently, and I think it’s worth sharing the inner mechanism of its source code.

# Generator

When you run the command hexo generate, hexo will generate all the static files for you. This is the part we are going to start with. The directory where hexo generate is executed is /home/searene/Development/hexo-twenty-sixteen in this tutorial.

First, we can find the location of the tool hexo with the command which.

My output is

This is a soft link, we get the location of the original file with ll

The contents of hexo are as follows.

It requires a js file called hexo, which is located in /home/searene/.nvm/versions/node/v5.0.0/lib/node_modules/hexo-cli/lib/hexo.js in my case. The beginning part of the file is as follows.

It requires several packages.

1. chalk is used for colorful outputs,.
2. tildify is used to convert an absolute path to a tilde path:, like /Users/sindresorhus/dev~/dev.
3. context is used to //TODO
4. find_pkg is used to find the local hexo directory that contains node_modules
5. goodbye is used to generate a random goodbye sentence.
6. minimist is used to parse argument options.
7. camelCaseKeys is used to convert keys in parameters to the camelCased ones

Let’s continue to read the file.

Notice the last part

Remember the contents of hexo.js?

So what hexo.js does is calling the entry function. Here comes the question, what does entry do?

1. It searched upwards from the working directory looking for package.json containing the key hexo.

2. Then it loads the local hexo package.

3. new a Hexo object. The souce code of Hexo is as follows:

Hexo gets several directories such as public_dir, source_dir, etc. Then it defines the this.extend object, which contains console, deployer, etc. The format of each instance in the this.extend object is as follows:

All of them contain the same object this.store, which is used to map the name to the corresponding function. For example, this.store in Console is as follows:

Each key in the object such as clean, config is of type string. What they are mapped to are functions that implement them.

Then it creates several instances, logger, Render, Router, Post, Scaffold, database etc. logger is used to log information on the console and the file, Render is used to render files(e.g. render markdownf files to html), Router is used to save all paths used in the site, Post is used to //TODO, Scaffold is used to //TODO, database is a JSON-based database.

It then registered following schemas using registerModels(this).

Afterwards, two instances are initiated, Source, Theme,which represents source and theme folders respectively. They are both being processed by Box.

First, let’s look at source.js.

ctx refers to Hexo, Source function calls Box and gets the processor list, then it inherits Box. Box is used to read and render files in source or theme folder. To find out what’s going on, we need to look into the source code of Box.

It sets several variables, then it gets the Cache model. The source code of ctx.model function is as follows.

If the model was created before, this.database.model will just return the model, or it will create the model with the specified name and schema.

Note that We have created the model in the register_model function, which is located in the Hexo function. When the code new Hexo is run, all the models are registered.

The models created here were as follows.

Which includes cache. So we can get the created cache model with this.Cache = ctx.model('Cache');. This model is used to cache generated posts and stuff, and store a hashed value for all of them. If the hash value is identical, hexo will not generate the post again, which reduces the generation time to a degree.

Now we only have a line left in the box function.

this.File is used to read file contents and render it. The source code of _createFileClass() function is as follows.

File.call(this, data) sets _File‘s source, path, params and type as the same as ones in data. You can see it in the source of the File constructor.

_File inherits File afterwards. Then it sets render and renderSync function of _File and returns it. As you can tell from their names, they are used to render files or strings, like this:

## Render

Let’s look into the Render object.

Still remember renderer? It’s used to store all the information about rendering.

The most important function in render.js is Render.prototype.render, the function is used to render text or files, the source code of it is as follows.

• First, it checks if data exists or not, data contains text(data.text) or file(data.path) that is going to be rendered, it throws an error if it doesn’t exist.

• Then if data.text exists, it will try to render the text first.

• If data.path exists, it will try to analyze the file specified by data.path.

• It tries to find out if the rendering engine exists in renderer.store, which maps the rendering engine’s name to the corresponding rendering function. If the engine exists, it will call the corresponding function to render it, return the original text if the engine doesn’t exist.

renderer refers to the function that is used to render text or files. What renderer.call() returns is usually of JSON format. For example, if the file to be rendered is like this:

The rendered result will be an object like this:

Some files are not rendered in this way, e.g. md. The rendering results of markdown files are of type string. Hexo creates a toString function to make the conversion happen.

The source code of toString function is as follows.

Because md files’s rendering results are of type string. so toString returns the original result directly in this case. Sometimes it needs to be further processed.

Afterwards, onRenderEnd is followed in order to modify some contents of result after rendering.

Then the result is transferred to the next then function, and the after_render filter is executed.

To make it even clearer about how to use execFilter, Here I give an example from the official Hexo website, the following code is used to uglify js files.

This is the source code of register function.

As you can see, it stores type(e.g. after_post_render:js) and the corresponding processing function in the this.store object. After the registration is over, Filter.prototype.exec executes the specified filter (after_post_render:js in this case).

OK, this part is over, let’s look into the theme/index.js file next.

The function Theme also calls Box(), then it adds several processors and sets languages and views.

Then it bind locals.

1. After loading module is over, it calls console to execute the provided command

Let’s look through the source code of ./console.

It registers several commands using console.register, let’s look through its source code.

Share