diff --git a/README.md b/README.md index da1f2e5678..8d3f8b4fc5 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,10 @@ $ hexo generate ## More Information -- Read the [documentation](http://hexo.io/) -- Find solutions in [troubleshooting](http://hexo.io/docs/troubleshooting.html) +- Read the [documentation](https://hexo.io/) +- Find solutions in [troubleshooting](https://hexo.io/docs/troubleshooting.html) - Join discussion on [Google Group](https://groups.google.com/group/hexo) -- See the [plugin list](https://github.com/hexojs/hexo/wiki/Plugins) and the [theme list](https://github.com/hexojs/hexo/wiki/Themes) on wiki +- See the [plugin list](https://hexo.io/plugins/) and the [theme list](https://hexo.io/themes/) on wiki - Follow [@hexojs](https://twitter.com/hexojs) for latest news ## License diff --git a/lib/plugins/generator/page.js b/lib/plugins/generator/page.js index 6ea17c02c8..fe96db5a09 100644 --- a/lib/plugins/generator/page.js +++ b/lib/plugins/generator/page.js @@ -1,35 +1,11 @@ 'use strict'; -var minimatch = require('minimatch'); -var _ = require('lodash'); - function pageGenerator(locals) { - var skipRender = this.config.skip_render || []; - if (!Array.isArray(skipRender)) skipRender = [skipRender]; - skipRender = _.compact(skipRender); - - var skipRenderLen = skipRender.length; - - function isSkipRender(path) { - if (!skipRenderLen) return false; - - for (var i = 0; i < skipRenderLen; i++) { - if (minimatch(path, skipRender[i])) return true; - } - - return false; - } - return locals.pages.map(function(page) { var layout = page.layout; var path = page.path; - if (isSkipRender(page.source)) { - return { - path: page.source, - data: page.raw - }; - } else if (!layout || layout === 'false') { + if (!layout || layout === 'false' || layout === 'off') { return { path: path, data: page.content diff --git a/lib/plugins/processor/asset.js b/lib/plugins/processor/asset.js index 4fb70e21f2..495618376f 100644 --- a/lib/plugins/processor/asset.js +++ b/lib/plugins/processor/asset.js @@ -4,113 +4,128 @@ var common = require('./common'); var Promise = require('bluebird'); var yfm = require('hexo-front-matter'); var pathFn = require('path'); +var util = require('hexo-util'); +var Pattern = util.Pattern; + +module.exports = function(ctx) { + function processPage(file) { + var Page = ctx.model('Page'); + var path = file.path; + var doc = Page.findOne({source: path}); + var config = ctx.config; + var timezone = config.timezone; + + if (file.type === 'delete') { + if (doc) { + return doc.remove(); + } -exports.process = function(file) { - if (this.render.isRenderable(file.path)) { - return processPage.call(this, file); - } - - return processAsset.call(this, file); -}; - -exports.pattern = common.ignoreTmpAndHiddenFile; - -function processPage(file) { - var Page = this.model('Page'); - var path = file.path; - var doc = Page.findOne({source: path}); - var self = this; - var config = this.config; - var timezone = config.timezone; - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); + return; } - return; - } + return file.changed().then(function(changed) { + if (!changed && doc) return; - return file.changed().then(function(changed) { - if (!changed && doc) return; + return Promise.all([ + file.stat(), + file.read() + ]).spread(function(stats, content) { + var data = yfm(content); + var output = ctx.render.getOutput(path); - return Promise.all([ - file.stat(), - file.read() - ]).spread(function(stats, content) { - var data = yfm(content); - var output = self.render.getOutput(path); + data.source = path; + data.raw = content; - data.source = path; - data.raw = content; + data.date = common.toDate(data.date); - data.date = common.toDate(data.date); + if (data.date) { + if (timezone) data.date = common.timezone(data.date, timezone); + } else { + data.date = stats.ctime; + } - if (data.date) { - if (timezone) data.date = common.timezone(data.date, timezone); - } else { - data.date = stats.ctime; - } + data.updated = common.toDate(data.updated); - data.updated = common.toDate(data.updated); + if (data.updated) { + if (timezone) data.updated = common.timezone(data.updated, timezone); + } else { + data.updated = stats.mtime; + } - if (data.updated) { - if (timezone) data.updated = common.timezone(data.updated, timezone); - } else { - data.updated = stats.mtime; - } + if (data.permalink) { + data.path = data.permalink; + delete data.permalink; - if (data.permalink) { - data.path = data.permalink; - delete data.permalink; + if (data.path[data.path.length - 1] === '/') { + data.path += 'index'; + } - if (data.path[data.path.length - 1] === '/') { - data.path += 'index'; + if (!pathFn.extname(data.path)) { + data.path += '.' + output; + } + } else { + var extname = pathFn.extname(path); + data.path = path.substring(0, path.length - extname.length) + '.' + output; } - if (!pathFn.extname(data.path)) { - data.path += '.' + output; + if (!data.layout && output !== 'html' && output !== 'htm') { + data.layout = false; } - } else { - var extname = pathFn.extname(path); - data.path = path.substring(0, path.length - extname.length) + '.' + output; - } - if (!data.layout && output !== 'html' && output !== 'htm') { - data.layout = false; - } + // FIXME: Data may be inserted when reading files. Load it again to prevent + // race condition. We have to solve this in warehouse. + var doc = Page.findOne({source: path}); - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - var doc = Page.findOne({source: path}); + if (doc) { + return doc.replace(data); + } + return Page.insert(data); + }); + }); + } + + function processAsset(file) { + var id = file.source.substring(ctx.base_dir.length).replace(/\\/g, '/'); + var Asset = ctx.model('Asset'); + var doc = Asset.findById(id); + + if (file.type === 'delete') { if (doc) { - return doc.replace(data); + return doc.remove(); } - return Page.insert(data); + return; + } + + return file.changed().then(function(changed) { + return Asset.save({ + _id: id, + path: file.path, + modified: changed + }); }); - }); -} + } -function processAsset(file) { - var id = file.source.substring(this.base_dir.length).replace(/\\/g, '/'); - var Asset = this.model('Asset'); - var doc = Asset.findById(id); + return { + pattern: new Pattern(function(path) { + if (common.isTmpFile(path) || common.isMatch(path, ctx.config.exclude)) return; - if (file.type === 'delete') { - if (doc) { - return doc.remove(); - } + if (common.isHiddenFile(path) && !common.isMatch(path, ctx.config.include)) { + return; + } - return; - } + return { + renderable: ctx.render.isRenderable(path) && !common.isMatch(path, ctx.config.skip_render) + }; + }), - return file.changed().then(function(changed) { - return Asset.save({ - _id: id, - path: file.path, - modified: changed - }); - }); -} + process: function assetProcessor(file) { + if (file.params.renderable) { + return processPage(file); + } + + return processAsset(file); + } + }; +}; diff --git a/lib/plugins/processor/common.js b/lib/plugins/processor/common.js index 64eeb582dc..3069efc418 100644 --- a/lib/plugins/processor/common.js +++ b/lib/plugins/processor/common.js @@ -2,6 +2,8 @@ var Pattern = require('hexo-util').Pattern; var moment = require('moment-timezone'); +var minimatch = require('minimatch'); +var _ = require('lodash'); var DURATION_MINUTE = 1000 * 60; @@ -11,8 +13,7 @@ function isTmpFile(path) { } function isHiddenFile(path) { - if (path[0] === '_') return true; - return /\/_/.test(path); + return /(^|\/)[_\.]/.test(path); } exports.ignoreTmpAndHiddenFile = new Pattern(function(path) { @@ -45,3 +46,17 @@ exports.timezone = function(date, timezone) { return new Date(ms - diff); }; + +exports.isMatch = function(path, patterns) { + if (!patterns) return false; + if (!Array.isArray(patterns)) patterns = [patterns]; + + patterns = _.compact(patterns); + if (!patterns.length) return false; + + for (var i = 0, len = patterns.length; i < len; i++) { + if (minimatch(path, patterns[i])) return true; + } + + return false; +}; diff --git a/lib/plugins/processor/data.js b/lib/plugins/processor/data.js index d76209292c..f1ff3f0214 100644 --- a/lib/plugins/processor/data.js +++ b/lib/plugins/processor/data.js @@ -4,33 +4,36 @@ var util = require('hexo-util'); var pathFn = require('path'); var Pattern = util.Pattern; -exports.process = function(file) { - var Data = this.model('Data'); - var path = file.params.path; - var extname = pathFn.extname(path); - var id = path.substring(0, path.length - extname.length); - var doc = Data.findById(id); - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); +module.exports = function(ctx) { + return { + pattern: new Pattern('_data/*path'), + process: function dataProcessor(file) { + var Data = ctx.model('Data'); + var path = file.params.path; + var extname = pathFn.extname(path); + var id = path.substring(0, path.length - extname.length); + var doc = Data.findById(id); + + if (file.type === 'delete') { + if (doc) { + return doc.remove(); + } + + return; + } + + return file.changed().then(function(changed) { + if (!changed && doc) return; + + return file.render(); + }).then(function(result) { + if (result == null) return; + + return Data.save({ + _id: id, + data: result + }); + }); } - - return; - } - - return file.changed().then(function(changed) { - if (!changed && doc) return; - - return file.render(); - }).then(function(result) { - if (result == null) return; - - return Data.save({ - _id: id, - data: result - }); - }); + }; }; - -exports.pattern = new Pattern('_data/*path'); diff --git a/lib/plugins/processor/index.js b/lib/plugins/processor/index.js index 7b42c77462..ded6a9197f 100644 --- a/lib/plugins/processor/index.js +++ b/lib/plugins/processor/index.js @@ -4,7 +4,7 @@ module.exports = function(ctx) { var processor = ctx.extend.processor; function register(name) { - var obj = require('./' + name); + var obj = require('./' + name)(ctx); processor.register(obj.pattern, obj.process); } diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index c199f0ffc7..fd98f1eefb 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -5,6 +5,7 @@ var Promise = require('bluebird'); var yfm = require('hexo-front-matter'); var pathFn = require('path'); var fs = require('hexo-fs'); +var _ = require('lodash'); var util = require('hexo-util'); var slugize = util.slugize; var Pattern = util.Pattern; @@ -23,161 +24,229 @@ var preservedKeys = { i_day: true }; -function startsWith(str, prefix) { - return str.substring(0, prefix.length) === prefix; -} - -exports.process = function(file) { - if (this.render.isRenderable(file.path)) { - return processPost.call(this, file); - } else if (this.config.post_asset_folder) { - return processAsset.call(this, file); - } -}; - -exports.pattern = new Pattern(function(path) { - if (common.isTmpFile(path)) return false; - - var result; - - if (startsWith(path, postDir)) { - result = { - published: true, - path: path.substring(postDir.length) - }; - } else if (startsWith(path, draftDir)) { - result = { - published: false, - path: path.substring(draftDir.length) - }; - } else { - return false; - } - - if (common.isHiddenFile(result.path)) return false; - return result; -}); - -function processPost(file) { - var Post = this.model('Post'); - var path = file.params.path; - var doc = Post.findOne({source: file.path}); - var self = this; - var config = this.config; - var timezone = config.timezone; - var categories, tags; - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); - } - - return; - } +module.exports = function(ctx) { + function processPost(file) { + var Post = ctx.model('Post'); + var path = file.params.path; + var doc = Post.findOne({source: file.path}); + var config = ctx.config; + var timezone = config.timezone; + var categories, tags; - return file.changed().then(function(changed) { - if (!changed && doc) return; - - return Promise.all([ - file.stat(), - file.read() - ]).spread(function(stats, content) { - var data = yfm(content); - var info = parseFilename(config.new_post_name, path); - var keys = Object.keys(info); - var key; - - data.source = file.path; - data.raw = content; - data.slug = info.title; - - if (file.params.published) { - if (!data.hasOwnProperty('published')) data.published = true; - } else { - data.published = false; - } - - for (var i = 0, len = keys.length; i < len; i++) { - key = keys[i]; - if (!preservedKeys[key]) data[key] = info[key]; + if (file.type === 'delete') { + if (doc) { + return doc.remove(); } - if (data.date) { - data.date = common.toDate(data.date); - } else if (info && info.year && (info.month || info.i_month) && (info.day || info.i_day)) { - data.date = new Date( - info.year, - parseInt(info.month || info.i_month, 10) - 1, - parseInt(info.day || info.i_day, 10) - ); - } + return; + } - if (data.date) { - if (timezone) data.date = common.timezone(data.date, timezone); - } else { - data.date = stats.birthtime; - } + return file.changed().then(function(changed) { + if (!changed && doc) return; - data.updated = common.toDate(data.updated); + return Promise.all([ + file.stat(), + file.read() + ]).spread(function(stats, content) { + var data = yfm(content); + var info = parseFilename(config.new_post_name, path); + var keys = Object.keys(info); + var key; + + data.source = file.path; + data.raw = content; + data.slug = info.title; + + if (file.params.published) { + if (!data.hasOwnProperty('published')) data.published = true; + } else { + data.published = false; + } + + for (var i = 0, len = keys.length; i < len; i++) { + key = keys[i]; + if (!preservedKeys[key]) data[key] = info[key]; + } + + if (data.date) { + data.date = common.toDate(data.date); + } else if (info && info.year && (info.month || info.i_month) && (info.day || info.i_day)) { + data.date = new Date( + info.year, + parseInt(info.month || info.i_month, 10) - 1, + parseInt(info.day || info.i_day, 10) + ); + } + + if (data.date) { + if (timezone) data.date = common.timezone(data.date, timezone); + } else { + data.date = stats.birthtime; + } + + data.updated = common.toDate(data.updated); + + if (data.updated) { + if (timezone) data.updated = common.timezone(data.updated, timezone); + } else { + data.updated = stats.mtime; + } + + if (data.category && !data.categories) { + data.categories = data.category; + delete data.category; + } + + if (data.tag && !data.tags) { + data.tags = data.tag; + delete data.tag; + } + + categories = data.categories || []; + tags = data.tags || []; + + if (!Array.isArray(categories)) categories = [categories]; + if (!Array.isArray(tags)) tags = [tags]; + + if (data.photo && !data.photos) { + data.photos = data.photo; + delete data.photo; + } + + if (data.photos && !Array.isArray(data.photos)) { + data.photos = [data.photos]; + } + + if (data.link && !data.title) { + data.title = data.link.replace(/^https?:\/\/|\/$/g, ''); + } + + if (data.permalink) { + data.slug = data.permalink; + delete data.permalink; + } + + // FIXME: Data may be inserted when reading files. Load it again to prevent + // race condition. We have to solve this in warehouse. + var doc = Post.findOne({source: file.path}); + + if (doc) { + return doc.replace(data); + } + + return Post.insert(data); + }).then(function(doc) { + return Promise.all([ + doc.setCategories(categories), + doc.setTags(tags), + scanAssetDir(doc) + ]); + }); + }); + } - if (data.updated) { - if (timezone) data.updated = common.timezone(data.updated, timezone); - } else { - data.updated = stats.mtime; - } + function scanAssetDir(post) { + if (!ctx.config.post_asset_folder) return; + + var assetDir = post.asset_dir; + var baseDir = ctx.base_dir; + var baseDirLength = baseDir.length; + var PostAsset = ctx.model('PostAsset'); + + return fs.stat(assetDir).then(function(stats) { + if (!stats.isDirectory()) return []; + + return fs.listDir(assetDir); + }).catch(function(err) { + if (err.cause && err.cause.code === 'ENOENT') return []; + throw err; + }).filter(function(item) { + return !common.isTmpFile(item) && !common.isHiddenFile(item); + }).map(function(item) { + var id = pathFn.join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/'); + var asset = PostAsset.findById(id); + if (asset) return; + + return PostAsset.save({ + _id: id, + post: post._id, + slug: item, + modified: true + }); + }); + } - if (data.category && !data.categories) { - data.categories = data.category; - delete data.category; - } + function processAsset(file) { + var PostAsset = ctx.model('PostAsset'); + var Post = ctx.model('Post'); + var id = file.source.substring(ctx.base_dir.length).replace(/\\/g, '/'); + var doc = PostAsset.findById(id); - if (data.tag && !data.tags) { - data.tags = data.tag; - delete data.tag; + if (file.type === 'delete') { + if (doc) { + return doc.remove(); } - categories = data.categories || []; - tags = data.tags || []; - - if (!Array.isArray(categories)) categories = [categories]; - if (!Array.isArray(tags)) tags = [tags]; + return; + } - if (data.photo && !data.photos) { - data.photos = data.photo; - delete data.photo; + return file.changed().then(function(changed) { + // TODO: Better post searching + var posts = Post.toArray(); + var post; + + for (var i = 0, len = posts.length; i < len; i++) { + post = posts[i]; + + if (_.startsWith(file.source, post.asset_dir)) { + return PostAsset.save({ + _id: id, + slug: file.source.substring(post.asset_dir.length), + post: post._id, + modified: changed + }); + } } - if (data.photos && !Array.isArray(data.photos)) { - data.photos = [data.photos]; + if (doc) { + return doc.remove(); } + }); + } - if (data.link && !data.title) { - data.title = data.link.replace(/^https?:\/\/|\/$/g, ''); + return { + pattern: new Pattern(function(path) { + if (common.isTmpFile(path)) return; + + var result; + + if (_.startsWith(path, postDir)) { + result = { + published: true, + path: path.substring(postDir.length) + }; + } else if (_.startsWith(path, draftDir)) { + result = { + published: false, + path: path.substring(draftDir.length) + }; } - if (data.permalink) { - data.slug = data.permalink; - delete data.permalink; - } + if (!result || common.isHiddenFile(result.path)) return; - // FIXME: Data may be inserted when reading files. Load it again to prevent - // race condition. We have to solve this in warehouse. - var doc = Post.findOne({source: file.path}); + result.renderable = ctx.render.isRenderable(path) && !common.isMatch(path, ctx.config.skip_render); + return result; + }), - if (doc) { - return doc.replace(data); + process: function postProcessor(file) { + if (file.params.renderable) { + return processPost(file); + } else if (ctx.config.post_asset_folder) { + return processAsset(file); } - - return Post.insert(data); - }).then(function(doc) { - return Promise.all([ - doc.setCategories(categories), - doc.setTags(tags), - scanAssetDir.call(self, doc) - ]); - }); - }); -} + } + }; +}; function parseFilename(config, path) { config = config.substring(0, config.length - pathFn.extname(config).length); @@ -205,72 +274,3 @@ function parseFilename(config, path) { title: slugize(path) }; } - -function scanAssetDir(post) { - if (!this.config.post_asset_folder) return; - - var assetDir = post.asset_dir; - var baseDir = this.base_dir; - var baseDirLength = baseDir.length; - var PostAsset = this.model('PostAsset'); - - return fs.stat(assetDir).then(function(stats) { - if (!stats.isDirectory()) return []; - - return fs.listDir(assetDir); - }).catch(function(err) { - if (err.cause && err.cause.code === 'ENOENT') return []; - throw err; - }).filter(function(item) { - return !common.isTmpFile(item) && !common.isHiddenFile(item); - }).map(function(item) { - var id = pathFn.join(assetDir, item).substring(baseDirLength).replace(/\\/g, '/'); - var asset = PostAsset.findById(id); - if (asset) return; - - return PostAsset.save({ - _id: id, - post: post._id, - slug: item, - modified: true - }); - }); -} - -function processAsset(file) { - var PostAsset = this.model('PostAsset'); - var Post = this.model('Post'); - var id = file.source.substring(this.base_dir.length).replace(/\\/g, '/'); - var doc = PostAsset.findById(id); - - if (file.type === 'delete') { - if (doc) { - return doc.remove(); - } - - return; - } - - return file.changed().then(function(changed) { - // TODO: Better post searching - var posts = Post.toArray(); - var post; - - for (var i = 0, len = posts.length; i < len; i++) { - post = posts[i]; - - if (startsWith(file.source, post.asset_dir)) { - return PostAsset.save({ - _id: id, - slug: file.source.substring(post.asset_dir.length), - post: post._id, - modified: changed - }); - } - } - - if (doc) { - return doc.remove(); - } - }); -} diff --git a/lib/theme/processors/source.js b/lib/theme/processors/source.js index a306686f8f..fe463b7ad2 100644 --- a/lib/theme/processors/source.js +++ b/lib/theme/processors/source.js @@ -1,12 +1,9 @@ 'use strict'; +var _ = require('lodash'); var Pattern = require('hexo-util').Pattern; var common = require('../../plugins/processor/common'); -function startsWith(str, prefix){ - return str.substring(0, prefix.length) === prefix; -} - exports.process = function(file) { var Asset = this.model('Asset'); var id = file.source.substring(this.base_dir.length).replace(/\\/g, '/'); @@ -31,7 +28,7 @@ exports.process = function(file) { }; exports.pattern = new Pattern(function(path) { - if (!startsWith(path, 'source/')) return false; + if (!_.startsWith(path, 'source/')) return false; path = path.substring(7); if (common.isHiddenFile(path) || common.isTmpFile(path) || ~path.indexOf('node_modules')) return false; diff --git a/test/scripts/generators/page.js b/test/scripts/generators/page.js index 66a2104d5b..81fe9e6f41 100644 --- a/test/scripts/generators/page.js +++ b/test/scripts/generators/page.js @@ -49,76 +49,18 @@ describe('page', function() { }); }); - it('layout disabled', function() { - return Page.insert({ - source: 'foo', - path: 'bar', - layout: false - }).then(function(page) { - return generator(locals()).then(function(data) { - should.not.exist(data[0].layout); - - return page.remove(); - }); - }); - }); - - it('skip render', function() { - hexo.config.skip_render = 'lab/**/*.js'; - - return Page.insert({ - source: 'lab/assets/jquery.min.js', - path: 'lab/assets/jquery.min.js', - layout: false, - raw: 'jquery raw' - }).then(function(page) { - return generator(locals()).then(function(data) { - data.should.eql([ - {path: page.source, data: page.raw} - ]); - - hexo.config.skip_render = []; - return page.remove(); - }); - }); - }); - - it('skip render - multiple rules', function() { - hexo.config.skip_render = ['lab/**/*.js']; - - return Page.insert({ - source: 'lab/assets/jquery.min.js', - path: 'lab/assets/jquery.min.js', - layout: false, - raw: 'jquery raw' - }).then(function(page) { - return generator(locals()).then(function(data) { - data.should.eql([ - {path: page.source, data: page.raw} - ]); - - hexo.config.skip_render = []; - return page.remove(); - }); - }); - }); - - it('skip render - don\'t replace extension name', function() { - hexo.config.skip_render = 'README.md'; - - return Page.insert({ - source: 'README.md', - path: 'README.html', - layout: 'page', - raw: 'readme raw' - }).then(function(page) { - return generator(locals()).then(function(data) { - data.should.eql([ - {path: page.source, data: page.raw} - ]); - - hexo.config.skip_render = []; - return page.remove(); + [false, 'false', 'off'].forEach(function(layout) { + it('layout = ' + JSON.stringify(layout), function() { + return Page.insert({ + source: 'foo', + path: 'bar', + layout: layout + }).then(function(page) { + return generator(locals()).then(function(data) { + should.not.exist(data[0].layout); + }).finally(function() { + return page.remove(); + }); }); }); }); diff --git a/test/scripts/processors/asset.js b/test/scripts/processors/asset.js index 1e3b53ca4b..08f2f7b2de 100644 --- a/test/scripts/processors/asset.js +++ b/test/scripts/processors/asset.js @@ -11,8 +11,9 @@ describe('asset', function() { var Hexo = require('../../../lib/hexo'); var baseDir = pathFn.join(__dirname, 'asset_test'); var hexo = new Hexo(baseDir); - var asset = require('../../../lib/plugins/processor/asset'); + var asset = require('../../../lib/plugins/processor/asset')(hexo); var process = asset.process.bind(hexo); + var pattern = asset.pattern; var source = hexo.source; var File = source.File; var Asset = hexo.model('Asset'); @@ -20,6 +21,10 @@ describe('asset', function() { function newFile(options) { options.source = pathFn.join(source.base, options.path); + options.params = { + renderable: options.renderable + }; + return new File(options); } @@ -33,10 +38,44 @@ describe('asset', function() { return fs.rmdir(baseDir); }); + it('pattern', function() { + // Renderable files + pattern.match('foo.json').should.have.property('renderable', true); + + // Non-renderable files + pattern.match('foo.txt').should.have.property('renderable', false); + + // Tmp files + should.not.exist(pattern.match('foo.txt~')); + should.not.exist(pattern.match('foo.txt%')); + + // Hidden files + should.not.exist(pattern.match('_foo.txt')); + should.not.exist(pattern.match('test/_foo.txt')); + should.not.exist(pattern.match('.foo.txt')); + should.not.exist(pattern.match('test/.foo.txt')); + + // Include files + hexo.config.include = ['fff/**']; + pattern.match('fff/_foo.txt').should.exist; + hexo.config.include = []; + + // Exclude files + hexo.config.exclude = ['fff/**']; + should.not.exist(pattern.match('fff/foo.txt')); + hexo.config.exclude = []; + + // Skip render files + hexo.config.skip_render = ['fff/**']; + pattern.match('fff/foo.json').should.have.property('renderable', false); + hexo.config.skip_render = []; + }); + it('asset - type: create', function() { var file = newFile({ path: 'foo.jpg', - type: 'create' + type: 'create', + renderable: false }); return fs.writeFile(file.source, 'foo').then(function() { @@ -58,7 +97,8 @@ describe('asset', function() { it('asset - type: update', function() { var file = newFile({ path: 'foo.jpg', - type: 'update' + type: 'update', + renderable: false }); var id = 'source/' + file.path; @@ -88,7 +128,8 @@ describe('asset', function() { it('asset - changed', function() { var file = newFile({ path: 'foo.jpg', - type: 'update' + type: 'update', + renderable: false }); file.changed = function() { @@ -120,7 +161,8 @@ describe('asset', function() { it('asset - unchanged', function() { var file = newFile({ path: 'foo.jpg', - type: 'update' + type: 'update', + renderable: false }); file.changed = function() { @@ -152,7 +194,8 @@ describe('asset', function() { it('asset - type: delete', function() { var file = newFile({ path: 'foo.jpg', - type: 'delete' + type: 'delete', + renderable: false }); var id = 'source/' + file.path; @@ -179,7 +222,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -212,7 +255,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'update', - content: new Buffer(body) + renderable: true }); var id; @@ -239,7 +282,8 @@ describe('asset', function() { it('page - type: delete', function() { var file = newFile({ path: 'hello.swig', - type: 'delete' + type: 'delete', + renderable: true }); return Page.insert({ @@ -255,7 +299,8 @@ describe('asset', function() { it('page - use the status of the source file if date not set', function() { var file = newFile({ path: 'hello.swig', - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, '').then(function() { @@ -286,7 +331,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -313,7 +358,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -340,7 +385,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -363,7 +408,7 @@ describe('asset', function() { var file = newFile({ path: 'test.yml', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -390,7 +435,7 @@ describe('asset', function() { var file = newFile({ path: 'test.yml', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -418,7 +463,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -447,7 +492,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -474,7 +519,7 @@ describe('asset', function() { var file = newFile({ path: 'test.min.js', type: 'create', - content: new Buffer(body) + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -502,7 +547,7 @@ describe('asset', function() { var file = newFile({ path: 'hello.swig', type: 'create', - content: new Buffer(body) + renderable: true }); hexo.config.timezone = 'UTC'; @@ -515,12 +560,12 @@ describe('asset', function() { page.date.utc().format(dateFormat).should.eql('2014-04-24 00:00:00'); page.updated.utc().format(dateFormat).should.eql('2015-05-05 00:00:00'); - hexo.config.timezone = ''; - return Promise.all([ page.remove(), fs.unlink(file.source) ]); + }).finally(function() { + hexo.config.timezone = ''; }); }); }); diff --git a/test/scripts/processors/common.js b/test/scripts/processors/common.js index 107942d886..eb9ceb4bfe 100644 --- a/test/scripts/processors/common.js +++ b/test/scripts/processors/common.js @@ -16,6 +16,8 @@ describe('common', function() { common.isHiddenFile('foo').should.be.false; common.isHiddenFile('_foo').should.be.true; common.isHiddenFile('foo/_bar').should.be.true; + common.isHiddenFile('.foo').should.be.true; + common.isHiddenFile('foo/.bar').should.be.true; }); it('ignoreTmpAndHiddenFile()', function() { @@ -26,6 +28,8 @@ describe('common', function() { pattern.match('foo~').should.be.false; pattern.match('_foo').should.be.false; pattern.match('foo/_bar').should.be.false; + pattern.match('.foo').should.be.false; + pattern.match('foo/.bar').should.be.false; }); it('toDate()', function() { @@ -40,4 +44,19 @@ describe('common', function() { common.toDate('Apr 24 2014').should.eql(new Date(2014, 3, 24)); should.not.exist(common.toDate('foo')); }); + + it('isMatch() - string', function() { + // String + common.isMatch('foo/test.html', 'foo/*.html').should.be.true; + common.isMatch('foo/test.html', 'bar/*.html').should.be.false; + + // Array + common.isMatch('foo/test.html', []).should.be.false; + common.isMatch('foo/test.html', ['foo/*.html']).should.be.true; + common.isMatch('foo/test.html', ['bar/*.html', 'foo/*.html']).should.be.true; + common.isMatch('foo/test.html', ['bar/*.html', 'baz/*.html']).should.be.false; + + // Undefined + common.isMatch('foo/test.html').should.be.false; + }); }); diff --git a/test/scripts/processors/data.js b/test/scripts/processors/data.js index 6562914771..e880dc5afc 100644 --- a/test/scripts/processors/data.js +++ b/test/scripts/processors/data.js @@ -9,7 +9,7 @@ describe('data', function() { var Hexo = require('../../../lib/hexo'); var baseDir = pathFn.join(__dirname, 'data_test'); var hexo = new Hexo(baseDir); - var processor = require('../../../lib/plugins/processor/data'); + var processor = require('../../../lib/plugins/processor/data')(hexo); var process = Promise.method(processor.process).bind(hexo); var source = hexo.source; var File = source.File; diff --git a/test/scripts/processors/post.js b/test/scripts/processors/post.js index 04f9e368a1..fd481f4a0d 100644 --- a/test/scripts/processors/post.js +++ b/test/scripts/processors/post.js @@ -13,7 +13,7 @@ describe('post', function() { var Hexo = require('../../../lib/hexo'); var baseDir = pathFn.join(__dirname, 'post_test'); var hexo = new Hexo(baseDir); - var post = require('../../../lib/plugins/processor/post'); + var post = require('../../../lib/plugins/processor/post')(hexo); var process = Promise.method(post.process.bind(hexo)); var pattern = post.pattern; var source = hexo.source; @@ -29,7 +29,8 @@ describe('post', function() { options.params = { published: options.published, - path: path + path: path, + renderable: options.renderable }; return new File(options); @@ -46,21 +47,50 @@ describe('post', function() { }); it('pattern', function() { + // Renderable files pattern.match('_posts/foo.html').should.eql({ published: true, - path: 'foo.html' + path: 'foo.html', + renderable: true }); pattern.match('_drafts/bar.html').should.eql({ published: false, - path: 'bar.html' + path: 'bar.html', + renderable: true }); - pattern.match('_posts/foo.html~').should.be.false; - pattern.match('_posts/foo.html%').should.be.false; - pattern.match('_posts/_foo.html').should.be.false; - pattern.match('_posts/foo/_bar.html').should.be.false; - pattern.match('_foo/bar.html').should.be.false; + // Non-renderable files + pattern.match('_posts/foo.txt').should.eql({ + published: true, + path: 'foo.txt', + renderable: false + }); + + pattern.match('_drafts/bar.txt').should.eql({ + published: false, + path: 'bar.txt', + renderable: false + }); + + // Tmp files + should.not.exist(pattern.match('_posts/foo.html~')); + should.not.exist(pattern.match('_posts/foo.html%')); + + // Hidden files + should.not.exist(pattern.match('_posts/_foo.html')); + should.not.exist(pattern.match('_posts/foo/_bar.html')); + should.not.exist(pattern.match('_posts/.foo.html')); + should.not.exist(pattern.match('_posts/foo/.bar.html')); + + // Outside "_posts" and "_drafts" folder + should.not.exist(pattern.match('_foo/bar.html')); + should.not.exist(pattern.match('baz.html')); + + // Skip render files + hexo.config.skip_render = ['_posts/foo/**']; + pattern.match('_posts/foo/bar.html').should.have.property('renderable', false); + hexo.config.skip_render = []; }); it('asset - post_asset_folder disabled', function() { @@ -69,7 +99,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'create' + type: 'create', + renderable: false }); return process(file).then(function() { @@ -84,7 +115,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'create' + type: 'create', + renderable: false }); var postId; @@ -121,7 +153,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'update' + type: 'update', + renderable: false }); file.changed = function() { @@ -167,7 +200,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'update' + type: 'update', + renderable: false }); file.changed = function() { @@ -213,7 +247,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'delete' + type: 'delete', + renderable: false }); var id = 'source/' + file.path; @@ -247,7 +282,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'create' + type: 'create', + renderable: false }); var id = 'source/' + file.path; @@ -269,7 +305,8 @@ describe('post', function() { var file = newFile({ path: 'foo/bar.jpg', published: true, - type: 'update' + type: 'update', + renderable: false }); var id = 'source/' + file.path; @@ -303,7 +340,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -320,10 +358,9 @@ describe('post', function() { post.slug.should.eql('foo'); post.published.should.be.true; - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -336,7 +373,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'update' + type: 'update', + renderable: true }); var id; @@ -353,10 +391,9 @@ describe('post', function() { post._id.should.eql(id); post.title.should.eql('New world'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -364,7 +401,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'delete' + type: 'delete', + renderable: true }); return Post.insert({ @@ -386,7 +424,8 @@ describe('post', function() { var file = newFile({ path: '2006/01/02/foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); hexo.config.new_post_name = ':year/:month/:day/:title'; @@ -394,17 +433,15 @@ describe('post', function() { return fs.writeFile(file.source, body).then(function() { return process(file); }).then(function() { - hexo.config.new_post_name = newPostName; - var post = Post.findOne({source: file.path}); post.slug.should.eql('foo'); post.date.format('YYYY-MM-DD').should.eql('2006-01-02'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + hexo.config.new_post_name = newPostName; + return fs.unlink(file.source); }); }); @@ -417,7 +454,8 @@ describe('post', function() { var file = newFile({ path: 'zh/foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); hexo.config.new_post_name = ':lang/:title'; @@ -425,16 +463,14 @@ describe('post', function() { return fs.writeFile(file.source, body).then(function() { return process(file); }).then(function() { - hexo.config.new_post_name = newPostName; - var post = Post.findOne({source: file.path}); post.lang.should.eql('zh'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + hexo.config.new_post_name = newPostName; + return fs.unlink(file.source); }); }); @@ -447,7 +483,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); hexo.config.new_post_name = ':year/:month/:day/:title'; @@ -455,16 +492,14 @@ describe('post', function() { return fs.writeFile(file.source, body).then(function() { return process(file); }).then(function() { - hexo.config.new_post_name = newPostName; - var post = Post.findOne({source: file.path}); post.slug.should.eql('foo'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + hexo.config.new_post_name = newPostName; + return fs.unlink(file.source); }); }); @@ -478,7 +513,8 @@ describe('post', function() { var file = newFile({ path: 'zh/foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -488,10 +524,9 @@ describe('post', function() { post.published.should.be.false; - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -505,7 +540,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: false, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -515,10 +551,9 @@ describe('post', function() { post.published.should.be.false; - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -531,7 +566,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -545,10 +581,9 @@ describe('post', function() { post.date.toDate().should.eql(stats.ctime); post.updated.toDate().should.eql(stats.mtime); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -564,7 +599,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -579,10 +615,9 @@ describe('post', function() { should.not.exist(post.photo); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -596,7 +631,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -608,10 +644,9 @@ describe('post', function() { 'http://hexo.io/foo.jpg' ]); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -624,7 +659,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -635,10 +671,9 @@ describe('post', function() { post.link.should.eql('http://hexo.io/'); post.title.should.eql('hexo.io'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -654,7 +689,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -667,10 +703,9 @@ describe('post', function() { return item.name; }).should.eql(['foo', 'bar']); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -684,7 +719,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -696,10 +732,9 @@ describe('post', function() { return item.name; }).should.eql(['foo']); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -715,7 +750,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -728,10 +764,9 @@ describe('post', function() { return item.name; }).should.have.members(['foo', 'bar']); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -745,7 +780,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -757,10 +793,9 @@ describe('post', function() { return item.name; }).should.eql(['foo']); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -775,7 +810,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); var assetId = 'source/_posts/foo/bar.jpg'; @@ -787,8 +823,6 @@ describe('post', function() { ]).then(function() { return process(file); }).then(function() { - hexo.config.post_asset_folder = false; - var post = Post.findOne({source: file.path}); var asset = PostAsset.findById(assetId); @@ -796,8 +830,11 @@ describe('post', function() { asset.post.should.eql(post._id); asset.modified.should.be.true; + return post.remove(); + }).finally(function() { + hexo.config.post_asset_folder = false; + return Promise.all([ - post.remove(), fs.unlink(file.source), fs.unlink(assetPath) ]); @@ -810,7 +847,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); var assetId = 'source/_posts/foo/bar.jpg'; @@ -825,8 +863,9 @@ describe('post', function() { var post = Post.findOne({source: file.path}); should.not.exist(PostAsset.findById(assetId)); + return post.remove(); + }).finally(function() { return Promise.all([ - post.remove(), fs.unlink(file.source), fs.unlink(assetPath) ]); @@ -844,7 +883,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -855,10 +895,9 @@ describe('post', function() { post.date.format(dateFormat).should.eql('2014-04-24 00:00:00'); post.updated.format(dateFormat).should.eql('2015-05-05 00:00:00'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -873,7 +912,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -887,10 +927,9 @@ describe('post', function() { post.date.toDate().should.eql(stats.ctime); post.updated.toDate().should.eql(stats.mtime); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); @@ -905,7 +944,8 @@ describe('post', function() { var file = newFile({ path: 'foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); hexo.config.timezone = 'UTC'; @@ -918,12 +958,10 @@ describe('post', function() { post.date.utc().format(dateFormat).should.eql('2014-04-24 00:00:00'); post.updated.utc().format(dateFormat).should.eql('2015-05-05 00:00:00'); + return post.remove(); + }).finally(function() { hexo.config.timezone = ''; - - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return fs.unlink(file.source); }); }); @@ -936,7 +974,8 @@ describe('post', function() { var file = newFile({ path: '2006/01/02/foo.html', published: true, - type: 'create' + type: 'create', + renderable: true }); hexo.config.new_post_name = ':year/:month/:day/:title'; @@ -945,17 +984,16 @@ describe('post', function() { return fs.writeFile(file.source, body).then(function() { return process(file); }).then(function() { - hexo.config.new_post_name = newPostName; - hexo.config.timezone = ''; - var post = Post.findOne({source: file.path}); post.date.utc().format(dateFormat).should.eql('2006-01-02 00:00:00'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + hexo.config.new_post_name = newPostName; + hexo.config.timezone = ''; + + return fs.unlink(file.source); }); }); @@ -969,7 +1007,8 @@ describe('post', function() { var file = newFile({ path: 'test.html', published: true, - type: 'create' + type: 'create', + renderable: true }); return fs.writeFile(file.source, body).then(function() { @@ -978,10 +1017,9 @@ describe('post', function() { var post = Post.findOne({source: file.path}); post.slug.should.eql('foooo'); - return Promise.all([ - post.remove(), - fs.unlink(file.source) - ]); + return post.remove(); + }).finally(function() { + return fs.unlink(file.source); }); }); });