UNPKG

3.05 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const http = require("http");
9
10/** @typedef {import("../Compiler")} Compiler */
11
12/**
13 * @param {Compiler} compiler compiler
14 * @param {string} client client reference
15 * @param {function(Error?, any?): void} callback callback
16 * @returns {void}
17 */
18module.exports = (compiler, client, callback) => {
19 const logger = compiler.getInfrastructureLogger("LazyCompilationBackend");
20 const activeModules = new Map();
21 const prefix = "/lazy-compilation-using-";
22
23 const requestListener = (req, res) => {
24 const keys = req.url.slice(prefix.length).split("@");
25 req.socket.on("close", () => {
26 setTimeout(() => {
27 for (const key of keys) {
28 const oldValue = activeModules.get(key) || 0;
29 activeModules.set(key, oldValue - 1);
30 if (oldValue === 1) {
31 logger.log(
32 `${key} is no longer in use. Next compilation will skip this module.`
33 );
34 }
35 }
36 }, 120000);
37 });
38 req.socket.setNoDelay(true);
39 res.writeHead(200, {
40 "content-type": "text/event-stream",
41 "Access-Control-Allow-Origin": "*"
42 });
43 res.write("\n");
44 let moduleActivated = false;
45 for (const key of keys) {
46 const oldValue = activeModules.get(key) || 0;
47 activeModules.set(key, oldValue + 1);
48 if (oldValue === 0) {
49 logger.log(`${key} is now in use and will be compiled.`);
50 moduleActivated = true;
51 }
52 }
53 if (moduleActivated && compiler.watching) compiler.watching.invalidate();
54 };
55 const server = http.createServer(requestListener);
56 let isClosing = false;
57 /** @type {Set<import("net").Socket>} */
58 const sockets = new Set();
59 server.on("connection", socket => {
60 sockets.add(socket);
61 socket.on("close", () => {
62 sockets.delete(socket);
63 });
64 if (isClosing) socket.destroy();
65 });
66 server.listen(err => {
67 if (err) return callback(err);
68 const addr = server.address();
69 if (typeof addr === "string") throw new Error("addr must not be a string");
70 const urlBase =
71 addr.address === "::" || addr.address === "0.0.0.0"
72 ? `http://localhost:${addr.port}`
73 : addr.family === "IPv6"
74 ? `http://[${addr.address}]:${addr.port}`
75 : `http://${addr.address}:${addr.port}`;
76 logger.log(
77 `Server-Sent-Events server for lazy compilation open at ${urlBase}.`
78 );
79 callback(null, {
80 dispose(callback) {
81 isClosing = true;
82 // Removing the listener is a workaround for a memory leak in node.js
83 server.off("request", requestListener);
84 server.close(err => {
85 callback(err);
86 });
87 for (const socket of sockets) {
88 socket.destroy(new Error("Server is disposing"));
89 }
90 },
91 module(originalModule) {
92 const key = `${encodeURIComponent(
93 originalModule.identifier().replace(/\\/g, "/").replace(/@/g, "_")
94 ).replace(/%(2F|3A|24|26|2B|2C|3B|3D|3A)/g, decodeURIComponent)}`;
95 const active = activeModules.get(key) > 0;
96 return {
97 client: `${client}?${encodeURIComponent(urlBase + prefix)}`,
98 data: key,
99 active
100 };
101 }
102 });
103 });
104};