hot-reloader.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.renderScriptError = renderScriptError;
  6. exports.default = void 0;
  7. var _webpack = require("next/dist/compiled/webpack/webpack");
  8. var _middleware = require("next/dist/compiled/@next/react-dev-overlay/dist/middleware");
  9. var _hotMiddleware = require("./hot-middleware");
  10. var _path = require("path");
  11. var _entries = require("../../build/entries");
  12. var _output = require("../../build/output");
  13. var Log = _interopRequireWildcard(require("../../build/output/log"));
  14. var _webpackConfig = _interopRequireDefault(require("../../build/webpack-config"));
  15. var _constants = require("../../lib/constants");
  16. var _recursiveDelete = require("../../lib/recursive-delete");
  17. var _constants1 = require("../../shared/lib/constants");
  18. var _pathMatch = require("../../shared/lib/router/utils/path-match");
  19. var _findPageFile = require("../lib/find-page-file");
  20. var _onDemandEntryHandler = require("./on-demand-entry-handler");
  21. var _denormalizePagePath = require("../../shared/lib/page-path/denormalize-page-path");
  22. var _normalizePathSep = require("../../shared/lib/page-path/normalize-path-sep");
  23. var _getRouteFromEntrypoint = _interopRequireDefault(require("../get-route-from-entrypoint"));
  24. var _fileExists = require("../../lib/file-exists");
  25. var _utils = require("../../build/utils");
  26. var _utils1 = require("../../shared/lib/utils");
  27. var _trace = require("../../trace");
  28. var _isError = require("../../lib/is-error");
  29. var _ws = _interopRequireDefault(require("next/dist/compiled/ws"));
  30. var _fs = require("fs");
  31. var _getPageStaticInfo = require("../../build/analysis/get-page-static-info");
  32. class HotReloader {
  33. clientError = null;
  34. serverError = null;
  35. pagesMapping = {};
  36. constructor(dir, { config , pagesDir , distDir , buildId , previewProps , rewrites , appDir }){
  37. this.buildId = buildId;
  38. this.dir = dir;
  39. this.interceptors = [];
  40. this.pagesDir = pagesDir;
  41. this.appDir = appDir;
  42. this.distDir = distDir;
  43. this.clientStats = null;
  44. this.serverStats = null;
  45. this.edgeServerStats = null;
  46. this.serverPrevDocumentHash = null;
  47. this.config = config;
  48. this.hasReactRoot = !!process.env.__NEXT_REACT_ROOT;
  49. this.hasServerComponents = this.hasReactRoot && !!config.experimental.serverComponents;
  50. this.previewProps = previewProps;
  51. this.rewrites = rewrites;
  52. this.hotReloaderSpan = (0, _trace).trace("hot-reloader", undefined, {
  53. version: "12.3.4"
  54. });
  55. // Ensure the hotReloaderSpan is flushed immediately as it's the parentSpan for all processing
  56. // of the current `next dev` invocation.
  57. this.hotReloaderSpan.stop();
  58. }
  59. async run(req, res, parsedUrl) {
  60. // Usually CORS support is not needed for the hot-reloader (this is dev only feature)
  61. // With when the app runs for multi-zones support behind a proxy,
  62. // the current page is trying to access this URL via assetPrefix.
  63. // That's when the CORS support is needed.
  64. const { preflight } = addCorsSupport(req, res);
  65. if (preflight) {
  66. return {};
  67. }
  68. // When a request comes in that is a page bundle, e.g. /_next/static/<buildid>/pages/index.js
  69. // we have to compile the page using on-demand-entries, this middleware will handle doing that
  70. // by adding the page to on-demand-entries, waiting till it's done
  71. // and then the bundle will be served like usual by the actual route in server/index.js
  72. const handlePageBundleRequest = async (pageBundleRes, parsedPageBundleUrl)=>{
  73. const { pathname } = parsedPageBundleUrl;
  74. const params = matchNextPageBundleRequest(pathname);
  75. if (!params) {
  76. return {};
  77. }
  78. let decodedPagePath;
  79. try {
  80. decodedPagePath = `/${params.path.map((param)=>decodeURIComponent(param)).join("/")}`;
  81. } catch (_) {
  82. throw new _utils1.DecodeError("failed to decode param");
  83. }
  84. const page = (0, _denormalizePagePath).denormalizePagePath(decodedPagePath);
  85. if (page === "/_error" || _constants1.BLOCKED_PAGES.indexOf(page) === -1) {
  86. try {
  87. await this.ensurePage({
  88. page,
  89. clientOnly: true
  90. });
  91. } catch (error) {
  92. await renderScriptError(pageBundleRes, (0, _isError).getProperError(error));
  93. return {
  94. finished: true
  95. };
  96. }
  97. const errors = await this.getCompilationErrors(page);
  98. if (errors.length > 0) {
  99. await renderScriptError(pageBundleRes, errors[0], {
  100. verbose: false
  101. });
  102. return {
  103. finished: true
  104. };
  105. }
  106. }
  107. return {};
  108. };
  109. const { finished } = await handlePageBundleRequest(res, parsedUrl);
  110. for (const fn of this.interceptors){
  111. await new Promise((resolve, reject)=>{
  112. fn(req, res, (err)=>{
  113. if (err) return reject(err);
  114. resolve();
  115. });
  116. });
  117. }
  118. return {
  119. finished
  120. };
  121. }
  122. onHMR(req, _res, head) {
  123. wsServer.handleUpgrade(req, req.socket, head, (client)=>{
  124. var ref, ref1;
  125. (ref = this.webpackHotMiddleware) == null ? void 0 : ref.onHMR(client);
  126. (ref1 = this.onDemandEntries) == null ? void 0 : ref1.onHMR(client);
  127. client.addEventListener("message", ({ data })=>{
  128. data = typeof data !== "string" ? data.toString() : data;
  129. try {
  130. const payload = JSON.parse(data);
  131. let traceChild;
  132. switch(payload.event){
  133. case "client-hmr-latency":
  134. {
  135. traceChild = {
  136. name: payload.event,
  137. startTime: BigInt(payload.startTime * 1000 * 1000),
  138. endTime: BigInt(payload.endTime * 1000 * 1000)
  139. };
  140. break;
  141. }
  142. case "client-reload-page":
  143. case "client-success":
  144. {
  145. traceChild = {
  146. name: payload.event
  147. };
  148. break;
  149. }
  150. case "client-error":
  151. {
  152. traceChild = {
  153. name: payload.event,
  154. attrs: {
  155. errorCount: payload.errorCount
  156. }
  157. };
  158. break;
  159. }
  160. case "client-warning":
  161. {
  162. traceChild = {
  163. name: payload.event,
  164. attrs: {
  165. warningCount: payload.warningCount
  166. }
  167. };
  168. break;
  169. }
  170. case "client-removed-page":
  171. case "client-added-page":
  172. {
  173. traceChild = {
  174. name: payload.event,
  175. attrs: {
  176. page: payload.page || ""
  177. }
  178. };
  179. break;
  180. }
  181. case "client-full-reload":
  182. {
  183. var _stackTrace;
  184. traceChild = {
  185. name: payload.event,
  186. attrs: {
  187. stackTrace: (_stackTrace = payload.stackTrace) != null ? _stackTrace : ""
  188. }
  189. };
  190. Log.warn("Fast Refresh had to perform a full reload. Read more: https://nextjs.org/docs/basic-features/fast-refresh#how-it-works");
  191. if (payload.stackTrace) {
  192. console.warn(payload.stackTrace);
  193. }
  194. break;
  195. }
  196. default:
  197. {
  198. break;
  199. }
  200. }
  201. if (traceChild) {
  202. this.hotReloaderSpan.manualTraceChild(traceChild.name, traceChild.startTime || process.hrtime.bigint(), traceChild.endTime || process.hrtime.bigint(), {
  203. ...traceChild.attrs,
  204. clientId: payload.id
  205. });
  206. }
  207. } catch (_) {
  208. // invalid WebSocket message
  209. }
  210. });
  211. });
  212. }
  213. async clean(span) {
  214. return span.traceChild("clean").traceAsyncFn(()=>(0, _recursiveDelete).recursiveDelete((0, _path).join(this.dir, this.config.distDir), /^cache/));
  215. }
  216. async getWebpackConfig(span) {
  217. const webpackConfigSpan = span.traceChild("get-webpack-config");
  218. const pageExtensions = this.config.pageExtensions;
  219. return webpackConfigSpan.traceAsyncFn(async ()=>{
  220. const pagePaths = !this.pagesDir ? [] : await webpackConfigSpan.traceChild("get-page-paths").traceAsyncFn(()=>Promise.all([
  221. (0, _findPageFile).findPageFile(this.pagesDir, "/_app", pageExtensions, false),
  222. (0, _findPageFile).findPageFile(this.pagesDir, "/_document", pageExtensions, false),
  223. ]));
  224. this.pagesMapping = webpackConfigSpan.traceChild("create-pages-mapping").traceFn(()=>(0, _entries).createPagesMapping({
  225. isDev: true,
  226. pageExtensions: this.config.pageExtensions,
  227. pagesType: "pages",
  228. pagePaths: pagePaths.filter((i)=>typeof i === "string"),
  229. pagesDir: this.pagesDir
  230. }));
  231. const entrypoints = await webpackConfigSpan.traceChild("create-entrypoints").traceAsyncFn(()=>(0, _entries).createEntrypoints({
  232. appDir: this.appDir,
  233. buildId: this.buildId,
  234. config: this.config,
  235. envFiles: [],
  236. isDev: true,
  237. pages: this.pagesMapping,
  238. pagesDir: this.pagesDir,
  239. previewMode: this.previewProps,
  240. rootDir: this.dir,
  241. target: "server",
  242. pageExtensions: this.config.pageExtensions
  243. }));
  244. const commonWebpackOptions = {
  245. dev: true,
  246. buildId: this.buildId,
  247. config: this.config,
  248. hasReactRoot: this.hasReactRoot,
  249. pagesDir: this.pagesDir,
  250. rewrites: this.rewrites,
  251. runWebpackSpan: this.hotReloaderSpan,
  252. appDir: this.appDir
  253. };
  254. return webpackConfigSpan.traceChild("generate-webpack-config").traceAsyncFn(()=>Promise.all([
  255. // order is important here
  256. (0, _webpackConfig).default(this.dir, {
  257. ...commonWebpackOptions,
  258. compilerType: _constants1.COMPILER_NAMES.client,
  259. entrypoints: entrypoints.client
  260. }),
  261. (0, _webpackConfig).default(this.dir, {
  262. ...commonWebpackOptions,
  263. compilerType: _constants1.COMPILER_NAMES.server,
  264. entrypoints: entrypoints.server
  265. }),
  266. (0, _webpackConfig).default(this.dir, {
  267. ...commonWebpackOptions,
  268. compilerType: _constants1.COMPILER_NAMES.edgeServer,
  269. entrypoints: entrypoints.edgeServer
  270. }),
  271. ]));
  272. });
  273. }
  274. async buildFallbackError() {
  275. if (this.fallbackWatcher) return;
  276. const fallbackConfig = await (0, _webpackConfig).default(this.dir, {
  277. runWebpackSpan: this.hotReloaderSpan,
  278. dev: true,
  279. compilerType: _constants1.COMPILER_NAMES.client,
  280. config: this.config,
  281. buildId: this.buildId,
  282. pagesDir: this.pagesDir,
  283. rewrites: {
  284. beforeFiles: [],
  285. afterFiles: [],
  286. fallback: []
  287. },
  288. isDevFallback: true,
  289. entrypoints: (await (0, _entries).createEntrypoints({
  290. appDir: this.appDir,
  291. buildId: this.buildId,
  292. config: this.config,
  293. envFiles: [],
  294. isDev: true,
  295. pages: {
  296. "/_app": "next/dist/pages/_app",
  297. "/_error": "next/dist/pages/_error"
  298. },
  299. pagesDir: this.pagesDir,
  300. previewMode: this.previewProps,
  301. rootDir: this.dir,
  302. target: "server",
  303. pageExtensions: this.config.pageExtensions
  304. })).client,
  305. hasReactRoot: this.hasReactRoot
  306. });
  307. const fallbackCompiler = (0, _webpack).webpack(fallbackConfig);
  308. this.fallbackWatcher = await new Promise((resolve)=>{
  309. let bootedFallbackCompiler = false;
  310. fallbackCompiler.watch(// @ts-ignore webpack supports an array of watchOptions when using a multiCompiler
  311. fallbackConfig.watchOptions, // Errors are handled separately
  312. (_err)=>{
  313. if (!bootedFallbackCompiler) {
  314. bootedFallbackCompiler = true;
  315. resolve(true);
  316. }
  317. });
  318. });
  319. }
  320. async start(initial) {
  321. const startSpan = this.hotReloaderSpan.traceChild("start");
  322. startSpan.stop() // Stop immediately to create an artificial parent span
  323. ;
  324. if (initial) {
  325. await this.clean(startSpan);
  326. // Ensure distDir exists before writing package.json
  327. await _fs.promises.mkdir(this.distDir, {
  328. recursive: true
  329. });
  330. const distPackageJsonPath = (0, _path).join(this.distDir, "package.json");
  331. // Ensure commonjs handling is used for files in the distDir (generally .next)
  332. // Files outside of the distDir can be "type": "module"
  333. await _fs.promises.writeFile(distPackageJsonPath, '{"type": "commonjs"}');
  334. }
  335. this.activeConfigs = await this.getWebpackConfig(startSpan);
  336. for (const config1 of this.activeConfigs){
  337. const defaultEntry = config1.entry;
  338. config1.entry = async (...args)=>{
  339. // @ts-ignore entry is always a function
  340. const entrypoints = await defaultEntry(...args);
  341. const isClientCompilation = config1.name === _constants1.COMPILER_NAMES.client;
  342. const isNodeServerCompilation = config1.name === _constants1.COMPILER_NAMES.server;
  343. const isEdgeServerCompilation = config1.name === _constants1.COMPILER_NAMES.edgeServer;
  344. await Promise.all(Object.keys(_onDemandEntryHandler.entries).map(async (entryKey)=>{
  345. const entryData = _onDemandEntryHandler.entries[entryKey];
  346. const { bundlePath , dispose } = entryData;
  347. const result = /^(client|server|edge-server)(.*)/g.exec(entryKey);
  348. const [, key, page] = result// this match should always happen
  349. ;
  350. if (key === _constants1.COMPILER_NAMES.client && !isClientCompilation) return;
  351. if (key === _constants1.COMPILER_NAMES.server && !isNodeServerCompilation) return;
  352. if (key === _constants1.COMPILER_NAMES.edgeServer && !isEdgeServerCompilation) return;
  353. const isEntry = entryData.type === _onDemandEntryHandler.EntryTypes.ENTRY;
  354. const isChildEntry = entryData.type === _onDemandEntryHandler.EntryTypes.CHILD_ENTRY;
  355. // Check if the page was removed or disposed and remove it
  356. if (isEntry) {
  357. const pageExists = !dispose && await (0, _fileExists).fileExists(entryData.absolutePagePath);
  358. if (!pageExists) {
  359. delete _onDemandEntryHandler.entries[entryKey];
  360. return;
  361. }
  362. }
  363. const isAppPath = !!this.appDir && bundlePath.startsWith("app/");
  364. const staticInfo = isEntry ? await (0, _getPageStaticInfo).getPageStaticInfo({
  365. pageFilePath: entryData.absolutePagePath,
  366. nextConfig: this.config,
  367. isDev: true
  368. }) : {};
  369. const isServerComponent = isAppPath && staticInfo.rsc !== _constants1.RSC_MODULE_TYPES.client;
  370. await (0, _entries).runDependingOnPageType({
  371. page,
  372. pageRuntime: staticInfo.runtime,
  373. onEdgeServer: ()=>{
  374. // TODO-APP: verify if child entry should support.
  375. if (!isEdgeServerCompilation || !isEntry) return;
  376. const appDirLoader = isAppPath && this.appDir ? (0, _entries).getAppEntry({
  377. name: bundlePath,
  378. appPaths: entryData.appPaths,
  379. pagePath: _path.posix.join(_constants.APP_DIR_ALIAS, (0, _path).relative(this.appDir, entryData.absolutePagePath).replace(/\\/g, "/")),
  380. appDir: this.appDir,
  381. pageExtensions: this.config.pageExtensions
  382. }).import : undefined;
  383. _onDemandEntryHandler.entries[entryKey].status = _onDemandEntryHandler.BUILDING;
  384. entrypoints[bundlePath] = (0, _entries).finalizeEntrypoint({
  385. compilerType: _constants1.COMPILER_NAMES.edgeServer,
  386. name: bundlePath,
  387. value: (0, _entries).getEdgeServerEntry({
  388. absolutePagePath: entryData.absolutePagePath,
  389. rootDir: this.dir,
  390. buildId: this.buildId,
  391. bundlePath,
  392. config: this.config,
  393. isDev: true,
  394. page,
  395. pages: this.pagesMapping,
  396. isServerComponent,
  397. appDirLoader,
  398. pagesType: isAppPath ? "app" : undefined
  399. }),
  400. appDir: this.config.experimental.appDir
  401. });
  402. },
  403. onClient: ()=>{
  404. if (!isClientCompilation) return;
  405. if (isChildEntry) {
  406. _onDemandEntryHandler.entries[entryKey].status = _onDemandEntryHandler.BUILDING;
  407. entrypoints[bundlePath] = (0, _entries).finalizeEntrypoint({
  408. name: bundlePath,
  409. compilerType: _constants1.COMPILER_NAMES.client,
  410. value: entryData.request,
  411. appDir: this.config.experimental.appDir
  412. });
  413. } else {
  414. _onDemandEntryHandler.entries[entryKey].status = _onDemandEntryHandler.BUILDING;
  415. entrypoints[bundlePath] = (0, _entries).finalizeEntrypoint({
  416. name: bundlePath,
  417. compilerType: _constants1.COMPILER_NAMES.client,
  418. value: (0, _entries).getClientEntry({
  419. absolutePagePath: entryData.absolutePagePath,
  420. page
  421. }),
  422. appDir: this.config.experimental.appDir
  423. });
  424. }
  425. },
  426. onServer: ()=>{
  427. // TODO-APP: verify if child entry should support.
  428. if (!isNodeServerCompilation || !isEntry) return;
  429. _onDemandEntryHandler.entries[entryKey].status = _onDemandEntryHandler.BUILDING;
  430. let relativeRequest = (0, _path).relative(config1.context, entryData.absolutePagePath);
  431. if (!(0, _path).isAbsolute(relativeRequest) && !relativeRequest.startsWith("../")) {
  432. relativeRequest = `./${relativeRequest}`;
  433. }
  434. entrypoints[bundlePath] = (0, _entries).finalizeEntrypoint({
  435. compilerType: "server",
  436. name: bundlePath,
  437. isServerComponent,
  438. value: this.appDir && bundlePath.startsWith("app/") ? (0, _entries).getAppEntry({
  439. name: bundlePath,
  440. appPaths: entryData.appPaths,
  441. pagePath: _path.posix.join(_constants.APP_DIR_ALIAS, (0, _path).relative(this.appDir, entryData.absolutePagePath).replace(/\\/g, "/")),
  442. appDir: this.appDir,
  443. pageExtensions: this.config.pageExtensions
  444. }) : relativeRequest,
  445. appDir: this.config.experimental.appDir
  446. });
  447. }
  448. });
  449. }));
  450. return entrypoints;
  451. };
  452. }
  453. // Enable building of client compilation before server compilation in development
  454. // @ts-ignore webpack 5
  455. this.activeConfigs.parallelism = 1;
  456. this.multiCompiler = (0, _webpack).webpack(this.activeConfigs);
  457. (0, _output).watchCompilers(this.multiCompiler.compilers[0], this.multiCompiler.compilers[1], this.multiCompiler.compilers[2]);
  458. // Watch for changes to client/server page files so we can tell when just
  459. // the server file changes and trigger a reload for GS(S)P pages
  460. const changedClientPages = new Set();
  461. const changedServerPages = new Set();
  462. const changedEdgeServerPages = new Set();
  463. const changedCSSImportPages = new Set();
  464. const prevClientPageHashes = new Map();
  465. const prevServerPageHashes = new Map();
  466. const prevEdgeServerPageHashes = new Map();
  467. const prevCSSImportModuleHashes = new Map();
  468. const trackPageChanges = (pageHashMap, changedItems)=>{
  469. return (stats)=>{
  470. try {
  471. stats.entrypoints.forEach((entry, key)=>{
  472. if (key.startsWith("pages/") || key.startsWith("app/") || (0, _utils).isMiddlewareFilename(key)) {
  473. // TODO this doesn't handle on demand loaded chunks
  474. entry.chunks.forEach((chunk)=>{
  475. if (chunk.id === key) {
  476. const modsIterable = stats.chunkGraph.getChunkModulesIterable(chunk);
  477. let hasCSSModuleChanges = false;
  478. let chunksHash = new _webpack.StringXor();
  479. modsIterable.forEach((mod)=>{
  480. if (mod.resource && mod.resource.replace(/\\/g, "/").includes(key)) {
  481. // use original source to calculate hash since mod.hash
  482. // includes the source map in development which changes
  483. // every time for both server and client so we calculate
  484. // the hash without the source map for the page module
  485. const hash = require("crypto").createHash("sha256").update(mod.originalSource().buffer()).digest().toString("hex");
  486. chunksHash.add(hash);
  487. } else {
  488. var ref;
  489. // for non-pages we can use the module hash directly
  490. const hash = stats.chunkGraph.getModuleHash(mod, chunk.runtime);
  491. chunksHash.add(hash);
  492. // Both CSS import changes from server and client
  493. // components are tracked.
  494. if (key.startsWith("app/") && ((ref = mod.resource) == null ? void 0 : ref.endsWith(".css"))) {
  495. const prevHash = prevCSSImportModuleHashes.get(mod.resource);
  496. if (prevHash && prevHash !== hash) {
  497. hasCSSModuleChanges = true;
  498. }
  499. prevCSSImportModuleHashes.set(mod.resource, hash);
  500. }
  501. }
  502. });
  503. const prevHash1 = pageHashMap.get(key);
  504. const curHash = chunksHash.toString();
  505. if (prevHash1 && prevHash1 !== curHash) {
  506. changedItems.add(key);
  507. }
  508. pageHashMap.set(key, curHash);
  509. if (hasCSSModuleChanges) {
  510. changedCSSImportPages.add(key);
  511. }
  512. }
  513. });
  514. }
  515. });
  516. } catch (err) {
  517. console.error(err);
  518. }
  519. };
  520. };
  521. this.multiCompiler.compilers[0].hooks.emit.tap("NextjsHotReloaderForClient", trackPageChanges(prevClientPageHashes, changedClientPages));
  522. this.multiCompiler.compilers[1].hooks.emit.tap("NextjsHotReloaderForServer", trackPageChanges(prevServerPageHashes, changedServerPages));
  523. this.multiCompiler.compilers[2].hooks.emit.tap("NextjsHotReloaderForServer", trackPageChanges(prevEdgeServerPageHashes, changedEdgeServerPages));
  524. // This plugin watches for changes to _document.js and notifies the client side that it should reload the page
  525. this.multiCompiler.compilers[1].hooks.failed.tap("NextjsHotReloaderForServer", (err)=>{
  526. this.serverError = err;
  527. this.serverStats = null;
  528. });
  529. this.multiCompiler.compilers[2].hooks.done.tap("NextjsHotReloaderForServer", (stats)=>{
  530. this.serverError = null;
  531. this.edgeServerStats = stats;
  532. });
  533. this.multiCompiler.compilers[1].hooks.done.tap("NextjsHotReloaderForServer", (stats)=>{
  534. this.serverError = null;
  535. this.serverStats = stats;
  536. if (!this.pagesDir) {
  537. return;
  538. }
  539. const { compilation } = stats;
  540. // We only watch `_document` for changes on the server compilation
  541. // the rest of the files will be triggered by the client compilation
  542. const documentChunk = compilation.namedChunks.get("pages/_document");
  543. // If the document chunk can't be found we do nothing
  544. if (!documentChunk) {
  545. console.warn("_document.js chunk not found");
  546. return;
  547. }
  548. // Initial value
  549. if (this.serverPrevDocumentHash === null) {
  550. this.serverPrevDocumentHash = documentChunk.hash || null;
  551. return;
  552. }
  553. // If _document.js didn't change we don't trigger a reload
  554. if (documentChunk.hash === this.serverPrevDocumentHash) {
  555. return;
  556. }
  557. // Notify reload to reload the page, as _document.js was changed (different hash)
  558. this.send("reloadPage");
  559. this.serverPrevDocumentHash = documentChunk.hash || null;
  560. });
  561. this.multiCompiler.hooks.done.tap("NextjsHotReloaderForServer", ()=>{
  562. const serverOnlyChanges = (0, _utils).difference(changedServerPages, changedClientPages);
  563. const serverComponentChanges = serverOnlyChanges.filter((key)=>key.startsWith("app/")).concat(Array.from(changedCSSImportPages));
  564. const pageChanges = serverOnlyChanges.filter((key)=>key.startsWith("pages/"));
  565. const middlewareChanges = Array.from(changedEdgeServerPages).filter((name)=>(0, _utils).isMiddlewareFilename(name));
  566. changedClientPages.clear();
  567. changedServerPages.clear();
  568. changedEdgeServerPages.clear();
  569. changedCSSImportPages.clear();
  570. if (middlewareChanges.length > 0) {
  571. this.send({
  572. event: "middlewareChanges"
  573. });
  574. }
  575. if (pageChanges.length > 0) {
  576. this.send({
  577. event: "serverOnlyChanges",
  578. pages: serverOnlyChanges.map((pg)=>(0, _denormalizePagePath).denormalizePagePath(pg.slice("pages".length)))
  579. });
  580. }
  581. if (serverComponentChanges.length > 0) {
  582. this.send({
  583. action: "serverComponentChanges"
  584. });
  585. }
  586. });
  587. this.multiCompiler.compilers[0].hooks.failed.tap("NextjsHotReloaderForClient", (err)=>{
  588. this.clientError = err;
  589. this.clientStats = null;
  590. });
  591. this.multiCompiler.compilers[0].hooks.done.tap("NextjsHotReloaderForClient", (stats)=>{
  592. this.clientError = null;
  593. this.clientStats = stats;
  594. const { compilation } = stats;
  595. const chunkNames = new Set([
  596. ...compilation.namedChunks.keys()
  597. ].filter((name)=>!!(0, _getRouteFromEntrypoint).default(name)));
  598. if (this.prevChunkNames) {
  599. // detect chunks which have to be replaced with a new template
  600. // e.g, pages/index.js <-> pages/_error.js
  601. const addedPages = diff(chunkNames, this.prevChunkNames);
  602. const removedPages = diff(this.prevChunkNames, chunkNames);
  603. if (addedPages.size > 0) {
  604. for (const addedPage of addedPages){
  605. const page = (0, _getRouteFromEntrypoint).default(addedPage);
  606. this.send("addedPage", page);
  607. }
  608. }
  609. if (removedPages.size > 0) {
  610. for (const removedPage of removedPages){
  611. const page = (0, _getRouteFromEntrypoint).default(removedPage);
  612. this.send("removedPage", page);
  613. }
  614. }
  615. }
  616. this.prevChunkNames = chunkNames;
  617. });
  618. this.webpackHotMiddleware = new _hotMiddleware.WebpackHotMiddleware(this.multiCompiler.compilers);
  619. let booted = false;
  620. this.watcher = await new Promise((resolve)=>{
  621. var ref;
  622. const watcher = (ref = this.multiCompiler) == null ? void 0 : ref.watch(// @ts-ignore webpack supports an array of watchOptions when using a multiCompiler
  623. this.activeConfigs.map((config)=>config.watchOptions), // Errors are handled separately
  624. (_err)=>{
  625. if (!booted) {
  626. booted = true;
  627. resolve(watcher);
  628. }
  629. });
  630. });
  631. this.onDemandEntries = (0, _onDemandEntryHandler).onDemandEntryHandler({
  632. multiCompiler: this.multiCompiler,
  633. pagesDir: this.pagesDir,
  634. appDir: this.appDir,
  635. rootDir: this.dir,
  636. nextConfig: this.config,
  637. ...this.config.onDemandEntries
  638. });
  639. this.interceptors = [
  640. (0, _middleware).getOverlayMiddleware({
  641. rootDirectory: this.dir,
  642. stats: ()=>this.clientStats,
  643. serverStats: ()=>this.serverStats,
  644. edgeServerStats: ()=>this.edgeServerStats
  645. }),
  646. ];
  647. // trigger invalidation to ensure any previous callbacks
  648. // are handled in the on-demand-entry-handler
  649. if (!initial) {
  650. this.invalidate();
  651. }
  652. }
  653. invalidate() {
  654. var ref;
  655. return (ref = (0, _onDemandEntryHandler).getInvalidator()) == null ? void 0 : ref.invalidate();
  656. }
  657. async stop() {
  658. await new Promise((resolve, reject)=>{
  659. this.watcher.close((err)=>err ? reject(err) : resolve(true));
  660. });
  661. if (this.fallbackWatcher) {
  662. await new Promise((resolve, reject)=>{
  663. this.fallbackWatcher.close((err)=>err ? reject(err) : resolve(true));
  664. });
  665. }
  666. this.multiCompiler = undefined;
  667. }
  668. async getCompilationErrors(page) {
  669. var ref4, ref2, ref3;
  670. const getErrors = ({ compilation })=>{
  671. var ref;
  672. const failedPages = erroredPages(compilation);
  673. const normalizedPage = (0, _normalizePathSep).normalizePathSep(page);
  674. // If there is an error related to the requesting page we display it instead of the first error
  675. return ((ref = failedPages[normalizedPage]) == null ? void 0 : ref.length) > 0 ? failedPages[normalizedPage] : compilation.errors;
  676. };
  677. if (this.clientError || this.serverError) {
  678. return [
  679. this.clientError || this.serverError
  680. ];
  681. } else if ((ref4 = this.clientStats) == null ? void 0 : ref4.hasErrors()) {
  682. return getErrors(this.clientStats);
  683. } else if ((ref2 = this.serverStats) == null ? void 0 : ref2.hasErrors()) {
  684. return getErrors(this.serverStats);
  685. } else if ((ref3 = this.edgeServerStats) == null ? void 0 : ref3.hasErrors()) {
  686. return getErrors(this.edgeServerStats);
  687. } else {
  688. return [];
  689. }
  690. }
  691. send(action, ...args) {
  692. this.webpackHotMiddleware.publish(action && typeof action === "object" ? action : {
  693. action,
  694. data: args
  695. });
  696. }
  697. async ensurePage({ page , clientOnly , appPaths }) {
  698. var ref;
  699. // Make sure we don't re-build or dispose prebuilt pages
  700. if (page !== "/_error" && _constants1.BLOCKED_PAGES.indexOf(page) !== -1) {
  701. return;
  702. }
  703. const error = clientOnly ? this.clientError : this.serverError || this.clientError;
  704. if (error) {
  705. return Promise.reject(error);
  706. }
  707. return (ref = this.onDemandEntries) == null ? void 0 : ref.ensurePage({
  708. page,
  709. clientOnly,
  710. appPaths
  711. });
  712. }
  713. }
  714. exports.default = HotReloader;
  715. function _interopRequireDefault(obj) {
  716. return obj && obj.__esModule ? obj : {
  717. default: obj
  718. };
  719. }
  720. function _getRequireWildcardCache() {
  721. if (typeof WeakMap !== "function") return null;
  722. var cache = new WeakMap();
  723. _getRequireWildcardCache = function() {
  724. return cache;
  725. };
  726. return cache;
  727. }
  728. function _interopRequireWildcard(obj) {
  729. if (obj && obj.__esModule) {
  730. return obj;
  731. }
  732. if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
  733. return {
  734. default: obj
  735. };
  736. }
  737. var cache = _getRequireWildcardCache();
  738. if (cache && cache.has(obj)) {
  739. return cache.get(obj);
  740. }
  741. var newObj = {};
  742. var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
  743. for(var key in obj){
  744. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  745. var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
  746. if (desc && (desc.get || desc.set)) {
  747. Object.defineProperty(newObj, key, desc);
  748. } else {
  749. newObj[key] = obj[key];
  750. }
  751. }
  752. }
  753. newObj.default = obj;
  754. if (cache) {
  755. cache.set(obj, newObj);
  756. }
  757. return newObj;
  758. }
  759. function diff(a, b) {
  760. return new Set([
  761. ...a
  762. ].filter((v)=>!b.has(v)));
  763. }
  764. const wsServer = new _ws.default.Server({
  765. noServer: true
  766. });
  767. async function renderScriptError(res, error, { verbose =true } = {}) {
  768. // Asks CDNs and others to not to cache the errored page
  769. res.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
  770. if (error.code === "ENOENT") {
  771. res.statusCode = 404;
  772. res.end("404 - Not Found");
  773. return;
  774. }
  775. if (verbose) {
  776. console.error(error.stack);
  777. }
  778. res.statusCode = 500;
  779. res.end("500 - Internal Error");
  780. }
  781. function addCorsSupport(req, res) {
  782. // Only rewrite CORS handling when URL matches a hot-reloader middleware
  783. if (!req.url.startsWith("/__next")) {
  784. return {
  785. preflight: false
  786. };
  787. }
  788. if (!req.headers.origin) {
  789. return {
  790. preflight: false
  791. };
  792. }
  793. res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
  794. res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET");
  795. // Based on https://github.com/primus/access-control/blob/4cf1bc0e54b086c91e6aa44fb14966fa5ef7549c/index.js#L158
  796. if (req.headers["access-control-request-headers"]) {
  797. res.setHeader("Access-Control-Allow-Headers", req.headers["access-control-request-headers"]);
  798. }
  799. if (req.method === "OPTIONS") {
  800. res.writeHead(200);
  801. res.end();
  802. return {
  803. preflight: true
  804. };
  805. }
  806. return {
  807. preflight: false
  808. };
  809. }
  810. const matchNextPageBundleRequest = (0, _pathMatch).getPathMatch("/_next/static/chunks/pages/:path*.js(\\.map|)");
  811. // Recursively look up the issuer till it ends up at the root
  812. function findEntryModule(compilation, issuerModule) {
  813. const issuer = compilation.moduleGraph.getIssuer(issuerModule);
  814. if (issuer) {
  815. return findEntryModule(compilation, issuer);
  816. }
  817. return issuerModule;
  818. }
  819. function erroredPages(compilation) {
  820. const failedPages = {};
  821. for (const error of compilation.errors){
  822. if (!error.module) {
  823. continue;
  824. }
  825. const entryModule = findEntryModule(compilation, error.module);
  826. const { name } = entryModule;
  827. if (!name) {
  828. continue;
  829. }
  830. // Only pages have to be reloaded
  831. const enhancedName = (0, _getRouteFromEntrypoint).default(name);
  832. if (!enhancedName) {
  833. continue;
  834. }
  835. if (!failedPages[enhancedName]) {
  836. failedPages[enhancedName] = [];
  837. }
  838. failedPages[enhancedName].push(error);
  839. }
  840. return failedPages;
  841. }
  842. //# sourceMappingURL=hot-reloader.js.map