"use strict";exports.__esModule=true;exports.renderScriptError=renderScriptError;exports.default=void 0;var _middleware=require("@next/react-dev-overlay/lib/middleware");var _webpackHotMiddleware=_interopRequireDefault(require("next/dist/compiled/webpack-hot-middleware"));var _path=require("path");var _webpack=_interopRequireDefault(require("webpack"));var _entries=require("../build/entries");var _output=require("../build/output");var _webpackConfig=_interopRequireDefault(require("../build/webpack-config"));var _constants=require("../lib/constants");var _recursiveDelete=require("../lib/recursive-delete");var _constants2=require("../next-server/lib/constants");var _router=require("../next-server/server/router");var _errorOverlayMiddleware=_interopRequireDefault(require("./lib/error-overlay-middleware"));var _findPageFile=require("./lib/find-page-file");var _onDemandEntryHandler=_interopRequireWildcard(require("./on-demand-entry-handler"));var _normalizePagePath=require("../next-server/server/normalize-page-path");var _getRouteFromEntrypoint=_interopRequireDefault(require("../next-server/server/get-route-from-entrypoint"));var _isWriteable=require("../build/is-writeable");var _querystring=require("querystring");function _getRequireWildcardCache(){if(typeof WeakMap!=="function")return null;var cache=new WeakMap();_getRequireWildcardCache=function(){return cache;};return cache;}function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj;}if(obj===null||typeof obj!=="object"&&typeof obj!=="function"){return{default:obj};}var cache=_getRequireWildcardCache();if(cache&&cache.has(obj)){return cache.get(obj);}var newObj={};var hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;if(desc&&(desc.get||desc.set)){Object.defineProperty(newObj,key,desc);}else{newObj[key]=obj[key];}}}newObj.default=obj;if(cache){cache.set(obj,newObj);}return newObj;}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}async function renderScriptError(res,error){// Asks CDNs and others to not to cache the errored page
res.setHeader('Cache-Control','no-cache, no-store, max-age=0, must-revalidate');if(error.code==='ENOENT'||error.message==='INVALID_BUILD_ID'){res.statusCode=404;res.end('404 - Not Found');return;}console.error(error.stack);res.statusCode=500;res.end('500 - Internal Error');}function addCorsSupport(req,res){const isApiRoute=req.url.match(_constants.API_ROUTE);// API routes handle their own CORS headers
if(isApiRoute){return{preflight:false};}if(!req.headers.origin){return{preflight:false};}res.setHeader('Access-Control-Allow-Origin',req.headers.origin);res.setHeader('Access-Control-Allow-Methods','OPTIONS, GET');// Based on https://github.com/primus/access-control/blob/4cf1bc0e54b086c91e6aa44fb14966fa5ef7549c/index.js#L158
if(req.headers['access-control-request-headers']){res.setHeader('Access-Control-Allow-Headers',req.headers['access-control-request-headers']);}if(req.method==='OPTIONS'){res.writeHead(200);res.end();return{preflight:true};}return{preflight:false};}const matchNextPageBundleRequest=(0,_router.route)('/_next/static/pages/:path*.js(\\.map|)');// Recursively look up the issuer till it ends up at the root
function findEntryModule(issuer){if(issuer.issuer){return findEntryModule(issuer.issuer);}return issuer;}function erroredPages(compilation){const failedPages={};for(const error of compilation.errors){if(!error.origin){continue;}const entryModule=findEntryModule(error.origin);const{name}=entryModule;if(!name){continue;}// Only pages have to be reloaded
if(!(0,_getRouteFromEntrypoint.default)(name)){continue;}const enhancedName=(0,_getRouteFromEntrypoint.default)(name);if(!enhancedName){continue;}if(!failedPages[enhancedName]){failedPages[enhancedName]=[];}failedPages[enhancedName].push(error);}return failedPages;}class HotReloader{constructor(dir,{config,pagesDir,buildId,previewProps}){this.dir=void 0;this.buildId=void 0;this.middlewares=void 0;this.pagesDir=void 0;this.webpackHotMiddleware=void 0;this.config=void 0;this.stats=void 0;this.serverStats=void 0;this.serverPrevDocumentHash=void 0;this.prevChunkNames=void 0;this.onDemandEntries=void 0;this.previewProps=void 0;this.watcher=void 0;this.buildId=buildId;this.dir=dir;this.middlewares=[];this.pagesDir=pagesDir;this.webpackHotMiddleware=null;this.stats=null;this.serverStats=null;this.serverPrevDocumentHash=null;this.config=config;this.previewProps=previewProps;}async run(req,res,parsedUrl){// Usually CORS support is not needed for the hot-reloader (this is dev only feature)
// With when the app runs for multi-zones support behind a proxy,
// the current page is trying to access this URL via assetPrefix.
// That's when the CORS support is needed.
const{preflight}=addCorsSupport(req,res);if(preflight){return;}// When a request comes in that is a page bundle, e.g. /_next/static/<buildid>/pages/index.js
// we have to compile the page using on-demand-entries, this middleware will handle doing that
// by adding the page to on-demand-entries, waiting till it's done
// and then the bundle will be served like usual by the actual route in server/index.js
const handlePageBundleRequest=async(pageBundleRes,parsedPageBundleUrl)=>{const{pathname}=parsedPageBundleUrl;const params=matchNextPageBundleRequest(pathname);if(!params){return{};}const page=(0,_normalizePagePath.denormalizePagePath)(`/${params.path.join('/')}`);if(page==='/_error'||_constants2.BLOCKED_PAGES.indexOf(page)===-1){try{await this.ensurePage(page);}catch(error){await renderScriptError(pageBundleRes,error);return{finished:true};}const errors=await this.getCompilationErrors(page);if(errors.length>0){await renderScriptError(pageBundleRes,errors[0]);return{finished:true};}}return{};};const{finished}=await handlePageBundleRequest(res,parsedUrl);for(const fn of this.middlewares){await new Promise((resolve,reject)=>{fn(req,res,err=>{if(err)return reject(err);resolve();});});}return{finished};}async clean(){return(0,_recursiveDelete.recursiveDelete)((0,_path.join)(this.dir,this.config.distDir),/^cache/);}async getWebpackConfig(){const pagePaths=await Promise.all([(0,_findPageFile.findPageFile)(this.pagesDir,'/_app',this.config.pageExtensions),(0,_findPageFile.findPageFile)(this.pagesDir,'/_document',this.config.pageExtensions)]);const pages=(0,_entries.createPagesMapping)(pagePaths.filter(i=>i!==null),this.config.pageExtensions);const entrypoints=(0,_entries.createEntrypoints)(pages,'server',this.buildId,this.previewProps,this.config,[]);let additionalClientEntrypoints={};additionalClientEntrypoints[_constants2.CLIENT_STATIC_FILES_RUNTIME_AMP]=`.${_path.sep}`+(0,_path.relative)(this.dir,(0,_path.join)(_constants.NEXT_PROJECT_ROOT_DIST_CLIENT,'dev','amp-dev'));additionalClientEntrypoints[_constants2.CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH]=require.resolve(`@next/react-refresh-utils/runtime`);return Promise.all([(0,_webpackConfig.default)(this.dir,{dev:true,isServer:false,config:this.config,buildId:this.buildId,pagesDir:this.pagesDir,entrypoints:{...entrypoints.client,...additionalClientEntrypoints}}),(0,_webpackConfig.default)(this.dir,{dev:true,isServer:true,config:this.config,buildId:this.buildId,pagesDir:this.pagesDir,entrypoints:entrypoints.server})]);}async start(){await this.clean();const configs=await this.getWebpackConfig();for(const config of configs){const defaultEntry=config.entry;config.entry=async(...args)=>{// @ts-ignore entry is always a functon
const entrypoints=await defaultEntry(...args);const isClientCompilation=config.name==='client';await Promise.all(Object.keys(_onDemandEntryHandler.entries).map(async page=>{if(isClientCompilation&&page.match(_constants.API_ROUTE)){return;}const{serverBundlePath,clientBundlePath,absolutePagePath}=_onDemandEntryHandler.entries[page];const pageExists=await(0,_isWriteable.isWriteable)(absolutePagePath);if(!pageExists){// page was removed
delete _onDemandEntryHandler.entries[page];return;}_onDemandEntryHandler.entries[page].status=_onDemandEntryHandler.BUILDING;const pageLoaderOpts={page,absolutePagePath};entrypoints[isClientCompilation?clientBundlePath:serverBundlePath]=isClientCompilation?`next-client-pages-loader?${(0,_querystring.stringify)(pageLoaderOpts)}!`:absolutePagePath;}));return entrypoints;};}const multiCompiler=(0,_webpack.default)(configs);(0,_output.watchCompilers)(multiCompiler.compilers[0],multiCompiler.compilers[1]);// This plugin watches for changes to _document.js and notifies the client side that it should reload the page
multiCompiler.compilers[1].hooks.done.tap('NextjsHotReloaderForServer',stats=>{this.serverStats=stats;const{compilation}=stats;// We only watch `_document` for changes on the server compilation
// the rest of the files will be triggered by the client compilation
const documentChunk=compilation.chunks.find(c=>c.name===(0,_path.normalize)(`pages/_document`));// If the document chunk can't be found we do nothing
if(!documentChunk){console.warn('_document.js chunk not found');return;}// Initial value
if(this.serverPrevDocumentHash===null){this.serverPrevDocumentHash=documentChunk.hash;return;}// If _document.js didn't change we don't trigger a reload
if(documentChunk.hash===this.serverPrevDocumentHash){return;}// Notify reload to reload the page, as _document.js was changed (different hash)
this.send('reloadPage');this.serverPrevDocumentHash=documentChunk.hash;});multiCompiler.compilers[0].hooks.done.tap('NextjsHotReloaderForClient',stats=>{const{compilation}=stats;const chunkNames=new Set(compilation.chunks.map(c=>c.name).filter(name=>!!(0,_getRouteFromEntrypoint.default)(name)));if(this.prevChunkNames){// detect chunks which have to be replaced with a new template
// e.g, pages/index.js <-> pages/_error.js
const addedPages=diff(chunkNames,this.prevChunkNames);const removedPages=diff(this.prevChunkNames,chunkNames);if(addedPages.size>0){for(const addedPage of addedPages){const page=(0,_getRouteFromEntrypoint.default)(addedPage);this.send('addedPage',page);}}if(removedPages.size>0){for(const removedPage of removedPages){const page=(0,_getRouteFromEntrypoint.default)(removedPage);this.send('removedPage',page);}}}this.stats=stats;this.prevChunkNames=chunkNames;});this.webpackHotMiddleware=(0,_webpackHotMiddleware.default)(multiCompiler.compilers[0],{path:'/_next/webpack-hmr',log:false,heartbeat:2500});let booted=false;this.watcher=await new Promise(resolve=>{const watcher=multiCompiler.watch(// @ts-ignore webpack supports an array of watchOptions when using a multiCompiler
configs.map(config=>config.watchOptions),// Errors are handled separately
_err=>{if(!booted){booted=true;resolve(watcher);}});});this.onDemandEntries=(0,_onDemandEntryHandler.default)(this.watcher,multiCompiler,{pagesDir:this.pagesDir,pageExtensions:this.config.pageExtensions,...this.config.onDemandEntries});this.middlewares=[// must come before hotMiddleware
this.onDemandEntries.middleware(),this.webpackHotMiddleware,(0,_errorOverlayMiddleware.default)({dir:this.dir}),(0,_middleware.getOverlayMiddleware)({rootDirectory:this.dir,stats:()=>this.stats,serverStats:()=>this.serverStats})];}async stop(){return new Promise((resolve,reject)=>{this.watcher.close(err=>err?reject(err):resolve());});}async getCompilationErrors(page){const normalizedPage=(0,_normalizePagePath.normalizePathSep)(page);if(this.stats.hasErrors()){const{compilation}=this.stats;const failedPages=erroredPages(compilation);// If there is an error related to the requesting page we display it instead of the first error
if(failedPages[normalizedPage]&&failedPages[normalizedPage].length>0){return failedPages[normalizedPage];}// If none were found we still have to show the other errors
return this.stats.compilation.errors;}return[];}send(action,...args){this.webpackHotMiddleware.publish({action,data:args});}async ensurePage(page){// Make sure we don't re-build or dispose prebuilt pages
if(page!=='/_error'&&_constants2.BLOCKED_PAGES.indexOf(page)!==-1){return;}return this.onDemandEntries.ensurePage(page);}}exports.default=HotReloader;function diff(a,b){return new Set([...a].filter(v=>!b.has(v)));}
//# sourceMappingURL=hot-reloader.js.map