text: /*!
   * @nuxt/vue-renderer v2.14.12 (c) 2016-2021
  
   * - All the amazing contributors
   * Released under the MIT License.
   * Website: https://nuxtjs.org
  */
  'use strict';
  
  Object.defineProperty(exports, '__esModule', { value: true });
  
  const path = require('path');
  const fs = require('fs-extra');
  const consola = require('consola');
  const lodash = require('lodash');
  const utils = require('@nuxt/utils');
  const ufo = require('ufo');
  const VueMeta = require('vue-meta');
  const vueServerRenderer = require('vue-server-renderer');
  const LRU = require('lru-cache');
  const devalue = require('@nuxt/devalue');
  const crypto = require('crypto');
  const util = require('util');
  
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  
  const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
  const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
  const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
  const VueMeta__default = /*#__PURE__*/_interopDefaultLegacy(VueMeta);
  const LRU__default = /*#__PURE__*/_interopDefaultLegacy(LRU);
  const devalue__default = /*#__PURE__*/_interopDefaultLegacy(devalue);
  const crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
  
  class BaseRenderer {
    constructor (serverContext) {
      this.serverContext = serverContext;
      this.options = serverContext.options;
  
      this.vueRenderer = this.createRenderer();
    }
  
    createRenderer () {
      throw new Error('`createRenderer()` needs to be implemented')
    }
  
    renderTemplate (templateFn, opts) {
      // Fix problem with HTMLPlugin's minify option (#3392)
      opts.html_attrs = opts.HTML_ATTRS;
      opts.head_attrs = opts.HEAD_ATTRS;
      opts.body_attrs = opts.BODY_ATTRS;
  
      return templateFn(opts)
    }
  
    render () {
      throw new Error('`render()` needs to be implemented')
    }
  }
  
  class SPARenderer extends BaseRenderer {
    constructor (serverContext) {
      super(serverContext);
  
      this.cache = new LRU__default['default']();
  
      this.vueMetaConfig = {
        ssrAppId: '1',
        ...this.options.vueMeta,
        keyName: 'head',
        attribute: 'data-n-head',
        ssrAttribute: 'data-n-head-ssr',
        tagIDKeyName: 'hid'
      };
    }
  
    createRenderer () {
      return vueServerRenderer.createRenderer()
    }
  
    async render (renderContext) {
      const { url = '/', req = {} } = renderContext;
      const modernMode = this.options.modern;
      const modern = (modernMode && this.options.target === utils.TARGETS.static) || utils.isModernRequest(req, modernMode);
      const cacheKey = `${modern ? 'modern:' : 'legacy:'}${url}`;
      let meta = this.cache.get(cacheKey);
  
      if (meta) {
        // Return a copy of the content, so that future
        // modifications do not effect the data in cache
        return lodash.cloneDeep(meta)
      }
  
      meta = {
        HTML_ATTRS: '',
        HEAD_ATTRS: '',
        BODY_ATTRS: '',
        HEAD: '',
        BODY_SCRIPTS_PREPEND: '',
        BODY_SCRIPTS: ''
      };
  
      if (this.options.features.meta) {
        // Get vue-meta context
        renderContext.head = typeof this.options.head === 'function'
          ? this.options.head()
          : lodash.cloneDeep(this.options.head);
      }
  
      // Allow overriding renderContext
      await this.serverContext.nuxt.callHook('vue-renderer:spa:prepareContext', renderContext);
  
      if (this.options.features.meta) {
        const m = VueMeta__default['default'].generate(renderContext.head || {}, this.vueMetaConfig);
  
        // HTML_ATTRS
        meta.HTML_ATTRS = m.htmlAttrs.text();
  
        // HEAD_ATTRS
        meta.HEAD_ATTRS = m.headAttrs.text();
  
        // BODY_ATTRS
        meta.BODY_ATTRS = m.bodyAttrs.text();
  
        // HEAD tags
        meta.HEAD =
          m.title.text() +
          m.meta.text() +
          m.link.text() +
          m.style.text() +
          m.script.text() +
          m.noscript.text();
  
        // Add <base href=""> meta if router base specified
        if (this.options._routerBaseSpecified) {
          meta.HEAD += `<base href="${this.options.router.base}">`;
        }
  
        // BODY_SCRIPTS (PREPEND)
        meta.BODY_SCRIPTS_PREPEND =
          m.meta.text({ pbody: true }) +
          m.link.text({ pbody: true }) +
          m.style.text({ pbody: true }) +
          m.script.text({ pbody: true }) +
          m.noscript.text({ pbody: true });
  
        // BODY_SCRIPTS (APPEND)
        meta.BODY_SCRIPTS =
          m.meta.text({ body: true }) +
          m.link.text({ body: true }) +
          m.style.text({ body: true }) +
          m.script.text({ body: true }) +
          m.noscript.text({ body: true });
      }
  
      // Resources Hints
      meta.resourceHints = '';
  
      const { resources: { modernManifest, clientManifest } } = this.serverContext;
      const manifest = modern ? modernManifest : clientManifest;
  
      const { shouldPreload, shouldPrefetch } = this.options.render.bundleRenderer;
  
      if (this.options.render.resourceHints && manifest) {
        const publicPath = manifest.publicPath || '/_nuxt/';
  
        // Preload initial resources
        if (Array.isArray(manifest.initial)) {
          const { crossorigin } = this.options.render;
          const cors = `${crossorigin ? ` crossorigin="${crossorigin}"` : ''}`;
  
          meta.preloadFiles = manifest.initial
            .map(SPARenderer.normalizeFile)
            .filter(({ fileWithoutQuery, asType }) => shouldPreload(fileWithoutQuery, asType))
            .map(file => ({ ...file, modern }));
  
          meta.resourceHints += meta.preloadFiles
            .map(({ file, extension, fileWithoutQuery, asType, modern }) => {
              let extra = '';
              if (asType === 'font') {
                extra = ` type="font/${extension}"${cors ? '' : ' crossorigin'}`;
              }
              const rel = modern && asType === 'script' ? 'modulepreload' : 'preload';
              return `<link rel="${rel}"${cors} href="${publicPath}${file}"${
                asType !== '' ? ` as="${asType}"` : ''}${extra}>`
            })
            .join('');
        }
  
        // Prefetch async resources
        if (Array.isArray(manifest.async)) {
          meta.resourceHints += manifest.async
            .map(SPARenderer.normalizeFile)
            .filter(({ fileWithoutQuery, asType }) => shouldPrefetch(fileWithoutQuery, asType))
            .map(({ file }) => `<link rel="prefetch" href="${publicPath}${file}">`)
            .join('');
        }
  
        // Add them to HEAD
        if (meta.resourceHints) {
          meta.HEAD += meta.resourceHints;
        }
      }
  
      // Serialize state (runtime config)
      let APP = `${meta.BODY_SCRIPTS_PREPEND}<div id="${this.serverContext.globals.id}">${this.serverContext.resources.loadingHTML}</div>${meta.BODY_SCRIPTS}`;
  
      const payload = {
        config: renderContext.runtimeConfig.public
      };
      if (renderContext.staticAssetsBase) {
        payload.staticAssetsBase = renderContext.staticAssetsBase;
      }
      APP += `<script>window.${this.serverContext.globals.context}=${devalue__default['default'](payload)}</script>`;
  
      // Prepare template params
      const templateParams = {
        ...meta,
        APP,
        ENV: this.options.env
      };
  
      // Call spa:templateParams hook
      await this.serverContext.nuxt.callHook('vue-renderer:spa:templateParams', templateParams);
  
      // Render with SPA template
      const html = this.renderTemplate(this.serverContext.resources.spaTemplate, templateParams);
      const content = {
        html,
        preloadFiles: meta.preloadFiles || []
      };
  
      // Set meta tags inside cache
      this.cache.set(cacheKey, content);
  
      // Return a copy of the content, so that future
      // modifications do not effect the data in cache
      return lodash.cloneDeep(content)
    }
  
    static normalizeFile (file) {
      const withoutQuery = file.replace(/\?.*/, '');
      const extension = path.extname(withoutQuery).slice(1);
      return {
        file,
        extension,
        fileWithoutQuery: withoutQuery,
        asType: SPARenderer.getPreloadType(extension)
      }
    }
  
    static getPreloadType (ext) {
      if (ext === 'js') {
        return 'script'
      } else if (ext === 'css') {
        return 'style'
      } else if (/jpe?g|png|svg|gif|webp|ico|avif/.test(ext)) {
        return 'image'
      } else if (/woff2?|ttf|otf|eot/.test(ext)) {
        return 'font'
      } else {
        return ''
      }
    }
  }
  
  class SSRRenderer extends BaseRenderer {
    get rendererOptions () {
      const hasModules = fs__default['default'].existsSync(path__default['default'].resolve(this.options.rootDir, 'node_modules'));
  
      return {
        clientManifest: this.serverContext.resources.clientManifest,
        // for globally installed nuxt command, search dependencies in global dir
        basedir: hasModules ? this.options.rootDir : __dirname,
        ...this.options.render.bundleRenderer
      }
    }
  
    addAttrs (tags, referenceTag, referenceAttr) {
      const reference = referenceTag ? `<${referenceTag}` : referenceAttr;
      if (!reference) {
        return tags
      }
  
      const { render: { crossorigin } } = this.options;
      if (crossorigin) {
        tags = tags.replace(
          new RegExp(reference, 'g'),
          `${reference} crossorigin="${crossorigin}"`
        );
      }
  
      return tags
    }
  
    renderResourceHints (renderContext) {
      return this.addAttrs(renderContext.renderResourceHints(), null, 'rel="preload"')
    }
  
    renderScripts (renderContext) {
      let renderedScripts = this.addAttrs(renderContext.renderScripts(), 'script');
      if (this.options.render.asyncScripts) {
        renderedScripts = renderedScripts.replace(/defer>/g, 'defer async>');
      }
      return renderedScripts
    }
  
    renderStyles (renderContext) {
      return this.addAttrs(renderContext.renderStyles(), 'link')
    }
  
    getPreloadFiles (renderContext) {
      return renderContext.getPreloadFiles()
    }
  
    createRenderer () {
      // Create bundle renderer for SSR
      return vueServerRenderer.createBundleRenderer(
        this.serverContext.resources.serverManifest,
        this.rendererOptions
      )
    }
  
    useSSRLog () {
      if (!this.options.render.ssrLog) {
        return
      }
      const logs = [];
      const devReporter = {
        log (logObj) {
          logs.push({
            ...logObj,
            args: logObj.args.map(arg => util.format(arg))
          });
        }
      };
      consola__default['default'].addReporter(devReporter);
  
      return () => {
        consola__default['default'].removeReporter(devReporter);
        return logs
      }
    }
  
    async render (renderContext) {
      // Call ssr:context hook to extend context from modules
      await this.serverContext.nuxt.callHook('vue-renderer:ssr:prepareContext', renderContext);
  
      const getSSRLog = this.useSSRLog();
  
      // Call Vue renderer renderToString
      let APP = await this.vueRenderer.renderToString(renderContext);
  
      if (typeof getSSRLog === 'function') {
        renderContext.nuxt.logs = getSSRLog();
      }
  
      // Call ssr:context hook
      await this.serverContext.nuxt.callHook('vue-renderer:ssr:context', renderContext);
  
      // TODO: Remove in next major release (#4722)
      await this.serverContext.nuxt.callHook('_render:context', renderContext.nuxt);
  
      // Fallback to empty response
      if (!renderContext.nuxt.serverRendered) {
        APP = `<div id="${this.serverContext.globals.id}"></div>`;
      }
  
      // Perf: early returns if server target and redirected
      if (renderContext.redirected && renderContext.target === utils.TARGETS.server) {
        return {
          html: APP,
          error: renderContext.nuxt.error,
          redirected: renderContext.redirected
        }
      }
  
      let HEAD = '';
  
      // Inject head meta
      // (this is unset when features.meta is false in server template)
      const meta = renderContext.meta && renderContext.meta.inject({
        isSSR: renderContext.nuxt.serverRendered,
        ln: this.options.dev
      });
  
      if (meta) {
        HEAD += meta.title.text() + meta.meta.text();
      }
  
      // Add <base href=""> meta if router base specified
      if (this.options._routerBaseSpecified) {
        HEAD += `<base href="${this.options.router.base}">`;
      }
  
      if (meta) {
        HEAD += meta.link.text() +
          meta.style.text() +
          meta.script.text() +
          meta.noscript.text();
      }
  
      // Check if we need to inject scripts and state
      const shouldInjectScripts = this.options.render.injectScripts !== false;
  
      // Inject resource hints
      if (this.options.render.resourceHints && shouldInjectScripts) {
        HEAD += this.renderResourceHints(renderContext);
      }
  
      // Inject styles
      HEAD += this.renderStyles(renderContext);
  
      if (meta) {
        const prependInjectorOptions = { pbody: true };
  
        const BODY_PREPEND =
          meta.meta.text(prependInjectorOptions) +
          meta.link.text(prependInjectorOptions) +
          meta.style.text(prependInjectorOptions) +
          meta.script.text(prependInjectorOptions) +
          meta.noscript.text(prependInjectorOptions);
  
        if (BODY_PREPEND) {
          APP = `${BODY_PREPEND}${APP}`;
        }
      }
  
      const { csp } = this.options.render;
      // Only add the hash if 'unsafe-inline' rule isn't present to avoid conflicts (#5387)
      const containsUnsafeInlineScriptSrc = csp.policies && csp.policies['script-src'] && csp.policies['script-src'].includes('\'unsafe-inline\'');
      const shouldHashCspScriptSrc = csp && (csp.unsafeInlineCompatibility || !containsUnsafeInlineScriptSrc);
      const inlineScripts = [];
  
      if (renderContext.staticAssetsBase) {
        const preloadScripts = [];
        renderContext.staticAssets = [];
        const { staticAssetsBase, url, nuxt, staticAssets } = renderContext;
        const { data, fetch, mutations, ...state } = nuxt;
  
        // Initial state
        const stateScript = `window.${this.serverContext.globals.context}=${devalue__default['default']({
          staticAssetsBase,
          ...state
        })};`;
  
        // Make chunk for initial state > 10 KB
        const stateScriptKb = (stateScript.length * 4 /* utf8 */) / 100;
        if (stateScriptKb > 10) {
          const statePath = utils.urlJoin(url, 'state.js');
          const stateUrl = utils.urlJoin(staticAssetsBase, statePath);
          staticAssets.push({ path: statePath, src: stateScript });
          if (this.options.render.asyncScripts) {
            APP += `<script defer async src="${stateUrl}"></script>`;
          } else {
            APP += `<script defer src="${stateUrl}"></script>`;
          }
          preloadScripts.push(stateUrl);
        } else {
          APP += `<script>${stateScript}</script>`;
        }
  
        // Save payload only if no error or redirection were made
        if (!renderContext.nuxt.error && !renderContext.redirected) {
          // Page level payload.js (async loaded for CSR)
          const payloadPath = utils.urlJoin(url, 'payload.js');
          const payloadUrl = utils.urlJoin(staticAssetsBase, payloadPath);
          let routePath = ufo.parsePath(url).pathname; // remove query params
          if (!this.options.router.trailingSlash) {
            routePath = ufo.withoutTrailingSlash(routePath) || '/';
          }
          const payloadScript = `__NUXT_JSONP__("${routePath}", ${devalue__default['default']({ data, fetch, mutations })});`;
          staticAssets.push({ path: payloadPath, src: payloadScript });
          preloadScripts.push(payloadUrl);
          // Add manifest preload
          if (this.options.generate.manifest) {
            const manifestUrl = utils.urlJoin(staticAssetsBase, 'manifest.js');
            preloadScripts.push(manifestUrl);
          }
        }
  
        // Preload links
        for (const href of preloadScripts) {
          HEAD += `<link rel="preload" href="${href}" as="script">`;
        }
      } else {
        // Serialize state
        let serializedSession;
        if (shouldInjectScripts || shouldHashCspScriptSrc) {
          // Only serialized session if need inject scripts or csp hash
          serializedSession = `window.${this.serverContext.globals.context}=${devalue__default['default'](renderContext.nuxt)};`;
          inlineScripts.push(serializedSession);
        }
  
        if (shouldInjectScripts) {
          APP += `<script>${serializedSession}</script>`;
        }
      }
  
      // Calculate CSP hashes
      const cspScriptSrcHashes = csp.policies && csp.policies['script-src'] ? csp.policies['script-src'] : [];
      if (csp) {
        if (shouldHashCspScriptSrc) {
          for (const script of inlineScripts) {
            const hash = crypto__default['default'].createHash(csp.hashAlgorithm);
            hash.update(script);
            cspScriptSrcHashes.push(`'${csp.hashAlgorithm}-${hash.digest('base64')}'`);
          }
        }
  
        // Call ssr:csp hook
        await this.serverContext.nuxt.callHook('vue-renderer:ssr:csp', cspScriptSrcHashes);
  
        // Add csp meta tags
        if (csp.addMeta) {
          HEAD += `<meta http-equiv="Content-Security-Policy" content="script-src ${cspScriptSrcHashes.join()}">`;
        }
      }
  
      // Prepend scripts
      if (shouldInjectScripts) {
        APP += this.renderScripts(renderContext);
      }
  
      if (meta) {
        const appendInjectorOptions = { body: true };
  
        // Append body scripts
        APP += meta.meta.text(appendInjectorOptions);
        APP += meta.link.text(appendInjectorOptions);
        APP += meta.style.text(appendInjectorOptions);
        APP += meta.script.text(appendInjectorOptions);
        APP += meta.noscript.text(appendInjectorOptions);
      }
  
      // Template params
      const templateParams = {
        HTML_ATTRS: meta ? meta.htmlAttrs.text(renderContext.nuxt.serverRendered /* addSrrAttribute */) : '',
        HEAD_ATTRS: meta ? meta.headAttrs.text() : '',
        BODY_ATTRS: meta ? meta.bodyAttrs.text() : '',
        HEAD,
        APP,
        ENV: this.options.env
      };
  
      // Call ssr:templateParams hook
      await this.serverContext.nuxt.callHook('vue-renderer:ssr:templateParams', templateParams, renderContext);
  
      // Render with SSR template
      const html = this.renderTemplate(this.serverContext.resources.ssrTemplate, templateParams);
  
      let preloadFiles;
      if (this.options.render.http2.push) {
        preloadFiles = this.getPreloadFiles(renderContext);
      }
  
      return {
        html,
        cspScriptSrcHashes,
        preloadFiles,
        error: renderContext.nuxt.error,
        redirected: renderContext.redirected
      }
    }
  }
  
  class ModernRenderer extends SSRRenderer {
    constructor (serverContext) {
      super(serverContext);
  
      const { build: { publicPath }, router: { base } } = this.options;
      this.publicPath = utils.isUrl(publicPath) ? publicPath : utils.urlJoin(base, publicPath);
    }
  
    get assetsMapping () {
      if (this._assetsMapping) {
        return this._assetsMapping
      }
  
      const { clientManifest, modernManifest } = this.serverContext.resources;
      const legacyAssets = clientManifest.assetsMapping;
      const modernAssets = modernManifest.assetsMapping;
      const mapping = {};
  
      Object.keys(legacyAssets).forEach((componentHash) => {
        const modernComponentAssets = modernAssets[componentHash] || [];
        legacyAssets[componentHash].forEach((legacyAssetName, index) => {
          mapping[legacyAssetName] = modernComponentAssets[index];
        });
      });
      delete clientManifest.assetsMapping;
      delete modernManifest.assetsMapping;
      this._assetsMapping = mapping;
  
      return mapping
    }
  
    get isServerMode () {
      return this.options.modern === 'server'
    }
  
    get rendererOptions () {
      const rendererOptions = super.rendererOptions;
      if (this.isServerMode) {
        rendererOptions.clientManifest = this.serverContext.resources.modernManifest;
      }
      return rendererOptions
    }
  
    renderScripts (renderContext) {
      const scripts = super.renderScripts(renderContext);
  
      if (this.isServerMode) {
        return scripts
      }
  
      const scriptPattern = /<script[^>]*?src="([^"]*?)" defer( async)?><\/script>/g;
  
      const modernScripts = scripts.replace(scriptPattern, (scriptTag, jsFile) => {
        const legacyJsFile = jsFile.replace(this.publicPath, '');
        const modernJsFile = this.assetsMapping[legacyJsFile];
        if (!modernJsFile) {
          return scriptTag.replace('<script', `<script nomodule`)
        }
        const moduleTag = scriptTag
          .replace('<script', `<script type="module"`)
          .replace(legacyJsFile, modernJsFile);
        const noModuleTag = scriptTag.replace('<script', `<script nomodule`);
  
        return noModuleTag + moduleTag
      });
  
      const safariNoModuleFixScript = `<script>${utils.safariNoModuleFix}</script>`;
  
      return safariNoModuleFixScript + modernScripts
    }
  
    getModernFiles (legacyFiles = []) {
      const modernFiles = [];
  
      for (const legacyJsFile of legacyFiles) {
        const modernFile = { ...legacyJsFile, modern: true };
        if (modernFile.asType === 'script') {
          const file = this.assetsMapping[legacyJsFile.file];
          modernFile.file = file;
          modernFile.fileWithoutQuery = file.replace(/\?.*/, '');
        }
        modernFiles.push(modernFile);
      }
  
      return modernFiles
    }
  
    getPreloadFiles (renderContext) {
      const preloadFiles = super.getPreloadFiles(renderContext);
      // In eligible server modern mode, preloadFiles are modern bundles from modern renderer
      return this.isServerMode ? preloadFiles : this.getModernFiles(preloadFiles)
    }
  
    renderResourceHints (renderContext) {
      const resourceHints = super.renderResourceHints(renderContext);
      if (this.isServerMode) {
        return resourceHints
      }
  
      const linkPattern = /<link[^>]*?href="([^"]*?)"[^>]*?as="script"[^>]*?>/g;
  
      return resourceHints.replace(linkPattern, (linkTag, jsFile) => {
        const legacyJsFile = jsFile.replace(this.publicPath, '');
        const modernJsFile = this.assetsMapping[legacyJsFile];
        if (!modernJsFile) {
          return ''
        }
        return linkTag
          .replace('rel="preload"', `rel="modulepreload"`)
          .replace(legacyJsFile, modernJsFile)
      })
    }
  
    render (renderContext) {
      if (this.isServerMode) {
        renderContext.res.setHeader('Vary', 'User-Agent');
      }
      return super.render(renderContext)
    }
  }
  
  class VueRenderer {
    constructor (context) {
      this.serverContext = context;
      this.options = this.serverContext.options;
  
      // Will be set by createRenderer
      this.renderer = {
        ssr: undefined,
        modern: undefined,
        spa: undefined
      };
  
      // Renderer runtime resources
      Object.assign(this.serverContext.resources, {
        clientManifest: undefined,
        modernManifest: undefined,
        serverManifest: undefined,
        ssrTemplate: undefined,
        spaTemplate: undefined,
        errorTemplate: this.parseTemplate('Nuxt Internal Server Error')
      });
  
      // Default status
      this._state = 'created';
      this._error = null;
    }
  
    ready () {
      if (!this._readyPromise) {
        this._state = 'loading';
        this._readyPromise = this._ready()
          .then(() => {
            this._state = 'ready';
            return this
          })
          .catch((error) => {
            this._state = 'error';
            this._error = error;
            throw error
          });
      }
  
      return this._readyPromise
    }
  
    async _ready () {
      // Resolve dist path
      this.distPath = path__default['default'].resolve(this.options.buildDir, 'dist', 'server');
  
      // -- Development mode --
      if (this.options.dev) {
        this.serverContext.nuxt.hook('build:resources', mfs => this.loadResources(mfs));
        return
      }
  
      // -- Production mode --
  
      // Try once to load SSR resources from fs
      await this.loadResources(fs__default['default']);
  
      // Without using `nuxt start` (programmatic, tests and generate)
      if (!this.options._start) {
        this.serverContext.nuxt.hook('build:resources', () => this.loadResources(fs__default['default']));
        return
      }
  
      // Verify resources
      if (this.options.modern && !this.isModernReady) {
        throw new Error(
          `No modern build files found in ${this.distPath}.\nUse either \`nuxt build --modern\` or \`modern\` option to build modern files.`
        )
      } else if (!this.isReady) {
        throw new Error(
          `No build files found in ${this.distPath}.\nUse either \`nuxt build\` or \`builder.build()\` or start nuxt in development mode.`
        )
      }
    }
  
    async loadResources (_fs) {
      const updated = [];
  
      const readResource = async (fileName, encoding) => {
        try {
          const fullPath = path__default['default'].resolve(this.distPath, fileName);
  
          if (!await _fs.exists(fullPath)) {
            return
          }
          const contents = await _fs.readFile(fullPath, encoding);
  
          return contents
        } catch (err) {
          consola__default['default'].error('Unable to load resource:', fileName, err);
        }
      };
  
      for (const resourceName in this.resourceMap) {
        const { fileName, transform, encoding } = this.resourceMap[resourceName];
  
        // Load resource
        let resource = await readResource(fileName, encoding);
  
        // Skip unavailable resources
        if (!resource) {
          continue
        }
  
        // Apply transforms
        if (typeof transform === 'function') {
          resource = await transform(resource, { readResource });
        }
  
        // Update resource
        this.serverContext.resources[resourceName] = resource;
        updated.push(resourceName);
      }
  
      // Load templates
      await this.loadTemplates();
  
      await this.serverContext.nuxt.callHook('render:resourcesLoaded', this.serverContext.resources);
  
      // Detect if any resource updated
      if (updated.length > 0) {
        // Create new renderer
        this.createRenderer();
      }
    }
  
    async loadTemplates () {
      // Reload error template
      const errorTemplatePath = path__default['default'].resolve(this.options.buildDir, 'views/error.html');
  
      if (await fs__default['default'].exists(errorTemplatePath)) {
        const errorTemplate = await fs__default['default'].readFile(errorTemplatePath, 'utf8');
        this.serverContext.resources.errorTemplate = this.parseTemplate(errorTemplate);
      }
  
      // Reload loading template
      const loadingHTMLPath = path__default['default'].resolve(this.options.buildDir, 'loading.html');
  
      if (await fs__default['default'].exists(loadingHTMLPath)) {
        this.serverContext.resources.loadingHTML = await fs__default['default'].readFile(loadingHTMLPath, 'utf8');
        this.serverContext.resources.loadingHTML = this.serverContext.resources.loadingHTML.replace(/\r|\n|[\t\s]{3,}/g, '');
      } else {
        this.serverContext.resources.loadingHTML = '';
      }
    }
  
    // TODO: Remove in Nuxt 3
    get noSSR () { /* Backward compatibility */
      return this.options.render.ssr === false
    }
  
    get SSR () {
      return this.options.render.ssr === true
    }
  
    get isReady () {
      // SPA
      if (!this.serverContext.resources.spaTemplate || !this.renderer.spa) {
        return false
      }
  
      // SSR
      if (this.SSR && (!this.serverContext.resources.ssrTemplate || !this.renderer.ssr)) {
        return false
      }
  
      return true
    }
  
    get isModernReady () {
      return this.isReady && this.serverContext.resources.modernManifest
    }
  
    // TODO: Remove in Nuxt 3
    get isResourcesAvailable () { /* Backward compatibility */
      return this.isReady
    }
  
    detectModernBuild () {
      const { options, resources } = this.serverContext;
      if ([false, 'client', 'server'].includes(options.modern)) {
        return
      }
  
      const isExplicitStaticModern = options.target === utils.TARGETS.static && options.modern;
      if (!resources.modernManifest && !isExplicitStaticModern) {
        options.modern = false;
        return
      }
  
      options.modern = options.render.ssr ? 'server' : 'client';
      consola__default['default'].info(`Modern bundles are detected. Modern mode (\`${options.modern}\`) is enabled now.`);
    }
  
    createRenderer () {
      // Resource clientManifest is always required
      if (!this.serverContext.resources.clientManifest) {
        return
      }
  
      this.detectModernBuild();
  
      // Create SPA renderer
      if (this.serverContext.resources.spaTemplate) {
        this.renderer.spa = new SPARenderer(this.serverContext);
      }
  
      // Skip the rest if SSR resources are not available
      if (this.serverContext.resources.ssrTemplate && this.serverContext.resources.serverManifest) {
        // Create bundle renderer for SSR
        this.renderer.ssr = new SSRRenderer(this.serverContext);
  
        if (this.options.modern !== false) {
          this.renderer.modern = new ModernRenderer(this.serverContext);
        }
      }
    }
  
    renderSPA (renderContext) {
      return this.renderer.spa.render(renderContext)
    }
  
    renderSSR (renderContext) {
      // Call renderToString from the bundleRenderer and generate the HTML (will update the renderContext as well)
      const renderer = renderContext.modern ? this.renderer.modern : this.renderer.ssr;
      return renderer.render(renderContext)
    }
  
    async renderRoute (url, renderContext = {}, _retried = 0) {
      /* istanbul ignore if */
      if (!this.isReady) {
        // Fall-back to loading-screen if enabled
        if (this.options.build.loadingScreen) {
          // Tell nuxt middleware to use `server:nuxt:renderLoading hook
          return false
        }
  
        // Retry
        const retryLimit = this.options.dev ? 60 : 3;
        if (_retried < retryLimit && this._state !== 'error') {
          await this.ready().then(() => utils.waitFor(1000));
          return this.renderRoute(url, renderContext, _retried + 1)
        }
  
        // Throw Error
        switch (this._state) {
          case 'created':
            throw new Error('Renderer ready() is not called! Please ensure `nuxt.ready()` is called and awaited.')
          case 'loading':
            throw new Error('Renderer is loading.')
          case 'error':
            throw this._error
          case 'ready':
            throw new Error(`Renderer resources are not loaded! Please check possible console errors and ensure dist (${this.distPath}) exists.`)
          default:
            throw new Error('Renderer is in unknown state!')
        }
      }
  
      // Log rendered url
      consola__default['default'].debug(`Rendering url ${url}`);
  
      // Add url to the renderContext
      renderContext.url = ufo.normalizeURL(url);
  
      // Add target to the renderContext
      renderContext.target = this.options.target;
  
      const { req = {}, res = {} } = renderContext;
  
      // renderContext.spa
      if (renderContext.spa === undefined) {
        // TODO: Remove reading from renderContext.res in Nuxt3
        renderContext.spa = !this.SSR || req.spa || res.spa;
      }
  
      // renderContext.modern
      if (renderContext.modern === undefined) {
        const modernMode = this.options.modern;
        renderContext.modern = modernMode === 'client' || utils.isModernRequest(req, modernMode);
      }
  
      // Set runtime config on renderContext
      renderContext.runtimeConfig = {
        private: renderContext.spa ? {} : { ...this.options.privateRuntimeConfig },
        public: { ...this.options.publicRuntimeConfig }
      };
  
      // Call renderContext hook
      await this.serverContext.nuxt.callHook('vue-renderer:context', renderContext);
  
      // Render SPA or SSR
      return renderContext.spa
        ? this.renderSPA(renderContext)
        : this.renderSSR(renderContext)
    }
  
    get resourceMap () {
      const publicPath = utils.urlJoin(this.options.app.cdnURL || '/', this.options.app.assetsPath);
      return {
        clientManifest: {
          fileName: 'client.manifest.json',
          transform: src => Object.assign(JSON.parse(src), { publicPath })
        },
        modernManifest: {
          fileName: 'modern.manifest.json',
          transform: src => Object.assign(JSON.parse(src), { publicPath })
        },
        serverManifest: {
          fileName: 'server.manifest.json',
          // BundleRenderer needs resolved contents
          transform: async (src, { readResource }) => {
            const serverManifest = JSON.parse(src);
  
            const readResources = async (obj) => {
              const _obj = {};
              await Promise.all(Object.keys(obj).map(async (key) => {
                _obj[key] = await readResource(obj[key]);
              }));
              return _obj
            };
  
            const [files, maps] = await Promise.all([
              readResources(serverManifest.files),
              readResources(serverManifest.maps)
            ]);
  
            // Try to parse sourcemaps
            for (const map in maps) {
              if (maps[map] && maps[map].version) {
                continue
              }
              try {
                maps[map] = JSON.parse(maps[map]);
              } catch (e) {
                maps[map] = { version: 3, sources: [], mappings: '' };
              }
            }
  
            return {
              ...serverManifest,
              files,
              maps
            }
          }
        },
        ssrTemplate: {
          fileName: 'index.ssr.html',
          transform: src => this.parseTemplate(src)
        },
        spaTemplate: {
          fileName: 'index.spa.html',
          transform: src => this.parseTemplate(src)
        }
      }
    }
  
    parseTemplate (templateStr) {
      return lodash.template(templateStr, {
        interpolate: /{{([\s\S]+?)}}/g,
        evaluate: /{%([\s\S]+?)%}/g
      })
    }
  
    close () {
      if (this.__closed) {
        return
      }
      this.__closed = true;
  
      for (const key in this.renderer) {
        delete this.renderer[key];
      }
    }
  }
  
  exports.VueRenderer = VueRenderer;
  