前言

hexo inithexo new|server .etc是如何从命令行执行到对应的代码的,

通过下面的步骤将会有所了解

步骤讲解

npm安装hexo-cli

npm install -g hexo-cli

安装完之后,会在npm的安装路径下面有如下内容生成

image-20241130201535530

上面图片中红框的内容保证了我们可以在命令行中执行hexo 命令

hexo init调用链

命令行执行hexo之后会调用,当前目录下node_modules下面的hexo-cli模块对应脚本

image-20241130202952938

这个脚本最终会执行对应目录的hexo.js脚本

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
// cmd是我们传入的命令参数
function entry(cwd = process.cwd(), args) {
args = camelCaseKeys(args || minimist(process.argv.slice(2), { string: ['_', 'p', 'path', 's', 'slug'] }));

let hexo = new Context(cwd, args);
let { log } = hexo;

// Change the title in console
process.title = 'hexo';

function handleError(err) {
if (err && !(err instanceof HexoNotFoundError)) {
log.fatal(err);
}

process.exitCode = 2;
}

// 寻找有没有安装对应命令的模块,
// 判断hexo-cli/package.json中是否有hexo的依赖,这里是"main": "dist/hexo"

return findPkg(cwd, args).then(path => {
if (!path) return;

hexo.base_dir = path;

// 加载hexo模块
return loadModule(path, args).catch(err).then(mod => {
if (mod) hexo = mod;
log = hexo.log;
// 注册hexo命令行命令
registerConsole(hexo);
return hexo.init();
}).then(() => {
let cmd = 'help';

if (!args.h && !args.help) {
const c = args._.shift();
if (c && hexo.extend.console.get(c)) cmd = c;
}

watchSignal(hexo);
// 调用hexo命令
// hexo init
return hexo.call(cmd, args).then(() => hexo.exit()).catch(err);
}

*这里loadModule函数加载的hexo仍然是hexo-cli/bin/hexo*(可以看下package.json下面的hexo配置),主要的目的是调用registerConsole函数注册hexo可以调用的命令 init/help|version **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 同样,找的ts代码
// https://github.com/hexojs/hexo-cli/blob/master/lib/console/index.ts
export = function (ctx: Context) {
const { console } = ctx.extend;

console.register('help', 'Get help on a command.', {}, helpConsole);

console.register('init', 'Create a new Hexo folder.', {
desc: 'Create a new Hexo folder at the specified path or the current directory.',
usage: '[destination]',
arguments: [
{ name: 'destination', desc: 'Folder path. Initialize in current folder if not specified' }
],
options: [
{ name: '--no-clone', desc: 'Copy files instead of cloning from GitHub' },
{ name: '--no-install', desc: 'Skip npm install' }
]
}, initConsole);

console.register('version', 'Display version information.', {}, versionConsole);
};

对应命令的实现也在console目录下。

不带参数(参数错误)执行结果如下:

image-20241130202243209

hexo new调用链

*hexo new命令在*hexo init之后可以执行,同样,找node_modules目录下hexo-cli里面的 hexo脚本,同样是注册命令行命令然后执行对应的cmd,只不过注册命令函数有变化。**

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
// 初始化函数
// ts源码路径
// https://github.com/hexojs/hexo/blob/master/lib/hexo/index.ts
init(): Promise<void> {
this.log.debug('Hexo version: %s', magenta(this.version));
this.log.debug('Working directory: %s', magenta(tildify(this.base_dir)));

// Load internal plugins
require('../plugins/console')(this); // 这里的console里面注册了所有需要的内置命令
require('../plugins/filter')(this);
require('../plugins/generator')(this);
require('../plugins/helper')(this);
require('../plugins/highlight')(this);
require('../plugins/injector')(this);
require('../plugins/processor')(this);
require('../plugins/renderer')(this);
require('../plugins/tag').default(this);

// Load config
return Promise.each([
'update_package', // Update package.json
'load_config', // Load config
'load_theme_config', // Load alternate theme config
'load_plugins' // Load external plugins & scripts 加载我们外部定义的脚本和插件
], name => require(`./${name}`)(this)).then(() => this.execFilter('after_init', null, { context: this })).then(() => {
// Ready to go!
this.emit('ready');
});
}

../plugins/console内容如下:

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
import type Hexo from '../../hexo';

// 源码路径:https://github.com/hexojs/hexo/blob/master/lib/plugins/console/index.ts
export = function (ctx: Hexo) {
const { console } = ctx.extend;

console.register('clean', 'Remove generated files and cache.', require('./clean'));

console.register('config', 'Get or set configurations.', {
usage: '[name] [value]',
arguments: [
{ name: 'name', desc: 'Setting name. Leave it blank if you want to show all configurations.' },
{ name: 'value', desc: 'New value of a setting. Leave it blank if you just want to show a single configuration.' }
]
}, require('./config'));

console.register('deploy', 'Deploy your website.', {
options: [
{ name: '--setup', desc: 'Setup without deployment' },
{ name: '-g, --generate', desc: 'Generate before deployment' }
]
}, require('./deploy'));

console.register('generate', 'Generate static files.', {
options: [
{ name: '-d, --deploy', desc: 'Deploy after generated' },
{ name: '-f, --force', desc: 'Force regenerate' },
{ name: '-w, --watch', desc: 'Watch file changes' },
{ name: '-b, --bail', desc: 'Raise an error if any unhandled exception is thrown during generation' },
{ name: '-c, --concurrency', desc: 'Maximum number of files to be generated in parallel. Default is infinity' }
]
}, require('./generate'));

console.register('list', 'List the information of the site', {
desc: 'List the information of the site.',
usage: '<type>',
arguments: [
{ name: 'type', desc: 'Available types: page, post, route, tag, category' }
]
}, require('./list'));

console.register('migrate', 'Migrate your site from other system to Hexo.', {
init: true,
usage: '<type>',
arguments: [
{ name: 'type', desc: 'Migrator type.' }
]
}, require('./migrate'));

console.register('new', 'Create a new post.', {
usage: '[layout] <title>',
arguments: [
{ name: 'layout', desc: 'Post layout. Use post, page, draft or whatever you want.' },
{ name: 'title', desc: 'Post title. Wrap it with quotations to escape.' }
],
options: [
{ name: '-r, --replace', desc: 'Replace the current post if existed.' },
{ name: '-s, --slug', desc: 'Post slug. Customize the URL of the post.' },
{ name: '-p, --path', desc: 'Post path. Customize the path of the post.' }
]
}, require('./new'));

console.register('publish', 'Moves a draft post from _drafts to _posts folder.', {
usage: '[layout] <filename>',
arguments: [
{ name: 'layout', desc: 'Post layout. Use post, page, draft or whatever you want.' },
{ name: 'filename', desc: 'Draft filename. "hello-world" for example.' }
]
}, require('./publish'));

console.register('render', 'Render files with renderer plugins.', {
init: true,
desc: 'Render files with renderer plugins (e.g. Markdown) and save them at the specified path.',
usage: '<file1> [file2] ...',
options: [
{ name: '--output', desc: 'Output destination. Result will be printed in the terminal if the output destination is not set.' },
{ name: '--engine', desc: 'Specify render engine' },
{ name: '--pretty', desc: 'Prettify JSON output' }
]
}, require('./render'));
}

总体的简易流程图是这样的:

hexo-draw