UNPKG

apim-tools

Version:

APIM Tools

249 lines (226 loc) 6.07 kB
/** * @file smarty 文件渲染处理器 * @author sparklewhy@gmail.com */ 'use strict'; const pathUtil = require('path'); const fs = require('fs'); const isPlainObject = require('lodash.isplainobject'); const phpProcessor = require('./php-processor'); const helper = require('./helper'); /** * 默认选项定义 * * @type {Object} */ const defaultOptions = { /** * php 处理器的配置选项 * * @type Object */ php: null, /** * 渲染 smarty 的 php 文件基础路径 * * @type {string} */ baseDir: '', /** * 渲染的模板的 php 文件,路径相对于 `baseDir` 选项 * * @type {string} */ renderFile: 'smarty.php' }; /** * 初始化 smarty 模板渲染文件 * * @param {Object} options 处理器选项 * @param {Object} logger log 工具 */ function initSmartyRendererFile(options, logger) { let renderFile = pathUtil.join(options.baseDir, options.renderFile); // 创建 smarty 模板渲染的 php 文件,如果不存在的话 try { if (!fs.existsSync(renderFile)) { let renderFileContent = fs.readFileSync( pathUtil.join(__dirname, './smarty-renderer.php') ).toString(); logger.info('create smarty render file: ', renderFile); fs.writeFileSync(renderFile, renderFileContent); } } catch (e) { logger.error('init smarty render file fail:', e.toString()); } options.renderSmartyPhpFile = renderFile; } /** * 渲染 smarty 模板 * * @param {Object} data 要处理的 php 文件的数据 * @param {Object} options 预处理器的选项 * @param {Object} context 当前请求的上下文信息 * @param {Function} callback 执行的回调 */ function processSmarty(data, options, context, callback) { phpProcessor( { tplPath: data._tpl, tplData: JSON.stringify(helper.normalizeMockData(data)), path: options.renderSmartyPhpFile }, options.php, context, callback ); } /** * 将包含特定 smarty 渲染信息的数据进行渲染。 * e.g., * <code> * { * _tpl: 'personList.tpl', * _data: { * listData: [{name: 'Jack', from: 'USA'}] * } * } * </code> * 上述数据,最后将转成 `personList.tpl` 渲染后的 html 字符串 * * 局部渲染的例子: * <code> * { * status: 0, * count: 100, * page: 1, * tpl: { * _tpl: 'personList.tpl', * _data: { * listData: [{name: 'Jack', from: 'USA'}] * } * } * } * </code> * * 上述数据,tpl值将最后替换成 `personList.tpl` 渲染后的 html 字符串 * <code> * { * status: 0, * count: 100, * page: 1, * tpl: '<personList.tpl render result>' * } * </code> * * @param {Object} data 要渲染的数据 * @param {Object} options 预处理器的选项 * @param {Object} context 当前请求的上下文信息 * @param {Function} callback 执行的回调 * @return {void} */ function renderSmarty(data, options, context, callback) { let tplPath = data._tpl; if (tplPath) { return processSmarty(data, options, context, callback); } let processObjCounter = 0; let errorInfos = []; let resultData = {}; let renderDone = function (name, err, result) { processObjCounter--; if (err) { errorInfos.push(err.toString()); } else { name && (resultData[name] = result && result._data); } if (processObjCounter <= 0) { if (errorInfos.length) { callback(errorInfos.join('; ')); } else { callback(null, resultData); } } }; let keys = Object.keys(data); let toProcessObjs = []; for (let i = 0, len = keys.length; i < len; i++) { let name = keys[i]; let value = data[name]; if (isPlainObject(value)) { processObjCounter++; toProcessObjs.push({ name, value }); } else { resultData[name] = value; } } if (processObjCounter) { toProcessObjs.forEach(function (item) { renderSmarty( item.value, options, context, renderDone.bind(this, item.name) ); }); } else { renderDone(); } } /** * 滤掉空的选项 * * @param {Object} options 选项 * @return {Object} */ function filterNullOptions(options) { let result = {}; options && Object.keys(options).forEach(k => { if (options[k] != null) { result[k] = options[k]; } }); return result; } /** * smarty 处理器 * * @param {Object} mockData 要渲染的 smarty 数据 * @param {Object=} options 处理器选项 * @param {string=} options.baseDir 模板渲染的根目录 * @param {string=} options.php php 处理器的选项,具体参见 {@link php-processor} ,可选 * @param {string=} options.renderFile 负责渲染 smarty 的 php 文件路径,默认会自动生成, * 如果不存在,路径相对于 `baseDir`,可选 * @param {Object} context 请求上下文 * @return {Promise} */ function runSmarty(mockData, options, context) { let processorOptions = {}; Object.assign( processorOptions, defaultOptions, filterNullOptions(options) ); initSmartyRendererFile(processorOptions, context.logger); return new Promise((resolve, reject) => { if (!isPlainObject(mockData)) { /* eslint-disable fecs-camelcase */ return resolve({_data: mockData}); } renderSmarty(mockData, processorOptions, context, (err, result) => { if (err) { reject(err); } else { resolve(result); } }); }); } module.exports = exports = runSmarty;