diff --git a/lib/box/index.js b/lib/box/index.js index 03e31a365d..9c3703f920 100644 --- a/lib/box/index.js +++ b/lib/box/index.js @@ -6,9 +6,8 @@ var _ = require('lodash'); var File = require('./file'); var util = require('hexo-util'); var fs = require('hexo-fs'); -var crypto = require('crypto'); var chalk = require('chalk'); -var through2 = require('through2'); +var ShasumStream = require('./shasum_stream'); var Pattern = util.Pattern; var escapeRegExp = util.escapeRegExp; @@ -101,7 +100,11 @@ Box.prototype.process = function(callback){ if (!exist) return; return self._loadFiles(); }).then(function(files){ - if (files && files.length) return self._process(files); + if (!files || !files.length) return; + + return self._process(files).finally(function(){ + files.length = 0; + }); }).nodeify(callback); }; @@ -110,8 +113,8 @@ Box.prototype.load = Box.prototype.process; function listDir(path){ return fs.listDir(path).catch(function(err){ // Return an empty array if path does not exist - if (err.cause.code !== 'ENOENT') throw err; - return []; + if (err.cause.code === 'ENOENT') return []; + throw err; }).map(escapeBackslash); } @@ -166,6 +169,8 @@ Box.prototype._loadFiles = function(){ return result; }).map(function(item){ + existed.length = 0; + switch (item.type){ case 'create': case 'update': @@ -179,15 +184,14 @@ Box.prototype._loadFiles = function(){ function getShasum(path){ return new Promise(function(resolve, reject){ - var hash = crypto.createHash('sha1'); - var stream = fs.createReadStream(path); - - stream.pipe(through2(function(chunk, enc, callback){ - hash.update(chunk); - callback(); - }, function(){ - resolve(hash.digest('hex')); - })).on('error', reject); + var src = fs.createReadStream(path); + var stream = new ShasumStream(); + + src.pipe(stream) + .on('error', reject) + .on('finish', function(){ + resolve(stream.getShasum()); + }); }); } @@ -246,7 +250,7 @@ Box.prototype._handleDeletedFile = function(path){ if (!cache) return resolve(); ctx.log.debug('Deleted: %s', chalk.magenta(id)); - return cache.remove().then(resolve, reject); + cache.remove().then(resolve, reject); }).thenReturn({ type: 'delete', path: path diff --git a/lib/box/shasum_stream.js b/lib/box/shasum_stream.js new file mode 100644 index 0000000000..976b617507 --- /dev/null +++ b/lib/box/shasum_stream.js @@ -0,0 +1,32 @@ +'use strict'; + +var Stream = require('stream'); +var Transform = Stream.Transform; +var crypto = require('crypto'); + +function ShasumStream(options){ + Transform.call(this, options); + + this._hash = crypto.createHash('sha1'); + this._shasum = ''; +} + +require('util').inherits(ShasumStream, Transform); + +ShasumStream.prototype._transform = function(chunk, enc, callback){ + var buffer = chunk instanceof Buffer ? chunk : new Buffer(chunk, enc); + + this._hash.update(buffer); + callback(); +}; + +ShasumStream.prototype._flush = function(callback){ + this._shasum = this._hash.digest('hex'); + callback(); +}; + +ShasumStream.prototype.getShasum = function(){ + return this._shasum; +}; + +module.exports = ShasumStream; \ No newline at end of file diff --git a/lib/hexo/index.js b/lib/hexo/index.js index 79031a47a7..99ed4c484e 100644 --- a/lib/hexo/index.js +++ b/lib/hexo/index.js @@ -244,7 +244,7 @@ Hexo.prototype.load = function(callback){ self.theme.process() ]); }).then(function(){ - return self._generate(); + return self._generate({cache: true}); }).nodeify(callback); }; @@ -252,7 +252,7 @@ Hexo.prototype.watch = function(callback){ var self = this; function generate(){ - return self._generate({watch: true}); + return self._generate({cache: false}); } return loadDatabase(this).then(function(){ @@ -307,7 +307,7 @@ Hexo.prototype._generate = function(options){ Locals.prototype.theme = _.extend({}, config, theme.config, config.theme_config); Locals.prototype._ = _; Locals.prototype.layout = 'layout'; - Locals.prototype.cache = !options.watch; + Locals.prototype.cache = options.cache; Locals.prototype.env = this.env; Locals.prototype.view_dir = pathFn.join(this.theme_dir, 'layout') + sep; @@ -362,7 +362,7 @@ Hexo.prototype._generate = function(options){ return self.execFilter('template_locals', locals, {context: self}) .then(function(locals){ route.set(path, function(){ - if (cache != null) return cache; + if (options.cache && cache != null) return cache; var view, name; @@ -387,6 +387,9 @@ Hexo.prototype._generate = function(options){ route.remove(removed[i]); } + routeList.length = 0; + newRouteList.length = 0; + self.emit('generateAfter'); // Run after_generate filters diff --git a/lib/plugins/helper/date.js b/lib/plugins/helper/date.js index 7fa28f5bcf..d69accab75 100644 --- a/lib/plugins/helper/date.js +++ b/lib/plugins/helper/date.js @@ -1,37 +1,47 @@ 'use strict'; -var moment = require('moment'); +var moment = require('moment-timezone'); var isMoment = moment.isMoment; +var isDate = require('util').isDate; + +function output(date, format, lang, timezone){ + if (date == null) date = moment(); + if (!isMoment(date)) date = moment(isDate(date) ? date : new Date(date)); -function output(date, format, lang){ - if (!isMoment(date)) date = moment(date); if (lang) date = date.locale(lang); + if (timezone) date = date.tz(timezone); return date.format(format); } function toISOString(date){ + if (date == null){ + return new Date().toISOString(); + } + if (date instanceof Date || isMoment(date)){ return date.toISOString(); - } else { - return new Date(date).toISOString(); } + + return new Date(date).toISOString(); } function dateHelper(date, format){ /* jshint validthis: true */ - return output(date, format || this.config.date_format, getLanguage.call(this)); + var config = this.config; + return output(date, format || config.date_format, getLanguage(this), config.timezone); } function timeHelper(date, format){ /* jshint validthis: true */ - return output(date, format || this.config.time_format, getLanguage.call(this)); + var config = this.config; + return output(date, format || config.time_format, getLanguage(this), config.timezone); } function fullDateHelper(date, format){ /* jshint validthis: true */ if (format){ - return output(date, format, getLanguage.call(this)); + return output(date, format, getLanguage(this), this.config.timezone); } else { return this.date(date) + ' ' + this.time(date); } @@ -39,12 +49,12 @@ function fullDateHelper(date, format){ function timeTagHelper(date, format){ /* jshint validthis: true */ - return ''; + var config = this.config; + return ''; } -function getLanguage(){ - /* jshint validthis: true */ - var lang = this.page.lang || this.page.language || this.config.language; +function getLanguage(ctx){ + var lang = ctx.page.lang || ctx.page.language || ctx.config.language; if (!lang) return; if (Array.isArray(lang)) lang = lang[0]; diff --git a/test/scripts/helpers/date.js b/test/scripts/helpers/date.js index ce2de30246..6051973296 100644 --- a/test/scripts/helpers/date.js +++ b/test/scripts/helpers/date.js @@ -1,12 +1,22 @@ 'use strict'; -var moment = require('moment'); +var moment = require('moment-timezone'); var should = require('chai').should(); +var sinon = require('sinon'); describe('date', function(){ var Hexo = require('../../../lib/hexo'); var hexo = new Hexo(); var dateHelper = require('../../../lib/plugins/helper/date'); + var clock; + + before(function(){ + clock = sinon.useFakeTimers(Date.now()); + }); + + after(function(){ + clock.restore(); + }); it('date', function(){ var ctx = { @@ -16,46 +26,51 @@ describe('date', function(){ var date = dateHelper.date.bind(ctx); + // now + date().should.eql(moment().format(hexo.config.date_format)); + // moment - var now = moment(); - date(now).should.eql(now.format(hexo.config.date_format)); - date(now, 'MMM-D-YYYY').should.eql(now.format('MMM-D-YYYY')); + date(moment()).should.eql(moment().format(hexo.config.date_format)); + date(moment(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // date - now = new Date(); - date(now).should.eql(moment(now).format(hexo.config.date_format)); - date(now, 'MMM-D-YYYY').should.eql(moment(now).format('MMM-D-YYYY')); + date(new Date()).should.eql(moment().format(hexo.config.date_format)); + date(new Date(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // number - now = Date.now(); - date(now).should.eql(moment(now).format(hexo.config.date_format)); - date(now, 'MMM-D-YYYY').should.eql(moment(now).format('MMM-D-YYYY')); + date(Date.now()).should.eql(moment().format(hexo.config.date_format)); + date(Date.now(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // page.lang ctx.page.lang = 'zh-tw'; - date(now).should.eql(moment(now).locale('zh-tw').format(hexo.config.date_format)); + date(Date.now(), 'MMM D YYYY').should.eql(moment().locale('zh-tw').format('MMM D YYYY')); ctx.page.lang = ''; // config.language ctx.config.language = 'ja'; - date(now).should.eql(moment(now).locale('ja').format(hexo.config.date_format)); + date(Date.now(), 'MMM D YYYY').should.eql(moment().locale('ja').format('MMM D YYYY')); ctx.config.language = ''; + + // timezone + ctx.config.timezone = 'UTC'; + date(Date.now(), 'LLL').should.eql(moment().tz('UTC').format('LLL')); + ctx.config.timezone = ''; }); it('date_xml', function(){ var dateXML = dateHelper.date_xml; + // now + dateXML().should.eql(moment().toISOString()); + // moment - var now = moment(); - dateXML(now).should.eql(now.toISOString()); + dateXML(moment()).should.eql(moment().toISOString()); // date - now = new Date(); - dateXML(now).should.eql(now.toISOString()); + dateXML(new Date()).should.eql(moment().toISOString()); // number - now = Date.now(); - dateXML(now).should.eql(new Date(now).toISOString()); + dateXML(Date.now()).should.eql(moment().toISOString()); }); it('time', function(){ @@ -66,30 +81,35 @@ describe('date', function(){ var time = dateHelper.time.bind(ctx); + // now + time().should.eql(moment().format(hexo.config.time_format)); + // moment - var now = moment(); - time(now).should.eql(now.format(hexo.config.time_format)); - time(now, 'H:mm').should.eql(now.format('H:mm')); + time(moment()).should.eql(moment().format(hexo.config.time_format)); + time(moment(), 'H:mm').should.eql(moment().format('H:mm')); // date - now = new Date(); - time(now).should.eql(moment(now).format(hexo.config.time_format)); - time(now, 'H:mm').should.eql(moment(now).format('H:mm')); + time(new Date()).should.eql(moment().format(hexo.config.time_format)); + time(new Date(), 'H:mm').should.eql(moment().format('H:mm')); // number - now = Date.now(); - time(now).should.eql(moment(now).format(hexo.config.time_format)); - time(now, 'H:mm').should.eql(moment(now).format('H:mm')); + time(Date.now()).should.eql(moment().format(hexo.config.time_format)); + time(Date.now(), 'H:mm').should.eql(moment().format('H:mm')); // page.lang ctx.page.lang = 'zh-tw'; - time(now).should.eql(moment(now).locale('zh-tw').format(hexo.config.time_format)); + time(Date.now(), 'A H:mm').should.eql(moment().locale('zh-tw').format('A H:mm')); ctx.page.lang = ''; // config.language ctx.config.language = 'ja'; - time(now).should.eql(moment(now).locale('ja').format(hexo.config.time_format)); + time(Date.now(), 'A H:mm').should.eql(moment().locale('ja').format('A H:mm')); ctx.config.language = ''; + + // timezone + ctx.config.timezone = 'UTC'; + time().should.eql(moment().tz('UTC').format(hexo.config.time_format)); + ctx.config.timezone = ''; }); it('full_date', function(){ @@ -103,30 +123,35 @@ describe('date', function(){ var fullDate = dateHelper.full_date.bind(ctx); var fullDateFormat = hexo.config.date_format + ' ' + hexo.config.time_format; + // now + fullDate().should.eql(moment().format(fullDateFormat)); + // moment - var now = moment(); - fullDate(now).should.eql(now.format(fullDateFormat)); - fullDate(now, 'MMM-D-YYYY').should.eql(now.format('MMM-D-YYYY')); + fullDate(moment()).should.eql(moment().format(fullDateFormat)); + fullDate(moment(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // date - now = new Date(); - fullDate(now).should.eql(moment(now).format(fullDateFormat)); - fullDate(now, 'MMM-D-YYYY').should.eql(moment(now).format('MMM-D-YYYY')); + fullDate(new Date()).should.eql(moment().format(fullDateFormat)); + fullDate(new Date(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // number - now = Date.now(); - fullDate(now).should.eql(moment(now).format(fullDateFormat)); - fullDate(now, 'MMM-D-YYYY').should.eql(moment(now).format('MMM-D-YYYY')); + fullDate(Date.now()).should.eql(moment().format(fullDateFormat)); + fullDate(Date.now(), 'MMM-D-YYYY').should.eql(moment().format('MMM-D-YYYY')); // page.lang ctx.page.lang = 'zh-tw'; - fullDate(now).should.eql(moment(now).locale('zh-tw').format(fullDateFormat)); + fullDate(Date.now(), 'LLL').should.eql(moment().locale('zh-tw').format('LLL')); ctx.page.lang = ''; // config.language ctx.config.language = 'ja'; - fullDate(now).should.eql(moment(now).locale('ja').format(fullDateFormat)); + fullDate(Date.now(), 'LLL').should.eql(moment().locale('ja').format('LLL')); ctx.config.language = ''; + + // timezone + ctx.config.timezone = 'UTC'; + fullDate().should.eql(moment().tz('UTC').format(fullDateFormat)); + ctx.config.timezone = ''; }); it('time_tag', function(){ @@ -138,29 +163,45 @@ describe('date', function(){ var timeTag = dateHelper.time_tag.bind(ctx); + function result(date, format){ + date = date || new Date(); + format = format || hexo.config.date_format; + return ''; + } + + function check(date, format){ + format = format || hexo.config.date_format; + timeTag(date, format).should.eql(result(date, format)); + } + + // now + timeTag().should.eql(result()); + // moment - var now = moment(); - timeTag(now).should.eql(''); - timeTag(now, 'MMM-D-YYYY').should.eql(''); + check(moment()); + check(moment(), 'MMM-D-YYYY'); // date - now = new Date(); - timeTag(now).should.eql(''); - timeTag(now, 'MMM-D-YYYY').should.eql(''); + check(new Date()); + check(new Date(), 'MMM-D-YYYY'); // number - now = Date.now(); - timeTag(now).should.eql(''); - timeTag(now, 'MMM-D-YYYY').should.eql(''); + check(Date.now()); + check(Date.now(), 'MMM-D-YYYY'); // page.lang ctx.page.lang = 'zh-tw'; - timeTag(now).should.eql(''); + timeTag(Date.now(), 'LLL').should.eql(''); ctx.page.lang = ''; // config.language ctx.config.language = 'ja'; - timeTag(now).should.eql(''); + timeTag(Date.now(), 'LLL').should.eql(''); ctx.config.language = ''; + + // timezone + ctx.config.timezone = 'UTC'; + timeTag(Date.now(), 'LLL').should.eql(''); + ctx.config.timezone = ''; }); }); \ No newline at end of file