|
|
const fs = require('fs');
|
|
|
const path = require('path');
|
|
|
const LRU = require('lru-cache');
|
|
|
const url = require('url');
|
|
|
const sourceMap = require('source-map');
|
|
|
const _ = require('lodash');
|
|
|
const md5 = require('yoho-md5');
|
|
|
const pkg = require('../../package.json');
|
|
|
const routes = require('../../config/ssr-routes');
|
|
|
const redis = require('../../utils/redis');
|
|
|
const {createBundleRenderer} = require('vue-server-renderer');
|
|
|
const logger = global.yoho.logger;
|
|
|
const config = global.yoho.config;
|
|
|
|
|
|
const microCache = LRU({ // eslint-disable-line
|
|
|
max: 1000,
|
|
|
maxAge: 2000
|
|
|
});
|
|
|
const REG_STACK = /at ([^:]+):(\d+):(\d+)/;
|
|
|
|
|
|
const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV;
|
|
|
|
|
|
let renderer;
|
|
|
let serverBundle;
|
|
|
|
|
|
if (!isDev) {
|
|
|
const template = fs.readFileSync(path.join(__dirname, '../../apps/index.html'), 'utf-8');
|
|
|
const serverBundle = require(`../../dist/yohoblk-wap/bundle/yoho-ssr-server-${pkg.version}.json`);
|
|
|
|
|
|
serverBundle = require(`../../dist/yohoblk-wap/bundle/yoho-ssr-server-${pkg.version}.json`);
|
|
|
const clientManifest = require(`../../dist/yohoblk-wap/bundle/yoho-ssr-client-${pkg.version}.json`);
|
|
|
|
|
|
renderer = createBundleRenderer(serverBundle, {
|
...
|
...
|
@@ -43,32 +46,85 @@ const getContext = (req) => { |
|
|
};
|
|
|
};
|
|
|
|
|
|
const render = ({cache, cacheRule}) => {
|
|
|
return (req, res, next) => {
|
|
|
const reqUrl = url.parse(req.url);
|
|
|
const getCacheKey = (urlPath, cackeKey = '') => {
|
|
|
const urlObj = url.parse(urlPath);
|
|
|
|
|
|
return md5(cackeKey
|
|
|
.replace('$url', urlObj.pathname)
|
|
|
.replace('$params', urlObj.query));
|
|
|
};
|
|
|
|
|
|
const parseError = async({stack = ''}) => {
|
|
|
try {
|
|
|
const splits = stack.split('\n');
|
|
|
const lastError = splits.map(str => {
|
|
|
const match = str.match(REG_STACK);
|
|
|
|
|
|
if (cache && reqUrl[cacheRule]) {
|
|
|
const html = microCache.get(reqUrl[cacheRule]);
|
|
|
if (match) {
|
|
|
return {file: match[1], line: parseInt(match[2], 10), column: parseInt(match[3], 10)};
|
|
|
}
|
|
|
return false;
|
|
|
}).find(match => match);
|
|
|
|
|
|
if (lastError && lastError.file) {
|
|
|
const consumer = await new sourceMap.SourceMapConsumer(serverBundle.maps[lastError.file]);
|
|
|
|
|
|
const origin = consumer.originalPositionFor({
|
|
|
line: lastError.line,
|
|
|
column: 342
|
|
|
});
|
|
|
|
|
|
console.log(origin);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
logger.error(error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const render = (route) => {
|
|
|
return async(req, res, next) => {
|
|
|
res.setHeader('X-YOHO-Version', pkg.version);
|
|
|
const ck = getCacheKey(req.url, route.cackeKey);
|
|
|
|
|
|
if (config.useCache && route.cache && ck) {
|
|
|
const html = await redis.getAsync(ck);
|
|
|
|
|
|
if (html) {
|
|
|
logger.debug(`cached ${req.url}`);
|
|
|
res.setHeader('X-YOHO-Cached', 'HIT');
|
|
|
return res.send(html);
|
|
|
}
|
|
|
res.setHeader('X-YOHO-Cached', 'MISS');
|
|
|
}
|
|
|
let context = getContext(req);
|
|
|
|
|
|
renderer.renderToString(context, (err, html) => {
|
|
|
if (err) {
|
|
|
return next(err);
|
|
|
parseError(err);
|
|
|
return next(err.message);
|
|
|
}
|
|
|
if (cache && reqUrl[cacheRule]) {
|
|
|
microCache.set(reqUrl[cacheRule], html);
|
|
|
if (config.useCache && route.cache && ck) {
|
|
|
redis.setex(ck, route.cacheTime || 60, html);
|
|
|
}
|
|
|
return res.send(html);
|
|
|
});
|
|
|
};
|
|
|
};
|
|
|
const devRender = () => {
|
|
|
return (req, res, next) => {
|
|
|
const devRender = (route) => {
|
|
|
return async(req, res, next) => {
|
|
|
res.setHeader('X-YOHO-Version', pkg.version);
|
|
|
const ck = getCacheKey(req.url, route.cackeKey);
|
|
|
|
|
|
if (config.useCache && route.cache && ck) {
|
|
|
const html = await redis.getAsync(ck);
|
|
|
|
|
|
if (html) {
|
|
|
logger.debug(`cached ${req.url}`);
|
|
|
res.setHeader('X-YOHO-Cached', 'HIT');
|
|
|
return res.send(html);
|
|
|
}
|
|
|
res.setHeader('X-YOHO-Cached', 'MISS');
|
|
|
}
|
|
|
let context = getContext(req);
|
|
|
|
|
|
process.send({action: 'ssr_request', context});
|
...
|
...
|
@@ -90,6 +146,9 @@ const devRender = () => { |
|
|
});
|
|
|
}
|
|
|
}
|
|
|
if (config.useCache && route.cache && ck) {
|
|
|
redis.setex(ck, route.cacheTime || 60, msg.html);
|
|
|
}
|
|
|
return res.end(msg.html);
|
|
|
}
|
|
|
};
|
...
|
...
|
|