@kohi9noor/hotloop 1.3.6 → 1.3.7
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.
- package/dist/cli.js +52 -23
- package/dist/hmr-client.js +31 -0
- package/dist/index.js +38 -14
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -89,10 +89,11 @@ __export(builder_exports, {
|
|
|
89
89
|
import * as esbuild from "esbuild";
|
|
90
90
|
import * as fs from "fs/promises";
|
|
91
91
|
import * as path from "path";
|
|
92
|
-
var ExtensionBuilder;
|
|
92
|
+
var HMR_CLIENT_NAME, ExtensionBuilder;
|
|
93
93
|
var init_builder = __esm({
|
|
94
94
|
"src/builder.ts"() {
|
|
95
95
|
init_logger();
|
|
96
|
+
HMR_CLIENT_NAME = "__hotloop_hmr__.js";
|
|
96
97
|
ExtensionBuilder = class {
|
|
97
98
|
constructor(cwd = process.cwd()) {
|
|
98
99
|
this.cwd = cwd;
|
|
@@ -135,26 +136,28 @@ var init_builder = __esm({
|
|
|
135
136
|
await fs.copyFile(srcPath, destPath);
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Writes the manifest file to the destination.
|
|
141
|
+
* If injectHmr is true, it automatically injects the HMR client script into content_scripts.
|
|
142
|
+
*/
|
|
143
|
+
async writeManifest(options, injectHmr = false) {
|
|
139
144
|
const srcPath = path.join(this.cwd, options.src);
|
|
140
145
|
const destPath = path.join(this.cwd, options.dest);
|
|
141
146
|
const destDir = path.dirname(destPath);
|
|
142
147
|
const raw = await fs.readFile(srcPath, "utf-8");
|
|
143
148
|
const manifest = JSON.parse(raw);
|
|
144
|
-
if (
|
|
145
|
-
const hmrScript = options.hmrScript || "content/hmr.js";
|
|
149
|
+
if (injectHmr) {
|
|
146
150
|
const hmrMatches = options.hmrMatches || ["<all_urls>"];
|
|
147
|
-
const hmrRunAt = options.hmrRunAt || "document_start";
|
|
148
151
|
const existing = Array.isArray(manifest.content_scripts) ? manifest.content_scripts : [];
|
|
149
152
|
const alreadyInjected = existing.some((entry) => {
|
|
150
153
|
const scripts = entry.js || [];
|
|
151
|
-
return
|
|
154
|
+
return scripts.includes(HMR_CLIENT_NAME);
|
|
152
155
|
});
|
|
153
156
|
if (!alreadyInjected) {
|
|
154
157
|
existing.push({
|
|
155
158
|
matches: hmrMatches,
|
|
156
|
-
js: [
|
|
157
|
-
run_at:
|
|
159
|
+
js: [HMR_CLIENT_NAME],
|
|
160
|
+
run_at: "document_start"
|
|
158
161
|
});
|
|
159
162
|
}
|
|
160
163
|
manifest.content_scripts = existing;
|
|
@@ -162,6 +165,26 @@ var init_builder = __esm({
|
|
|
162
165
|
await fs.mkdir(destDir, { recursive: true });
|
|
163
166
|
await fs.writeFile(destPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
164
167
|
}
|
|
168
|
+
async buildHmrClient(port, manifestDest) {
|
|
169
|
+
const hmrClientPath = path.join(import.meta.dirname, "hmr-client.js");
|
|
170
|
+
const outputDir = path.dirname(path.join(this.cwd, manifestDest));
|
|
171
|
+
const outputPath = path.join(outputDir, HMR_CLIENT_NAME);
|
|
172
|
+
try {
|
|
173
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
174
|
+
await esbuild.build({
|
|
175
|
+
entryPoints: [hmrClientPath],
|
|
176
|
+
bundle: true,
|
|
177
|
+
format: "iife",
|
|
178
|
+
logLevel: "silent",
|
|
179
|
+
outfile: outputPath,
|
|
180
|
+
define: {
|
|
181
|
+
__HOTLOOP_PORT__: port.toString()
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
logger.error(`Failed to build HMR client: ${error}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
165
188
|
};
|
|
166
189
|
}
|
|
167
190
|
});
|
|
@@ -260,11 +283,9 @@ async function runBuild(cwd, options) {
|
|
|
260
283
|
const { FileWatcher: FileWatcher2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
|
|
261
284
|
const config = await loadConfig2(cwd);
|
|
262
285
|
validateConfig2(config);
|
|
263
|
-
if (config.manifest && options?.injectHmr === false) {
|
|
264
|
-
config.manifest = { ...config.manifest, injectHmr: false };
|
|
265
|
-
}
|
|
266
286
|
const outDir = config.outDir || "dist";
|
|
267
287
|
const buildOutDir = config.buildOutDir || outDir;
|
|
288
|
+
const injectHmr = options?.injectHmr ?? false;
|
|
268
289
|
const replaceOutDir = (target) => {
|
|
269
290
|
const normalized = target.replace(/\\/g, "/");
|
|
270
291
|
const prefix = `${outDir.replace(/\\/g, "/")}/`;
|
|
@@ -297,13 +318,16 @@ async function runBuild(cwd, options) {
|
|
|
297
318
|
}));
|
|
298
319
|
}
|
|
299
320
|
}
|
|
300
|
-
const port = config.port ||
|
|
321
|
+
const port = config.port || 8e3;
|
|
301
322
|
const watchDir = config.watchDir || "src";
|
|
302
323
|
const server = options?.watch === false ? null : new HmrServer2(port);
|
|
303
324
|
const builder = new ExtensionBuilder2(cwd);
|
|
325
|
+
if (injectHmr && config.manifest) {
|
|
326
|
+
await builder.buildHmrClient(port, config.manifest.dest);
|
|
327
|
+
}
|
|
304
328
|
await builder.build(config.entries);
|
|
305
329
|
if (config.manifest) {
|
|
306
|
-
await builder.writeManifest(config.manifest);
|
|
330
|
+
await builder.writeManifest(config.manifest, injectHmr);
|
|
307
331
|
}
|
|
308
332
|
if (config.assets) {
|
|
309
333
|
await builder.copy(config.assets);
|
|
@@ -319,7 +343,7 @@ async function runBuild(cwd, options) {
|
|
|
319
343
|
try {
|
|
320
344
|
await builder.build(config.entries);
|
|
321
345
|
if (config.manifest) {
|
|
322
|
-
await builder.writeManifest(config.manifest);
|
|
346
|
+
await builder.writeManifest(config.manifest, injectHmr);
|
|
323
347
|
}
|
|
324
348
|
if (config.assets) {
|
|
325
349
|
await builder.copy(config.assets);
|
|
@@ -441,24 +465,28 @@ p {
|
|
|
441
465
|
path3.join(targetDir, "src", "ui", "popup.ts"),
|
|
442
466
|
popupScript
|
|
443
467
|
);
|
|
468
|
+
const contentScript = `console.log("Content script loaded");
|
|
469
|
+
`;
|
|
470
|
+
await fs3.writeFile(
|
|
471
|
+
path3.join(targetDir, "src", "content", "index.ts"),
|
|
472
|
+
contentScript
|
|
473
|
+
);
|
|
444
474
|
const configFile = `export default {
|
|
445
|
-
port:
|
|
446
|
-
watchDir: "src",
|
|
447
|
-
outDir: "dist",
|
|
448
|
-
buildOutDir: "build",
|
|
475
|
+
port: 8000,
|
|
449
476
|
entries: [
|
|
450
477
|
{ input: "src/background/index.ts", output: "dist/background/index.js" },
|
|
478
|
+
{ input: "src/content/index.ts", output: "dist/content/index.js" },
|
|
451
479
|
{ input: "src/ui/popup.ts", output: "dist/ui/popup.js" },
|
|
452
480
|
],
|
|
481
|
+
|
|
453
482
|
manifest: {
|
|
454
483
|
src: "src/manifest.json",
|
|
455
484
|
dest: "dist/manifest.json",
|
|
456
|
-
|
|
457
|
-
hmrScript: "content/hmr.js",
|
|
485
|
+
// Injects HMR client only in development mode
|
|
458
486
|
hmrMatches: ["<all_urls>"],
|
|
459
|
-
hmrRunAt: "document_start",
|
|
460
487
|
},
|
|
461
|
-
|
|
488
|
+
|
|
489
|
+
copy: [
|
|
462
490
|
{ src: "src/ui/popup.html", dest: "dist/ui/popup.html" },
|
|
463
491
|
{ src: "src/ui/popup.css", dest: "dist/ui/popup.css" },
|
|
464
492
|
],
|
|
@@ -488,10 +516,11 @@ p {
|
|
|
488
516
|
compilerOptions: {
|
|
489
517
|
target: "ES2020",
|
|
490
518
|
module: "ESNext",
|
|
491
|
-
lib: ["ES2020"],
|
|
519
|
+
lib: ["ES2020", "DOM", "WebWorker"],
|
|
492
520
|
skipLibCheck: true,
|
|
493
521
|
esModuleInterop: true,
|
|
494
522
|
resolveJsonModule: true,
|
|
523
|
+
moduleResolution: "node",
|
|
495
524
|
strict: true
|
|
496
525
|
},
|
|
497
526
|
include: ["src"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* src/hmr-client.js */
|
|
2
|
+
|
|
3
|
+
// These constants will be replaced by esbuild during the build process
|
|
4
|
+
const port = __HOTLOOP_PORT__ || 8000;
|
|
5
|
+
const host = "localhost";
|
|
6
|
+
|
|
7
|
+
const socket = new WebSocket(`ws://${host}:${port}`);
|
|
8
|
+
|
|
9
|
+
socket.onopen = () => {
|
|
10
|
+
console.log("[hotloop] HMR connected");
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
socket.onmessage = (event) => {
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(event.data);
|
|
16
|
+
if (data.type === "reload") {
|
|
17
|
+
console.log("[hotloop] Change detected, reloading extension...");
|
|
18
|
+
chrome.runtime.reload();
|
|
19
|
+
}
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error("[hotloop] Failed to parse HMR message:", err);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
socket.onclose = () => {
|
|
26
|
+
console.log("[hotloop] HMR disconnected. Refresh the page to reconnect.");
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
socket.onerror = (error) => {
|
|
30
|
+
console.error("[hotloop] HMR WebSocket error:", error);
|
|
31
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -88,10 +88,11 @@ __export(builder_exports, {
|
|
|
88
88
|
import * as esbuild from "esbuild";
|
|
89
89
|
import * as fs from "fs/promises";
|
|
90
90
|
import * as path from "path";
|
|
91
|
-
var ExtensionBuilder;
|
|
91
|
+
var HMR_CLIENT_NAME, ExtensionBuilder;
|
|
92
92
|
var init_builder = __esm({
|
|
93
93
|
"src/builder.ts"() {
|
|
94
94
|
init_logger();
|
|
95
|
+
HMR_CLIENT_NAME = "__hotloop_hmr__.js";
|
|
95
96
|
ExtensionBuilder = class {
|
|
96
97
|
constructor(cwd = process.cwd()) {
|
|
97
98
|
this.cwd = cwd;
|
|
@@ -134,26 +135,28 @@ var init_builder = __esm({
|
|
|
134
135
|
await fs.copyFile(srcPath, destPath);
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Writes the manifest file to the destination.
|
|
140
|
+
* If injectHmr is true, it automatically injects the HMR client script into content_scripts.
|
|
141
|
+
*/
|
|
142
|
+
async writeManifest(options, injectHmr = false) {
|
|
138
143
|
const srcPath = path.join(this.cwd, options.src);
|
|
139
144
|
const destPath = path.join(this.cwd, options.dest);
|
|
140
145
|
const destDir = path.dirname(destPath);
|
|
141
146
|
const raw = await fs.readFile(srcPath, "utf-8");
|
|
142
147
|
const manifest = JSON.parse(raw);
|
|
143
|
-
if (
|
|
144
|
-
const hmrScript = options.hmrScript || "content/hmr.js";
|
|
148
|
+
if (injectHmr) {
|
|
145
149
|
const hmrMatches = options.hmrMatches || ["<all_urls>"];
|
|
146
|
-
const hmrRunAt = options.hmrRunAt || "document_start";
|
|
147
150
|
const existing = Array.isArray(manifest.content_scripts) ? manifest.content_scripts : [];
|
|
148
151
|
const alreadyInjected = existing.some((entry) => {
|
|
149
152
|
const scripts = entry.js || [];
|
|
150
|
-
return
|
|
153
|
+
return scripts.includes(HMR_CLIENT_NAME);
|
|
151
154
|
});
|
|
152
155
|
if (!alreadyInjected) {
|
|
153
156
|
existing.push({
|
|
154
157
|
matches: hmrMatches,
|
|
155
|
-
js: [
|
|
156
|
-
run_at:
|
|
158
|
+
js: [HMR_CLIENT_NAME],
|
|
159
|
+
run_at: "document_start"
|
|
157
160
|
});
|
|
158
161
|
}
|
|
159
162
|
manifest.content_scripts = existing;
|
|
@@ -161,6 +164,26 @@ var init_builder = __esm({
|
|
|
161
164
|
await fs.mkdir(destDir, { recursive: true });
|
|
162
165
|
await fs.writeFile(destPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
163
166
|
}
|
|
167
|
+
async buildHmrClient(port, manifestDest) {
|
|
168
|
+
const hmrClientPath = path.join(import.meta.dirname, "hmr-client.js");
|
|
169
|
+
const outputDir = path.dirname(path.join(this.cwd, manifestDest));
|
|
170
|
+
const outputPath = path.join(outputDir, HMR_CLIENT_NAME);
|
|
171
|
+
try {
|
|
172
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
173
|
+
await esbuild.build({
|
|
174
|
+
entryPoints: [hmrClientPath],
|
|
175
|
+
bundle: true,
|
|
176
|
+
format: "iife",
|
|
177
|
+
logLevel: "silent",
|
|
178
|
+
outfile: outputPath,
|
|
179
|
+
define: {
|
|
180
|
+
__HOTLOOP_PORT__: port.toString()
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logger.error(`Failed to build HMR client: ${error}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
164
187
|
};
|
|
165
188
|
}
|
|
166
189
|
});
|
|
@@ -259,11 +282,9 @@ async function runBuild(cwd, options) {
|
|
|
259
282
|
const { FileWatcher: FileWatcher2 } = await Promise.resolve().then(() => (init_watcher(), watcher_exports));
|
|
260
283
|
const config = await loadConfig2(cwd);
|
|
261
284
|
validateConfig2(config);
|
|
262
|
-
if (config.manifest && options?.injectHmr === false) {
|
|
263
|
-
config.manifest = { ...config.manifest, injectHmr: false };
|
|
264
|
-
}
|
|
265
285
|
const outDir = config.outDir || "dist";
|
|
266
286
|
const buildOutDir = config.buildOutDir || outDir;
|
|
287
|
+
const injectHmr = options?.injectHmr ?? false;
|
|
267
288
|
const replaceOutDir = (target) => {
|
|
268
289
|
const normalized = target.replace(/\\/g, "/");
|
|
269
290
|
const prefix = `${outDir.replace(/\\/g, "/")}/`;
|
|
@@ -296,13 +317,16 @@ async function runBuild(cwd, options) {
|
|
|
296
317
|
}));
|
|
297
318
|
}
|
|
298
319
|
}
|
|
299
|
-
const port = config.port ||
|
|
320
|
+
const port = config.port || 8e3;
|
|
300
321
|
const watchDir = config.watchDir || "src";
|
|
301
322
|
const server = options?.watch === false ? null : new HmrServer2(port);
|
|
302
323
|
const builder = new ExtensionBuilder2(cwd);
|
|
324
|
+
if (injectHmr && config.manifest) {
|
|
325
|
+
await builder.buildHmrClient(port, config.manifest.dest);
|
|
326
|
+
}
|
|
303
327
|
await builder.build(config.entries);
|
|
304
328
|
if (config.manifest) {
|
|
305
|
-
await builder.writeManifest(config.manifest);
|
|
329
|
+
await builder.writeManifest(config.manifest, injectHmr);
|
|
306
330
|
}
|
|
307
331
|
if (config.assets) {
|
|
308
332
|
await builder.copy(config.assets);
|
|
@@ -318,7 +342,7 @@ async function runBuild(cwd, options) {
|
|
|
318
342
|
try {
|
|
319
343
|
await builder.build(config.entries);
|
|
320
344
|
if (config.manifest) {
|
|
321
|
-
await builder.writeManifest(config.manifest);
|
|
345
|
+
await builder.writeManifest(config.manifest, injectHmr);
|
|
322
346
|
}
|
|
323
347
|
if (config.assets) {
|
|
324
348
|
await builder.copy(config.assets);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kohi9noor/hotloop",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"description": "Minimal, framework-free development tool for small-scale browser extensions. TypeScript, HMR, and Node module support—nothing else.",
|
|
5
5
|
"author": "kohi9noor",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"development-tool"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --external:ws --external:esbuild --external:fs --external:fs/promises --external:path --external:child_process --external:url --outdir=dist && esbuild src/cli.ts --bundle --platform=node --format=esm --external:ws --external:esbuild --external:fs --external:fs/promises --external:path --external:child_process --external:url --outfile=dist/cli.js",
|
|
26
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --external:ws --external:esbuild --external:fs --external:fs/promises --external:path --external:child_process --external:url --outdir=dist && esbuild src/cli.ts --bundle --platform=node --format=esm --external:ws --external:esbuild --external:fs --external:fs/promises --external:path --external:child_process --external:url --outfile=dist/cli.js && cp src/hmr-client.js dist/",
|
|
27
27
|
"prepare": "npm run build"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|