All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.lib.hmr.lazyCompilationBackend.js Maven / Gradle / Ivy

Go to download

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

The newest version!
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

/** @typedef {import("http").ServerOptions} HttpServerOptions */
/** @typedef {import("https").ServerOptions} HttpsServerOptions */
/** @typedef {import("../../declarations/WebpackOptions").LazyCompilationDefaultBackendOptions} LazyCompilationDefaultBackendOptions */
/** @typedef {import("../Compiler")} Compiler */

/**
 * @callback BackendHandler
 * @param {Compiler} compiler compiler
 * @param {function((Error | null)=, any=): void} callback callback
 * @returns {void}
 */

/**
 * @param {Omit & { client: NonNullable}} options additional options for the backend
 * @returns {BackendHandler} backend
 */
module.exports = options => (compiler, callback) => {
	const logger = compiler.getInfrastructureLogger("LazyCompilationBackend");
	const activeModules = new Map();
	const prefix = "/lazy-compilation-using-";

	const isHttps =
		options.protocol === "https" ||
		(typeof options.server === "object" &&
			("key" in options.server || "pfx" in options.server));

	const createServer =
		typeof options.server === "function"
			? options.server
			: (() => {
					const http = isHttps ? require("https") : require("http");
					return http.createServer.bind(http, options.server);
				})();
	const listen =
		typeof options.listen === "function"
			? options.listen
			: server => {
					let listen = options.listen;
					if (typeof listen === "object" && !("port" in listen))
						listen = { ...listen, port: undefined };
					server.listen(listen);
				};

	const protocol = options.protocol || (isHttps ? "https" : "http");

	const requestListener = (req, res) => {
		const keys = req.url.slice(prefix.length).split("@");
		req.socket.on("close", () => {
			setTimeout(() => {
				for (const key of keys) {
					const oldValue = activeModules.get(key) || 0;
					activeModules.set(key, oldValue - 1);
					if (oldValue === 1) {
						logger.log(
							`${key} is no longer in use. Next compilation will skip this module.`
						);
					}
				}
			}, 120000);
		});
		req.socket.setNoDelay(true);
		res.writeHead(200, {
			"content-type": "text/event-stream",
			"Access-Control-Allow-Origin": "*",
			"Access-Control-Allow-Methods": "*",
			"Access-Control-Allow-Headers": "*"
		});
		res.write("\n");
		let moduleActivated = false;
		for (const key of keys) {
			const oldValue = activeModules.get(key) || 0;
			activeModules.set(key, oldValue + 1);
			if (oldValue === 0) {
				logger.log(`${key} is now in use and will be compiled.`);
				moduleActivated = true;
			}
		}
		if (moduleActivated && compiler.watching) compiler.watching.invalidate();
	};

	const server = /** @type {import("net").Server} */ (createServer());
	server.on("request", requestListener);

	let isClosing = false;
	/** @type {Set} */
	const sockets = new Set();
	server.on("connection", socket => {
		sockets.add(socket);
		socket.on("close", () => {
			sockets.delete(socket);
		});
		if (isClosing) socket.destroy();
	});
	server.on("clientError", e => {
		if (e.message !== "Server is disposing") logger.warn(e);
	});
	server.on("listening", err => {
		if (err) return callback(err);
		const addr = server.address();
		if (typeof addr === "string") throw new Error("addr must not be a string");
		const urlBase =
			addr.address === "::" || addr.address === "0.0.0.0"
				? `${protocol}://localhost:${addr.port}`
				: addr.family === "IPv6"
					? `${protocol}://[${addr.address}]:${addr.port}`
					: `${protocol}://${addr.address}:${addr.port}`;
		logger.log(
			`Server-Sent-Events server for lazy compilation open at ${urlBase}.`
		);
		callback(null, {
			dispose(callback) {
				isClosing = true;
				// Removing the listener is a workaround for a memory leak in node.js
				server.off("request", requestListener);
				server.close(err => {
					callback(err);
				});
				for (const socket of sockets) {
					socket.destroy(new Error("Server is disposing"));
				}
			},
			module(originalModule) {
				const key = `${encodeURIComponent(
					originalModule.identifier().replace(/\\/g, "/").replace(/@/g, "_")
				).replace(/%(2F|3A|24|26|2B|2C|3B|3D|3A)/g, decodeURIComponent)}`;
				const active = activeModules.get(key) > 0;
				return {
					client: `${options.client}?${encodeURIComponent(urlBase + prefix)}`,
					data: key,
					active
				};
			}
		});
	});
	listen(server);
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy