123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- 'use strict';
- const { dirname, extname, join } = require('path');
- const { parse: yfm } = require('hexo-front-matter');
- const Promise = require('bluebird');
- const assignIn = (target, ...sources) => {
- const length = sources.length;
- if (length < 1 || target == null) return target;
- for (let i = 0; i < length; i++) {
- const source = sources[i];
- for (const key in source) {
- target[key] = source[key];
- }
- }
- return target;
- };
- class View {
- constructor(path, data) {
- this.path = path;
- this.source = join(this._theme.base, 'layout', path);
- this.data = typeof data === 'string' ? yfm(data) : data;
- this._precompile();
- }
- render(options = {}, callback) {
- if (!callback && typeof options === 'function') {
- callback = options;
- options = {};
- }
- const { data } = this;
- const { layout = options.layout } = data;
- const locals = this._buildLocals(options);
- return this._compiled(this._bindHelpers(locals)).then(result => {
- if (result == null || !layout) return result;
- const layoutView = this._resolveLayout(layout);
- if (!layoutView) return result;
- const layoutLocals = {
- ...locals,
- body: result,
- layout: false
- };
- return layoutView.render(layoutLocals, callback);
- }).asCallback(callback);
- }
- renderSync(options = {}) {
- const { data } = this;
- const { layout = options.layout } = data;
- 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 = {
- ...locals,
- body: result,
- layout: false
- };
- return layoutView.renderSync(layoutLocals);
- }
- _buildLocals(locals) {
- // eslint-disable-next-line no-unused-vars
- const { layout, _content, ...data } = this.data;
- return assignIn({}, locals, data, {
- filename: this.source
- });
- }
- _bindHelpers(locals) {
- const helpers = this._helper.list();
- const keys = Object.keys(helpers);
- for (const key of keys) {
- locals[key] = helpers[key].bind(locals);
- }
- return locals;
- }
- _resolveLayout(name) {
- // Relative path
- const layoutPath = join(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;
- }
- _precompile() {
- const render = this._render;
- const ctx = render.context;
- const ext = 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;
|