UNPKG

prerender-spa-plugin

Version:

Flexible, framework-agnostic static site generation for sites and SPAs built with webpack.

171 lines (140 loc) 6.6 kB
'use strict'; var path = require('path'); var Prerenderer = require('@prerenderer/prerenderer'); var PuppeteerRenderer = require('@prerenderer/renderer-puppeteer'); var _require = require('html-minifier'), minify = _require.minify; function PrerenderSPAPlugin() { var _this = this; var rendererOptions = {}; // Primarily for backwards-compatibility. this._options = {}; // Normal args object. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (args.length === 1) { this._options = args[0] || {}; // Backwards-compatibility with v2 } else { console.warn("[prerender-spa-plugin] You appear to be using the v2 argument-based configuration options. It's recommended that you migrate to the clearer object-based configuration system.\nCheck the documentation for more information."); var staticDir = void 0, routes = void 0; args.forEach(function (arg) { if (typeof arg === 'string') staticDir = arg;else if (Array.isArray(arg)) routes = arg;else if (typeof arg === 'object') _this._options = arg; }); staticDir ? this._options.staticDir = staticDir : null; routes ? this._options.routes = routes : null; } // Backwards compatiblity with v2. if (this._options.captureAfterDocumentEvent) { console.warn('[prerender-spa-plugin] captureAfterDocumentEvent has been renamed to renderAfterDocumentEvent and should be moved to the renderer options.'); rendererOptions.renderAfterDocumentEvent = this._options.captureAfterDocumentEvent; } if (this._options.captureAfterDocumentEvent) { console.warn('[prerender-spa-plugin] captureAfterElementExists has been renamed to renderAfterElementExists and should be moved to the renderer options.'); rendererOptions.renderAfterElementExists = this._options.captureAfterElementExists; } if (this._options.captureAfterTime) { console.warn('[prerender-spa-plugin] captureAfterTime has been renamed to renderAfterTime and should be moved to the renderer options.'); rendererOptions.renderAfterTime = this._options.captureAfterTime; } this._options.server = this._options.server || {}; this._options.renderer = this._options.renderer || new PuppeteerRenderer(Object.assign({}, { headless: true }, rendererOptions)); if (this._options.postProcessHtml) { console.warn('[prerender-spa-plugin] postProcessHtml should be migrated to postProcess! Consult the documentation for more information.'); } } PrerenderSPAPlugin.prototype.apply = function (compiler) { var _this2 = this; var compilerFS = compiler.outputFileSystem; // From https://github.com/ahmadnassri/mkdirp-promise/blob/master/lib/index.js var mkdirp = function mkdirp(dir, opts) { return new Promise(function (resolve, reject) { compilerFS.mkdirp(dir, opts, function (err, made) { return err === null ? resolve(made) : reject(err); }); }); }; var afterEmit = function afterEmit(compilation, done) { var PrerendererInstance = new Prerenderer(_this2._options); PrerendererInstance.initialize().then(function () { return PrerendererInstance.renderRoutes(_this2._options.routes || []); }) // Backwards-compatibility with v2 (postprocessHTML should be migrated to postProcess) .then(function (renderedRoutes) { return _this2._options.postProcessHtml ? renderedRoutes.map(function (renderedRoute) { var processed = _this2._options.postProcessHtml(renderedRoute); if (typeof processed === 'string') renderedRoute.html = processed;else renderedRoute = processed; return renderedRoute; }) : renderedRoutes; }) // Run postProcess hooks. .then(function (renderedRoutes) { return _this2._options.postProcess ? Promise.all(renderedRoutes.map(function (renderedRoute) { return _this2._options.postProcess(renderedRoute); })) : renderedRoutes; }) // Check to ensure postProcess hooks returned the renderedRoute object properly. .then(function (renderedRoutes) { var isValid = renderedRoutes.every(function (r) { return typeof r === 'object'; }); if (!isValid) { throw new Error('[prerender-spa-plugin] Rendered routes are empty, did you forget to return the `context` object in postProcess?'); } return renderedRoutes; }) // Minify html files if specified in config. .then(function (renderedRoutes) { if (!_this2._options.minify) return renderedRoutes; renderedRoutes.forEach(function (route) { route.html = minify(route.html, _this2._options.minify); }); return renderedRoutes; }) // Calculate outputPath if it hasn't been set already. .then(function (renderedRoutes) { renderedRoutes.forEach(function (rendered) { if (!rendered.outputPath) { rendered.outputPath = path.join(_this2._options.outputDir || _this2._options.staticDir, rendered.route, 'index.html'); } }); return renderedRoutes; }) // Create dirs and write prerendered files. .then(function (processedRoutes) { var promises = Promise.all(processedRoutes.map(function (processedRoute) { return mkdirp(path.dirname(processedRoute.outputPath)).then(function () { return new Promise(function (resolve, reject) { compilerFS.writeFile(processedRoute.outputPath, processedRoute.html.trim(), function (err) { if (err) reject(`[prerender-spa-plugin] Unable to write rendered route to file "${processedRoute.outputPath}" \n ${err}.`);else resolve(); }); }); }).catch(function (err) { if (typeof err === 'string') { err = `[prerender-spa-plugin] Unable to create directory ${path.dirname(processedRoute.outputPath)} for route ${processedRoute.route}. \n ${err}`; } throw err; }); })); return promises; }).then(function (r) { PrerendererInstance.destroy(); done(); }).catch(function (err) { PrerendererInstance.destroy(); var msg = '[prerender-spa-plugin] Unable to prerender all routes!'; console.error(msg); compilation.errors.push(new Error(msg)); done(); }); }; if (compiler.hooks) { var plugin = { name: 'PrerenderSPAPlugin' }; compiler.hooks.afterEmit.tapAsync(plugin, afterEmit); } else { compiler.plugin('after-emit', afterEmit); } }; PrerenderSPAPlugin.PuppeteerRenderer = PuppeteerRenderer; module.exports = PrerenderSPAPlugin;