source: frontend/node_modules/webpack/lib/hmr/lazyCompilationBackend.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.3 KB
RevLine 
[9af201e]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8/** @typedef {import("http").RequestListener} RequestListener */
9/** @typedef {import("http").ServerOptions} HttpServerOptions */
10/** @typedef {import("http").Server} HttpServer */
11/** @typedef {import("https").ServerOptions} HttpsServerOptions */
12/** @typedef {import("https").Server} HttpsServer */
13/** @typedef {import("net").AddressInfo} AddressInfo */
14/** @typedef {import("./LazyCompilationPlugin").BackendHandler} BackendHandler */
15/** @typedef {import("../../declarations/WebpackOptions").LazyCompilationDefaultBackendOptions} LazyCompilationDefaultBackendOptions */
16
17/** @typedef {HttpServer | HttpsServer} Server */
18/** @typedef {(server: Server) => void} Listen */
19/** @typedef {() => Server} CreateServerFunction */
20
21/**
22 * Returns backend.
23 * @param {Omit<LazyCompilationDefaultBackendOptions, "client"> & { client: NonNullable<LazyCompilationDefaultBackendOptions["client"]> }} options additional options for the backend
24 * @returns {BackendHandler} backend
25 */
26module.exports = (options) => (compiler, callback) => {
27 const logger = compiler.getInfrastructureLogger("LazyCompilationBackend");
28 /** @type {Map<string, number>} */
29 const activeModules = new Map();
30 const prefix = "/lazy-compilation-using-";
31
32 const isHttps =
33 options.protocol === "https" ||
34 (typeof options.server === "object" &&
35 ("key" in options.server || "pfx" in options.server));
36
37 /** @type {CreateServerFunction} */
38 const createServer =
39 typeof options.server === "function"
40 ? options.server
41 : (() => {
42 const http = isHttps ? require("https") : require("http");
43 return /** @type {(this: import("http") | import("https"), options: HttpServerOptions | HttpsServerOptions) => Server} */ (
44 http.createServer
45 ).bind(
46 http,
47 /** @type {HttpServerOptions | HttpsServerOptions} */
48 (options.server)
49 );
50 })();
51 /** @type {Listen} */
52 const listen =
53 typeof options.listen === "function"
54 ? options.listen
55 : (server) => {
56 let listen = options.listen;
57 if (typeof listen === "object" && !("port" in listen)) {
58 listen = { ...listen, port: undefined };
59 }
60 server.listen(listen);
61 };
62
63 const protocol = options.protocol || (isHttps ? "https" : "http");
64
65 /** @type {RequestListener} */
66 const requestListener = (req, res) => {
67 if (req.url === undefined) return;
68 const keys = req.url.slice(prefix.length).split("@");
69 req.socket.on("close", () => {
70 setTimeout(() => {
71 for (const key of keys) {
72 const oldValue = activeModules.get(key) || 0;
73 activeModules.set(key, oldValue - 1);
74 if (oldValue === 1) {
75 logger.log(
76 `${key} is no longer in use. Next compilation will skip this module.`
77 );
78 }
79 }
80 }, 120000);
81 });
82 req.socket.setNoDelay(true);
83 res.writeHead(200, {
84 "content-type": "text/event-stream",
85 "Access-Control-Allow-Origin": "*",
86 "Access-Control-Allow-Methods": "*",
87 "Access-Control-Allow-Headers": "*"
88 });
89 res.write("\n");
90 let moduleActivated = false;
91 for (const key of keys) {
92 const oldValue = activeModules.get(key) || 0;
93 activeModules.set(key, oldValue + 1);
94 if (oldValue === 0) {
95 logger.log(`${key} is now in use and will be compiled.`);
96 moduleActivated = true;
97 }
98 }
99 if (moduleActivated && compiler.watching) compiler.watching.invalidate();
100 };
101
102 const server = createServer();
103 server.on("request", requestListener);
104
105 let isClosing = false;
106 /** @type {Set<import("net").Socket>} */
107 const sockets = new Set();
108 server.on("connection", (socket) => {
109 sockets.add(socket);
110 socket.on("close", () => {
111 sockets.delete(socket);
112 });
113 if (isClosing) socket.destroy();
114 });
115 server.on("clientError", (e) => {
116 if (e.message !== "Server is disposing") logger.warn(e);
117 });
118
119 server.on(
120 "listening",
121 /**
122 * Handles the callback logic for this hook.
123 * @param {Error} err error
124 * @returns {void}
125 */
126 (err) => {
127 if (err) return callback(err);
128 const _addr = server.address();
129 if (typeof _addr === "string") {
130 throw new Error("addr must not be a string");
131 }
132 const addr = /** @type {AddressInfo} */ (_addr);
133 const urlBase =
134 addr.address === "::" || addr.address === "0.0.0.0"
135 ? `${protocol}://localhost:${addr.port}`
136 : addr.family === "IPv6"
137 ? `${protocol}://[${addr.address}]:${addr.port}`
138 : `${protocol}://${addr.address}:${addr.port}`;
139 logger.log(
140 `Server-Sent-Events server for lazy compilation open at ${urlBase}.`
141 );
142 callback(null, {
143 dispose(callback) {
144 isClosing = true;
145 // Removing the listener is a workaround for a memory leak in node.js
146 server.off("request", requestListener);
147 server.close((err) => {
148 callback(err);
149 });
150 for (const socket of sockets) {
151 socket.destroy(new Error("Server is disposing"));
152 }
153 },
154 module(originalModule) {
155 const key = `${encodeURIComponent(
156 originalModule.identifier().replace(/\\/g, "/").replace(/@/g, "_")
157 ).replace(/%(2F|3A|24|26|2B|2C|3B|3D)/g, decodeURIComponent)}`;
158 const active = /** @type {number} */ (activeModules.get(key)) > 0;
159 return {
160 client: `${options.client}?${encodeURIComponent(urlBase + prefix)}`,
161 data: key,
162 active
163 };
164 }
165 });
166 }
167 );
168 listen(server);
169};
Note: See TracBrowser for help on using the repository browser.