You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
2.4 KiB
JavaScript
82 lines
2.4 KiB
JavaScript
6 years ago
|
"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 = "<!DOCTYPE html>") {
|
||
|
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) => {
|
||
4 years ago
|
console.log("QUERY RESULT:", require("util").inspect(result, { colors: true, depth: null }));
|
||
|
|
||
6 years ago
|
let mergedOptions = Object.assign({}, options, result);
|
||
|
return renderComponent(componentAtPath(moduleRoot, componentPath), mergedOptions);
|
||
|
});
|
||
|
}).finally(() => {
|
||
|
if (options.settings.env === "development") {
|
||
|
clearRequireCache(viewPaths);
|
||
|
}
|
||
|
});
|
||
|
}).asCallback(callback);
|
||
4 years ago
|
};
|
||
6 years ago
|
}
|
||
4 years ago
|
};
|