cindel 1.0.4 → 1.0.5
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/README.md +38 -1
- package/dist/client/file-loader.d.ts +26 -2
- package/dist/client/file-loader.d.ts.map +1 -1
- package/dist/client/hmr-client.d.ts +11 -0
- package/dist/client/hmr-client.d.ts.map +1 -1
- package/dist/client/stub.d.ts +2 -0
- package/dist/client/stub.d.ts.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.iife.js +181 -17
- package/dist/client.iife.js.map +4 -4
- package/dist/client.iife.min.js +1 -1
- package/dist/client.iife.min.js.map +4 -4
- package/dist/client.js +181 -17
- package/dist/client.js.map +4 -4
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1544,8 +1544,11 @@ var require_picomatch2 = __commonJS({
|
|
|
1544
1544
|
|
|
1545
1545
|
// src/client/file-loader.js
|
|
1546
1546
|
var FileLoader = class {
|
|
1547
|
-
constructor(httpUrl) {
|
|
1547
|
+
constructor(httpUrl, { iframeTarget = null, iframeOrigin = "*", css = "iframe" } = {}) {
|
|
1548
1548
|
this.httpUrl = httpUrl;
|
|
1549
|
+
this.iframeTarget = iframeTarget;
|
|
1550
|
+
this.iframeOrigin = iframeOrigin;
|
|
1551
|
+
this.css = css;
|
|
1549
1552
|
this.loadQueue = /* @__PURE__ */ new Map();
|
|
1550
1553
|
this.versions = /* @__PURE__ */ new Map();
|
|
1551
1554
|
}
|
|
@@ -1560,8 +1563,28 @@ var FileLoader = class {
|
|
|
1560
1563
|
// the old one. This fixes the brief flash of unstyled content that
|
|
1561
1564
|
// happens when you remove the old sheet before the new one is parsed.
|
|
1562
1565
|
async loadCSS(path) {
|
|
1563
|
-
const existing = document.querySelector(`link[data-file="${path}"]`);
|
|
1564
1566
|
const url = this.makeUrl(path);
|
|
1567
|
+
const toIframe = this.iframeTarget && this.css !== "parent";
|
|
1568
|
+
const toParent = !this.iframeTarget || this.css !== "iframe";
|
|
1569
|
+
const ops = [];
|
|
1570
|
+
if (toIframe) {
|
|
1571
|
+
ops.push(
|
|
1572
|
+
fetch(url).then((r) => {
|
|
1573
|
+
if (!r.ok) throw new Error(`Failed to fetch CSS: ${path} (${r.status})`);
|
|
1574
|
+
return r.text();
|
|
1575
|
+
}).then((code) => {
|
|
1576
|
+
this._inject("css", code, path);
|
|
1577
|
+
})
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
if (toParent) {
|
|
1581
|
+
ops.push(this._loadCSSInParent(path, url));
|
|
1582
|
+
}
|
|
1583
|
+
await Promise.all(ops);
|
|
1584
|
+
return true;
|
|
1585
|
+
}
|
|
1586
|
+
_loadCSSInParent(path, url) {
|
|
1587
|
+
const existing = document.querySelector(`link[data-file="${path}"]`);
|
|
1565
1588
|
const link = document.createElement("link");
|
|
1566
1589
|
link.rel = "stylesheet";
|
|
1567
1590
|
link.href = url;
|
|
@@ -1580,6 +1603,14 @@ var FileLoader = class {
|
|
|
1580
1603
|
}
|
|
1581
1604
|
async loadModule(path) {
|
|
1582
1605
|
const url = this.makeUrl(path);
|
|
1606
|
+
if (this.iframeTarget) {
|
|
1607
|
+
const code = await fetch(url).then((r) => {
|
|
1608
|
+
if (!r.ok) throw new Error(`Failed to fetch module: ${path} (${r.status})`);
|
|
1609
|
+
return r.text();
|
|
1610
|
+
});
|
|
1611
|
+
this._inject("module", code, path);
|
|
1612
|
+
return true;
|
|
1613
|
+
}
|
|
1583
1614
|
await import(url);
|
|
1584
1615
|
return true;
|
|
1585
1616
|
}
|
|
@@ -1587,6 +1618,14 @@ var FileLoader = class {
|
|
|
1587
1618
|
const url = this.makeUrl(path);
|
|
1588
1619
|
const existing = document.querySelector(`script[data-file="${path}"]`);
|
|
1589
1620
|
if (existing) existing.remove();
|
|
1621
|
+
if (this.iframeTarget) {
|
|
1622
|
+
const code = await fetch(url).then((r) => {
|
|
1623
|
+
if (!r.ok) throw new Error(`Failed to fetch script: ${path} (${r.status})`);
|
|
1624
|
+
return r.text();
|
|
1625
|
+
});
|
|
1626
|
+
this._inject("script", code, path);
|
|
1627
|
+
return true;
|
|
1628
|
+
}
|
|
1590
1629
|
const script = document.createElement("script");
|
|
1591
1630
|
script.src = url;
|
|
1592
1631
|
script.setAttribute("data-file", path);
|
|
@@ -1632,10 +1671,15 @@ var FileLoader = class {
|
|
|
1632
1671
|
for (const { reject } of entry.resolvers) reject(new Error(`File removed: ${path}`));
|
|
1633
1672
|
this.loadQueue.delete(path);
|
|
1634
1673
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1674
|
+
if (this.iframeTarget) {
|
|
1675
|
+
this._post({ type: "hmr:remove", file: path });
|
|
1676
|
+
await Promise.resolve();
|
|
1677
|
+
} else {
|
|
1678
|
+
const el = document.querySelector(`[data-file="${path}"]`);
|
|
1679
|
+
if (el) {
|
|
1680
|
+
el.remove();
|
|
1681
|
+
await Promise.resolve();
|
|
1682
|
+
}
|
|
1639
1683
|
}
|
|
1640
1684
|
this.versions.delete(path);
|
|
1641
1685
|
}
|
|
@@ -1645,6 +1689,14 @@ var FileLoader = class {
|
|
|
1645
1689
|
this.versions.set(path, v);
|
|
1646
1690
|
return `${this.httpUrl}${path}?v=${v}`;
|
|
1647
1691
|
}
|
|
1692
|
+
// Send a raw postMessage to the iframe target
|
|
1693
|
+
_post(message) {
|
|
1694
|
+
this.iframeTarget.postMessage(message, this.iframeOrigin);
|
|
1695
|
+
}
|
|
1696
|
+
// Forward a file payload to the iframe target
|
|
1697
|
+
_inject(kind, code, file) {
|
|
1698
|
+
this._post({ type: "hmr:inject", kind, code, file });
|
|
1699
|
+
}
|
|
1648
1700
|
};
|
|
1649
1701
|
|
|
1650
1702
|
// src/shared/constants.js
|
|
@@ -1750,6 +1802,10 @@ var HMRClient = class {
|
|
|
1750
1802
|
* @param {function(string): void} [options.onFileLoaded] - Called after each file loads or reloads. Receives `(filePath)`.
|
|
1751
1803
|
* @param {function(string[]): string[]} [options.sortFiles] - Custom sort for the initial file load order. When provided, replaces `defaultSortFiles` entirely and `loadOrder` is ignored.
|
|
1752
1804
|
* @param {Array<Function>} [options.loadOrder] - Stages prepended before the built-in sort (CSS-first, cold-first, alphabetical). One argument: return true to load that file first. Two arguments: works like a normal sort callback.
|
|
1805
|
+
* @param {boolean | Object} [options.iframe] - Forward files to an iframe via `postMessage` (for Private Network Access restricted environments). Pass `true` for defaults.
|
|
1806
|
+
* @param {Window | HTMLIFrameElement} [options.iframe.target] - Target a specific same-origin iframe directly, skipping auto-discovery. Reattachment is not automatic.
|
|
1807
|
+
* @param {string} [options.iframe.origin] - The iframe's origin used to validate incoming handshake responses. Defaults to `'*'`.
|
|
1808
|
+
* @param {'iframe'|'parent'|'both'} [options.iframe.css='iframe'] - Where CSS files are loaded when `iframe` is set.
|
|
1753
1809
|
*/
|
|
1754
1810
|
constructor(options) {
|
|
1755
1811
|
const opts = typeof options === "object" && !Array.isArray(options) ? options : {};
|
|
@@ -1779,7 +1835,18 @@ var HMRClient = class {
|
|
|
1779
1835
|
this._reconnectTimer = null;
|
|
1780
1836
|
this._messageQueue = [];
|
|
1781
1837
|
this._processingMessages = false;
|
|
1782
|
-
|
|
1838
|
+
const iframeOpts = opts.iframe === true ? {} : opts.iframe;
|
|
1839
|
+
const iframeTarget = iframeOpts?.target ? iframeOpts.target?.contentWindow ?? null : null;
|
|
1840
|
+
const iframeOrigin = iframeOpts?.origin ?? "*";
|
|
1841
|
+
this._iframeTarget = iframeTarget;
|
|
1842
|
+
this._iframeOrigin = iframeOrigin;
|
|
1843
|
+
this._stubManaged = !!iframeOpts && !iframeTarget;
|
|
1844
|
+
this._onReattach = null;
|
|
1845
|
+
this.fileLoader = new FileLoader(this.httpUrl, {
|
|
1846
|
+
iframeTarget,
|
|
1847
|
+
iframeOrigin,
|
|
1848
|
+
css: iframeOpts?.css ?? "iframe"
|
|
1849
|
+
});
|
|
1783
1850
|
this.overrideMap = /* @__PURE__ */ new Map();
|
|
1784
1851
|
this._reverseOverrideMap = /* @__PURE__ */ new Map();
|
|
1785
1852
|
this.logStyles = {
|
|
@@ -1926,13 +1993,13 @@ var HMRClient = class {
|
|
|
1926
1993
|
}
|
|
1927
1994
|
}
|
|
1928
1995
|
const withOverrides = this.buildOverrideMap(toLoad);
|
|
1929
|
-
const
|
|
1930
|
-
this.logInitFileGroup(
|
|
1931
|
-
for (const file of
|
|
1996
|
+
const sorted = this.sortFiles(withOverrides);
|
|
1997
|
+
this.logInitFileGroup(sorted, this.overrideMap, this.isColdFile.bind(this));
|
|
1998
|
+
for (const file of sorted) {
|
|
1932
1999
|
await this.fileLoader.loadFile(file);
|
|
1933
2000
|
if (this.onFileLoaded) this.onFileLoaded(file);
|
|
1934
2001
|
}
|
|
1935
|
-
this.log("success", `HMR client ready (${
|
|
2002
|
+
this.log("success", `HMR client ready (${sorted.length} files loaded)`);
|
|
1936
2003
|
}
|
|
1937
2004
|
async handleFileChange(file, action, serverCold = false) {
|
|
1938
2005
|
if (this.shouldSkipFile(file, this.allFiles)) {
|
|
@@ -2036,7 +2103,7 @@ var HMRClient = class {
|
|
|
2036
2103
|
await this.processInitFiles(data.files);
|
|
2037
2104
|
} else {
|
|
2038
2105
|
const modeLabel = this.watchFiles ? "HMR ready" : "Static snapshot ready";
|
|
2039
|
-
this.log("success", `${modeLabel} (
|
|
2106
|
+
this.log("success", `${modeLabel} (0 files loaded)`);
|
|
2040
2107
|
}
|
|
2041
2108
|
return;
|
|
2042
2109
|
}
|
|
@@ -2131,6 +2198,48 @@ var HMRClient = class {
|
|
|
2131
2198
|
}
|
|
2132
2199
|
this._processingMessages = false;
|
|
2133
2200
|
}
|
|
2201
|
+
// Wait for stub's hmr:ready signal. Stub fires it proactively on run.
|
|
2202
|
+
// Times out after 5s if stub was never injected.
|
|
2203
|
+
_waitForStub() {
|
|
2204
|
+
return new Promise((resolve, reject) => {
|
|
2205
|
+
const timer = setTimeout(() => {
|
|
2206
|
+
window.removeEventListener("message", onReady);
|
|
2207
|
+
reject(new Error("Timed out waiting for hmr:ready. Was HMR.stub() called in the iframe?"));
|
|
2208
|
+
}, 5e3);
|
|
2209
|
+
const onReady = (e) => {
|
|
2210
|
+
if (e.data?.type !== "hmr:ready") return;
|
|
2211
|
+
const originOk = this._iframeOrigin === "*" || e.origin === this._iframeOrigin;
|
|
2212
|
+
if (!originOk) return;
|
|
2213
|
+
clearTimeout(timer);
|
|
2214
|
+
window.removeEventListener("message", onReady);
|
|
2215
|
+
if (this._stubManaged) {
|
|
2216
|
+
this._iframeTarget = e.source;
|
|
2217
|
+
this.fileLoader.iframeTarget = e.source;
|
|
2218
|
+
}
|
|
2219
|
+
resolve();
|
|
2220
|
+
};
|
|
2221
|
+
window.addEventListener("message", onReady);
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
// Persistent listener for hmr:ready that handles iframe reattachment. If the
|
|
2225
|
+
// iframe reloads or is replaced, the stub fires hmr:ready again so we can
|
|
2226
|
+
// update the target and re-inject all currently loaded files.
|
|
2227
|
+
_listenForReattach() {
|
|
2228
|
+
if (this._onReattach) return;
|
|
2229
|
+
this._onReattach = async (e) => {
|
|
2230
|
+
if (e.data?.type !== "hmr:ready") return;
|
|
2231
|
+
const originOk = this._iframeOrigin === "*" || e.origin === this._iframeOrigin;
|
|
2232
|
+
if (!originOk) return;
|
|
2233
|
+
if (e.source === this._iframeTarget) return;
|
|
2234
|
+
this._iframeTarget = e.source;
|
|
2235
|
+
this.fileLoader.iframeTarget = e.source;
|
|
2236
|
+
this.log("success", "HMR reattached to new iframe");
|
|
2237
|
+
for (const path of this.fileLoader.versions.keys()) {
|
|
2238
|
+
await this.fileLoader.loadFile(path);
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
window.addEventListener("message", this._onReattach);
|
|
2242
|
+
}
|
|
2134
2243
|
/**
|
|
2135
2244
|
* Connect to the HMR server
|
|
2136
2245
|
* @returns {Promise<void>}
|
|
@@ -2153,14 +2262,27 @@ var HMRClient = class {
|
|
|
2153
2262
|
let settled = false;
|
|
2154
2263
|
try {
|
|
2155
2264
|
this.socket = new WebSocket(this.wsUrl);
|
|
2156
|
-
this.socket.onopen = () => {
|
|
2265
|
+
this.socket.onopen = async () => {
|
|
2157
2266
|
settled = true;
|
|
2158
2267
|
this.isConnected = true;
|
|
2159
2268
|
this.reconnectAttempts = 0;
|
|
2160
|
-
this._messageQueue = [];
|
|
2161
|
-
this._processingMessages = false;
|
|
2162
2269
|
this.log("success", "HMR connected");
|
|
2163
2270
|
this.emit("connect");
|
|
2271
|
+
this._messageQueue = [];
|
|
2272
|
+
this._processingMessages = true;
|
|
2273
|
+
if (this._iframeTarget || this._stubManaged) {
|
|
2274
|
+
try {
|
|
2275
|
+
await this._waitForStub();
|
|
2276
|
+
if (this._stubManaged) this._listenForReattach();
|
|
2277
|
+
} catch (e) {
|
|
2278
|
+
this.log("error", e.message);
|
|
2279
|
+
this._processingMessages = false;
|
|
2280
|
+
reject(e);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
this._processingMessages = false;
|
|
2285
|
+
if (this._messageQueue.length > 0) this._drainMessageQueue();
|
|
2164
2286
|
resolve();
|
|
2165
2287
|
};
|
|
2166
2288
|
this.socket.onclose = () => {
|
|
@@ -2216,15 +2338,57 @@ var HMRClient = class {
|
|
|
2216
2338
|
this.isConnected = false;
|
|
2217
2339
|
clearTimeout(this._reconnectTimer);
|
|
2218
2340
|
this._reconnectTimer = null;
|
|
2341
|
+
if (this._onReattach) {
|
|
2342
|
+
window.removeEventListener("message", this._onReattach);
|
|
2343
|
+
this._onReattach = null;
|
|
2344
|
+
}
|
|
2219
2345
|
if (this.socket) {
|
|
2220
2346
|
this.socket.close();
|
|
2221
2347
|
this.socket = null;
|
|
2222
2348
|
}
|
|
2223
2349
|
}
|
|
2224
2350
|
};
|
|
2351
|
+
|
|
2352
|
+
// src/client/stub.js
|
|
2353
|
+
function stub() {
|
|
2354
|
+
const byFile = (tag, file) => document.querySelector(`${tag}[data-file="${CSS.escape(file)}"]`);
|
|
2355
|
+
const removeIfExists = (el) => {
|
|
2356
|
+
if (el) el.remove();
|
|
2357
|
+
};
|
|
2358
|
+
const injectScript = (kind, code, file) => {
|
|
2359
|
+
removeIfExists(byFile("script", file));
|
|
2360
|
+
const script = document.createElement("script");
|
|
2361
|
+
if (kind === "module") script.type = "module";
|
|
2362
|
+
script.textContent = code;
|
|
2363
|
+
script.dataset.file = file;
|
|
2364
|
+
document.documentElement.appendChild(script);
|
|
2365
|
+
script.remove();
|
|
2366
|
+
};
|
|
2367
|
+
const injectStyle = (code, file) => {
|
|
2368
|
+
const existing = byFile("style", file);
|
|
2369
|
+
const style = document.createElement("style");
|
|
2370
|
+
style.textContent = code;
|
|
2371
|
+
style.dataset.file = file;
|
|
2372
|
+
document.head.appendChild(style);
|
|
2373
|
+
removeIfExists(existing);
|
|
2374
|
+
};
|
|
2375
|
+
window.addEventListener("message", (e) => {
|
|
2376
|
+
const data = e.data;
|
|
2377
|
+
if (!data?.type) return;
|
|
2378
|
+
if (data.type === "hmr:remove") {
|
|
2379
|
+
removeIfExists(document.querySelector(`[data-file="${CSS.escape(data.file)}"]`));
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (data.type !== "hmr:inject") return;
|
|
2383
|
+
const { kind, code, file } = data;
|
|
2384
|
+
if (kind === "script" || kind === "module") injectScript(kind, code, file);
|
|
2385
|
+
else if (kind === "css") injectStyle(code, file);
|
|
2386
|
+
});
|
|
2387
|
+
window.parent.postMessage({ type: "hmr:ready" }, "*");
|
|
2388
|
+
}
|
|
2225
2389
|
export {
|
|
2226
|
-
FileLoader,
|
|
2227
2390
|
HMRClient,
|
|
2228
|
-
HMRClient as default
|
|
2391
|
+
HMRClient as default,
|
|
2392
|
+
stub
|
|
2229
2393
|
};
|
|
2230
2394
|
//# sourceMappingURL=client.js.map
|