UNPKG

10.2 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _stringify = require('babel-runtime/core-js/json/stringify');
8
9var _stringify2 = _interopRequireDefault(_stringify);
10
11var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
12
13var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
14
15var _promise = require('babel-runtime/core-js/promise');
16
17var _promise2 = _interopRequireDefault(_promise);
18
19var _path = require('path');
20
21var _fsExtra = require('fs-extra');
22
23var _lodash = require('lodash');
24
25var _vueServerRenderer = require('vue-server-renderer');
26
27var _path2 = require('./utils/path');
28
29var _webpack = require('./utils/webpack');
30
31var _template = require('./utils/template');
32
33var _template2 = _interopRequireDefault(_template);
34
35var _logger = require('./utils/logger');
36
37var _logger2 = _interopRequireDefault(_logger);
38
39var _constants = require('./constants');
40
41function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
42
43let templateName; /**
44 * @file ssr renderer
45 * @author *__ author __*{% if: *__ email __* %}(*__ email __*){% /if %}
46 */
47
48class Renderer {
49 constructor(core) {
50 this.isProd = core.isProd;
51 this.config = core.config;
52 this.rootDir = this.config.globals && this.config.globals.rootDir;
53 this.cwd = core.cwd;
54 this.renderer = null;
55 this.serverBundle = null;
56 this.clientManifest = null;
57 this.template = null;
58 this.resolve = null;
59 this.readyPromise = new _promise2.default(r => this.resolve = r);
60 }
61
62 /**
63 * get template name
64 *
65 * @param {boolean} fromDist from dist dir
66 * @return {string} template path
67 */
68 getTemplateName(fromDist) {
69 if (fromDist) {
70 if ((0, _fsExtra.pathExistsSync)((0, _path.join)(this.rootDir, 'lavas', _constants.SSR_TEMPLATE_HTML))) {
71 return _constants.SSR_TEMPLATE_HTML;
72 }
73
74 return _constants.TEMPLATE_HTML;
75 }
76
77 if (templateName) {
78 return templateName;
79 }
80
81 return (0, _path.basename)(this.getTemplatePath());
82 }
83
84 /**
85 * get template path
86 *
87 * @param {?string} entryName entryName when MPA, undefined when SPA & SSR
88 * @return {string} template path
89 */
90 getTemplatePath() {
91 if (templateName) {
92 return (0, _path.join)(this.rootDir, `core/${templateName}`);
93 }
94
95 // core/ssr.html.tmpl
96 let tempPath = (0, _path.join)(this.rootDir, `core/${_constants.SSR_TEMPLATE_HTML}`);
97 if (!(0, _fsExtra.pathExistsSync)(tempPath)) {
98 // core/index.html.tmpl
99 tempPath = (0, _path.join)(this.rootDir, `core/${_constants.TEMPLATE_HTML}`);
100 }
101
102 if (!(0, _fsExtra.pathExistsSync)(tempPath)) {
103 throw new Error(`${_constants.SSR_TEMPLATE_HTML} or ${_constants.TEMPLATE_HTML} required`);
104 }
105
106 templateName = (0, _path.basename)(tempPath);
107 return tempPath;
108 }
109
110 /**
111 * return SSR template content
112 *
113 * @param {string} base base url
114 * @return {string} templateContent
115 */
116 getTemplate(base = '/') {
117 var _this = this;
118
119 return (0, _asyncToGenerator3.default)(function* () {
120 let templatePath = _this.getTemplatePath();
121
122 return _template2.default.server((yield (0, _fsExtra.readFile)(templatePath, 'utf8')), base);
123 })();
124 }
125
126 /**
127 * create renderer with built serverBundle & clientManifest in production mode
128 */
129 createWithBundle() {
130 var _this2 = this;
131
132 return (0, _asyncToGenerator3.default)(function* () {
133 _this2.serverBundle = yield (0, _fsExtra.readJson)((0, _path2.distLavasPath)(_this2.cwd, _constants.SERVER_BUNDLE));
134
135 let templatePath = (0, _path2.distLavasPath)(_this2.cwd, _this2.getTemplateName(true));
136 let manifestPath = (0, _path2.distLavasPath)(_this2.cwd, _constants.CLIENT_MANIFEST);
137 if (_this2.config.build.ssr) {
138 _this2.template = yield (0, _fsExtra.readFile)(templatePath, 'utf-8');
139 _this2.clientManifest = yield (0, _fsExtra.readJson)(manifestPath);
140 }
141
142 yield _this2.createRenderer();
143 })();
144 }
145
146 buildProd() {
147 var _this3 = this;
148
149 return (0, _asyncToGenerator3.default)(function* () {
150 let { ssr, path, stats } = _this3.config.build;
151
152 // start to build client & server configs
153 yield (0, _webpack.webpackCompile)([_this3.clientConfig, _this3.serverConfig], stats);
154
155 // copy index.template.html to dist/lavas/
156 if (ssr) {
157 let templateContent = yield _this3.getTemplate(_this3.config.router.base);
158 let distTemplatePath = (0, _path2.distLavasPath)(path, _this3.getTemplateName());
159
160 yield (0, _fsExtra.outputFile)(distTemplatePath, templateContent);
161 }
162 })();
163 }
164
165 buildDev() {
166 var _this4 = this;
167
168 return (0, _asyncToGenerator3.default)(function* () {
169 // add watcher for each template
170 let templatePath = _this4.getTemplatePath();
171 _this4.addWatcher(templatePath, 'change', (0, _asyncToGenerator3.default)(function* () {
172 yield _this4.refreshFiles();
173 }));
174 })();
175 }
176
177 /**
178 * if any of clientManifest, serverBundle and template changed, refresh them and
179 * create new renderer
180 */
181 refreshFiles() {
182 var _this5 = this;
183
184 return (0, _asyncToGenerator3.default)(function* () {
185 _logger2.default.info('build', 'refresh ssr bundle & manifest');
186
187 let changed = false;
188 let templateChanged = false;
189
190 let clientManifestPath = (0, _path2.distLavasPath)(_this5.clientConfig.output.path, _constants.CLIENT_MANIFEST);
191 if (_this5.clientMFS.existsSync(clientManifestPath)) {
192 let clientManifestContent = _this5.clientMFS.readFileSync(clientManifestPath, 'utf-8');
193 if (_this5.clientManifest && (0, _stringify2.default)(_this5.clientManifest) !== clientManifestContent) {
194 changed = true;
195 }
196 _this5.clientManifest = JSON.parse(clientManifestContent);
197 }
198
199 let serverBundlePath = (0, _path2.distLavasPath)(_this5.serverConfig.output.path, _constants.SERVER_BUNDLE);
200 if (_this5.serverMFS.existsSync(serverBundlePath)) {
201 let serverBundleContent = _this5.serverMFS.readFileSync(serverBundlePath, 'utf8');
202 if (_this5.serverBundle && (0, _stringify2.default)(_this5.serverBundle) !== serverBundleContent) {
203 changed = true;
204 }
205 _this5.serverBundle = JSON.parse(serverBundleContent);
206 }
207
208 let templateContent = yield _this5.getTemplate(_this5.config.router.base);
209 if (_this5.template !== templateContent) {
210 changed = true;
211 templateChanged = true;
212 }
213 _this5.template = templateContent;
214
215 if (changed) {
216 yield _this5.createRenderer();
217
218 // if we detect template changed, publish reload event to client
219 if (templateChanged) {
220 _this5.reloadClient();
221 }
222 }
223 })();
224 }
225
226 /**
227 * only called in SSR mode
228 *
229 * @param {Object} clientConfig client webpack config
230 * @param {Object} serverConfig server webpack config
231 */
232 build(clientConfig, serverConfig) {
233 var _this6 = this;
234
235 return (0, _asyncToGenerator3.default)(function* () {
236 _this6.clientConfig = clientConfig;
237 _this6.serverConfig = serverConfig;
238
239 if (_this6.isProd) {
240 yield _this6.buildProd();
241 } else {
242 yield _this6.buildDev();
243 }
244 })();
245 }
246
247 /**
248 * create renderer
249 */
250 createRenderer() {
251 var _this7 = this;
252
253 return (0, _asyncToGenerator3.default)(function* () {
254 if (_this7.serverBundle && _this7.clientManifest) {
255 let isFirstTime = !_this7.renderer;
256 let inject = !/\{\{\{\s*render/.test(_this7.template);
257 _this7.renderer = (0, _vueServerRenderer.createBundleRenderer)(_this7.serverBundle, {
258 template: _this7.template,
259 clientManifest: _this7.clientManifest,
260 shouldPrefetch: function (file, type) {
261 if (type === 'script') {
262 // exclude the workbox files in /static copied by copy-webpack-plugin
263 return !/(workbox-v\d+\.\d+\.\d+.*)|(sw-register\.js)|(precache-manifest\.)/.test(file);
264 }
265 return true;
266 },
267 runInNewContext: false,
268 inject
269 });
270
271 // If this is the first time, use resolve
272 if (isFirstTime) {
273 _this7.resolve(_this7.renderer);
274 }
275 }
276 })();
277 }
278
279 render(context = {}) {
280 var _this8 = this;
281
282 return (0, _asyncToGenerator3.default)(function* () {
283 let ctx = {};
284
285 // merge with default context
286 (0, _lodash.merge)(ctx, {
287 title: 'Lavas', // default title
288 config: _this8.config // mount config to ctx which will be used when rendering template
289 }, context);
290
291 let renderer = yield _this8.renderer ? _promise2.default.resolve(_this8.renderer) : _this8.readyPromise;
292
293 // render to string
294 return new _promise2.default(function (resolve) {
295 renderer.renderToString(ctx, function (err, html) {
296 return resolve({ err, html });
297 });
298 });
299 })();
300 }
301}
302exports.default = Renderer;
303module.exports = exports['default'];
\No newline at end of file