import invert from 'lodash/invert' import { isUrl, urlJoin } from '@nuxt/utils' import SSRRenderer from './ssr' export default class ModernRenderer extends SSRRenderer { constructor(serverContext) { super(serverContext) const { build: { publicPath }, router: { base } } = this.options this.publicPath = isUrl(publicPath) ? publicPath : urlJoin(base, publicPath) } get assetsMapping() { if (this._assetsMapping) { return this._assetsMapping } const { clientManifest, modernManifest } = this.serverContext.resources const legacyAssets = clientManifest.assetsMapping const modernAssets = invert(modernManifest.assetsMapping) const mapping = {} for (const legacyJsFile in legacyAssets) { const chunkNamesHash = legacyAssets[legacyJsFile] mapping[legacyJsFile] = modernAssets[chunkNamesHash] } 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 = /]*?src="([^"]*?)"[^>]*?>[^<]*?<\/script>/g return scripts.replace(scriptPattern, (scriptTag, jsFile) => { const legacyJsFile = jsFile.replace(this.publicPath, '') const modernJsFile = this.assetsMapping[legacyJsFile] const { build: { crossorigin } } = this.options const cors = `${crossorigin ? ` crossorigin="${crossorigin}"` : ''}` const moduleTag = modernJsFile ? scriptTag .replace(']*?href="([^"]*?)"[^>]*?as="script"[^>]*?>/g return resourceHints.replace(linkPattern, (linkTag, jsFile) => { const legacyJsFile = jsFile.replace(this.publicPath, '') const modernJsFile = this.assetsMapping[legacyJsFile] if (!modernJsFile) { return '' } const { crossorigin } = this.options.build const cors = `${crossorigin ? ` crossorigin="${crossorigin}"` : ''}` return linkTag.replace('rel="preload"', `rel="modulepreload"${cors}`).replace(legacyJsFile, modernJsFile) }) } async render(renderContext) { const result = await super.render(renderContext) if (this.isServerMode) { renderContext.res.setHeader('Vary', 'User-Agent') } return result } }