john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

hexo_postname

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

new_post_path.js

hexo的文章生成名称经过newPostFilter这个过滤器来生成的:

1
2
3
4
ctx.execFilter('new_post_path', data, {
args: [replace],
context: ctx
})

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
function newPostPathFilter(data, replace){
/* jshint validthis: true */
data = data || {};//data里带有文章标题

var sourceDir = this.source_dir;//this指向hexo实例
//this.source_dir = pathFn.join(base, 'source') + sep; hexo/index.js
var draftDir = pathFn.join(sourceDir, '_drafts');
var postDir = pathFn.join(sourceDir, '_posts');
var config = this.config;
var newPostName = config.new_post_name;//默认为:title.md
var permalinkDefaults = config.permalink_defaults;
var path = data.path;//默认为空
var layout = data.layout;//默认为空
var slug = data.slug;//文章标题
var target = '';

if (!permalink || permalink.rule !== newPostName){
permalink = new Permalink(newPostName); //固定链接 :title.md
}

if (path){
switch (layout){
case 'page':
target = pathFn.join(sourceDir, path);
break;

case 'draft':
target = pathFn.join(draftDir, path);
break;

default:
target = pathFn.join(postDir, path);
}
} else if (slug){
switch (layout){
case 'page':
target = pathFn.join(sourceDir, slug, 'index');
break;

case 'draft':
target = pathFn.join(draftDir, slug);
break;

default:
var date = moment(data.date || Date.now());//moment库
var keys = Object.keys(data);
var key = '';

var filenameData = {
year: date.format('YYYY'),
month: date.format('MM'),
i_month: date.format('M'),
day: date.format('DD'),
i_day: date.format('D'),
title: slug
};

for (var i = 0, len = keys.length; i < len; i++){
key = keys[i];
if (!reservedKeys[key]) filenameData[key] = data[key];
}
//console.log('defaults:'+ _.defaults(filenameData, permalinkDefaults).title);
//console.log('postDir:'+postDir);

//permalink.stringify 正则替换把permalink中的title替换为filenamedata中的titile,就是文章标题
//_.defaults lodash库
//Assigns own enumerable properties of source object(s) to the destination object for all destination properties that resolve to undefined. Once a property is set, additional values of the same property are ignored.

target = pathFn.join(postDir, permalink.stringify(
_.defaults(filenameData, permalinkDefaults)));
}
} else {
return Promise.reject(new TypeError('Either data.path or data.slug is required!'));
}

if (!pathFn.extname(target)){
target += pathFn.extname(newPostName) || '.md';
}

if (replace){
return Promise.resolve(target);
} else {
return fs.ensurePath(target);
}
}

hexo-fs/lib/fs.js

1
2
3
4
5
6
7
8
9
10
11
function ensurePath(path, callback){
if (!path) throw new TypeError('path is required!');

return exists(path).then(function(exist){
if (!exist) return path;

return readdirAsync(dirname(path)).then(function(files){
return _findUnusedPath(path, files);
});
}).nodeify(callback);
}
1
2
3
4
5
6
7
8
9
10
function exists(path, callback){
if (!path) throw new TypeError('path is required!');

return new Promise(function(resolve, reject){
fs.exists(path, function(exist){
resolve(exist);
if (typeof callback === 'function') callback(exist);
});
});
}

最终文件名的秘密就在这里,相同的文件名就自动加上数字

_findUnusedPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function _findUnusedPath(path, files){
var extname = pathFn.extname(path);
var basename = pathFn.basename(path, extname);
var regex = new RegExp('^' + escapeRegExp(basename) + '(?:-(\\d+))?' + escapeRegExp(extname) + '$');
var item = '';
var num = -1;
var match, matchNum;

for (var i = 0, len = files.length; i < len; i++){
item = files[i];
if (!regex.test(item)) continue;

match = item.match(regex);
matchNum = match[1] ? parseInt(match[1], 10) : 0;

if (matchNum > num){
num = matchNum;
}
}

return join(dirname(path), basename + '-' + (num + 1) + extname);
}

hexo_util工具包分析1

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

hexo util包分析

index.js

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

exports.escapeDiacritic = require('./escape_diacritic');
exports.escapeHTML = require('./escape_html');
exports.escapeRegExp = require('./escape_regexp');
exports.highlight = require('./highlight');
exports.htmlTag = require('./html_tag');
exports.Pattern = require('./pattern');
exports.Permalink = require('./permalink');
exports.slugize = require('./slugize');
exports.spawn = require('./spawn');
exports.stripHTML = require('./strip_html');
exports.truncate = require('./truncate');
exports.wordWrap = require('./word_wrap');

导出了这么多工具类,我们先看slugize,因为关系到post创建的过程

