1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const http = require("http");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | module.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 |
|
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 |
|
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 | };
|