hexojs / hexo

A fast, simple & powerful blog framework, powered by Node.js.
https://hexo.io
MIT License
39.21k stars 4.82k forks source link

Asynchronous after_render hook #3079

Closed ertrzyiks closed 6 years ago

ertrzyiks commented 6 years ago

Environment Info

Node version(node -v): v8.9.3

Hexo and Plugin version(npm ls --depth 0): 3.2.0

"hexo": "^3.2.0",
"hexo-renderer-ejs": "^0.3.0",

For question

I want to apply some changes to the output HTML file, but the operation I want to do is asynchronous.

My initial thought was to use after_render:html hook, but it seems to work only synchronously in my case.

// OK!
hexo.extend.filter.register('after_render:html', function (html) {
  return 'new-content-here'
})

// NOT OK :(
hexo.extend.filter.register('after_render:html', function (html) {
  return new Promise(function (resolve) {
    doSomething(() => resolve('new-content-here'))
  })
})

What I really need is an asynchronous helper, but seems like EJS supports only synchronous helpers. What alternatives do I have with the current API?

ertrzyiks commented 6 years ago

Looks like it's possible in the after_generate filter.

var Promise = require('bluebird')
var streamToArray = require('stream-to-array')
var streamToArrayAsync = Promise.promisify(streamToArray)

function loadFileContent(stream) {
  return streamToArrayAsync(stream).then(function (parts) {
    const buffers = parts.map(function (part) {
      return util.isBuffer(part) ? part : Buffer.from(part)
    });

    return Buffer.concat(buffers);
  })
}

function processFile(buffer) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () { 
      resolve(buffer.toString())
    }, 100)
  })
}

hexo.extend.filter.register('after_generate', function () {
  var filePath = '/some/page.html'

  return route.set(filePath, function () {
    var stream = route.get(filePath)
    return loadFileContent(stream).then(function (buffer) {
      return processFile(buffer)
    })
  })
})