"use strict"; const Promise = require("bluebird"); const React = require("react"); const ReactDOMServer = require("react-dom/server"); const defaultValue = require("default-value"); const dotty = require("dotty"); const registerBabel = require("./register-babel"); const clearRequireCache = require("./clear-require-cache"); /* FILEBUG: Express does not copy symbol-keyed properties from app.locals, but it probably should? */ // let ExpressReact = Symbol("ExpressReact"); let ExpressReact = "__EXPRESS_REACT_SETTINGS"; let LocalsContext = React.createContext({}); function componentAtPath(moduleRoot, componentPath) { if (componentPath == null) { return moduleRoot; } else { return dotty.get(moduleRoot, componentPath); } } function renderComponent(component, locals, doctype = "") { let tree = React.createElement(LocalsContext.Provider, {value: locals}, React.createElement(component, locals) ); try { /* TODO: Expose internals? Like koa-react */ return doctype + ReactDOMServer.renderToStaticMarkup(tree); } catch (err) { if (/Element type is invalid:/.test(err.message)) { throw new Error(`Expected a React component, but got '${component}' - maybe you forgot to specify a componentPath?`); } else { throw err; } } } module.exports = { Settings: ExpressReact, LocalsContext: LocalsContext, createEngine: function createEngine({ prepare } = {}) { let babelRegistered = false; return function asyncRender(filename, options, callback) { return Promise.try(() => { let viewPaths = options.settings.views; if (!babelRegistered) { registerBabel(viewPaths); babelRegistered = true; } let {componentPath} = defaultValue(options[ExpressReact], {}); return Promise.try(() => { let templateFile = require(filename); let moduleRoot = defaultValue(templateFile.exports, templateFile); return Promise.try(() => { if (prepare != null) { return prepare(moduleRoot, options); } }).then((result) => { console.log("QUERY RESULT:", require("util").inspect(result, { colors: true, depth: null })); let mergedOptions = Object.assign({}, options, result); return renderComponent(componentAtPath(moduleRoot, componentPath), mergedOptions); }); }).finally(() => { if (options.settings.env === "development") { clearRequireCache(viewPaths); } }); }).asCallback(callback); }; } };