@stencil/dev-server 0.0.19-1 → 5.0.0-alpha.10
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/{LICENSE → LICENSE.md} +12 -6
- package/dist/client/index.d.ts +132 -0
- package/dist/client/index.js +1078 -0
- package/dist/connector.html +34 -0
- package/dist/index.d.mts +52 -0
- package/dist/index.mjs +1522 -0
- package/dist/static/favicon.ico +0 -0
- package/dist/templates/directory-index.html +186 -0
- package/dist/templates/initial-load.html +157 -0
- package/dist/worker-thread.js +63 -0
- package/package.json +50 -51
- package/static/favicon.ico +0 -0
- package/templates/directory-index.html +186 -0
- package/templates/initial-load.html +157 -0
- package/README.md +0 -60
- package/assets/404.html +0 -14
- package/assets/__stencil-dev-server__/favicon.ico +0 -0
- package/assets/index.html +0 -39
- package/bin/stencil-dev-server +0 -16
- package/dist/definitions.js +0 -2
- package/dist/index.js +0 -275
- package/dist/middlewares.js +0 -106
- package/dist/promisify.js +0 -18
- package/dist/utils.js +0 -139
|
@@ -0,0 +1,1078 @@
|
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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 = `<`;
|
|
219
|
+
else if (inputChar === `>`) outputChar = `>`;
|
|
220
|
+
else if (inputChar === `"`) outputChar = `"`;
|
|
221
|
+
else if (inputChar === `'`) outputChar = `'`;
|
|
222
|
+
else if (inputChar === `&`) outputChar = `&`;
|
|
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
|
+
const SLOT_FB_CSS = "slot-fb{display:contents}slot-fb[hidden]{display:none}";
|
|
511
|
+
/**
|
|
512
|
+
* Update or remove a style element based on the HMR update.
|
|
513
|
+
* @param elm - the style element to update
|
|
514
|
+
* @param versionId - the HMR version identifier
|
|
515
|
+
* @param stylesUpdated - the HMR style update data
|
|
516
|
+
* @returns true if this element matched and was processed
|
|
517
|
+
*/
|
|
518
|
+
const hmrStyleElement = (elm, versionId, stylesUpdated) => {
|
|
519
|
+
if (elm.getAttribute(STYLE_ID_ATTR) === stylesUpdated.styleId) {
|
|
520
|
+
if (stylesUpdated.styleText) {
|
|
521
|
+
const slotFbSuffix = elm.hasAttribute("data-slot-fb") ? SLOT_FB_CSS : "";
|
|
522
|
+
elm.innerHTML = stylesUpdated.styleText.replace(/\\n/g, "\n") + slotFbSuffix;
|
|
523
|
+
elm.setAttribute("data-hmr", versionId);
|
|
524
|
+
} else elm.remove();
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
return false;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Find all component instances with the matching tag name and create style elements.
|
|
531
|
+
* Handles both shadow DOM components (style in shadow root) and scoped components (style in head).
|
|
532
|
+
* @param rootElm - the root element to search from
|
|
533
|
+
* @param versionId - the HMR version identifier
|
|
534
|
+
* @param styleUpdate - the HMR style update data
|
|
535
|
+
*/
|
|
536
|
+
const createStyleElementsForComponent = (rootElm, versionId, styleUpdate) => {
|
|
537
|
+
const { styleTag, styleId, styleText } = styleUpdate;
|
|
538
|
+
const doc = rootElm.ownerDocument;
|
|
539
|
+
const componentInstances = findComponentInstances(rootElm, styleTag);
|
|
540
|
+
if (componentInstances.length === 0) {
|
|
541
|
+
createStyleElement(doc.head, styleId, styleText, versionId);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const processedShadowRoots = /* @__PURE__ */ new Set();
|
|
545
|
+
let addedToHead = false;
|
|
546
|
+
for (const instance of componentInstances) if (instance.shadowRoot) {
|
|
547
|
+
if (!processedShadowRoots.has(instance.shadowRoot)) {
|
|
548
|
+
processedShadowRoots.add(instance.shadowRoot);
|
|
549
|
+
createStyleElement(instance.shadowRoot, styleId, styleText, versionId);
|
|
550
|
+
}
|
|
551
|
+
} else if (!addedToHead) {
|
|
552
|
+
addedToHead = true;
|
|
553
|
+
createStyleElement(doc.head, styleId, styleText, versionId);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* Find all instances of a component by tag name, including in shadow roots.
|
|
558
|
+
* @param elm - the element to search from
|
|
559
|
+
* @param tagName - the tag name to search for
|
|
560
|
+
* @returns an array of matching elements
|
|
561
|
+
*/
|
|
562
|
+
const findComponentInstances = (elm, tagName) => {
|
|
563
|
+
const instances = [];
|
|
564
|
+
findComponentInstancesTraverse(elm, tagName.toLowerCase(), instances);
|
|
565
|
+
return instances;
|
|
566
|
+
};
|
|
567
|
+
const findComponentInstancesTraverse = (elm, tagName, instances) => {
|
|
568
|
+
if (elm.nodeName.toLowerCase() === tagName) instances.push(elm);
|
|
569
|
+
if (hasShadowRoot(elm)) findComponentInstancesTraverse(elm.shadowRoot, tagName, instances);
|
|
570
|
+
if (elm.children) for (let i = 0; i < elm.children.length; i++) findComponentInstancesTraverse(elm.children[i], tagName, instances);
|
|
571
|
+
};
|
|
572
|
+
/**
|
|
573
|
+
* Create a new style element with the given content.
|
|
574
|
+
* @param container - the container to insert the style element into
|
|
575
|
+
* @param styleId - the style identifier
|
|
576
|
+
* @param styleText - the CSS content
|
|
577
|
+
* @param versionId - the HMR version identifier
|
|
578
|
+
*/
|
|
579
|
+
const createStyleElement = (container, styleId, styleText, versionId) => {
|
|
580
|
+
const styleElm = ("ownerDocument" in container ? container.ownerDocument : container.ownerDocument).createElement("style");
|
|
581
|
+
styleElm.innerHTML = styleText.replace(/\\n/g, "\n");
|
|
582
|
+
styleElm.setAttribute(STYLE_ID_ATTR, styleId);
|
|
583
|
+
styleElm.setAttribute("data-hmr", versionId);
|
|
584
|
+
if (container.firstChild) container.insertBefore(styleElm, container.firstChild);
|
|
585
|
+
else container.appendChild(styleElm);
|
|
586
|
+
};
|
|
587
|
+
//#endregion
|
|
588
|
+
//#region src/client/hmr/window.ts
|
|
589
|
+
const hmrWindow = (data) => {
|
|
590
|
+
const results = {
|
|
591
|
+
updatedComponents: [],
|
|
592
|
+
updatedExternalStyles: [],
|
|
593
|
+
updatedInlineStyles: [],
|
|
594
|
+
updatedImages: [],
|
|
595
|
+
versionId: ""
|
|
596
|
+
};
|
|
597
|
+
try {
|
|
598
|
+
if (!data || !data.window || !data.window.document.documentElement || !data.hmr || typeof data.hmr.versionId !== "string") return results;
|
|
599
|
+
const win = data.window;
|
|
600
|
+
const doc = win.document;
|
|
601
|
+
const hmr = data.hmr;
|
|
602
|
+
const documentElement = doc.documentElement;
|
|
603
|
+
const versionId = hmr.versionId;
|
|
604
|
+
results.versionId = versionId;
|
|
605
|
+
if (hmr.componentsUpdated) results.updatedComponents = hmrComponents(documentElement, versionId, hmr.componentsUpdated);
|
|
606
|
+
if (hmr.inlineStylesUpdated) results.updatedInlineStyles = hmrInlineStyles(documentElement, versionId, hmr.inlineStylesUpdated);
|
|
607
|
+
if (hmr.externalStylesUpdated) results.updatedExternalStyles = hmrExternalStyles(documentElement, versionId, hmr.externalStylesUpdated);
|
|
608
|
+
if (hmr.imagesUpdated) results.updatedImages = hmrImages(win, doc, versionId, hmr.imagesUpdated);
|
|
609
|
+
setHmrAttr(documentElement, versionId);
|
|
610
|
+
} catch (e) {
|
|
611
|
+
console.error(e);
|
|
612
|
+
}
|
|
613
|
+
return results;
|
|
614
|
+
};
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/client/logger.ts
|
|
617
|
+
const YELLOW = "#f39c12";
|
|
618
|
+
const RED = "#c0392b";
|
|
619
|
+
const BLUE = "#3498db";
|
|
620
|
+
const GRAY = "#717171";
|
|
621
|
+
const log = (color, prefix, msg) => {
|
|
622
|
+
console.log("%c" + prefix, `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`, msg);
|
|
623
|
+
};
|
|
624
|
+
const logBuild = (msg) => log(BLUE, "Build", msg);
|
|
625
|
+
const logReload = (msg) => logWarn("Reload", msg);
|
|
626
|
+
const logWarn = (prefix, msg) => log(YELLOW, prefix, msg);
|
|
627
|
+
const logDisabled = (prefix, msg) => log(GRAY, prefix, msg);
|
|
628
|
+
const logDiagnostic = (diag) => {
|
|
629
|
+
let color = RED;
|
|
630
|
+
let prefix = "Error";
|
|
631
|
+
if (diag.level === "warn") {
|
|
632
|
+
color = YELLOW;
|
|
633
|
+
prefix = "Warning";
|
|
634
|
+
}
|
|
635
|
+
if (diag.header) prefix = diag.header;
|
|
636
|
+
let msg = "";
|
|
637
|
+
if (diag.relFilePath) {
|
|
638
|
+
msg += diag.relFilePath;
|
|
639
|
+
if (typeof diag.lineNumber === "number" && diag.lineNumber > 0) {
|
|
640
|
+
msg += ", line " + diag.lineNumber;
|
|
641
|
+
if (typeof diag.columnNumber === "number" && diag.columnNumber > 0) msg += ", column " + diag.columnNumber;
|
|
642
|
+
}
|
|
643
|
+
msg += "\n";
|
|
644
|
+
}
|
|
645
|
+
msg += diag.messageText;
|
|
646
|
+
log(color, prefix, msg);
|
|
647
|
+
};
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/client/status.ts
|
|
650
|
+
/**
|
|
651
|
+
* Build status and favicon utilities for dev server client.
|
|
652
|
+
*/
|
|
653
|
+
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=";
|
|
654
|
+
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=";
|
|
655
|
+
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==";
|
|
656
|
+
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=";
|
|
657
|
+
const ICON_TYPE = "image/x-icon";
|
|
658
|
+
const initBuildStatus = (data) => {
|
|
659
|
+
const win = data.window;
|
|
660
|
+
const doc = win.document;
|
|
661
|
+
getFavIcons(doc).forEach((iconElm) => {
|
|
662
|
+
if (iconElm.href) {
|
|
663
|
+
iconElm.dataset.href = iconElm.href;
|
|
664
|
+
iconElm.dataset.type = iconElm.type;
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
onBuildStatus(win, (buildStatus) => {
|
|
668
|
+
updateBuildStatus(doc, buildStatus);
|
|
669
|
+
});
|
|
670
|
+
};
|
|
671
|
+
const updateBuildStatus = (doc, status) => {
|
|
672
|
+
getFavIcons(doc).forEach((iconElm) => {
|
|
673
|
+
updateFavIcon(iconElm, status);
|
|
674
|
+
});
|
|
675
|
+
};
|
|
676
|
+
const updateFavIcon = (linkElm, status) => {
|
|
677
|
+
if (status === "pending") {
|
|
678
|
+
linkElm.href = ICON_PENDING;
|
|
679
|
+
linkElm.type = ICON_TYPE;
|
|
680
|
+
linkElm.setAttribute("data-status", status);
|
|
681
|
+
} else if (status === "error") {
|
|
682
|
+
linkElm.href = ICON_ERROR;
|
|
683
|
+
linkElm.type = ICON_TYPE;
|
|
684
|
+
linkElm.setAttribute("data-status", status);
|
|
685
|
+
} else if (status === "disabled") {
|
|
686
|
+
linkElm.href = ICON_DISABLED;
|
|
687
|
+
linkElm.type = ICON_TYPE;
|
|
688
|
+
linkElm.setAttribute("data-status", status);
|
|
689
|
+
} else {
|
|
690
|
+
linkElm.removeAttribute("data-status");
|
|
691
|
+
if (linkElm.dataset.href) {
|
|
692
|
+
linkElm.href = linkElm.dataset.href;
|
|
693
|
+
linkElm.type = linkElm.dataset.type || ICON_TYPE;
|
|
694
|
+
} else {
|
|
695
|
+
linkElm.href = ICON_DEFAULT;
|
|
696
|
+
linkElm.type = ICON_TYPE;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
const getFavIcons = (doc) => {
|
|
701
|
+
const iconElms = [];
|
|
702
|
+
const linkElms = doc.querySelectorAll("link");
|
|
703
|
+
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]);
|
|
704
|
+
if (iconElms.length === 0) {
|
|
705
|
+
const linkElm = doc.createElement("link");
|
|
706
|
+
linkElm.rel = "shortcut icon";
|
|
707
|
+
doc.head.appendChild(linkElm);
|
|
708
|
+
iconElms.push(linkElm);
|
|
709
|
+
}
|
|
710
|
+
return iconElms;
|
|
711
|
+
};
|
|
712
|
+
const PROGRESS_BAR_ID = `dev-server-progress-bar`;
|
|
713
|
+
const initBuildProgress = (data) => {
|
|
714
|
+
const win = data.window;
|
|
715
|
+
const doc = win.document;
|
|
716
|
+
const barColor = `#5851ff`;
|
|
717
|
+
const errorColor = `#b70c19`;
|
|
718
|
+
let addBarTimerId;
|
|
719
|
+
let removeBarTimerId;
|
|
720
|
+
let opacityTimerId;
|
|
721
|
+
let incIntervalId;
|
|
722
|
+
let progressIncrease;
|
|
723
|
+
let currentProgress = 0;
|
|
724
|
+
function update() {
|
|
725
|
+
clearTimeout(opacityTimerId);
|
|
726
|
+
clearTimeout(removeBarTimerId);
|
|
727
|
+
const progressBar = getProgressBar();
|
|
728
|
+
if (!progressBar) {
|
|
729
|
+
createProgressBar();
|
|
730
|
+
addBarTimerId = setTimeout(update, 16);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
progressBar.style.background = barColor;
|
|
734
|
+
progressBar.style.opacity = `1`;
|
|
735
|
+
progressBar.style.transform = `scaleX(${Math.min(1, displayProgress())})`;
|
|
736
|
+
if (incIntervalId == null) incIntervalId = setInterval(() => {
|
|
737
|
+
progressIncrease += Math.random() * .05 + .01;
|
|
738
|
+
if (displayProgress() < .9) update();
|
|
739
|
+
else clearInterval(incIntervalId);
|
|
740
|
+
}, 800);
|
|
741
|
+
}
|
|
742
|
+
function reset() {
|
|
743
|
+
clearInterval(incIntervalId);
|
|
744
|
+
progressIncrease = .05;
|
|
745
|
+
incIntervalId = null;
|
|
746
|
+
clearTimeout(opacityTimerId);
|
|
747
|
+
clearTimeout(addBarTimerId);
|
|
748
|
+
clearTimeout(removeBarTimerId);
|
|
749
|
+
let progressBar = getProgressBar();
|
|
750
|
+
if (progressBar) {
|
|
751
|
+
if (currentProgress >= 1) progressBar.style.transform = `scaleX(1)`;
|
|
752
|
+
opacityTimerId = setTimeout(() => {
|
|
753
|
+
try {
|
|
754
|
+
progressBar = getProgressBar();
|
|
755
|
+
if (progressBar) progressBar.style.opacity = `0`;
|
|
756
|
+
} catch {}
|
|
757
|
+
}, 150);
|
|
758
|
+
removeBarTimerId = setTimeout(() => {
|
|
759
|
+
try {
|
|
760
|
+
progressBar = getProgressBar();
|
|
761
|
+
if (progressBar?.parentNode) progressBar.parentNode.removeChild(progressBar);
|
|
762
|
+
} catch {}
|
|
763
|
+
}, 1e3);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function displayProgress() {
|
|
767
|
+
const p = currentProgress + progressIncrease;
|
|
768
|
+
return Math.max(0, Math.min(1, p));
|
|
769
|
+
}
|
|
770
|
+
reset();
|
|
771
|
+
onBuildLog(win, (buildLog) => {
|
|
772
|
+
currentProgress = buildLog.progress;
|
|
773
|
+
if (currentProgress >= 0 && currentProgress < 1) update();
|
|
774
|
+
else reset();
|
|
775
|
+
});
|
|
776
|
+
onBuildResults(win, (buildResults) => {
|
|
777
|
+
if (buildResults.hasError) {
|
|
778
|
+
const progressBar = getProgressBar();
|
|
779
|
+
if (progressBar) {
|
|
780
|
+
progressBar.style.transform = `scaleX(1)`;
|
|
781
|
+
progressBar.style.background = errorColor;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
reset();
|
|
785
|
+
});
|
|
786
|
+
onBuildStatus(win, (buildStatus) => {
|
|
787
|
+
if (buildStatus === "disabled") reset();
|
|
788
|
+
});
|
|
789
|
+
if (doc.head.dataset.tmpl === "tmpl-initial-load") update();
|
|
790
|
+
function getProgressBar() {
|
|
791
|
+
return doc.getElementById(PROGRESS_BAR_ID);
|
|
792
|
+
}
|
|
793
|
+
function createProgressBar() {
|
|
794
|
+
const progressBar = doc.createElement("div");
|
|
795
|
+
progressBar.id = PROGRESS_BAR_ID;
|
|
796
|
+
progressBar.style.position = `absolute`;
|
|
797
|
+
progressBar.style.top = `0`;
|
|
798
|
+
progressBar.style.left = `0`;
|
|
799
|
+
progressBar.style.zIndex = `100001`;
|
|
800
|
+
progressBar.style.width = `100%`;
|
|
801
|
+
progressBar.style.height = `2px`;
|
|
802
|
+
progressBar.style.transform = `scaleX(0)`;
|
|
803
|
+
progressBar.style.opacity = `1`;
|
|
804
|
+
progressBar.style.background = barColor;
|
|
805
|
+
progressBar.style.transformOrigin = `left center`;
|
|
806
|
+
progressBar.style.transition = `transform .1s ease-in-out, opacity .5s ease-in`;
|
|
807
|
+
progressBar.style.contain = `strict`;
|
|
808
|
+
doc.body.appendChild(progressBar);
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region src/client/websocket.ts
|
|
813
|
+
/**
|
|
814
|
+
* WebSocket client for dev server communication.
|
|
815
|
+
*/
|
|
816
|
+
const initClientWebSocket = (win, config) => {
|
|
817
|
+
let clientWs = null;
|
|
818
|
+
let reconnectTmrId = null;
|
|
819
|
+
let reconnectAttempts = 0;
|
|
820
|
+
let requestBuildResultsTmrId = null;
|
|
821
|
+
let hasGottenBuildResults = false;
|
|
822
|
+
let buildResultsRequests = 0;
|
|
823
|
+
function onOpen() {
|
|
824
|
+
if (reconnectAttempts > 0) emitBuildStatus(win, "pending");
|
|
825
|
+
if (!hasGottenBuildResults) requestBuildResultsTmrId = setInterval(() => {
|
|
826
|
+
buildResultsRequests++;
|
|
827
|
+
if (!hasGottenBuildResults && this.readyState === WebSocket.OPEN && buildResultsRequests < 500) this.send(JSON.stringify({ requestBuildResults: true }));
|
|
828
|
+
else if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
|
|
829
|
+
}, 500);
|
|
830
|
+
if (reconnectTmrId) clearTimeout(reconnectTmrId);
|
|
831
|
+
}
|
|
832
|
+
function onError() {
|
|
833
|
+
queueReconnect();
|
|
834
|
+
}
|
|
835
|
+
function onClose(event) {
|
|
836
|
+
emitBuildStatus(win, "disabled");
|
|
837
|
+
if (event.code > 1e3) logWarn("Dev Server", `web socket closed: ${event.code} ${event.reason}`);
|
|
838
|
+
else logDisabled("Dev Server", "Disconnected, attempting to reconnect...");
|
|
839
|
+
queueReconnect();
|
|
840
|
+
}
|
|
841
|
+
function onMessage(event) {
|
|
842
|
+
const msg = JSON.parse(event.data);
|
|
843
|
+
if (reconnectAttempts > 0) {
|
|
844
|
+
if (msg.isActivelyBuilding) return;
|
|
845
|
+
if (msg.buildResults) {
|
|
846
|
+
logReload("Reconnected to dev server");
|
|
847
|
+
hasGottenBuildResults = true;
|
|
848
|
+
buildResultsRequests = 0;
|
|
849
|
+
if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
|
|
850
|
+
if (win["s-build-id"] !== msg.buildResults.buildId) win.location.reload();
|
|
851
|
+
win["s-build-id"] = msg.buildResults.buildId;
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (msg.buildLog) {
|
|
856
|
+
if (msg.buildLog.progress < 1) emitBuildStatus(win, "pending");
|
|
857
|
+
emitBuildLog(win, msg.buildLog);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (msg.buildResults) {
|
|
861
|
+
hasGottenBuildResults = true;
|
|
862
|
+
buildResultsRequests = 0;
|
|
863
|
+
if (requestBuildResultsTmrId) clearInterval(requestBuildResultsTmrId);
|
|
864
|
+
emitBuildStatus(win, "default");
|
|
865
|
+
emitBuildResults(win, msg.buildResults);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function connect() {
|
|
869
|
+
if (reconnectTmrId) clearTimeout(reconnectTmrId);
|
|
870
|
+
clientWs = new win.WebSocket(config.socketUrl, ["xmpp"]);
|
|
871
|
+
clientWs.addEventListener("open", onOpen);
|
|
872
|
+
clientWs.addEventListener("error", onError);
|
|
873
|
+
clientWs.addEventListener("close", onClose);
|
|
874
|
+
clientWs.addEventListener("message", onMessage);
|
|
875
|
+
}
|
|
876
|
+
function queueReconnect() {
|
|
877
|
+
hasGottenBuildResults = false;
|
|
878
|
+
if (clientWs) {
|
|
879
|
+
if (clientWs.readyState === WebSocket.OPEN || clientWs.readyState === WebSocket.CONNECTING) clientWs.close(NORMAL_CLOSURE_CODE);
|
|
880
|
+
clientWs.removeEventListener("open", onOpen);
|
|
881
|
+
clientWs.removeEventListener("error", onError);
|
|
882
|
+
clientWs.removeEventListener("close", onClose);
|
|
883
|
+
clientWs.removeEventListener("message", onMessage);
|
|
884
|
+
clientWs = null;
|
|
885
|
+
}
|
|
886
|
+
if (reconnectTmrId) clearTimeout(reconnectTmrId);
|
|
887
|
+
if (reconnectAttempts >= 1e3) logWarn("Dev Server", "Canceling reconnect attempts");
|
|
888
|
+
else {
|
|
889
|
+
reconnectAttempts++;
|
|
890
|
+
reconnectTmrId = setTimeout(connect, RECONNECT_RETRY_MS);
|
|
891
|
+
emitBuildStatus(win, "disabled");
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
connect();
|
|
895
|
+
};
|
|
896
|
+
//#endregion
|
|
897
|
+
//#region src/client/index.ts
|
|
898
|
+
/**
|
|
899
|
+
* Stencil Dev Server Client
|
|
900
|
+
*
|
|
901
|
+
* Browser-side HMR (Hot Module Replacement) client for Stencil dev server.
|
|
902
|
+
* Handles WebSocket communication, component updates, style updates, and image updates.
|
|
903
|
+
*
|
|
904
|
+
* This module runs in the browser and is injected into pages during development.
|
|
905
|
+
* @module @stencil/dev-server/client
|
|
906
|
+
*/
|
|
907
|
+
/**
|
|
908
|
+
* Initialize the app update handler for build results.
|
|
909
|
+
*
|
|
910
|
+
* @param win - the dev client window object
|
|
911
|
+
* @param config - the dev client configuration
|
|
912
|
+
*/
|
|
913
|
+
const initAppUpdate = (win, config) => {
|
|
914
|
+
onBuildResults(win, (buildResults) => {
|
|
915
|
+
appUpdate(win, config, buildResults);
|
|
916
|
+
});
|
|
917
|
+
};
|
|
918
|
+
/**
|
|
919
|
+
* Process app update based on build results.
|
|
920
|
+
*
|
|
921
|
+
* @param win - the dev client window object
|
|
922
|
+
* @param config - the dev client configuration
|
|
923
|
+
* @param buildResults - the compiler build results
|
|
924
|
+
*/
|
|
925
|
+
const appUpdate = (win, config, buildResults) => {
|
|
926
|
+
try {
|
|
927
|
+
if (buildResults.buildId === win["s-build-id"]) return;
|
|
928
|
+
win["s-build-id"] = buildResults.buildId;
|
|
929
|
+
clearAppErrorModal({ window: win });
|
|
930
|
+
if (buildResults.hasError) {
|
|
931
|
+
const errorResults = appError({
|
|
932
|
+
window: win,
|
|
933
|
+
buildResults,
|
|
934
|
+
openInEditor: Array.isArray(config.editors) && config.editors.length > 0 ? (data) => {
|
|
935
|
+
const url = `${OPEN_IN_EDITOR_URL}?${new URLSearchParams({
|
|
936
|
+
file: data.file,
|
|
937
|
+
line: String(data.line),
|
|
938
|
+
column: String(data.column)
|
|
939
|
+
}).toString()}`;
|
|
940
|
+
win.fetch(url).catch((err) => {
|
|
941
|
+
console.error("Failed to open in editor:", err);
|
|
942
|
+
});
|
|
943
|
+
} : void 0
|
|
944
|
+
});
|
|
945
|
+
errorResults.diagnostics.forEach(logDiagnostic);
|
|
946
|
+
if (errorResults.status) emitBuildStatus(win, errorResults.status);
|
|
947
|
+
if (win["s-initial-load"]) appReset(win, config, () => {
|
|
948
|
+
logReload("Initial load (with errors)");
|
|
949
|
+
win.location.reload();
|
|
950
|
+
});
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (win["s-initial-load"]) {
|
|
954
|
+
appReset(win, config, () => {
|
|
955
|
+
logReload("Initial load");
|
|
956
|
+
win.location.reload();
|
|
957
|
+
});
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
if (buildResults.hmr) appHmr(win, buildResults.hmr);
|
|
961
|
+
} catch (e) {
|
|
962
|
+
console.error(e);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
/**
|
|
966
|
+
* Apply hot module replacement updates to the window.
|
|
967
|
+
*
|
|
968
|
+
* @param win - the browser window
|
|
969
|
+
* @param hmr - the hot module replacement data
|
|
970
|
+
*/
|
|
971
|
+
const appHmr = (win, hmr) => {
|
|
972
|
+
let shouldWindowReload = false;
|
|
973
|
+
if (hmr.reloadStrategy === "pageReload") shouldWindowReload = true;
|
|
974
|
+
if (hmr.indexHtmlUpdated) {
|
|
975
|
+
logReload("Updated index.html");
|
|
976
|
+
shouldWindowReload = true;
|
|
977
|
+
}
|
|
978
|
+
if (hmr.serviceWorkerUpdated) {
|
|
979
|
+
logReload("Updated Service Worker: sw");
|
|
980
|
+
shouldWindowReload = true;
|
|
981
|
+
}
|
|
982
|
+
if (hmr.scriptsAdded && hmr.scriptsAdded.length > 0) {
|
|
983
|
+
logReload(`Added scripts: ${hmr.scriptsAdded.join(", ")}`);
|
|
984
|
+
shouldWindowReload = true;
|
|
985
|
+
}
|
|
986
|
+
if (hmr.scriptsDeleted && hmr.scriptsDeleted.length > 0) {
|
|
987
|
+
logReload(`Deleted scripts: ${hmr.scriptsDeleted.join(", ")}`);
|
|
988
|
+
shouldWindowReload = true;
|
|
989
|
+
}
|
|
990
|
+
if (hmr.excludeHmr && hmr.excludeHmr.length > 0) {
|
|
991
|
+
logReload(`Excluded From Hmr: ${hmr.excludeHmr.join(", ")}`);
|
|
992
|
+
shouldWindowReload = true;
|
|
993
|
+
}
|
|
994
|
+
if (shouldWindowReload) {
|
|
995
|
+
win.location.reload();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const results = hmrWindow({
|
|
999
|
+
window: win,
|
|
1000
|
+
hmr
|
|
1001
|
+
});
|
|
1002
|
+
if (results.updatedComponents.length > 0) logBuild(`Updated component${results.updatedComponents.length > 1 ? "s" : ""}: ${results.updatedComponents.join(", ")}`);
|
|
1003
|
+
if (results.updatedInlineStyles.length > 0) logBuild(`Updated styles: ${results.updatedInlineStyles.join(", ")}`);
|
|
1004
|
+
if (results.updatedExternalStyles.length > 0) logBuild(`Updated stylesheets: ${results.updatedExternalStyles.join(", ")}`);
|
|
1005
|
+
if (results.updatedImages.length > 0) logBuild(`Updated images: ${results.updatedImages.join(", ")}`);
|
|
1006
|
+
};
|
|
1007
|
+
/**
|
|
1008
|
+
* Reset the app state and unregister service workers.
|
|
1009
|
+
*
|
|
1010
|
+
* @param win - the dev client window object
|
|
1011
|
+
* @param config - the dev client configuration
|
|
1012
|
+
* @param cb - callback to invoke after reset
|
|
1013
|
+
*/
|
|
1014
|
+
const appReset = (win, config, cb) => {
|
|
1015
|
+
win.history.replaceState({}, "App", config.basePath);
|
|
1016
|
+
if (!win.navigator.serviceWorker?.getRegistration) cb();
|
|
1017
|
+
else win.navigator.serviceWorker.getRegistration().then((swRegistration) => {
|
|
1018
|
+
if (swRegistration) swRegistration.unregister().then((hasUnregistered) => {
|
|
1019
|
+
if (hasUnregistered) logBuild("unregistered service worker");
|
|
1020
|
+
cb();
|
|
1021
|
+
});
|
|
1022
|
+
else cb();
|
|
1023
|
+
}).catch((err) => {
|
|
1024
|
+
logWarn("Service Worker", err);
|
|
1025
|
+
cb();
|
|
1026
|
+
});
|
|
1027
|
+
};
|
|
1028
|
+
/**
|
|
1029
|
+
* Initialize the dev server client in the browser.
|
|
1030
|
+
*
|
|
1031
|
+
* @param win - the dev client window object
|
|
1032
|
+
* @param config - the dev client configuration
|
|
1033
|
+
*/
|
|
1034
|
+
const initDevClient = (win, config) => {
|
|
1035
|
+
try {
|
|
1036
|
+
if (win["s-dev-server"]) return;
|
|
1037
|
+
win["s-dev-server"] = true;
|
|
1038
|
+
win.devServerConfig = config;
|
|
1039
|
+
initBuildStatus({ window: win });
|
|
1040
|
+
initBuildProgress({ window: win });
|
|
1041
|
+
initAppUpdate(win, config);
|
|
1042
|
+
if (isInitialDevServerLoad(win, config)) {
|
|
1043
|
+
win["s-initial-load"] = true;
|
|
1044
|
+
appReset(win, config, () => {
|
|
1045
|
+
initClientWebSocket(win, config);
|
|
1046
|
+
});
|
|
1047
|
+
} else initClientWebSocket(win, config);
|
|
1048
|
+
} catch (e) {
|
|
1049
|
+
console.error(e);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
/**
|
|
1053
|
+
* Check if this is the initial dev server load.
|
|
1054
|
+
*
|
|
1055
|
+
* @param win - the dev client window object
|
|
1056
|
+
* @param config - the dev client configuration
|
|
1057
|
+
* @returns true if this is the initial load
|
|
1058
|
+
*/
|
|
1059
|
+
const isInitialDevServerLoad = (win, config) => {
|
|
1060
|
+
let pathname = win.location.pathname;
|
|
1061
|
+
pathname = "/" + pathname.substring(config.basePath.length);
|
|
1062
|
+
return pathname === DEV_SERVER_INIT_URL;
|
|
1063
|
+
};
|
|
1064
|
+
if (typeof appWindow !== "undefined" && typeof config !== "undefined") {
|
|
1065
|
+
const defaultConfig = {
|
|
1066
|
+
basePath: appWindow.location.pathname,
|
|
1067
|
+
editors: [],
|
|
1068
|
+
reloadStrategy: "hmr",
|
|
1069
|
+
socketUrl: `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.hostname}${location.port !== "" ? ":" + location.port : ""}/`
|
|
1070
|
+
};
|
|
1071
|
+
initDevClient(appWindow, {
|
|
1072
|
+
...defaultConfig,
|
|
1073
|
+
...appWindow.devServerConfig,
|
|
1074
|
+
...config
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
//#endregion
|
|
1078
|
+
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 };
|