1
2
3
4
5
6
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();
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
'use strict';
//最重要的就是这两个函数escapeDiacritic和escapeRegExp
var escapeDiacritic = require('./escape_diacritic');
var escapeRegExp = require('./escape_regexp');
var rControl = /[\u0000-\u001f]/g;
var rSpecial = /[\s~`!@#\$%\^&\*\(\)\-_\+=\[\]\{\}\|\\;:"'<>,\.\?\/]+/g;

module.exports = function(str, options){
if (typeof str !== 'string') throw new TypeError('str must be a string!');
options = options || {};

var separator = options.separator || '-';
var escapedSep = escapeRegExp(separator);// 输出\-

var result = escapeDiacritic(str)
// Remove control characters
.replace(rControl, '')
// Replace special characters
.replace(rSpecial, separator)
// Remove continous separators
.replace(new RegExp(escapedSep + '{2,}', 'g'), separator)
//n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
// Remove prefixing and trailing separtors
.replace(new RegExp('^' + escapedSep + '+|' + escapedSep + '+$', 'g'), '');

switch (options.transform){
case 1:
return result.toLowerCase();

case 2:
return result.toUpperCase();

default:
return result;
}
};

bluebird的promiseall浅析

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

Promise.all

hexo中load方法使用了Promise.all,来看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
};
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
var Promise = require('bluebird');

var call = function(name, args, callback){
if (!callback && typeof args === 'function'){
callback = args;
args = {};
}

return Promise.all([
(function(){
console.log('1');//这里会打印
//var i = '5';
})(),
(function(){
//console.log('2');//这里不会打印

throw '2';
})(),
(function(){
console.log('6');//这里不会打印
})()
]).then(function(){//数组里的都通过才会打印3
console.log('3');
}).nodeify(callback);
};

call('new', '2',function(e){
console.log('nodeify');
console.log(e);
console.log('callback'); //进入reject会调用callback

//throw ;
}).then(function() {
console.log('then');
}).catch(function(err) {
console.log('end');
console.log(err);//进入reject会调用
});

bluebird Promise框架学习一

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

hexo中的Promise用了bluebird提供的,比如Promise.each,Promise.all,new Promise,Promise.map 等等。

Promise.each

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Promise = require('bluebird');

Promise.each([
'update_package', // Update package.json
'load_config', // Load config
'load_plugins' // Load external plugins & scripts
], function(name){
if(name == 'load_config')
throw 'error';
console.log(name);//会输出update_package,其余不会输出
}).catch(function(e){

console.log(e);
});
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
var Promise = require('bluebird');

var call = function(name, args, callback){
if (!callback && typeof args === 'function'){
callback = args;
args = {};
}

return new Promise(function(resolve, reject){
var c = 1;

if (!c){
console.log('done');
} else {
//进入reject
reject(new Error('Console `` has not been registered yet!'));
}
}).nodeify(callback);
};

call('new', '2',function(e){
console.log(e);
console.log('callback'); //进入reject会调用callback
}).then(function() {
console.log('then');
}).catch(function(err) {
console.log(err);//进入reject会调用
});

hexo post.create的过程分析

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

hexo的post创建过程

要讲hexo的post.create先要看它的传入参数。

1
this.post.create(data, args.r || args.replace)

data定义如下:

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

hexo/post.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
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
Post.prototype.create = function(data, replace, callback){
//callback没传入 replace 为undefined
if (!callback && typeof replace === 'function'){
callback = replace;
replace = false;
}

var ctx = this.context;//为hexo对象
var config = ctx.config; //_.clone(defaultConfig) _:lodash库 浅拷贝
//https://github.com/lodash

//slugize为hexo_util库中的函数
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();//获取当前时间 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(){

//http://biyeah.iteye.com/blog/1308954
ctx.emit('new', result);
}).thenReturn(result);
}).nodeify(callback);
}

1
2
3
4
ctx.execFilter('new_post_path', data, {
args: [replace],
context: ctx
})

执行new_post_path类型的过滤器,hexo/index.js代码如下:

1
2
3
Hexo.prototype.execFilter = function(type, data, options){
return this.extend.filter.exec(type, data, options);
};

路径hexo/extend/filter.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Filter.prototype.exec = function(type, data, options){
options = options || {};

var filters = this.list(type);
var ctx = options.context;//hexo实例对象
var args = options.args || []; //上面的[replace]

args.unshift(data);

return Promise.each(filters, function(filter){
return Promise.method(filter).apply(ctx, args).then(function(result){
args[0] = result == null ? data : result;
return args[0];
});
}).then(function(){
return args[0];
});
};

这里我们执行的是new_post_path的filter:
文件路径为hexo/lib/plugins/filter/new_post_path.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
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
function newPostPathFilter(data, replace){
/* jshint validthis: true */
data = data || {};

var sourceDir = this.source_dir;
var draftDir = pathFn.join(sourceDir, '_drafts');
var postDir = pathFn.join(sourceDir, '_posts');
var config = this.config;
var newPostName = config.new_post_name;
var permalinkDefaults = config.permalink_defaults;
var path = data.path;
var layout = data.layout;
var slug = data.slug;
var target = '';

if (!permalink || permalink.rule !== newPostName){
permalink = new Permalink(newPostName);
}
//path为undefined
if (path){
switch (layout){
case 'page':
target = pathFn.join(sourceDir, path);
break;

case 'draft':
target = pathFn.join(draftDir, path);
break;

default:
target = pathFn.join(postDir, path);
}
} else if (slug){
switch (layout){
case 'page':
target = pathFn.join(sourceDir, slug, 'index');
break;

case 'draft':
target = pathFn.join(draftDir, slug);
break;

default:
var date = moment(data.date || Date.now());
var keys = Object.keys(data);
var key = '';

var filenameData = {
year: date.format('YYYY'),
month: date.format('MM'),
i_month: date.format('M'),
day: date.format('DD'),
i_day: date.format('D'),
title: slug
};

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

target = pathFn.join(postDir, permalink.stringify(
_.defaults(filenameData, permalinkDefaults)));
}
} else {
return Promise.reject(new TypeError('Either data.path or data.slug is required!'));
}

if (!pathFn.extname(target)){
target += pathFn.extname(newPostName) || '.md';
}

if (replace){
return Promise.resolve(target);
} else {
return fs.ensurePath(target);
}
}

1…383940…47

John

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