sliftutils 1.2.10 → 1.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import fs from "fs";
2
+ import crypto from "crypto";
2
3
  import { delay } from "socket-function/src/batching";
3
4
  import { bundleEntryCaller } from "../bundler/bundleEntryCaller";
4
5
  import yargs from "yargs";
@@ -6,6 +7,8 @@ import { formatTime } from "socket-function/src/formatting/format";
6
7
  import path from "path";
7
8
  import { getAllFiles } from "../misc/fs";
8
9
 
10
+ const BUILD_HASH_PREFIX = "::SLIFT_HOT_RELOAD_BUILD_HASH::";
11
+
9
12
  async function main() {
10
13
  let time = Date.now();
11
14
  //todonext
@@ -138,6 +141,22 @@ async function main() {
138
141
  console.log(`Copied ${filesCopied} changed assets`);
139
142
  }
140
143
 
144
+ // Hash the bundled JS outputs so the watch server can broadcast a version
145
+ // identifier the extension can compare against on a poll.
146
+ let jsOutputs = (await fs.promises.readdir(yargObj.outputFolder))
147
+ .filter(f => f.endsWith(".js"))
148
+ .sort();
149
+ let hasher = crypto.createHash("sha256");
150
+ for (let file of jsOutputs) {
151
+ let buf = await fs.promises.readFile(path.join(yargObj.outputFolder, file));
152
+ hasher.update(file);
153
+ hasher.update("\0");
154
+ hasher.update(buf);
155
+ hasher.update("\0");
156
+ }
157
+ let buildHash = hasher.digest("hex").slice(0, 16);
158
+ console.log(`${BUILD_HASH_PREFIX} ${buildHash}`);
159
+
141
160
  let duration = Date.now() - time;
142
161
  console.log(`Extension build completed in ${formatTime(duration)}`);
143
162
  }
@@ -2,6 +2,8 @@ import { watchFilesAndTriggerHotReloading } from "socket-function/hot/HotReloadC
2
2
  import { isInBrowser, isInChromeExtension, isInChromeExtensionBackground, isInChromeExtensionContentScript } from "../misc/environment";
3
3
 
4
4
  const DEFAULT_WATCH_PORT = 9876;
5
+ const CONTENT_SCRIPT_POLL_INTERVAL_MS = 1000;
6
+ const HOT_RELOAD_HASH_MESSAGE = "hotReload:getHash";
5
7
 
