123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /*!
- * @nuxt/generator v2.15.8 (c) 2016-2021
- * Released under the MIT License
- * Repository: https://github.com/nuxt/nuxt.js
- * Website: https://nuxtjs.org
- */
- 'use strict';
- Object.defineProperty(exports, '__esModule', { value: true });
- const path = require('path');
- const chalk = require('chalk');
- const consola = require('consola');
- const devalue = require('devalue');
- const fsExtra = require('fs-extra');
- const defu = require('defu');
- const htmlMinifier = require('html-minifier');
- const nodeHtmlParser = require('node-html-parser');
- const ufo = require('ufo');
- const utils = require('@nuxt/utils');
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
- const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
- const chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
- const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
- const devalue__default = /*#__PURE__*/_interopDefaultLegacy(devalue);
- const fsExtra__default = /*#__PURE__*/_interopDefaultLegacy(fsExtra);
- const defu__default = /*#__PURE__*/_interopDefaultLegacy(defu);
- const htmlMinifier__default = /*#__PURE__*/_interopDefaultLegacy(htmlMinifier);
- class Generator {
- constructor (nuxt, builder) {
- this.nuxt = nuxt;
- this.options = nuxt.options;
- this.builder = builder;
- // Set variables
- this.isFullStatic = utils.isFullStatic(this.options);
- if (this.isFullStatic) {
- consola__default['default'].info(`Full static generation activated`);
- }
- this.staticRoutes = path__default['default'].resolve(this.options.srcDir, this.options.dir.static);
- this.srcBuiltPath = path__default['default'].resolve(this.options.buildDir, 'dist', 'client');
- this.distPath = this.options.generate.dir;
- this.distNuxtPath = path__default['default'].join(
- this.distPath,
- utils.isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath
- );
- // Payloads for full static
- if (this.isFullStatic) {
- const { staticAssets, manifest } = this.options.generate;
- this.staticAssetsDir = path__default['default'].resolve(this.distNuxtPath, staticAssets.dir, staticAssets.version);
- this.staticAssetsBase = utils.urlJoin(this.options.app.cdnURL, this.options.generate.staticAssets.versionBase);
- if (manifest) {
- this.manifest = defu__default['default'](manifest, {
- routes: []
- });
- }
- }
- // Shared payload
- this._payload = null;
- this.setPayload = (payload) => {
- this._payload = defu__default['default'](payload, this._payload);
- };
- }
- async generate ({ build = true, init = true } = {}) {
- consola__default['default'].debug('Initializing generator...');
- await this.initiate({ build, init });
- consola__default['default'].debug('Preparing routes for generate...');
- const routes = await this.initRoutes();
- consola__default['default'].info('Generating pages' + (this.isFullStatic ? ' with full static mode' : ''));
- const errors = await this.generateRoutes(routes);
- await this.afterGenerate();
- // Save routes manifest for full static
- if (this.manifest) {
- await this.nuxt.callHook('generate:manifest', this.manifest, this);
- const manifestPath = path__default['default'].join(this.staticAssetsDir, 'manifest.js');
- await fsExtra__default['default'].ensureDir(this.staticAssetsDir);
- await fsExtra__default['default'].writeFile(manifestPath, `__NUXT_JSONP__("manifest.js", ${devalue__default['default'](this.manifest)})`, 'utf-8');
- consola__default['default'].success('Static manifest generated');
- }
- // Done hook
- await this.nuxt.callHook('generate:done', this, errors);
- await this.nuxt.callHook('export:done', this, { errors });
- return { errors }
- }
- async initiate ({ build = true, init = true } = {}) {
- // Wait for nuxt be ready
- await this.nuxt.ready();
- // Call before hook
- await this.nuxt.callHook('generate:before', this, this.options.generate);
- await this.nuxt.callHook('export:before', this);
- if (build) {
- // Add flag to set process.static
- this.builder.forGenerate();
- // Start build process
- await this.builder.build();
- } else {
- const hasBuilt = await fsExtra__default['default'].exists(path__default['default'].resolve(this.options.buildDir, 'dist', 'server', 'client.manifest.json'));
- if (!hasBuilt) {
- throw new Error(
- `No build files found in ${this.srcBuiltPath}.\nPlease run \`nuxt build\``
- )
- }
- }
- // Initialize dist directory
- if (init) {
- await this.initDist();
- }
- }
- async initRoutes (...args) {
- // Resolve config.generate.routes promises before generating the routes
- let generateRoutes = [];
- if (this.options.router.mode !== 'hash') {
- try {
- generateRoutes = await utils.promisifyRoute(
- this.options.generate.routes || [],
- ...args
- );
- } catch (e) {
- consola__default['default'].error('Could not resolve routes');
- throw e // eslint-disable-line no-unreachable
- }
- }
- let routes = [];
- // Generate only index.html for router.mode = 'hash' or client-side apps
- if (this.options.router.mode === 'hash') {
- routes = ['/'];
- } else {
- try {
- routes = utils.flatRoutes(this.getAppRoutes());
- } catch (err) {
- // Case: where we use custom router.js
- // https://github.com/nuxt-community/router-module/issues/83
- routes = ['/'];
- }
- }
- routes = routes.filter(route => this.shouldGenerateRoute(route));
- routes = this.decorateWithPayloads(routes, generateRoutes);
- // extendRoutes hook
- await this.nuxt.callHook('generate:extendRoutes', routes);
- await this.nuxt.callHook('export:extendRoutes', { routes });
- return routes
- }
- shouldGenerateRoute (route) {
- return this.options.generate.exclude.every((regex) => {
- if (typeof regex === 'string') {
- return regex !== route
- }
- return !regex.test(route)
- })
- }
- getBuildConfig () {
- try {
- return utils.requireModule(path__default['default'].join(this.options.buildDir, 'nuxt/config.json'))
- } catch (err) {
- return null
- }
- }
- getAppRoutes () {
- return utils.requireModule(path__default['default'].join(this.options.buildDir, 'routes.json'))
- }
- async generateRoutes (routes) {
- const errors = [];
- this.routes = [];
- this.generatedRoutes = new Set();
- routes.forEach(({ route, ...props }) => {
- route = decodeURI(this.normalizeSlash(route));
- this.routes.push({ route, ...props });
- // Add routes to the tracked generated routes (for crawler)
- this.generatedRoutes.add(route);
- });
- // Start generate process
- while (this.routes.length) {
- let n = 0;
- await Promise.all(
- this.routes
- .splice(0, this.options.generate.concurrency)
- .map(async ({ route, payload }) => {
- await utils.waitFor(n++ * this.options.generate.interval);
- await this.generateRoute({ route, payload, errors });
- })
- );
- }
- // Improve string representation for errors
- // TODO: Use consola for more consistency
- errors.toString = () => this._formatErrors(errors);
- return errors
- }
- _formatErrors (errors) {
- return errors
- .map(({ type, route, error }) => {
- const isHandled = type === 'handled';
- const color = isHandled ? 'yellow' : 'red';
- let line = chalk__default['default'][color](` ${route}\n\n`);
- if (isHandled) {
- line += chalk__default['default'].grey(JSON.stringify(error, undefined, 2) + '\n');
- } else {
- line += chalk__default['default'].grey(error.stack || error.message || `${error}`);
- }
- return line
- })
- .join('\n')
- }
- async afterGenerate () {
- const { fallback } = this.options.generate;
- // Disable SPA fallback if value isn't a non-empty string
- if (typeof fallback !== 'string' || !fallback) {
- return
- }
- const fallbackPath = path__default['default'].join(this.distPath, fallback);
- // Prevent conflicts
- if (await fsExtra__default['default'].exists(fallbackPath)) {
- consola__default['default'].warn(`SPA fallback was configured, but the configured path (${fallbackPath}) already exists.`);
- return
- }
- // Render and write the SPA template to the fallback path
- let { html } = await this.nuxt.server.renderRoute('/', {
- spa: true,
- staticAssetsBase: this.staticAssetsBase
- });
- try {
- html = this.minifyHtml(html);
- } catch (error) {
- consola__default['default'].warn('HTML minification failed for SPA fallback');
- }
- await fsExtra__default['default'].writeFile(fallbackPath, html, 'utf8');
- consola__default['default'].success('Client-side fallback created: `' + fallback + '`');
- }
- async initDist () {
- // Clean destination folder
- await fsExtra__default['default'].emptyDir(this.distPath);
- consola__default['default'].info(`Generating output directory: ${path__default['default'].basename(this.distPath)}/`);
- await this.nuxt.callHook('generate:distRemoved', this);
- await this.nuxt.callHook('export:distRemoved', this);
- // Copy static and built files
- if (await fsExtra__default['default'].exists(this.staticRoutes)) {
- await fsExtra__default['default'].copy(this.staticRoutes, this.distPath);
- }
- // Copy .nuxt/dist/client/ to dist/_nuxt/
- await fsExtra__default['default'].copy(this.srcBuiltPath, this.distNuxtPath);
- if (this.payloadDir) {
- await fsExtra__default['default'].ensureDir(this.payloadDir);
- }
- // Add .nojekyll file to let GitHub Pages add the _nuxt/ folder
- // https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/
- const nojekyllPath = path__default['default'].resolve(this.distPath, '.nojekyll');
- await fsExtra__default['default'].writeFile(nojekyllPath, '');
- await this.nuxt.callHook('generate:distCopied', this);
- await this.nuxt.callHook('export:distCopied', this);
- }
- normalizeSlash (route) {
- return this.options.router && this.options.router.trailingSlash ? ufo.withTrailingSlash(route) : ufo.withoutTrailingSlash(route)
- }
- decorateWithPayloads (routes, generateRoutes) {
- const routeMap = {};
- // Fill routeMap for known routes
- routes.forEach((route) => {
- routeMap[route] = { route: this.normalizeSlash(route), payload: null };
- });
- // Fill routeMap with given generate.routes
- generateRoutes.forEach((route) => {
- // route is either a string or like { route : '/my_route/1', payload: {} }
- const path = utils.isString(route) ? route : route.route;
- routeMap[path] = {
- route: this.normalizeSlash(path),
- payload: route.payload || null
- };
- });
- return Object.values(routeMap)
- }
- async generateRoute ({ route, payload = {}, errors = [] }) {
- let html;
- const pageErrors = [];
- route = this.normalizeSlash(route);
- const setPayload = (_payload) => {
- payload = defu__default['default'](_payload, payload);
- };
- // Apply shared payload
- if (this._payload) {
- payload = defu__default['default'](payload, this._payload);
- }
- await this.nuxt.callHook('generate:route', { route, setPayload });
- await this.nuxt.callHook('export:route', { route, setPayload });
- try {
- const renderContext = {
- payload,
- staticAssetsBase: this.staticAssetsBase
- };
- const res = await this.nuxt.server.renderRoute(route, renderContext);
- html = res.html;
- // If crawler activated and called from generateRoutes()
- if (this.options.generate.crawler && this.options.render.ssr) {
- nodeHtmlParser.parse(html).querySelectorAll('a').map((el) => {
- const sanitizedHref = (el.getAttribute('href') || '')
- .replace(this.options.router.base, '/')
- .split('?')[0]
- .split('#')[0]
- .replace(/\/+$/, '')
- .trim();
- const foundRoute = decodeURI(this.normalizeSlash(sanitizedHref));
- if (foundRoute.startsWith('/') && !foundRoute.startsWith('//') && !path__default['default'].extname(foundRoute) && this.shouldGenerateRoute(foundRoute) && !this.generatedRoutes.has(foundRoute)) {
- this.generatedRoutes.add(foundRoute);
- this.routes.push({ route: foundRoute });
- }
- return null
- });
- }
- // Save Static Assets
- if (this.staticAssetsDir && renderContext.staticAssets) {
- for (const asset of renderContext.staticAssets) {
- const assetPath = path__default['default'].join(this.staticAssetsDir, decodeURI(asset.path));
- await fsExtra__default['default'].ensureDir(path__default['default'].dirname(assetPath));
- await fsExtra__default['default'].writeFile(assetPath, asset.src, 'utf-8');
- }
- // Add route to manifest (only if no error and redirect)
- if (this.manifest && (!res.error && !res.redirected)) {
- this.manifest.routes.push(ufo.withoutTrailingSlash(route));
- }
- }
- // SPA fallback
- if (res.error) {
- pageErrors.push({ type: 'handled', route, error: res.error });
- }
- } catch (err) {
- pageErrors.push({ type: 'unhandled', route, error: err });
- errors.push(...pageErrors);
- await this.nuxt.callHook('generate:routeFailed', { route, errors: pageErrors });
- await this.nuxt.callHook('export:routeFailed', { route, errors: pageErrors });
- consola__default['default'].error(this._formatErrors(pageErrors));
- return false
- }
- try {
- html = this.minifyHtml(html);
- } catch (err) {
- const minifyErr = new Error(
- `HTML minification failed. Make sure the route generates valid HTML. Failed HTML:\n ${html}`
- );
- pageErrors.push({ type: 'unhandled', route, error: minifyErr });
- }
- let fileName;
- if (this.options.generate.subFolders) {
- fileName = route === '/404'
- ? path__default['default'].join(path__default['default'].sep, '404.html') // /404 -> /404.html
- : path__default['default'].join(route, path__default['default'].sep, 'index.html'); // /about -> /about/index.html
- } else {
- const normalizedRoute = route.replace(/\/$/, '');
- fileName = route.length > 1 ? path__default['default'].join(path__default['default'].sep, normalizedRoute + '.html') : path__default['default'].join(path__default['default'].sep, 'index.html');
- }
- // Call hook to let user update the path & html
- const page = {
- route,
- path: fileName,
- html,
- exclude: false,
- errors: pageErrors
- };
- page.page = page; // Backward compatibility for export:page hook
- await this.nuxt.callHook('generate:page', page);
- if (page.exclude) {
- return false
- }
- page.path = path__default['default'].join(this.distPath, page.path);
- // Make sure the sub folders are created
- await fsExtra__default['default'].mkdirp(path__default['default'].dirname(page.path));
- await fsExtra__default['default'].writeFile(page.path, page.html, 'utf8');
- await this.nuxt.callHook('generate:routeCreated', { route, path: page.path, errors: pageErrors });
- await this.nuxt.callHook('export:routeCreated', { route, path: page.path, errors: pageErrors });
- if (pageErrors.length) {
- consola__default['default'].error(`Error generating route "${route}": ${pageErrors.map(e => e.error.message).join(', ')}`);
- errors.push(...pageErrors);
- } else {
- consola__default['default'].success(`Generated route "${route}"`);
- }
- return true
- }
- minifyHtml (html) {
- let minificationOptions = this.options.build.html.minify;
- // Legacy: Override minification options with generate.minify if present
- // TODO: Remove in Nuxt version 3
- if (typeof this.options.generate.minify !== 'undefined') {
- minificationOptions = this.options.generate.minify;
- consola__default['default'].warn('generate.minify has been deprecated and will be removed in the next major version.' +
- ' Use build.html.minify instead!');
- }
- if (!minificationOptions) {
- return html
- }
- return htmlMinifier__default['default'].minify(html, minificationOptions)
- }
- }
- function getGenerator (nuxt) {
- return new Generator(nuxt)
- }
- exports.Generator = Generator;
- exports.getGenerator = getGenerator;
|