john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

hexo 命令call是如何调用的

发表于 2016-09-11 | 更新于 2019-05-07

hexo.call

首先调用hexo对象中的call方法:

1
2
3
4
5
6
7
return hexo.call(cmd, args).then(function() {
return hexo.exit();
}).catch(function(err) {
return hexo.exit(err).then(function() {
handleError(err);
});
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hexo.prototype.call = function(name, args, callback){
if (!callback && typeof args === 'function'){
callback = args;
args = {};
}

var self = this;

return new Promise(function(resolve, reject){
var c = self.extend.console.get(name);

if (c){
c.call(self, args).then(resolve, reject);
} else {
reject(new Error('Console `' + name + '` has not been registered yet!'));
}
}).nodeify(callback);
};

注意到,通过self.extend.console.get(name)获取到的其实是个函数function,所以c.call(self,args)其实是调用的js自带的call函数。因为console.register的时候是放入的函数,注意var c = this.store[name.toLowerCase()] = fn;这句代码,关于注册命令的分析,我们已经在(hexo new命令解析)[https://johnwonder.github.io/2016/09/09/hexo-new/]中分析过。

所以这里的call就是调用的每个命令js文件中定义的函数,如plugins/console/new.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
function newConsole(args){
/* jshint validthis: true */
// Display help message if user didn't input any arguments
//console.log('参数是:'+args._);
if (!args._.length){
return this.call('help', {_: ['new']});
}

var data = {
title: args._.pop(),//标题
//这里layout就是可以通过 hexo n title layout获取
layout: args._.length ? args._[0] : this.config.default_layout,
slug: args.s || args.slug,
path: args.p || args.path
};

var keys = Object.keys(args);
var key = '';
var self = this;

for (var i = 0, len = keys.length; i < len; i++){
key = keys[i];
if (!reservedKeys[key]) data[key] = args[key];
}

return this.post.create(data, args.r || args.replace).then(function(post){
self.log.info('Created: %s', chalk.magenta(tildify(post.path)));
});
}

这里的this对象就是hexo本身,所以this.post就是hexo对象中定义的post变量:

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
Post.prototype.create = function(data, replace, callback){
if (!callback && typeof replace === 'function'){
callback = replace;
replace = false;
}

var ctx = this.context;
var config = ctx.config;

data.slug = slugize((data.slug || data.title).toString(), {transform: config.filename_case});
data.layout = (data.layout || config.default_layout).toLowerCase();
data.date = data.date ? moment(data.date) : moment();

return Promise.all([
// Get the post path
ctx.execFilter('new_post_path', data, {
args: [replace],
context: ctx
}),
// Get the scaffold
this._getScaffold(data.layout)
]).spread(function(path, scaffold){
// Split data part from the raw scaffold
var split = yfm.split(scaffold);
var separator = split.separator || '---';
var jsonMode = separator[0] === ';';
var frontMatter = prepareFrontMatter(_.clone(data));
var content = '';

// Compile front-matter with data
var renderedData = swig.compile(split.data)(frontMatter);

// Parse front-matter
var compiled;

if (jsonMode){
compiled = JSON.parse('{' + renderedData + '}');
} else {
compiled = yaml.load(renderedData);
}

// Add data which are not in the front-matter
var keys = Object.keys(data);
var key = '';
var obj = compiled;

for (var i = 0, len = keys.length; i < len; i++){
key = keys[i];

if (!preservedKeys[key] && obj[key] == null){
obj[key] = data[key];
}
}

// Prepend the separator
if (split.prefixSeparator) content += separator + '\n';

content += yfm.stringify(obj, {
mode: jsonMode ? 'json' : ''
});

// Concat content
content += split.content;

if (data.content){
content += '\n' + data.content;
}

var result = {
path: path,
content: content
};

return Promise.all([
// Write content to file
fs.writeFile(path, content),
// Create asset folder
createAssetFolder(path, config.post_asset_folder)
]).then(function(){
ctx.emit('new', result);
}).thenReturn(result);
}).nodeify(callback);
}

hexo中的Promise应用

发表于 2016-09-11 | 更新于 2019-05-07

hexo Promise的使用

hexo中的Promise用的是bluebird框架提供的,到处都是,基本上每个和执行命令相关的都用上了Promise,我们来看相关代码:

Promise.each:

位于hexo/index.js:Hexo.prototype.init

1
2
3
4
5
6
7
8
9
10
11
12
13
// Load config
return Promise.each([
'update_package', // Update package.json
'load_config', // Load config
'load_plugins' // Load external plugins & scripts
], function(name){
return require('./' + name)(self);
}).then(function(){
return self.execFilter('after_init', null, {context: self});
}).then(function(){
// Ready to go!
self.emit('ready');
});

遍历参数数组中的每个元素传递给参数方法

new Promise(func)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hexo.prototype.call = function(name, args, callback){
if (!callback && typeof args === 'function'){
callback = args;
args = {};
}

var self = this;

return new Promise(function(resolve, reject){
var c = self.extend.console.get(name);

if (c){
c.call(self, args).then(resolve, reject);
} else {
reject(new Error('Console `' + name + '` has not been registered yet!'));
}
}).nodeify(callback);
};

实例化一个Promise,这个call是调用命令控制台命令的函数。

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
Hexo.prototype.load = function(callback){
var self = this;

return loadDatabase(this).then(function(){
return Promise.all([
self.source.process(),
self.theme.process()
]);
}).then(function(){
return self._generate({cache: true});
}).nodeify(callback);
};

调用所有数组中的方法。

hexo new命令解析

发表于 2016-09-09 | 更新于 2019-05-07

hexo new命令

hexo new命令是创建新文章的,后面跟文章的名字比如hexo new helloworld,那么就会创建一篇title为helloworld的文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var cmd = '';

if (!args.h && !args.help) {
cmd = args._.shift();

if (cmd) {
var c = hexo.extend.console.get(cmd);
if (!c) cmd = 'help';
} else {
cmd = 'help';
}
} else {
cmd = 'help';
}

args对象类似{ _: [ 'n', 'hexo_new' ] }这种形式,所以args._.shift()得到的结果是n。

hexo.extend.console.get是通过定义extend对象,extend对象中定义console对象:
代码在hexo目录index.js文件中:

头部先require入extend文件

1
var extend = require('../extend');

../extend默认会引入extend目录下的index.js文件:

1
2
3
4
5
6
7
8
9
10
11
'use strict';

exports.Console = require('./console');
exports.Deployer = require('./deployer');
exports.Filter = require('./filter');
exports.Generator = require('./generator');
exports.Helper = require('./helper');
exports.Migrator = require('./migrator');
exports.Processor = require('./processor');
exports.Renderer = require('./renderer');
exports.Tag = require('./tag');

require('./console')引入同级目录下的console文件:

1
2
3
4
5
6
   ...部分代码省略
Console.prototype.get = function(name){
name = name.toLowerCase();
return this.store[this.alias[name]];
};
...部分代码省略

随后在Hexo构造函数中定义:

1
2
3
4
5
6
7
8
9
10
11
this.extend = {
console: new extend.Console(),
deployer: new extend.Deployer(),
filter: new extend.Filter(),
generator: new extend.Generator(),
helper: new extend.Helper(),
migrator: new extend.Migrator(),
processor: new extend.Processor(),
renderer: new extend.Renderer(),
tag: new extend.Tag()
};

所以我们看到了hexo.extend.console.get的流程,既然是获取,那么肯定有注册的过程

注册时在hexo.init()的时候注册的

1
2
3
4
5
6
7
8
9
  ...hexo_cli/lib/hexo.js中 找到hexo模块后调用此方法,部分代码省略
function(mod) {
if (mod) hexo = mod;
log = hexo.log;

require('./console')(hexo);

return hexo.init();
}
1
2
3
4
5
6
7
8
9
10
Hexo.prototype.init = function(){
var self = this;

this.log.debug('Hexo version: %s', chalk.magenta(this.version));
this.log.debug('Working directory: %s', chalk.magenta(tildify(this.base_dir)));

// Load internal plugins
//加载内部插件
require('../plugins/console')(this);
}

plugins/console/index.js部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//先获取在构造函数中定义的console对象
var console = ctx.extend.console;

...部分代码省略
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'));

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
Console.prototype.register = function(name, desc, options, fn){
if (!name) throw new TypeError('name is required');

if (!fn){
if (options){
if (typeof options === 'function'){
fn = options;

if (typeof desc === 'object'){ // name, options, fn
options = desc;
desc = '';
} else { // name, desc, fn
options = {};
}
} else {
throw new TypeError('fn must be a function');
}
} else {
// name, fn
if (typeof desc === 'function'){
fn = desc;
options = {};
desc = '';
} else {
throw new TypeError('fn must be a function');
}
}
}

if (fn.length > 1){
fn = Promise.promisify(fn);
} else {
fn = Promise.method(fn);
}

var c = this.store[name.toLowerCase()] = fn;
c.options = options;
c.desc = desc;
//new 变成n ne
this.alias = abbrev(Object.keys(this.store));
}

我们看看执行new的函数:

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
function newConsole(args){
/* jshint validthis: true */
// Display help message if user didn't input any arguments
if (!args._.length){
return this.call('help', {_: ['new']});
//控制台打出类似`Usage: hexo new [layout] <title>`
}

var data = {
title: args._.pop(),
layout: args._.length ? args._[0] : this.config.default_layout,
slug: args.s || args.slug,
path: args.p || args.path
};

var keys = Object.keys(args);
var key = '';
var self = this;

for (var i = 0, len = keys.length; i < len; i++){
key = keys[i];
if (!reservedKeys[key]) data[key] = args[key];
}

return this.post.create(data, args.r || args.replace).then(function(post){
self.log.info('Created: %s', chalk.magenta(tildify(post.path)));
});
}

jquery_1.2.6版本中的nth函数

发表于 2016-09-08 | 更新于 2019-05-09

jquery1.2.6 版本的 nth函数

jQuery.each

1
2
3
4
5
6
7
8
9
10
11
12
13
14
next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
prev: function(elem){return jQuery.nth(elem,2,"previousSibling");}

...省略部分代码
function(name, fn) {
jQuery.fn[ name ] = function( selector ) {
var ret = jQuery.map( this, fn );

if ( selector && typeof selector == "string" )
ret = jQuery.multiFilter( selector, ret );

return this.pushStack( jQuery.unique( ret ) );
};
}

jquery对象函数next,prev中都用到了jQuery的静态函数nth:

1
2
3
4
5
6
7
8
9
10
nth: function(cur,result,dir,elem){
result = result || 1;
var num = 0;

for ( ; cur; cur = cur[dir] )
if ( cur.nodeType == 1 && ++num == result )
break;

return cur;
}

jQuery.map

再来看看1.2.6版本的map函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
map: function( elems, callback ) {
var ret = [];

// Go through the array, translating each of the items to their
// new value (or values).
//调用callback函数返回新值
for ( var i = 0, length = elems.length; i < length; i++ ) {
var value = callback( elems[ i ], i );

if ( value != null )
ret[ ret.length ] = value;
}

return ret.concat.apply( [], ret );
}

jQuery.unique

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unique: function( array ) {
var ret = [], done = {};

try {

for ( var i = 0, length = array.length; i < length; i++ ) {
var id = jQuery.data( array[ i ] );

if ( !done[ id ] ) {
done[ id ] = true;
ret.push( array[ i ] );
}
}

} catch( e ) {
ret = array;
}

return ret;
}

jQuery.data

在each方法中用到了jQuery.data方法,我们来看看:

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
data: function( elem, name, data ) {
elem = elem == window ?
windowData :
elem;

var id = elem[ expando ];

// Compute a unique ID for the element
if ( !id )
id = elem[ expando ] = ++uuid;

// Only generate the data cache if we're
// trying to access or manipulate it
if ( name && !jQuery.cache[ id ] )
jQuery.cache[ id ] = {};

// Prevent overriding the named cache with undefined values
if ( data !== undefined )
jQuery.cache[ id ][ name ] = data;

// Return the named cache data, or the ID for the element
return name ?
jQuery.cache[ id ][ name ] :
id;
}

expando和uuid是这样定义的:

1
var expando = "jQuery" + now(), uuid = 0

jquery_parents

发表于 2016-09-07 | 更新于 2019-05-09

jquery1.0源码解读

jquery1.0 版本的 静态parents函数

jQuery.parents:

1
2
3
4
5
6
7
8
9
parents: function( elem ){
var matched = [];
var cur = elem.parentNode;
while ( cur && cur != document ) {
matched.push( cur );
cur = cur.parentNode;
}
return matched;
},

通过jQuery.macros.axis来调用:

jQuery.macros:

1
2
3
4
5
6
7
8
jQuery.each( jQuery.macros.axis, function(i,n){
jQuery.fn[ i ] = function(a) {
var ret = jQuery.map(this,n);
if ( a && a.constructor == String )
ret = jQuery.filter(a,ret).r;
return this.pushStack( ret, arguments );
};
});

###

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
jQuery.macros = {
...省略部分代码
axis: {

parent: "a.parentNode",

ancestors: jQuery.parents,

parents: jQuery.parents,

next: "jQuery.sibling(a).next",

prev: "jQuery.sibling(a).prev",

siblings: jQuery.sibling,

children: "a.childNodes"
}
...省略部分代码
}

而在1.2.6版本中,我们可以看到做出的变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	jQuery.each({
parent: function(elem){return elem.parentNode;},
parents: function(elem){return jQuery.dir(elem,"parentNode");},
next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
children: function(elem){return jQuery.sibling(elem.firstChild);},
contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
jQuery.fn[ name ] = function( selector ) {
var ret = jQuery.map( this, fn );

if ( selector && typeof selector == "string" )
ret = jQuery.multiFilter( selector, ret );

return this.pushStack( jQuery.unique( ret ) );
};
});

通过调用jQuery.dir静态函数:

jQuery.dir:

1
jQuery.dir(elem,"parentNode");

jQuery.dir函数如下,其实和1.0版本中的jQuery.parents大同小异:

1
2
3
4
5
6
7
8
9
10
dir: function( elem, dir ){
var matched = [],
cur = elem[dir];
while ( cur && cur != document ) {
if ( cur.nodeType == 1 )
matched.push( cur );
cur = cur[dir];
}
return matched;
}

下篇我们来分析为什么要做出这种改变?

1…394041…47

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%