| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- 'use strict';
- const pathFn = require('path');
- const assignIn = require('lodash/assignIn');
- const omit = require('lodash/omit');
- const yfm = require('hexo-front-matter');
- const Promise = require('bluebird');
- function View(path, data) {
- this.path = path;
- this.source = pathFn.join(this._theme.base, 'layout', path);
- this.data = typeof data === 'string' ? yfm(data) : data;
- this._precompile();
- }
- View.prototype.render = function(options, callback) {
- if (!callback && typeof options === 'function') {
- callback = options;
- options = {};
- }
- options = options || {};
- const data = this.data;
- const layout = data.hasOwnProperty('layout') ? data.layout : options.layout;
- const locals = this._buildLocals(options);
- const self = this;
- return this._compiled(this._bindHelpers(locals)).then(result => {
- if (result == null || !layout) return result;
- const layoutView = self._resolveLayout(layout);
- if (!layoutView) return result;
- const layoutLocals = Object.assign({}, locals, {
- body: result,
- layout: false
- });
- return layoutView.render(layoutLocals, callback);
- }).asCallback(callback);
- };
- View.prototype.renderSync = function(options = {}) {
- const data = this.data;
- const layout = data.hasOwnProperty('layout') ? data.layout : options.layout;
- const locals = this._buildLocals(options);
- const result = this._compiledSync(this._bindHelpers(locals));
- if (result == null || !layout) return result;
- const layoutView = this._resolveLayout(layout);
- if (!layoutView) return result;
- const layoutLocals = Object.assign({}, locals, {
- body: result,
- layout: false
- });
- return layoutView.renderSync(layoutLocals);
- };
- View.prototype._buildLocals = function(locals) {
- return assignIn({}, locals, omit(this.data, ['layout', '_content']), {
- filename: this.source
- });
- };
- View.prototype._bindHelpers = function(locals) {
- const helpers = this._helper.list();
- const keys = Object.keys(helpers);
- let key = '';
- for (let i = 0, len = keys.length; i < len; i++) {
- key = keys[i];
- locals[key] = helpers[key].bind(locals);
- }
- return locals;
- };
- View.prototype._resolveLayout = function(name) {
- // Relative path
- const layoutPath = pathFn.join(pathFn.dirname(this.path), name);
- let layoutView = this._theme.getView(layoutPath);
- if (layoutView && layoutView.source !== this.source) return layoutView;
- // Absolute path
- layoutView = this._theme.getView(name);
- if (layoutView && layoutView.source !== this.source) return layoutView;
- };
- View.prototype._precompile = function() {
- const render = this._render;
- const ctx = render.context;
- const ext = pathFn.extname(this.path);
- const renderer = render.getRenderer(ext);
- const data = {
- path: this.source,
- text: this.data._content
- };
- function buildFilterArguments(result) {
- const output = render.getOutput(ext) || ext;
- return [
- `after_render:${output}`,
- result,
- {
- context: ctx,
- args: [data]
- }
- ];
- }
- if (renderer && typeof renderer.compile === 'function') {
- const compiled = renderer.compile(data);
- this._compiledSync = locals => {
- const result = compiled(locals);
- return ctx.execFilterSync(...buildFilterArguments(result));
- };
- this._compiled = locals => Promise.resolve(compiled(locals))
- .then(result => ctx.execFilter(...buildFilterArguments(result)));
- } else {
- this._compiledSync = locals => render.renderSync(data, locals);
- this._compiled = locals => render.render(data, locals);
- }
- };
- module.exports = View;
|