hexo-cli 1.0.2源码解读
hexo入口函数中的Context
hexo.js中定义了context.js,用来实例化hexo对象。
1 | function Context(base, args) { |
hexo.js中定义了context.js,用来实例化hexo对象。
1 | function Context(base, args) { |
package.json中定义了1
2
3"bin": {
"hexo": "./bin/hexo"
}
所以到bin目录下的hexo1
2
3
4
5#!/usr/bin/env node
'use strict';
require('../lib/hexo')();
再到lib目录下的hexo.js,发现定义了module.exports= entry,那么找到entry方法
在没有找到hexo模块的情况下什么命令都不起作用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
65function entry(cwd, args) {
cwd = cwd || process.cwd();
//如果没有输入cwd那么就获取运行当前脚本的工作目录的路径
//ChangeWorkingDirectory cwd
//比如 hexo n 那么就是{ _: [ 'n' ] }
//hexo n --cwd e:\ss 就是{ _: [ 'help' ], cwd: 'e:\\ss' }
args = camelCaseKeys(args || minimist(process.argv.slice(2)));
//Context中注册了EventEmitter
var hexo = new Context(cwd, args);
var log = hexo.log;
// Change the title in console
process.title = 'hexo';
function handleError(err) {
log.fatal(err);
process.exit(2);
}
//findPkg其实就是检查package.json中是否有hexo
//并加载node_modules中的hexo 模块
return findPkg(cwd, args).then(function(path) {
if (!path) return;
hexo.base_dir = path;
//加载node_modules中的hexo模块
return loadModule(path, args).catch(function() {
log.error('Local hexo not found in %s', chalk.magenta(tildify(path)));
log.error('Try running: \'npm install hexo --save\'');
process.exit(2);
});
}).then(function(mod) {
if (mod) hexo = mod;
log = hexo.log;
require('./console')(hexo);
return hexo.init();
}).then(function() {
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';
}
watchSignal(hexo);
return hexo.call(cmd, args).then(function() {
return hexo.exit();
}).catch(function(err) {
return hexo.exit(err).then(function() {
handleError(err);
});
});
}).catch(handleError);
}
1.0源码: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
162expr: {
"": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()",
"#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]",
":": {
// Position Checks
lt: "i<m[3]-0",
gt: "i>m[3]-0",
nth: "m[3]-0==i",
eq: "m[3]-0==i",
first: "i==0",
last: "i==r.length-1",
even: "i%2==0",
odd: "i%2",
// Child Checks
"first-child": "jQuery.sibling(a,0).cur",
"last-child": "jQuery.sibling(a,0).last",
"only-child": "jQuery.sibling(a).length==1",
// Parent Checks
parent: "a.childNodes.length",
empty: "!a.childNodes.length",
// Text Check
contains: "(a.innerText||a.innerHTML).indexOf(m[3])>=0",
// Visibility
visible: "a.type!='hidden'&&jQuery.css(a,'display')!='none'&&jQuery.css(a,'visibility')!='hidden'",
hidden: "a.type=='hidden'||jQuery.css(a,'display')=='none'||jQuery.css(a,'visibility')=='hidden'",
// Form elements
enabled: "!a.disabled",
disabled: "a.disabled",
checked: "a.checked",
selected: "a.selected"
},
".": "jQuery.className.has(a,m[2])",
"@": {
"=": "z==m[4]",
"!=": "z!=m[4]",
"^=": "!z.indexOf(m[4])",
"$=": "z.substr(z.length - m[4].length,m[4].length)==m[4]",
"*=": "z.indexOf(m[4])>=0",
"": "z"
},
"[": "jQuery.find(m[2],a).length"
},
// The regular expressions that power the parsing engine
//正则提供解析能力
parse: [
// Match: [@value='test'], [@foo]
[ "\\[ *(@)S *([!*$^=]*) *Q\\]", 1 ],
// Match: [div], [div p]
[ "(\\[)Q\\]", 0 ],
// Match: :contains('foo') * 匹配前面的子表达式任意次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。
[ "(:)S\\(Q\\)", 0 ],//(:)([a-z*_-][a-z0-9_-]*)\\(*'?\"?([^'\"]*?)'?\"? *\\)
// Match: :even, :last-chlid
[ "([:.#]*)S", 0 ]
],
//t 一般为字符串
//r为 元素集合
filter: function(t,r,not) {
// Figure out if we're doing regular, or inverse, filtering
//not 为undefined的时候 not!==false返回true
//所以g = jQuery.grep
//grep静态函数第三个参数为true的话就返回不满足条件的新的元素
//t为:not(.example)的时候就会递归调用
//最终调用的是grep函数
var g = not !== false ? jQuery.grep :
function(a,f) {return jQuery.grep(a,f,true);};
//not(.example) 符合正则
while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
var p = jQuery.parse;//parse数组
for ( var i = 0; i < p.length; i++ ) {
var re = new RegExp( "^" + p[i][0]
// Look for a string-like sequence
.replace( 'S', "([a-z*_-][a-z0-9_-]*)" )
// Look for something (optionally) enclosed with quotes
.replace( 'Q', " *'?\"?([^'\"]*?)'?\"? *" ), "i" );
var m = re.exec( t );
//:not(.example)递归调用到这
//满足.example的就是[ "([:.#]*)S", 0 ]
if ( m ) {
// Re-organize the match
if ( p[i][1] )//Match: [@value='test'], [@foo] p[i][1] 为1
m = ["", m[1], m[3], m[2], m[4]];
// Remove what we just matched
t = t.replace( re, "" );//一般t就为空了,所以最后就跳出while循环
//.example的话 t就为空了
break;//这里就退出循环了
}
}
// :not() is a special case that can be optomized by
// keeping it out of the expression list
//:not(.example)
//m[0] = :not(.example)
//m[1] = :
//m[2] = not
//m[3] = .example
if ( m[1] == ":" && m[2] == "not" )
r = jQuery.filter(m[3],r,false).r;//递归下 到里面grep就是为包装grep的函数了
// Otherwise, find the expression to execute
//m[0] = .example
//m[1] = .
//m[2] = example
else {
//m[1]为.那么 在expr数组中就返回jQuery.className.has(a,m[2])
var f = jQuery.expr[m[1]];
if ( f.constructor != String )
f = jQuery.expr[m[1]][m[2]];
// Build a custom macro to enclose it
eval("f = function(a,i){" +
( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) +
"return " + f + "}");
// Execute it against the current filter
//f 为 function(a,i){
// return jQuery.className.has(a,m[2]) //m[2] 此时为'example'
//}
//再结合 g = function(a,f) {return jQuery.grep(a,f,true);};
//相当于jQuery.grep(r,f,true)
r = g( r, f );
}
}
// Return an array of filtered elements (r)
// and the modified expression string (t)
return { r: r, t: t };
},
className: {
add: function(o,c){
if (jQuery.className.has(o,c)) return;
o.className += ( o.className ? " " : "" ) + c;
},
remove: function(o,c){
o.className = !c ? "" :
o.className.replace(
new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), "");
},
has: function(e,a) {
if ( e.className != undefined )
e = e.className;//直接把e.className给e
//以a开头和结束
//或者a前面和后面是空格
return new RegExp("(^|\\s)" + a + "(\\s|$)").test(e);
}
},
1.0源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24map: function(elems, fn) {
// If a string is passed in for the function, make a function
// for it (a handy shortcut)
if ( fn.constructor == String )
fn = new Function("a","return " + fn);
var result = [];//初始化一个空数组
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0; i < elems.length; i++ ) {
var val = fn(elems[i],i);//调用参数fn 函数 返回fn中的新结果
//跟grep的函数区别就在于返回的是函数中新的返回值
//这就是映射
//而不是返回满足函数条件的旧元素
if ( val !== null && val != undefined ) {
if ( val.constructor != Array ) val = [val];
result = jQuery.merge( result, val );//合并
}
}
return result;
}
1.0源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23filter: function(t) {
//返回pushStack的结果
//这种 与加或 的方法值得学习
return this.pushStack(
//如果参数是数组 那么就调用map函数
//返回新的结果
t.constructor == Array &&
jQuery.map(this,function(a){
for ( var i = 0; i < t.length; i++ )
if ( jQuery.filter(t[i],[a]).r.length )
return a;
}) ||
//如果参数是true那么就直接get()获取
//如果参数是false那么就返回空数组
t.constructor == Boolean &&
( t ? this.get() : [] ) ||
//如果参数是函数那么就调用grep函数
t.constructor == Function &&
jQuery.grep( this, t ) ||
//如果参数是其他类型那么就调用静态的filter函数
jQuery.filter(t,this).r, arguments );
}
1 | grep: function(elems, fn, inv) { |
比如1
2
3
4
5
6var elems = $('li');
elems.filter(function(index) {
//index就是elems的一个元素
return $("strong",index).length == 1;//
}).css('background-color', 'red');
jQuery的静态方法是通过extent扩展方法生成的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function jQuery(){
}
jQuery.extend = function(obj,prop) {
//巧妙的运用了obj =this;转换了角色
//obj就变成jQuery
//prop就变成了obj
if ( !prop ) { prop = obj; obj = this; }
for ( var i in prop ) obj[i] = prop[i];
return obj;
};
//jQuery静态方法就可以通过如下方法引入
jQuery.extend({
})