6
8
  export async function enableHotReloading(config?: {
7
9
  port?: number;
@@ -20,7 +22,7 @@ export async function enableHotReloading(config?: {
20
22
  }
21
23
  }
22
24
 
23
- function watchPortHotReload(port = DEFAULT_WATCH_PORT, onReload: () => void) {
25
+ function watchPortHotReload(port = DEFAULT_WATCH_PORT, onReload: () => void, onHash?: (hash: string | undefined) => void) {
24
26
  let reconnectTimer: number | undefined;
25
27
  let ws: WebSocket | undefined;
26
28
 
@@ -39,8 +41,12 @@ function watchPortHotReload(port = DEFAULT_WATCH_PORT, onReload: () => void) {
39
41
  try {
40
42
  let data = JSON.parse(event.data);
41
43
  if (data.type === "build-complete" && data.success) {
42
- console.log("[Hot Reload] Build complete, reloading page...");
43
- onReload();
44
+ if (onHash) {
45
+ onHash(data.hash);
46
+ } else {
47
+ console.log("[Hot Reload] Build complete, reloading page...");
48
+ onReload();
49
+ }
44
50
  }
45
51
  } catch (error) {
46
52
  console.error("[Hot Reload] Failed to parse message:", error);
@@ -71,29 +77,57 @@ function watchPortHotReload(port = DEFAULT_WATCH_PORT, onReload: () => void) {
71
77
  }
72
78
 
73
79
  function chromeExtensionBackgroundHotReload(port = DEFAULT_WATCH_PORT) {
74
- chrome.runtime.onConnect.addListener((port) => {
75
- if (port.name === "hotReload") {
76
- // Keep the port open so content scripts can detect when we disconnect
80
+ let currentHash: string | undefined;
81
+
82
+ // Content scripts poll the background for the latest build hash and reload
83
+ // themselves when it changes.
84
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
85
+ if (message && message.type === HOT_RELOAD_HASH_MESSAGE) {
86
+ sendResponse({ hash: currentHash });
87
+ return; // synchronous response
77
88
  }
89
+ return undefined;
78
90
  });
79
91
 
80
- watchPortHotReload(port, () => {
81
- chrome.runtime.reload();
92
+ watchPortHotReload(port, () => { /* unused — hash callback drives reloads */ }, (hash) => {
93
+ if (!hash) return;
94
+ if (currentHash === undefined) {
95
+ currentHash = hash;
96
+ console.log(`[Hot Reload] Initial build hash: ${hash}`);
97
+ return;
98
+ }
99
+ if (hash !== currentHash) {
100
+ console.log(`[Hot Reload] Build hash changed (${currentHash} -> ${hash}), reloading extension...`);
101
+ currentHash = hash;
102
+ chrome.runtime.reload();
103
+ }
82
104
  });
83
105
  }
84
106
 
85
107
  function chromeExtensionContentScriptHotReload() {
86
- let port = chrome.runtime.connect({ name: "hotReload" });
87
-
88
- let startTime = Date.now();
108
+ let lastHash: string | undefined;
89
109
 
90
- port.onDisconnect.addListener(() => {
91
- let timeToFail = Date.now() - startTime;
92
- if (timeToFail < 3000) {
93
- console.warn("[Hot Reload] Could not connect to background script. Make sure the background script calls enableHotReloading().");
94
- return;
110
+ setInterval(() => {
111
+ try {
112
+ chrome.runtime.sendMessage({ type: HOT_RELOAD_HASH_MESSAGE }, (response) => {
113
+ if (chrome.runtime.lastError) {
114
+ // Background may be reloading — try again on the next tick.
115
+ return;
116
+ }
117
+ let hash: string | undefined = response?.hash;
118
+ if (!hash) return;
119
+ if (lastHash === undefined) {
120
+ lastHash = hash;
121
+ return;
122
+ }
123
+ if (hash !== lastHash) {
124
+ console.log(`[Hot Reload] Build hash changed (${lastHash} -> ${hash}), refreshing page...`);
125
+ lastHash = hash;
126
+ window.location.reload();
127
+ }
128
+ });
129
+ } catch (error) {
130
+ // Ignore — extension context can be transiently invalidated during reload.
95
131
  }
96
- console.log("[Hot Reload] Extension reloaded, refreshing page...");
97
- window.location.reload();
98
- });
132
+ }, CONTENT_SCRIPT_POLL_INTERVAL_MS);
99
133
  }
package/builders/watch.ts CHANGED
@@ -5,6 +5,7 @@ import { WebSocketServer } from "ws";
5
5
 
6
6
  const DEBOUNCE_DELAY_MS = 250;
7
7
  const DEFAULT_WATCH_PORT = 9876;
8
+ const BUILD_HASH_REGEX = /::SLIFT_HOT_RELOAD_BUILD_HASH::\s+(\S+)/g;
8
9
 
9
10
  async function main() {
10
11
  let args = process.argv.slice(2);
@@ -48,6 +49,9 @@ async function main() {
48
49
 
49
50
  wss.on("connection", (ws) => {
50
51
  console.log(`[${new Date().toLocaleTimeString()}] WebSocket client connected`);
52
+ if (latestBuildHash) {
53
+ ws.send(JSON.stringify({ type: "build-complete", success: true, hash: latestBuildHash }));
54
+ }
51
55
  ws.on("close", () => {
52
56
  console.log(`[${new Date().toLocaleTimeString()}] WebSocket client disconnected`);
53
57
  });
@@ -63,6 +67,7 @@ async function main() {
63
67
  let debounceTimer: NodeJS.Timeout | undefined;
64
68
  let isRunning = false;
65
69
  let needsRerun = false;
70
+ let latestBuildHash: string | undefined;
66
71
 
67
72
  async function executeCommand() {
68
73
  if (isRunning) {
@@ -75,14 +80,25 @@ async function main() {
75
80
 
76
81
  try {
77
82
  console.log(`\n[${new Date().toLocaleTimeString()}] Running: ${command}`);
78
- await runPromise(command);
83
+ let output = await runPromise(command);
79
84
  console.log(`[${new Date().toLocaleTimeString()}] Completed successfully`);
80
85
 
86
+ // Extract the most recent [BUILD_HASH] line emitted by the build.
87
+ let match: RegExpExecArray | null;
88
+ let lastHash: string | undefined;
89
+ BUILD_HASH_REGEX.lastIndex = 0;
90
+ while ((match = BUILD_HASH_REGEX.exec(output)) !== null) {
91
+ lastHash = match[1];
92
+ }
93
+ if (lastHash) {
94
+ latestBuildHash = lastHash;
95
+ }
96
+
81
97
  // Notify all connected WebSocket clients
82
98
  if (wss) {
83
99
  wss.clients.forEach((client) => {
84
100
  if (client.readyState === 1) { // 1 = OPEN
85
- client.send(JSON.stringify({ type: "build-complete", success: true }));
101
+ client.send(JSON.stringify({ type: "build-complete", success: true, hash: latestBuildHash }));
86
102
  }
87
103
  });
88
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.2.10",
3
+ "version": "1.2.12",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [