@stencil/dev-server 0.0.19-1 → 5.0.0-alpha.1

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.
@@ -0,0 +1,1076 @@
1
+ //#region src/client/constants.ts
2
+ /**
3
+ * Client-side constants for dev server.
4
+ */
5
+ const DEV_SERVER_URL = "/~dev-server";
6
+ const DEV_SERVER_INIT_URL = `${DEV_SERVER_URL}-init`;
7
+ const OPEN_IN_EDITOR_URL = `${DEV_SERVER_URL}-open-in-editor`;
8
+ const BUILD_LOG = "devserver:buildlog";
9
+ const BUILD_RESULTS = "devserver:buildresults";
10
+ const BUILD_STATUS = "devserver:buildstatus";
11
+ const NODE_TYPE_ELEMENT = 1;
12
+ const NODE_TYPE_DOCUMENT_FRAGMENT = 11;
13
+ const RECONNECT_ATTEMPTS = 1e3;
14
+ const RECONNECT_RETRY_MS = 2500;
15
+ const NORMAL_CLOSURE_CODE = 1e3;
16
+ const REQUEST_BUILD_RESULTS_INTERVAL_MS = 500;
17
+ //#endregion
18
+ //#region src/client/error.css?inline
19
+ var error_default = "#dev-server-modal * {\n box-sizing: border-box !important;\n}\n\n#dev-server-modal {\n -webkit-font-smoothing: antialiased;\n text-rendering: optimizelegibility;\n text-size-adjust: none;\n word-wrap: break-word;\n user-select: auto;\n display: block;\n z-index: 99999 !important;\n -webkit-overflow-scrolling: touch !important;\n direction: ltr !important;\n background: #000000a8 !important;\n width: 100% !important;\n height: 100% !important;\n margin: 0 !important;\n padding: 0 !important;\n font-family: -apple-system, Roboto, BlinkMacSystemFont, Segoe UI, Helvetica Neue, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol !important;\n font-size: 14px !important;\n line-height: 1.5 !important;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n overflow-y: auto !important;\n}\n\n#dev-server-modal-inner {\n color: #333 !important;\n background-color: #fff !important;\n border-radius: 8px !important;\n max-width: 80vw !important;\n margin: 30px auto !important;\n padding: 25px !important;\n position: relative !important;\n box-shadow: 0 10px 40px #00000080 !important;\n}\n\n.dev-server-diagnostic {\n border: 1px solid #ddd !important;\n border-radius: 3px !important;\n margin: 20px !important;\n}\n\n.dev-server-diagnostic-masthead {\n padding: 8px 12px 12px !important;\n}\n\n.dev-server-diagnostic-title {\n color: #222 !important;\n margin: 0 !important;\n font-weight: bold !important;\n}\n\n.dev-server-diagnostic-message {\n color: #555 !important;\n margin-top: 4px !important;\n}\n\n.dev-server-diagnostic-file {\n border-top: 1px solid #ddd !important;\n position: relative !important;\n}\n\n.dev-server-diagnostic-file-header {\n color: #555 !important;\n background-color: #f9f9f9 !important;\n border-bottom: 1px solid #ddd !important;\n border-top-left-radius: 2px !important;\n border-top-right-radius: 2px !important;\n padding: 5px 10px !important;\n font-family: Consolas, Liberation Mono, Menlo, Courier, monospace !important;\n font-size: 12px !important;\n display: block !important;\n}\n\na.dev-server-diagnostic-file-header {\n color: #00e !important;\n text-decoration: underline !important;\n}\n\na.dev-server-diagnostic-file-header:hover {\n background-color: #f4f4f4 !important;\n text-decoration: none !important;\n}\n\n.dev-server-diagnostic-file-name {\n font-weight: bold !important;\n}\n\n.dev-server-diagnostic-blob {\n border-bottom-right-radius: 3px !important;\n border-bottom-left-radius: 3px !important;\n overflow: auto hidden !important;\n}\n\n.dev-server-diagnostic-table {\n -moz-tab-size: 2;\n tab-size: 2;\n border-spacing: 0 !important;\n border-collapse: collapse !important;\n border-style: none !important;\n border-width: 0 !important;\n margin: 0 !important;\n padding: 0 !important;\n}\n\n.dev-server-diagnostic-table td, .dev-server-diagnostic-table th {\n border-style: none !important;\n border-width: 0 !important;\n padding: 0 !important;\n}\n\ntd.dev-server-diagnostic-blob-num {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n color: #0000004d !important;\n text-align: right !important;\n white-space: nowrap !important;\n vertical-align: top !important;\n border: 0 solid #eee !important;\n border-right-width: 1px !important;\n width: 1% !important;\n min-width: 50px !important;\n padding-left: 10px !important;\n padding-right: 10px !important;\n font-family: Consolas, Liberation Mono, Menlo, Courier, monospace !important;\n font-size: 12px !important;\n line-height: 20px !important;\n}\n\ntd.dev-server-diagnostic-blob-num:before {\n content: attr(data-line-number) !important;\n}\n\n.dev-server-diagnostic-error-line td.dev-server-diagnostic-blob-num {\n background-color: #fdd !important;\n border-color: #ffc9c9 !important;\n}\n\n.dev-server-diagnostic-error-line td.dev-server-diagnostic-blob-code {\n z-index: -1;\n background: #ffdddd40 !important;\n}\n\n.dev-server-diagnostic-open-in-editor td.dev-server-diagnostic-blob-num:hover {\n cursor: pointer;\n font-weight: bold;\n background-color: #ffffe3 !important;\n}\n\n.dev-server-diagnostic-open-in-editor.dev-server-diagnostic-error-line td.dev-server-diagnostic-blob-num:hover {\n background-color: #ffdada !important;\n}\n\ntd.dev-server-diagnostic-blob-code {\n vertical-align: top !important;\n color: #333 !important;\n word-wrap: normal !important;\n white-space: pre !important;\n padding-left: 10px !important;\n padding-right: 10px !important;\n font-family: Consolas, Liberation Mono, Menlo, Courier, monospace !important;\n font-size: 12px !important;\n line-height: 20px !important;\n position: relative !important;\n overflow: visible !important;\n}\n\ntd.dev-server-diagnostic-blob-code:before {\n content: \"\" !important;\n}\n\n.dev-server-diagnostic-error-chr {\n position: relative !important;\n}\n\n.dev-server-diagnostic-error-chr:before {\n z-index: -1;\n content: \"\" !important;\n background-color: #fdd !important;\n width: 8px !important;\n height: 20px !important;\n position: absolute !important;\n top: -3px !important;\n left: 0 !important;\n}\n\n.hljs-comment, .hljs-meta {\n color: #969896;\n}\n\n.hljs-string, .hljs-variable, .hljs-template-variable, .hljs-strong, .hljs-emphasis, .hljs-quote {\n color: #df5000;\n}\n\n.hljs-keyword, .hljs-selector-tag, .hljs-type {\n color: #a71d5d;\n}\n\n.hljs-literal, .hljs-symbol, .hljs-bullet, .hljs-attribute {\n color: #0086b3;\n}\n\n.hljs-section, .hljs-name {\n color: #63a35c;\n}\n\n.hljs-tag {\n color: #333;\n}\n\n.hljs-title, .hljs-attr, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo {\n color: #795da3;\n}\n\n.hljs-addition {\n color: #55a532;\n background-color: #eaffea;\n}\n\n.hljs-deletion {\n color: #bd2c00;\n background-color: #ffecec;\n}\n\n.hljs-link {\n text-decoration: underline;\n}\n\n.dev-server-error-badge {\n z-index: 99998 !important;\n color: #fff !important;\n cursor: pointer !important;\n background: #f55 !important;\n border: none !important;\n border-radius: 8px !important;\n align-items: center !important;\n gap: 8px !important;\n padding: 10px 16px !important;\n font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, sans-serif !important;\n font-size: 14px !important;\n font-weight: 600 !important;\n transition: transform .2s, box-shadow .2s !important;\n display: flex !important;\n position: fixed !important;\n bottom: 20px !important;\n left: 20px !important;\n box-shadow: 0 4px 12px #0000004d !important;\n}\n\n.dev-server-error-badge:hover {\n transform: translateY(-2px) !important;\n box-shadow: 0 6px 16px #0006 !important;\n}\n\n.error-badge-icon {\n color: #f55 !important;\n background: #fff !important;\n border-radius: 50% !important;\n justify-content: center !important;\n align-items: center !important;\n width: 20px !important;\n height: 20px !important;\n font-size: 14px !important;\n font-weight: bold !important;\n display: flex !important;\n}\n\n.error-badge-count {\n font-size: 14px !important;\n font-weight: 600 !important;\n}\n";
20
+ //#endregion
21
+ //#region src/client/error.ts
22
+ let errorCount = 0;
23
+ const appError = (data) => {
24
+ const results = {
25
+ diagnostics: [],
26
+ status: null
27
+ };
28
+ if (data && data.window && Array.isArray(data.buildResults.diagnostics)) {
29
+ const diagnostics = data.buildResults.diagnostics.filter((diagnostic) => diagnostic.level === "error");
30
+ if (diagnostics.length > 0) {
31
+ errorCount = diagnostics.length;
32
+ const modal = getDevServerModal(data.window.document, data.openInEditor);
33
+ diagnostics.forEach((diagnostic) => {
34
+ results.diagnostics.push(diagnostic);
35
+ appendDiagnostic(data.window.document, data.openInEditor, modal, diagnostic);
36
+ });
37
+ removeErrorBadge(data.window.document);
38
+ results.status = "error";
39
+ }
40
+ }
41
+ return results;
42
+ };
43
+ const appendDiagnostic = (doc, openInEditor, modal, diagnostic) => {
44
+ const card = doc.createElement("div");
45
+ card.className = "dev-server-diagnostic";
46
+ const masthead = doc.createElement("div");
47
+ masthead.className = "dev-server-diagnostic-masthead";
48
+ masthead.title = `${escapeHtml(diagnostic.type)} error: ${escapeHtml(diagnostic.code ?? "unknown error")}`;
49
+ card.appendChild(masthead);
50
+ const title = doc.createElement("div");
51
+ title.className = "dev-server-diagnostic-title";
52
+ if (typeof diagnostic.header === "string" && diagnostic.header.trim().length > 0) title.textContent = diagnostic.header;
53
+ else title.textContent = `${titleCase(diagnostic.type)} ${titleCase(diagnostic.level)}`;
54
+ masthead.appendChild(title);
55
+ const message = doc.createElement("div");
56
+ message.className = "dev-server-diagnostic-message";
57
+ message.textContent = diagnostic.messageText;
58
+ masthead.appendChild(message);
59
+ const file = doc.createElement("div");
60
+ file.className = "dev-server-diagnostic-file";
61
+ card.appendChild(file);
62
+ const isUrl = typeof diagnostic.absFilePath === "string" && diagnostic.absFilePath.indexOf("http") === 0;
63
+ const canOpenInEditor = typeof openInEditor === "function" && typeof diagnostic.absFilePath === "string" && !isUrl;
64
+ if (isUrl) {
65
+ const fileHeader = doc.createElement("a");
66
+ fileHeader.href = diagnostic.absFilePath ?? "";
67
+ fileHeader.setAttribute("target", "_blank");
68
+ fileHeader.setAttribute("rel", "noopener noreferrer");
69
+ fileHeader.className = "dev-server-diagnostic-file-header";
70
+ const filePath = doc.createElement("span");
71
+ filePath.className = "dev-server-diagnostic-file-path";
72
+ filePath.textContent = diagnostic.absFilePath ?? "";
73
+ fileHeader.appendChild(filePath);
74
+ file.appendChild(fileHeader);
75
+ } else if (diagnostic.relFilePath) {
76
+ const fileHeader = doc.createElement(canOpenInEditor ? "a" : "div");
77
+ fileHeader.className = "dev-server-diagnostic-file-header";
78
+ if (diagnostic.absFilePath) {
79
+ fileHeader.title = escapeHtml(diagnostic.absFilePath);
80
+ if (canOpenInEditor) addOpenInEditor(openInEditor, fileHeader, diagnostic.absFilePath, diagnostic.lineNumber, diagnostic.columnNumber);
81
+ }
82
+ const parts = diagnostic.relFilePath.split("/");
83
+ const fileName = doc.createElement("span");
84
+ fileName.className = "dev-server-diagnostic-file-name";
85
+ fileName.textContent = parts.pop() ?? "";
86
+ const filePath = doc.createElement("span");
87
+ filePath.className = "dev-server-diagnostic-file-path";
88
+ filePath.textContent = parts.join("/") + "/";
89
+ fileHeader.appendChild(filePath);
90
+ fileHeader.appendChild(fileName);
91
+ file.appendChild(fileHeader);
92
+ }
93
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
94
+ const blob = doc.createElement("div");
95
+ blob.className = "dev-server-diagnostic-blob";
96
+ file.appendChild(blob);
97
+ const table = doc.createElement("table");
98
+ table.className = "dev-server-diagnostic-table";
99
+ blob.appendChild(table);
100
+ prepareLines(diagnostic.lines).forEach((l) => {
101
+ const tr = doc.createElement("tr");
102
+ if (l.errorCharStart > 0) tr.classList.add("dev-server-diagnostic-error-line");
103
+ if (canOpenInEditor) tr.classList.add("dev-server-diagnostic-open-in-editor");
104
+ table.appendChild(tr);
105
+ const tdNum = doc.createElement("td");
106
+ tdNum.className = "dev-server-diagnostic-blob-num";
107
+ if (l.lineNumber > 0) {
108
+ tdNum.setAttribute("data-line-number", l.lineNumber + "");
109
+ tdNum.title = escapeHtml(diagnostic.relFilePath ?? "") + ", line " + l.lineNumber;
110
+ const maybeFile = diagnostic.absFilePath;
111
+ if (canOpenInEditor && maybeFile) {
112
+ const column = l.lineNumber === diagnostic.lineNumber ? diagnostic.columnNumber : 1;
113
+ addOpenInEditor(openInEditor, tdNum, maybeFile, l.lineNumber, column);
114
+ }
115
+ }
116
+ tr.appendChild(tdNum);
117
+ const tdCode = doc.createElement("td");
118
+ tdCode.className = "dev-server-diagnostic-blob-code";
119
+ tdCode.innerHTML = highlightError(l.text ?? "", l.errorCharStart, l.errorLength ?? 0);
120
+ tr.appendChild(tdCode);
121
+ });
122
+ }
123
+ modal.appendChild(card);
124
+ };
125
+ const addOpenInEditor = (openInEditor, elm, file, line, column) => {
126
+ if (elm.tagName === "A") elm.href = "#open-in-editor";
127
+ const lineNumber = typeof line !== "number" || line < 1 ? 1 : line;
128
+ const columnNumber = typeof column !== "number" || column < 1 ? 1 : column;
129
+ elm.addEventListener("click", (ev) => {
130
+ ev.preventDefault();
131
+ ev.stopPropagation();
132
+ openInEditor({
133
+ file,
134
+ line: lineNumber,
135
+ column: columnNumber
136
+ });
137
+ });
138
+ };
139
+ const getDevServerModal = (doc, _openInEditor) => {
140
+ let outer = doc.getElementById(DEV_SERVER_MODAL);
141
+ let isNewModal = false;
142
+ if (!outer) {
143
+ isNewModal = true;
144
+ outer = doc.createElement("div");
145
+ outer.id = DEV_SERVER_MODAL;
146
+ outer.setAttribute("role", "dialog");
147
+ doc.body.appendChild(outer);
148
+ outer.innerHTML = `<style>${error_default}</style><div id="${DEV_SERVER_MODAL}-inner"></div>`;
149
+ const closeOnEsc = (e) => {
150
+ if (e.key === "Escape" || e.code === "Escape") closeDevServerModal(doc);
151
+ };
152
+ doc.addEventListener("keydown", closeOnEsc);
153
+ outer.__closeOnEsc = closeOnEsc;
154
+ outer.addEventListener("click", (e) => {
155
+ if (e.target === outer) closeDevServerModal(doc);
156
+ });
157
+ }
158
+ outer.style.display = "block";
159
+ const inner = doc.getElementById(`${DEV_SERVER_MODAL}-inner`);
160
+ inner.innerHTML = "";
161
+ if (isNewModal) inner.addEventListener("click", (e) => {
162
+ e.stopPropagation();
163
+ });
164
+ return inner;
165
+ };
166
+ const closeDevServerModal = (doc) => {
167
+ const outer = doc.getElementById(DEV_SERVER_MODAL);
168
+ if (outer) {
169
+ outer.style.display = "none";
170
+ showErrorBadge(doc);
171
+ }
172
+ };
173
+ const clearAppErrorModal = (data) => {
174
+ const appErrorElm = data.window.document.getElementById(DEV_SERVER_MODAL);
175
+ if (appErrorElm) {
176
+ const closeOnEsc = appErrorElm.__closeOnEsc;
177
+ if (closeOnEsc) data.window.document.removeEventListener("keydown", closeOnEsc);
178
+ if (appErrorElm.parentNode) appErrorElm.parentNode.removeChild(appErrorElm);
179
+ }
180
+ removeErrorBadge(data.window.document);
181
+ errorCount = 0;
182
+ };
183
+ const showErrorBadge = (doc) => {
184
+ if (errorCount === 0) return;
185
+ let badge = doc.getElementById(ERROR_BADGE_ID);
186
+ if (!badge) {
187
+ badge = doc.createElement("button");
188
+ badge.id = ERROR_BADGE_ID;
189
+ badge.className = "dev-server-error-badge";
190
+ badge.setAttribute("aria-label", "Show build errors");
191
+ doc.body.appendChild(badge);
192
+ badge.addEventListener("click", () => {
193
+ const modal = doc.getElementById(DEV_SERVER_MODAL);
194
+ if (modal) {
195
+ modal.style.display = "block";
196
+ removeErrorBadge(doc);
197
+ }
198
+ });
199
+ }
200
+ badge.innerHTML = `<span class="error-badge-icon">!</span><span class="error-badge-count">${errorCount}</span>`;
201
+ badge.style.display = "flex";
202
+ };
203
+ const removeErrorBadge = (doc) => {
204
+ const badge = doc.getElementById(ERROR_BADGE_ID);
205
+ if (badge) badge.style.display = "none";
206
+ };
207
+ const escapeHtml = (unsafe) => {
208
+ if (typeof unsafe === "number" || typeof unsafe === "boolean") return unsafe.toString();
209
+ if (typeof unsafe === "string") return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
210
+ return "";
211
+ };
212
+ const titleCase = (str) => str.charAt(0).toUpperCase() + str.slice(1);
213
+ const highlightError = (text, errorCharStart, errorLength) => {
214
+ if (typeof text !== "string") return "";
215
+ const errorCharEnd = errorCharStart + errorLength;
216
+ return text.split("").map((inputChar, charIndex) => {
217
+ let outputChar;
218
+ if (inputChar === `<`) outputChar = `&lt;`;
219
+ else if (inputChar === `>`) outputChar = `&gt;`;
220
+ else if (inputChar === `"`) outputChar = `&quot;`;
221
+ else if (inputChar === `'`) outputChar = `&#039;`;
222
+ else if (inputChar === `&`) outputChar = `&amp;`;
223
+ else outputChar = inputChar;
224
+ if (charIndex >= errorCharStart && charIndex < errorCharEnd) outputChar = `<span class="dev-server-diagnostic-error-chr">${outputChar}</span>`;
225
+ return outputChar;
226
+ }).join("");
227
+ };
228
+ const prepareLines = (orgLines) => {
229
+ const lines = JSON.parse(JSON.stringify(orgLines));
230
+ for (let x = 0; x < 100; x++) {
231
+ if (!eachLineHasLeadingWhitespace(lines)) return lines;
232
+ for (let i = 0; i < lines.length; i++) {
233
+ lines[i].text = lines[i].text?.slice(1) ?? "";
234
+ lines[i].errorCharStart--;
235
+ if (!lines[i].text?.length) return lines;
236
+ }
237
+ }
238
+ return lines;
239
+ };
240
+ const eachLineHasLeadingWhitespace = (lines) => {
241
+ if (!lines.length) return false;
242
+ for (let i = 0; i < lines.length; i++) {
243
+ if (!lines[i].text || (lines[i].text?.length ?? 0) < 1) return false;
244
+ const firstChar = lines[i].text?.charAt(0);
245
+ if (firstChar !== " " && firstChar !== " ") return false;
246
+ }
247
+ return true;
248
+ };
249
+ const DEV_SERVER_MODAL = `dev-server-modal`;
250
+ const ERROR_BADGE_ID = "dev-server-error-badge";
251
+ //#endregion
252
+ //#region src/client/events.ts
253
+ /**
254
+ * Client-side event system for dev server.
255
+ */
256
+ const emitBuildLog = (win, buildLog) => {
257
+ win.dispatchEvent(new CustomEvent(BUILD_LOG, { detail: buildLog }));
258
+ };
259
+ const emitBuildResults = (win, buildResults) => {
260
+ win.dispatchEvent(new CustomEvent(BUILD_RESULTS, { detail: buildResults }));
261
+ };
262
+ const emitBuildStatus = (win, buildStatus) => {
263
+ win.dispatchEvent(new CustomEvent(BUILD_STATUS, { detail: buildStatus }));
264
+ };
265
+ const onBuildLog = (win, cb) => {
266
+ win.addEventListener(BUILD_LOG, ((ev) => {
267
+ cb(ev.detail);
268
+ }));
269
+ };
270
+ const onBuildResults = (win, cb) => {
271
+ win.addEventListener(BUILD_RESULTS, ((ev) => {
272
+ cb(ev.detail);
273
+ }));
274
+ };
275
+ const onBuildStatus = (win, cb) => {
276
+ win.addEventListener(BUILD_STATUS, ((ev) => {
277
+ cb(ev.detail);
278
+ }));
279
+ };
280
+ //#endregion
281
+ //#region src/client/hmr/utils.ts
282
+ const getHmrHref = (versionId, fileName, testUrl) => {
283
+ if (typeof testUrl === "string" && testUrl.trim() !== "") {
284
+ if (getUrlFileName(fileName) === getUrlFileName(testUrl)) return setHmrQueryString(testUrl, versionId);
285
+ }
286
+ return testUrl;
287
+ };
288
+ const getUrlFileName = (url) => {
289
+ const splt = url.split("/");
290
+ return splt[splt.length - 1].split("&")[0].split("?")[0];
291
+ };
292
+ const parseQuerystring = (oldQs) => {
293
+ const newQs = {};
294
+ if (typeof oldQs === "string") oldQs.split("&").forEach((kv) => {
295
+ const splt = kv.split("=");
296
+ newQs[splt[0]] = splt[1] ? splt[1] : "";
297
+ });
298
+ return newQs;
299
+ };
300
+ const stringifyQuerystring = (qs) => Object.keys(qs).map((key) => key + "=" + qs[key]).join("&");
301
+ const setQueryString = (url, qsKey, qsValue) => {
302
+ const urlSplt = url.split("?");
303
+ const urlPath = urlSplt[0];
304
+ const qs = parseQuerystring(urlSplt[1]);
305
+ qs[qsKey] = qsValue;
306
+ return urlPath + "?" + stringifyQuerystring(qs);
307
+ };
308
+ const setHmrQueryString = (url, versionId) => setQueryString(url, "s-hmr", versionId);
309
+ const updateCssUrlValue = (versionId, fileName, oldCss) => {
310
+ const reg = /url\((['"]?)(.*)\1\)/gi;
311
+ let result;
312
+ let newCss = oldCss;
313
+ while ((result = reg.exec(oldCss)) !== null) {
314
+ const url = result[2];
315
+ newCss = newCss.replace(url, getHmrHref(versionId, fileName, url));
316
+ }
317
+ return newCss;
318
+ };
319
+ /**
320
+ * Determine whether a given element is a `<link>` tag pointing to a stylesheet
321
+ *
322
+ * @param elm the element to check
323
+ * @returns whether or not the element is a link stylesheet
324
+ */
325
+ const isLinkStylesheet = (elm) => elm.nodeName.toLowerCase() === "link" && !!elm.href && !!elm.rel && elm.rel.toLowerCase() === "stylesheet";
326
+ /**
327
+ * Determine whether or not a given element is a template element
328
+ *
329
+ * @param elm the element to check
330
+ * @returns whether or not the element of interest is a template element
331
+ */
332
+ const isTemplate = (elm) => elm.nodeName.toLowerCase() === "template" && !!elm.content && elm.content.nodeType === 11;
333
+ /**
334
+ * Set a new hmr version ID into the `data-hmr` attribute on an element.
335
+ *
336
+ * @param elm the element on which to set the property
337
+ * @param versionId a new HMR version id
338
+ */
339
+ const setHmrAttr = (elm, versionId) => {
340
+ elm.setAttribute("data-hmr", versionId);
341
+ };
342
+ /**
343
+ * Determine whether or not an element has a shadow root
344
+ *
345
+ * @param elm the element to check
346
+ * @returns whether or not it has a shadow root
347
+ */
348
+ const hasShadowRoot = (elm) => !!elm.shadowRoot && elm.shadowRoot.nodeType === 11 && elm.shadowRoot !== elm;
349
+ /**
350
+ * Determine whether or not an element is an element node
351
+ *
352
+ * @param elm the element to check
353
+ * @returns whether or not it is an element node
354
+ */
355
+ const isElement = (elm) => !!elm && elm.nodeType === 1 && !!elm.getAttribute;
356
+ //#endregion
357
+ //#region src/client/hmr/component.ts
358
+ const hmrComponents = (element, versionId, hmrTagNames) => {
359
+ const updatedTags = [];
360
+ hmrTagNames.forEach((hmrTagName) => {
361
+ hmrComponent(updatedTags, element, versionId, hmrTagName);
362
+ });
363
+ return updatedTags.sort();
364
+ };
365
+ const hmrComponent = (updatedTags, element, versionId, cmpTagName) => {
366
+ if (element.nodeName.toLowerCase() === cmpTagName && typeof element["s-hmr"] === "function") {
367
+ element["s-hmr"](versionId);
368
+ setHmrAttr(element, versionId);
369
+ if (updatedTags.indexOf(cmpTagName) === -1) updatedTags.push(cmpTagName);
370
+ }
371
+ if (hasShadowRoot(element)) hmrComponent(updatedTags, element.shadowRoot, versionId, cmpTagName);
372
+ if (element.children) for (let i = 0; i < element.children.length; i++) hmrComponent(updatedTags, element.children[i], versionId, cmpTagName);
373
+ };
374
+ //#endregion
375
+ //#region src/client/hmr/image.ts
376
+ const hmrImages = (win, doc, versionId, imageFileNames) => {
377
+ if (win.location.protocol !== "file:" && doc.styleSheets) hmrStyleSheetsImages(doc, versionId, imageFileNames);
378
+ hmrImagesElements(win, doc.documentElement, versionId, imageFileNames);
379
+ return imageFileNames.sort();
380
+ };
381
+ const hmrStyleSheetsImages = (doc, versionId, imageFileNames) => {
382
+ const cssImageProps = Object.keys(doc.documentElement.style).filter((cssProp) => {
383
+ return cssProp.endsWith("Image");
384
+ });
385
+ for (let i = 0; i < doc.styleSheets.length; i++) hmrStyleSheetImages(cssImageProps, doc.styleSheets[i], versionId, imageFileNames);
386
+ };
387
+ const hmrStyleSheetImages = (cssImageProps, styleSheet, versionId, imageFileNames) => {
388
+ try {
389
+ const cssRules = styleSheet.cssRules;
390
+ for (let i = 0; i < cssRules.length; i++) {
391
+ const cssRule = cssRules[i];
392
+ switch (cssRule.type) {
393
+ case CSSRule.IMPORT_RULE:
394
+ hmrStyleSheetImages(cssImageProps, cssRule.styleSheet, versionId, imageFileNames);
395
+ break;
396
+ case CSSRule.STYLE_RULE:
397
+ hmrStyleSheetRuleImages(cssImageProps, cssRule, versionId, imageFileNames);
398
+ break;
399
+ case CSSRule.MEDIA_RULE:
400
+ hmrStyleSheetImages(cssImageProps, cssRule, versionId, imageFileNames);
401
+ break;
402
+ }
403
+ }
404
+ } catch (e) {
405
+ console.error("hmrStyleSheetImages:", e);
406
+ }
407
+ };
408
+ const hmrStyleSheetRuleImages = (cssImageProps, cssRule, versionId, imageFileNames) => {
409
+ cssImageProps.forEach((cssImageProp) => {
410
+ imageFileNames.forEach((imageFileName) => {
411
+ const oldCssText = cssRule.style[cssImageProp];
412
+ const newCssText = updateCssUrlValue(versionId, imageFileName, oldCssText);
413
+ if (oldCssText !== newCssText) cssRule.style[cssImageProp] = newCssText;
414
+ });
415
+ });
416
+ };
417
+ const hmrImagesElements = (win, elm, versionId, imageFileNames) => {
418
+ const tagName = elm.nodeName.toLowerCase();
419
+ if (tagName === "img") hmrImgElement(elm, versionId, imageFileNames);
420
+ if (isElement(elm)) {
421
+ const styleAttr = elm.getAttribute("style");
422
+ if (styleAttr) hmrUpdateStyleAttr(elm, versionId, imageFileNames, styleAttr);
423
+ }
424
+ if (tagName === "style") hmrUpdateStyleElementUrl(elm, versionId, imageFileNames);
425
+ if (win.location.protocol !== "file:" && isLinkStylesheet(elm)) hmrUpdateLinkElementUrl(elm, versionId, imageFileNames);
426
+ if (isTemplate(elm)) hmrImagesElements(win, elm.content, versionId, imageFileNames);
427
+ if (hasShadowRoot(elm)) hmrImagesElements(win, elm.shadowRoot, versionId, imageFileNames);
428
+ if (elm.children) for (let i = 0; i < elm.children.length; i++) hmrImagesElements(win, elm.children[i], versionId, imageFileNames);
429
+ };
430
+ const hmrImgElement = (imgElm, versionId, imageFileNames) => {
431
+ imageFileNames.forEach((imageFileName) => {
432
+ const orgSrc = imgElm.getAttribute("src");
433
+ const newSrc = getHmrHref(versionId, imageFileName, orgSrc || "");
434
+ if (newSrc !== orgSrc) {
435
+ imgElm.setAttribute("src", newSrc);
436
+ setHmrAttr(imgElm, versionId);
437
+ }
438
+ });
439
+ };
440
+ const hmrUpdateStyleElementUrl = (styleElm, versionId, imageFileNames) => {
441
+ imageFileNames.forEach((imageFileName) => {
442
+ const oldCssText = styleElm.innerHTML;
443
+ const newCssText = updateCssUrlValue(versionId, imageFileName, oldCssText);
444
+ if (newCssText !== oldCssText) {
445
+ styleElm.innerHTML = newCssText;
446
+ setHmrAttr(styleElm, versionId);
447
+ }
448
+ });
449
+ };
450
+ const hmrUpdateLinkElementUrl = (linkElm, versionId, imageFileNames) => {
451
+ linkElm.href = setQueryString(linkElm.href, "s-hmr-urls", imageFileNames.sort().join(","));
452
+ linkElm.href = setHmrQueryString(linkElm.href, versionId);
453
+ linkElm.setAttribute("data-hmr", versionId);
454
+ };
455
+ const hmrUpdateStyleAttr = (elm, versionId, imageFileNames, oldStyleAttr) => {
456
+ imageFileNames.forEach((imageFileName) => {
457
+ const newStyleAttr = updateCssUrlValue(versionId, imageFileName, oldStyleAttr);
458
+ if (newStyleAttr !== oldStyleAttr) {
459
+ elm.setAttribute("style", newStyleAttr);
460
+ setHmrAttr(elm, versionId);
461
+ }
462
+ });
463
+ };
464
+ //#endregion
465
+ //#region src/client/hmr/style.ts
466
+ const STYLE_ID_ATTR = "sty-id";
467
+ const hmrExternalStyles = (elm, versionId, cssFileNames) => {
468
+ if (isLinkStylesheet(elm)) cssFileNames.forEach((cssFileName) => {
469
+ hmrStylesheetLink(elm, versionId, cssFileName);
470
+ });
471
+ if (isTemplate(elm)) hmrExternalStyles(elm.content, versionId, cssFileNames);
472
+ if (hasShadowRoot(elm)) hmrExternalStyles(elm.shadowRoot, versionId, cssFileNames);
473
+ if (elm.children) for (let i = 0; i < elm.children.length; i++) hmrExternalStyles(elm.children[i], versionId, cssFileNames);
474
+ return cssFileNames.sort();
475
+ };
476
+ const hmrStylesheetLink = (styleSheetElm, versionId, cssFileName) => {
477
+ const orgHref = styleSheetElm.getAttribute("href");
478
+ const newHref = getHmrHref(versionId, cssFileName, styleSheetElm.href);
479
+ if (newHref !== orgHref) {
480
+ styleSheetElm.setAttribute("href", newHref);
481
+ setHmrAttr(styleSheetElm, versionId);
482
+ }
483
+ };
484
+ const hmrInlineStyles = (elm, versionId, stylesUpdatedData) => {
485
+ const trackers = stylesUpdatedData.map((styleUpdate) => ({
486
+ styleUpdate,
487
+ updated: false
488
+ }));
489
+ hmrInlineStylesTraverse(elm, versionId, trackers);
490
+ for (const tracker of trackers) if (!tracker.updated && tracker.styleUpdate.styleText) createStyleElementsForComponent(elm, versionId, tracker.styleUpdate);
491
+ return stylesUpdatedData.map((s) => s.styleTag).reduce((arr, v) => {
492
+ if (arr.indexOf(v) === -1) arr.push(v);
493
+ return arr;
494
+ }, []).sort();
495
+ };
496
+ /**
497
+ * Traverse the DOM looking for style elements to update or remove.
498
+ * @param elm - the element to start traversal from
499
+ * @param versionId - the HMR version identifier
500
+ * @param trackers - the style update trackers
501
+ */
502
+ const hmrInlineStylesTraverse = (elm, versionId, trackers) => {
503
+ if (isElement(elm) && elm.nodeName.toLowerCase() === "style") trackers.forEach((tracker) => {
504
+ if (hmrStyleElement(elm, versionId, tracker.styleUpdate)) tracker.updated = true;
505
+ });
506
+ if (isTemplate(elm)) hmrInlineStylesTraverse(elm.content, versionId, trackers);
507
+ if (hasShadowRoot(elm)) hmrInlineStylesTraverse(elm.shadowRoot, versionId, trackers);
508
+ if (elm.children) for (let i = 0; i < elm.children.length; i++) hmrInlineStylesTraverse(elm.children[i], versionId, trackers);
509
+ };
510
+ /**
511
+ * Update or remove a style element based on the HMR update.
512
+ * @param elm - the style element to update
513
+ * @param versionId - the HMR version identifier
514
+ * @param stylesUpdated - the HMR style update data
515
+ * @returns true if this element matched and was processed
516
+ */
517
+ const hmrStyleElement = (elm, versionId, stylesUpdated) => {
518
+ if (elm.getAttribute(STYLE_ID_ATTR) === stylesUpdated.styleId) {
519
+ if (stylesUpdated.styleText) {
520
+ elm.innerHTML = stylesUpdated.styleText.replace(/\\n/g, "\n");
521
+ elm.setAttribute("data-hmr", versionId);
522
+ } else elm.remove();
523
+ return true;
524
+ }
525
+ return false;
526
+ };
527
+ /**
528
+ * Find all component instances with the matching tag name and create style elements.
529
+ * Handles both shadow DOM components (style in shadow root) and scoped components (style in head).
530
+ * @param rootElm - the root element to search from
531
+ * @param versionId - the HMR version identifier
532
+ * @param styleUpdate - the HMR style update data
533
+ */
534
+ const createStyleElementsForComponent = (rootElm, versionId, styleUpdate) => {
535
+ const { styleTag, styleId, styleText } = styleUpdate;
536
+ const doc = rootElm.ownerDocument;
537
+ const componentInstances = findComponentInstances(rootElm, styleTag);
538
+ if (componentInstances.length === 0) {
539
+ createStyleElement(doc.head, styleId, styleText, versionId);
540
+ return;
541
+ }
542
+ const processedShadowRoots = /* @__PURE__ */ new Set();
543
+ let addedToHead = false;
544
+ for (const instance of componentInstances) if (instance.shadowRoot) {
545
+ if (!processedShadowRoots.has(instance.shadowRoot)) {
546
+ processedShadowRoots.add(instance.shadowRoot);
547
+ createStyleElement(instance.shadowRoot, styleId, styleText, versionId);
548
+ }
549
+ } else if (!addedToHead) {
550
+ addedToHead = true;
551
+ createStyleElement(doc.head, styleId, styleText, versionId);
552
+ }
553
+ };
554
+ /**
555
+ * Find all instances of a component by tag name, including in shadow roots.
556
+ * @param elm - the element to search from
557
+ * @param tagName - the tag name to search for
558
+ * @returns an array of matching elements
559
+ */
560
+ const findComponentInstances = (elm, tagName) => {
561
+ const instances = [];
562
+ findComponentInstancesTraverse(elm, tagName.toLowerCase(), instances);
563
+ return instances;
564
+ };
565
+ const findComponentInstancesTraverse = (elm, tagName, instances) => {
566
+ if (elm.nodeName.toLowerCase() === tagName) instances.push(elm);
567
+ if (hasShadowRoot(elm)) findComponentInstancesTraverse(elm.shadowRoot, tagName, instances);
568
+ if (elm.children) for (let i = 0; i < elm.children.length; i++) findComponentInstancesTraverse(elm.children[i], tagName, instances);
569
+ };
570
+ /**
571
+ * Create a new style element with the given content.
572
+ * @param container - the container to insert the style element into
573
+ * @param styleId - the style identifier
574
+ * @param styleText - the CSS content
575
+ * @param versionId - the HMR version identifier
576
+ */
577
+ const createStyleElement = (container, styleId, styleText, versionId) => {
578
+ const styleElm = ("ownerDocument" in container ? container.ownerDocument : container.ownerDocument).createElement("style");
579
+ styleElm.innerHTML = styleText.replace(/\\n/g, "\n");
580
+ styleElm.setAttribute(STYLE_ID_ATTR, styleId);
581
+ styleElm.setAttribute("data-hmr", versionId);
582
+ if (container.firstChild) container.insertBefore(styleElm, container.firstChild);
583
+ else container.appendChild(styleElm);
584
+ };
585
+ //#endregion
586
+ //#region src/client/hmr/window.ts
587
+ const hmrWindow = (data) => {
588
+ const results = {
589
+ updatedComponents: [],
590
+ updatedExternalStyles: [],
591
+ updatedInlineStyles: [],
592
+ updatedImages: [],
593
+ versionId: ""
594
+ };
595
+ try {
596
+ if (!data || !data.window || !data.window.document.documentElement || !data.hmr || typeof data.hmr.versionId !== "string") return results;
597
+ const win = data.window;
598
+ const doc = win.document;
599
+ const hmr = data.hmr;
600
+ const documentElement = doc.documentElement;
601
+ const versionId = hmr.versionId;
602
+ results.versionId = versionId;
603
+ if (hmr.componentsUpdated) results.updatedComponents = hmrComponents(documentElement, versionId, hmr.componentsUpdated);
604
+ if (hmr.inlineStylesUpdated) results.updatedInlineStyles = hmrInlineStyles(documentElement, versionId, hmr.inlineStylesUpdated);
605
+ if (hmr.externalStylesUpdated) results.updatedExternalStyles = hmrExternalStyles(documentElement, versionId, hmr.externalStylesUpdated);
606
+ if (hmr.imagesUpdated) results.updatedImages = hmrImages(win, doc, versionId, hmr.imagesUpdated);
607
+ setHmrAttr(documentElement, versionId);
608
+ } catch (e) {
609
+ console.error(e);
610
+ }
611
+ return results;
612
+ };
613
+ //#endregion
614
+ //#region src/client/logger.ts
615
+ const YELLOW = "#f39c12";
616
+ const RED = "#c0392b";
617
+ const BLUE = "#3498db";
618
+ const GRAY = "#717171";
619
+ const log = (color, prefix, msg) => {
620
+ console.log("%c" + prefix, `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`, msg);
621
+ };
622
+ const logBuild = (msg) => log(BLUE, "Build", msg);
623
+ const logReload = (msg) => logWarn("Reload", msg);
624
+ const logWarn = (prefix, msg) => log(YELLOW, prefix, msg);
625
+ const logDisabled = (prefix, msg) => log(GRAY, prefix, msg);
626
+ const logDiagnostic = (diag) => {
627
+ let color = RED;
628
+ let prefix = "Error";
629
+ if (diag.level === "warn") {
630
+ color = YELLOW;
631
+ prefix = "Warning";
632
+ }
633
+ if (diag.header) prefix = diag.header;
634
+ let msg = "";
635
+ if (diag.relFilePath) {
636
+ msg += diag.relFilePath;
637
+ if (typeof diag.lineNumber === "number" && diag.lineNumber > 0) {
638
+ msg += ", line " + diag.lineNumber;
639
+ if (typeof diag.columnNumber === "number" && diag.columnNumber > 0) msg += ", column " + diag.columnNumber;
640
+ }
641
+ msg += "\n";
642
+ }
643
+ msg += diag.messageText;
644
+ log(color, prefix, msg);
645
+ };
646
+ //#endregion
647
+ //#region src/client/status.ts
648
+ /**
649
+ * Build status and favicon utilities for dev server client.
650
+ */
651
+ const ICON_DEFAULT = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAAnFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4jUzeAAAAM3RSTlMAsGDs4wML8QEbBvr2FMhAM7+ILCUPnNzXrX04otO6j3RiT0ggzLSTcmtWUUWoZlknghZc2mZzAAACrklEQVR42u3dWXLiUAyFYWEwg40x8wxhSIAwJtH+99ZVeeinfriXVpWk5Hyr+C2VrgkAAAAAAAAAAAw5sZQ7aUhYypw07FjKC2ko2yxk2SQFgwYLOWSkYFhlIZ06KWhNWMhqRApGKxYyaZGCeoeFVIekIDuwkEaXFDSXLKRdkoYjS9mRhjlLSUjDO0s5kYYzS+mThn3OQsYqAbQQC7hZSgoGYgHUy0jBa42FvKkEUDERC6CCFIzeWEjtlRRkPbGAG5CCtCIWQAtS0ByzkHxPGvos5UEaNizlnTRsWconhbM4wTpSFHMTrFtKCroNFrLGBOsJLbGAWxWkoFiJBRAmWE/I1r4nWOmNheTeJ1gX0vDJUrYUweAEa04aHs5XePvc9wpPboJ1SCmOsRVkr04aromUEQEAgB9lxaZ++ATFpNDv6Y8qm1QdBk9QTAr9ni6mbFK7DJ6g2LQLXoHZlFCQdMY2nYJXYDb1g1dgNo2boSswm2Zp6ArMptCFyIVtCl2IlDmbNC0QcPEQcD8l4HLvAXdxHnBb5wG3QcDFQ8D9mIDrIeCiIeDiA25oNeA+EHDREHDxAbdmmxBwT0HARQbciW0KDbiEbQoNuB3bFBxwbTYJAfcUBFxkwFG/YlNJAADgxzCRcqUY9m7KGgNSUEx9H3XXO76Puv/OY5wedX/flHk+6j46v2maO79purPvm6Yz+75puua+b5q6Dd/PEsrNMyZfFM5gAMW+ymPtWciYV3ksBpBOwKUH3wHXXLKUM2l4cR5wG+cBlzgPuJ3zgJNb6FRwlP4Ln1X8wrOKeFbxP6Qz3wEn+KzilWLYe5UnMuDwY5BvD+cBt899B9zC+49Bqr4DrlXzHXDF1HfA1Tu+Ay5b+w649OY74OjoO+Bo7jzg7s4DDgAAAAAAAAAA/u0POrfnVIaqz/QAAAAASUVORK5CYII=";
652
+ const ICON_PENDING = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAAjVBMVEUAAAD8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjL8kjLn7xn3AAAALnRSTlMAsFBgAaDxfPpAdTMcD/fs47kDBhVXJQpvLNbInIiBRvSqIb+TZ2OOONxdzUxpgKSpAAAAA69JREFUeNrt3FtvskAQxvERFQXFioqnCkqth572+3+8947dN00TliF5ZpP53ZOAveg/OzCklFJKKaWUUkoppQTZm77cCGFo+jIhhG/TlwchJAvTk/GIAA6x6Um+JoDti+nJ644A5h+mJ8eMALKj6cnHnAB2r80NLJ4jf3Vz+cuWANZ5cwPTM/l7by6PZwQwGptGQf4q++dLCOHdNIbkb2IvjwjAvYEf8pe6j4/wYxopr/9SQih4BXa3l5eEcJ7a++c9/gkSQE8bcCWvXwcrAjjYADrxHv8KCbi3JasgD5fm8i9IAG1swMXzDv0X2wDaEED21dzA5UDeVoPm8uUbAayvvAI42YA7EIDzA5pv8lc6/UoAoxMv4CZuvyKUpnHn9VNBAG6B7XkBtCeEO6/AbvbyihAiXsB92svfCcA9wap4j19DAmgWs37AZCrnBKvu8vgX9AmWE3BZh/6L7QkWJIA2RxtwHQpml9sAQp9gXWbkbxz4CdYDfIK1qk1j3IV9fPgJFlNECJXhYfSfsBHkhBCKwEd452nYI7wncwQJP8GKTU+uO0I4D/uSkVJKqXAkA5nK9icoIi3nrU9QRHrZtj5BESmetT5BEantPCh7NTJFrUdgMg1bj8BkSv1HYJ8RmjMQKf1HYDdC+/R/IyQFzbD4AxH+CIyPPxCJoEdQ/IFIMgXNEPkDkd8jMLQs5wRcTXA1J+By/BGO+0ovYwQGU3kPRLJfIzCkCSfgpgmhpc5AxD/gIkLb8wKO0DTgoNyaGQQecNfQAy7TgGtHA04DLtyA24UecHngAVdrwIkJuAitU8DJ1Dbghkam9gEnU+uAWxiRjhsdoXagI1TPgKNyIBO+ZpRSSrW3HfblTAA9/juPDwTAfiMK9VG3PY/hwX7Ubc9j+AoCWNWGp+NSH4HflE2IgXUEGPI3TTfmN4ndv2kSsRUJvpUn4W1FShbYb5rc84ySAtzKs3W3IgW4lWfO24q0zsFbebIjaysSjbtt5RHzUf0DHHCrAW8gVYEDzl0LGYW4lefB24uYQgOOfwN7dMANeW/k3DkBJ2CrUNE54GRsFYIHnPNR+iPEgHPWKo5DDDhnrWKeBRhwzlrFeNtlq5CgtYqzAAPODaBzgAH331rFAAOOqsDXKjL3IqboN7ILJ4BCDDh3r3SIAfd0AijEgHP3So/8wQNuvjRBbxVij5A6Bpy8EZJnwIkbIfkFnLwRkm/ASRshXbwDTtYICRRwt7BHqEoppZRSSimllFLqD/8AOXJZHefotiIAAAAASUVORK5CYII=";
653
+ const ICON_ERROR = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAAkFBMVEUAAAD5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0H5Q0HYvLBZAAAAL3RSTlMAsGDjA/rsC/ElHRUBBssz9pFCvoh0UEcsD9ec3K19OLiiaNLEYlmoVeiCbmE+GuMl4I8AAAKQSURBVHja7d1njupQDIZhAimEUIZQQu9taN7/7q50pfl/TmTJtvQ9q3hzLDsEAAAAAAAAAACGzFjKiTS0WcqONMxZypg0fH5YyLFPChZdFnIYkILil4VcclLw3bCQ85KULM8sZPMlBfmFhfwWpGBwYCHdESnoH1nIz4c0jFnKnDTsWEqbNJxYyow03FjKlDTUKQtZqwTQXizgtgkpWGQsZKIScL0OCxmqBFC5EQugkhQshyyk0yMFgwkLyRakIGmJBdCeFPTXLCStScOUpdwogsEXrBdpuLKUJ4XDC9afKmUh94QUjLy/YGViAZRTOIMBtypJQXn2HUC5WMBleMFqILmzkLSicBZfsB6k4clSrqTh5XyEd3MeQHXqe4Qn94LVSiicwRHkJScNdVvKkgAAwI+qZdM0/AXFpE4v+AXFpKwIfkExKfR7ulyxSWkV/IJi0zx4BGbTm4IkW7ZpFjwCs2kaPAKzad0PHYHZtE1CR2A2TQahIzCbhnnwCMykVYmAi4aAQ8BZ4T3grgi4BhBwCDgbEHCNIOAQcCYg4BpCwCHgLEDAaYgPuDfbhIBrBAGHgDMhNOBo2rKpIgAA8KNoS6kplq2dsu6CFJQr30vd+dD3Uvf/nTLHS93J3flZwrHznaad852mE/veaXqw752mKvW90zTq+j5LWGS+r/J8xQKoU1AUa2chm1zlsXQWUifgkoPvgOsffQccjZ0H3Mx5wL2dB9zcecB9sJTePOBM3cU+46wiziq6C7hk6zvg3J9VfDK7vir0ch5wN+cBV6e+A27v/ccgme+AkxshTXKKYW6EFH0X29gIKTLgzI2QYgPO2ggpLuDsvaDEBZy9EVJcwBkcIT0IAAAAAAAAAADs+AdjeyF69/r87QAAAABJRU5ErkJggg==";
654
+ const ICON_DISABLED = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAAeFBMVEUAAAC4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7+4t7/uGGySAAAAJ3RSTlMAsGAE7OMcAQvxJRX69kHWyL8zq5GIdEcsD5zcfVg4uKLNa1JPZoK/xdPIAAACiklEQVR42u3dW5KqUAyF4QgCCggqIt7t9pb5z/Ccvjz2w95UqpJ0r28Uf2WTQAAAAAAAAAAAYMiWpTxJQ8JSTqThwVI2pKFZsJC3ghTs5izkmpKCcspCljNSkB9ZSLsnBfuWhRxzUjBbspBpSQrSKwuZr0lB8cZCFg1p2LCUB2k4sZSENNxYypY0nFlKTxqGmoUcClJwEQu4SUoKdmIBtEpJQZ6xkHeVAKqOYgFUkYL9OwvJclKQrsQCbkcK0olYAF1IQXFgIfVAGnqWcqZwFidYN4phb4L1onCYYMlPsLqUFKwxwRozwTIYcG1FCqrWdwBhgqU7wUo7FlJ7n2DdScPL+RPezfkT3tl5AA217yc89xMssYBbzUjDkEjZEwAA+NFMbOrDJygmZXnwBMWkaRk8QTFpvg6eoJi0aIInKDY9gp/AbEqCJyg2bYOfwGzqKUzPNh2K0Ccwm0IfRBK2KfSLkDvbFPog0tRsUlsh4EZAwP2SgKu9B9wdATcOAg4BZwACbgQEHALOCATcCAg4BJwVCLhREHB/LOAebFNwwC3YJATcKAi4yICjfmJTQwAA4EeZSBkojrWdsvmO4hjbKYtd6ra2Uxa71G1tp0xnqbvo+IPfpe4Nf3K703Ridr3T9OQPfnea7szseaepqX3vNH3NM/xe5fmeZ7i9yiMXQFlJEeydhYy4ymMygCICzmQAxQactbOQMQFnMoBiAs7iVaHIgDN3VSgq4AxeFYoOOGNXhbCUPkaJs4o4q/iXzyp2vgPO/VnFl/OAu/F/jq8KnZ0H3FD7DriL9x+DTH0HXJ75Driq9R1ws6XvgEuvvgOu6HwHHG18BxydnAfc03nAAQAAAAAAAADAz/4BoL2Us9XM2zMAAAAASUVORK5CYII=";
655
+ const ICON_TYPE = "image/x-icon";
656
+ const initBuildStatus = (data) => {
657
+ const win = data.window;
658
+ const doc = win.document;
659
+ getFavIcons(doc).forEach((iconElm) => {
660
+ if (iconElm.href) {
661
+ iconElm.dataset.href = iconElm.href;
662
+ iconElm.dataset.type = iconElm.type;
663
+ }
664
+ });
665
+ onBuildStatus(win, (buildStatus) => {
666
+ updateBuildStatus(doc, buildStatus);
667
+ });
668
+ };
669
+ const updateBuildStatus = (doc, status) => {
670
+ getFavIcons(doc).forEach((iconElm) => {
671
+ updateFavIcon(iconElm, status);
672
+ });
673
+ };
674
+ const updateFavIcon = (linkElm, status) => {
675
+ if (status === "pending") {
676
+ linkElm.href = ICON_PENDING;
677
+ linkElm.type = ICON_TYPE;
678
+ linkElm.setAttribute("data-status", status);
679
+ } else if (status === "error") {
680
+ linkElm.href = ICON_ERROR;
681
+ linkElm.type = ICON_TYPE;
682
+ linkElm.setAttribute("data-status", status);
683
+ } else if (status === "disabled") {
684
+ linkElm.href = ICON_DISABLED;
685
+ linkElm.type = ICON_TYPE;
686
+ linkElm.setAttribute("data-status", status);
687
+ } else {
688
+ linkElm.removeAttribute("data-status");
689
+ if (linkElm.dataset.href) {
690
+ linkElm.href = linkElm.dataset.href;
691
+ linkElm.type = linkElm.dataset.type || ICON_TYPE;
692
+ } else {
693
+ linkElm.href = ICON_DEFAULT;
694
+ linkElm.type = ICON_TYPE;
695
+ }
696
+ }
697
+ };
698
+ const getFavIcons = (doc) => {
699
+ const iconElms = [];
700
+ const linkElms = doc.querySelectorAll("link");
701
+ for (let i = 0; i < linkElms.length; i++) if (linkElms[i].href && linkElms[i].rel && (linkElms[i].rel.indexOf("shortcut") > -1 || linkElms[i].rel.indexOf("icon") > -1)) iconElms.push(linkElms[i]);
702
+ if (iconElms.length === 0) {
703
+ const linkElm = doc.createElement("link");
704
+ linkElm.rel = "shortcut icon";
705
+ doc.head.appendChild(linkElm);
706
+ iconElms.push(linkElm);
707
+ }
708
+ return iconElms;
709
+ };
710
+ const PROGRESS_BAR_ID = `dev-server-progress-bar`;
711
+ const initBuildProgress = (data) => {
712
+ const win = data.window;
713
+ const doc = win.document;
714
+ const barColor = `#5851ff`;
715
+ const errorColor = `#b70c19`;
716
+ let addBarTimerId;
717
+ let removeBarTimerId;
718
+ let opacityTimerId;
719
+ let incIntervalId;
720
+ let progressIncrease;
721
+ let currentProgress = 0;
722
+ function update() {
723
+ clearTimeout(opacityTimerId);
724
+ clearTimeout(removeBarTimerId);
725
+ const progressBar = getProgressBar();
726
+ if (!progressBar) {
727
+ createProgressBar();
728
+ addBarTimerId = setTimeout(update, 16);
729
+ return;
730
+ }
731
+ progressBar.style.background = barColor;
732
+ progressBar.style.opacity = `1`;
733
+ progressBar.style.transform = `scaleX(${Math.min(1, displayProgress())})`;
734
+ if (incIntervalId == null) incIntervalId = setInterval(() => {
735
+ progressIncrease += Math.random() * .05 + .01;
736
+ if (displayProgress() < .9) update();
737
+ else clearInterval(incIntervalId);
738
+ }, 800);
739
+ }
740
+ function reset() {
741
+ clearInterval(incIntervalId);
742
+ progressIncrease = .05;
743
+ incIntervalId = null;
744
+ clearTimeout(opacityTimerId);
745
+ clearTimeout(addBarTimerId);
746
+ clearTimeout(removeBarTimerId);
747
+ let progressBar = getProgressBar();
748
+ if (progressBar) {
749
+ if (currentProgress >= 1) progressBar.style.transform = `scaleX(1)`;
750
+ opacityTimerId = setTimeout(() => {
751
+ try {
752
+ progressBar = getProgressBar();
753
+ if (progressBar) progressBar.style.opacity = `0`;
754
+ } catch {}
755
+ }, 150);
756
+ removeBarTimerId = setTimeout(() => {
757
+ try {
758
+ progressBar = getProgressBar();
759
+ if (progressBar?.parentNode) progressBar.parentNode.removeChild(progressBar);
760
+ } catch {}
761
+ }, 1e3);
762
+ }
763
+ }
764
+ function displayProgress() {
765
+ const p = currentProgress + progressIncrease;
766
+ return Math.max(0, Math.min(1, p));
767
+ }
768
+ reset();
769
+ onBuildLog(win, (buildLog) => {
770
+ currentProgress = buildLog.progress;
771
+ if (currentProgress >= 0 && currentProgress < 1) update();
772
+ else reset();
773
+ });
774
+ onBuildResults(win, (buildResults) => {
775
+ if (buildResults.hasError) {
776
+ const progressBar = getProgressBar();
777
+ if (progressBar) {
778
+ progressBar.style.transform = `scaleX(1)`;
779
+ progressBar.style.background = errorColor;
780
+ }
781
+ }
782
+ reset();
783
+ });
784
+ onBuildStatus(win, (buildStatus) => {
785
+ if (buildStatus === "disabled") reset();
786
+ });
787
+ if (doc.head.dataset.tmpl === "tmpl-initial-load") update();
788
+ function getProgressBar() {
789
+ return doc.getElementById(PROGRESS_BAR_ID);
790
+ }
791
+ function createProgressBar() {
792
+ const progressBar = doc.createElement("div");
793
+ progressBar.id = PROGRESS_BAR_ID;
794
+ progressBar.style.position = `absolute`;
795
+ progressBar.style.top = `0`;
796
+ progressBar.style.left = `0`;
797
+ progressBar.style.zIndex = `100001`;
798
+ progressBar.style.width = `100%`;
799
+ progressBar.style.height = `2px`;
800
+ progressBar.style.transform = `scaleX(0)`;
801
+ progressBar.style.opacity = `1`;
802
+ progressBar.style.background = barColor;
803
+ progressBar.style.transformOrigin = `left center`;
804
+ progressBar.style.transition = `transform .1s ease-in-out, opacity .5s ease-in`;
805
+ progressBar.style.contain = `strict`;
806
+ doc.body.appendChild(progressBar);
807
+ }
808
+ };
809
+ //#endregion
810
+ //#region src/client/websocket.ts
811
+ /**
812
+ * WebSocket client for dev server communication.
813
+ */
814
+ const initClientWebSocket = (win, config) => {
815
+ let clientWs = null;
816
+ let reconnectTmrId = null;
817
+ let reconnectAttempts = 0;
818
+ let requestBuildResultsTmrId = null;
819
+ let hasGottenBuildResults = false;
820
+ let buildResultsRequests = 0;
821
+ function onOpen() {
822
+ if (reconnectAttempts > 0) emitBuildStatus(win, "pending");
823
+ if (!hasGottenBuildResults) requestBuildResultsTmrId = setInterval(() => {
824
+ buildResultsRequests++;
825
+ if (!hasGottenBuildResults && this.readyState === WebSocket.OPEN && buildResultsRequests < 500) this.send(JSON.stringify({ requestBuildResults: true }));
826
+ else if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
827
+ }, 500);
828
+ if (reconnectTmrId) clearTimeout(reconnectTmrId);
829
+ }
830
+ function onError() {
831
+ queueReconnect();
832
+ }
833
+ function onClose(event) {
834
+ emitBuildStatus(win, "disabled");
835
+ if (event.code > 1e3) logWarn("Dev Server", `web socket closed: ${event.code} ${event.reason}`);
836
+ else logDisabled("Dev Server", "Disconnected, attempting to reconnect...");
837
+ queueReconnect();
838
+ }
839
+ function onMessage(event) {
840
+ const msg = JSON.parse(event.data);
841
+ if (reconnectAttempts > 0) {
842
+ if (msg.isActivelyBuilding) return;
843
+ if (msg.buildResults) {
844
+ logReload("Reconnected to dev server");
845
+ hasGottenBuildResults = true;
846
+ buildResultsRequests = 0;
847
+ if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
848
+ if (win["s-build-id"] !== msg.buildResults.buildId) win.location.reload();
849
+ win["s-build-id"] = msg.buildResults.buildId;
850
+ return;
851
+ }
852
+ }
853
+ if (msg.buildLog) {
854
+ if (msg.buildLog.progress < 1) emitBuildStatus(win, "pending");
855
+ emitBuildLog(win, msg.buildLog);
856
+ return;
857
+ }
858
+ if (msg.buildResults) {
859
+ hasGottenBuildResults = true;
860
+ buildResultsRequests = 0;
861
+ if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
862
+ emitBuildStatus(win, "default");
863
+ emitBuildResults(win, msg.buildResults);
864
+ }
865
+ }
866
+ function connect() {
867
+ if (reconnectTmrId) clearTimeout(reconnectTmrId);
868
+ clientWs = new win.WebSocket(config.socketUrl, ["xmpp"]);
869
+ clientWs.addEventListener("open", onOpen);
870
+ clientWs.addEventListener("error", onError);
871
+ clientWs.addEventListener("close", onClose);
872
+ clientWs.addEventListener("message", onMessage);
873
+ }
874
+ function queueReconnect() {
875
+ hasGottenBuildResults = false;
876
+ if (clientWs) {
877
+ if (clientWs.readyState === WebSocket.OPEN || clientWs.readyState === WebSocket.CONNECTING) clientWs.close(NORMAL_CLOSURE_CODE);
878
+ clientWs.removeEventListener("open", onOpen);
879
+ clientWs.removeEventListener("error", onError);
880
+ clientWs.removeEventListener("close", onClose);
881
+ clientWs.removeEventListener("message", onMessage);
882
+ clientWs = null;
883
+ }
884
+ if (reconnectTmrId) clearTimeout(reconnectTmrId);
885
+ if (reconnectAttempts >= 1e3) logWarn("Dev Server", "Canceling reconnect attempts");
886
+ else {
887
+ reconnectAttempts++;
888
+ reconnectTmrId = setTimeout(connect, RECONNECT_RETRY_MS);
889
+ emitBuildStatus(win, "disabled");
890
+ }
891
+ }
892
+ connect();
893
+ };
894
+ //#endregion
895
+ //#region src/client/index.ts
896
+ /**
897
+ * Stencil Dev Server Client
898
+ *
899
+ * Browser-side HMR (Hot Module Replacement) client for Stencil dev server.
900
+ * Handles WebSocket communication, component updates, style updates, and image updates.
901
+ *
902
+ * This module runs in the browser and is injected into pages during development.
903
+ * @module @stencil/dev-server/client
904
+ */
905
+ /**
906
+ * Initialize the app update handler for build results.
907
+ *
908
+ * @param win - the dev client window object
909
+ * @param config - the dev client configuration
910
+ */
911
+ const initAppUpdate = (win, config) => {
912
+ onBuildResults(win, (buildResults) => {
913
+ appUpdate(win, config, buildResults);
914
+ });
915
+ };
916
+ /**
917
+ * Process app update based on build results.
918
+ *
919
+ * @param win - the dev client window object
920
+ * @param config - the dev client configuration
921
+ * @param buildResults - the compiler build results
922
+ */
923
+ const appUpdate = (win, config, buildResults) => {
924
+ try {
925
+ if (buildResults.buildId === win["s-build-id"]) return;
926
+ win["s-build-id"] = buildResults.buildId;
927
+ clearAppErrorModal({ window: win });
928
+ if (buildResults.hasError) {
929
+ const errorResults = appError({
930
+ window: win,
931
+ buildResults,
932
+ openInEditor: Array.isArray(config.editors) && config.editors.length > 0 ? (data) => {
933
+ const url = `${OPEN_IN_EDITOR_URL}?${new URLSearchParams({
934
+ file: data.file,
935
+ line: String(data.line),
936
+ column: String(data.column)
937
+ }).toString()}`;
938
+ win.fetch(url).catch((err) => {
939
+ console.error("Failed to open in editor:", err);
940
+ });
941
+ } : void 0
942
+ });
943
+ errorResults.diagnostics.forEach(logDiagnostic);
944
+ if (errorResults.status) emitBuildStatus(win, errorResults.status);
945
+ if (win["s-initial-load"]) appReset(win, config, () => {
946
+ logReload("Initial load (with errors)");
947
+ win.location.reload();
948
+ });
949
+ return;
950
+ }
951
+ if (win["s-initial-load"]) {
952
+ appReset(win, config, () => {
953
+ logReload("Initial load");
954
+ win.location.reload();
955
+ });
956
+ return;
957
+ }
958
+ if (buildResults.hmr) appHmr(win, buildResults.hmr);
959
+ } catch (e) {
960
+ console.error(e);
961
+ }
962
+ };
963
+ /**
964
+ * Apply hot module replacement updates to the window.
965
+ *
966
+ * @param win - the browser window
967
+ * @param hmr - the hot module replacement data
968
+ */
969
+ const appHmr = (win, hmr) => {
970
+ let shouldWindowReload = false;
971
+ if (hmr.reloadStrategy === "pageReload") shouldWindowReload = true;
972
+ if (hmr.indexHtmlUpdated) {
973
+ logReload("Updated index.html");
974
+ shouldWindowReload = true;
975
+ }
976
+ if (hmr.serviceWorkerUpdated) {
977
+ logReload("Updated Service Worker: sw");
978
+ shouldWindowReload = true;
979
+ }
980
+ if (hmr.scriptsAdded && hmr.scriptsAdded.length > 0) {
981
+ logReload(`Added scripts: ${hmr.scriptsAdded.join(", ")}`);
982
+ shouldWindowReload = true;
983
+ }
984
+ if (hmr.scriptsDeleted && hmr.scriptsDeleted.length > 0) {
985
+ logReload(`Deleted scripts: ${hmr.scriptsDeleted.join(", ")}`);
986
+ shouldWindowReload = true;
987
+ }
988
+ if (hmr.excludeHmr && hmr.excludeHmr.length > 0) {
989
+ logReload(`Excluded From Hmr: ${hmr.excludeHmr.join(", ")}`);
990
+ shouldWindowReload = true;
991
+ }
992
+ if (shouldWindowReload) {
993
+ win.location.reload();
994
+ return;
995
+ }
996
+ const results = hmrWindow({
997
+ window: win,
998
+ hmr
999
+ });
1000
+ if (results.updatedComponents.length > 0) logBuild(`Updated component${results.updatedComponents.length > 1 ? "s" : ""}: ${results.updatedComponents.join(", ")}`);
1001
+ if (results.updatedInlineStyles.length > 0) logBuild(`Updated styles: ${results.updatedInlineStyles.join(", ")}`);
1002
+ if (results.updatedExternalStyles.length > 0) logBuild(`Updated stylesheets: ${results.updatedExternalStyles.join(", ")}`);
1003
+ if (results.updatedImages.length > 0) logBuild(`Updated images: ${results.updatedImages.join(", ")}`);
1004
+ };
1005
+ /**
1006
+ * Reset the app state and unregister service workers.
1007
+ *
1008
+ * @param win - the dev client window object
1009
+ * @param config - the dev client configuration
1010
+ * @param cb - callback to invoke after reset
1011
+ */
1012
+ const appReset = (win, config, cb) => {
1013
+ win.history.replaceState({}, "App", config.basePath);
1014
+ if (!win.navigator.serviceWorker?.getRegistration) cb();
1015
+ else win.navigator.serviceWorker.getRegistration().then((swRegistration) => {
1016
+ if (swRegistration) swRegistration.unregister().then((hasUnregistered) => {
1017
+ if (hasUnregistered) logBuild("unregistered service worker");
1018
+ cb();
1019
+ });
1020
+ else cb();
1021
+ }).catch((err) => {
1022
+ logWarn("Service Worker", err);
1023
+ cb();
1024
+ });
1025
+ };
1026
+ /**
1027
+ * Initialize the dev server client in the browser.
1028
+ *
1029
+ * @param win - the dev client window object
1030
+ * @param config - the dev client configuration
1031
+ */
1032
+ const initDevClient = (win, config) => {
1033
+ try {
1034
+ if (win["s-dev-server"]) return;
1035
+ win["s-dev-server"] = true;
1036
+ win.devServerConfig = config;
1037
+ initBuildStatus({ window: win });
1038
+ initBuildProgress({ window: win });
1039
+ initAppUpdate(win, config);
1040
+ if (isInitialDevServerLoad(win, config)) {
1041
+ win["s-initial-load"] = true;
1042
+ appReset(win, config, () => {
1043
+ initClientWebSocket(win, config);
1044
+ });
1045
+ } else initClientWebSocket(win, config);
1046
+ } catch (e) {
1047
+ console.error(e);
1048
+ }
1049
+ };
1050
+ /**
1051
+ * Check if this is the initial dev server load.
1052
+ *
1053
+ * @param win - the dev client window object
1054
+ * @param config - the dev client configuration
1055
+ * @returns true if this is the initial load
1056
+ */
1057
+ const isInitialDevServerLoad = (win, config) => {
1058
+ let pathname = win.location.pathname;
1059
+ pathname = "/" + pathname.substring(config.basePath.length);
1060
+ return pathname === DEV_SERVER_INIT_URL;
1061
+ };
1062
+ if (typeof appWindow !== "undefined" && typeof config !== "undefined") {
1063
+ const defaultConfig = {
1064
+ basePath: appWindow.location.pathname,
1065
+ editors: [],
1066
+ reloadStrategy: "hmr",
1067
+ socketUrl: `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.hostname}${location.port !== "" ? ":" + location.port : ""}/`
1068
+ };
1069
+ initDevClient(appWindow, {
1070
+ ...defaultConfig,
1071
+ ...appWindow.devServerConfig,
1072
+ ...config
1073
+ });
1074
+ }
1075
+ //#endregion
1076
+ export { BUILD_LOG, BUILD_RESULTS, BUILD_STATUS, DEV_SERVER_INIT_URL, DEV_SERVER_URL, NODE_TYPE_DOCUMENT_FRAGMENT, NODE_TYPE_ELEMENT, NORMAL_CLOSURE_CODE, OPEN_IN_EDITOR_URL, RECONNECT_ATTEMPTS, RECONNECT_RETRY_MS, REQUEST_BUILD_RESULTS_INTERVAL_MS, appError, clearAppErrorModal, emitBuildLog, emitBuildResults, emitBuildStatus, hmrWindow, initBuildProgress, initBuildStatus, initClientWebSocket, initDevClient, logBuild, logDiagnostic, logDisabled, logReload, logWarn, onBuildLog, onBuildResults, onBuildStatus, updateFavIcon };