UNPKG

5.15 kBJavaScriptView Raw
1"use strict";
2
3/* eslint-env browser */
4
5/*
6 eslint-disable
7 no-console,
8 func-names
9*/
10
11/** @typedef {any} TODO */
12var normalizeUrl = require("./normalize-url");
13
14var srcByModuleId = Object.create(null);
15var noDocument = typeof document === "undefined";
16var forEach = Array.prototype.forEach;
17/**
18 * @param {function} fn
19 * @param {number} time
20 * @returns {(function(): void)|*}
21 */
22
23function debounce(fn, time) {
24 var timeout = 0;
25 return function () {
26 // @ts-ignore
27 var self = this; // eslint-disable-next-line prefer-rest-params
28
29 var args = arguments;
30
31 var functionCall = function functionCall() {
32 return fn.apply(self, args);
33 };
34
35 clearTimeout(timeout); // @ts-ignore
36
37 timeout = setTimeout(functionCall, time);
38 };
39}
40
41function noop() {}
42/**
43 * @param {TODO} moduleId
44 * @returns {TODO}
45 */
46
47
48function getCurrentScriptUrl(moduleId) {
49 var src = srcByModuleId[moduleId];
50
51 if (!src) {
52 if (document.currentScript) {
53 src =
54 /** @type {HTMLScriptElement} */
55 document.currentScript.src;
56 } else {
57 var scripts = document.getElementsByTagName("script");
58 var lastScriptTag = scripts[scripts.length - 1];
59
60 if (lastScriptTag) {
61 src = lastScriptTag.src;
62 }
63 }
64
65 srcByModuleId[moduleId] = src;
66 }
67 /**
68 * @param {string} fileMap
69 * @returns {null | string[]}
70 */
71
72
73 return function (fileMap) {
74 if (!src) {
75 return null;
76 }
77
78 var splitResult = src.split(/([^\\/]+)\.js$/);
79 var filename = splitResult && splitResult[1];
80
81 if (!filename) {
82 return [src.replace(".js", ".css")];
83 }
84
85 if (!fileMap) {
86 return [src.replace(".js", ".css")];
87 }
88
89 return fileMap.split(",").map(function (mapRule) {
90 var reg = new RegExp("".concat(filename, "\\.js$"), "g");
91 return normalizeUrl(src.replace(reg, "".concat(mapRule.replace(/{fileName}/g, filename), ".css")));
92 });
93 };
94}
95/**
96 * @param {TODO} el
97 * @param {string} [url]
98 */
99
100
101function updateCss(el, url) {
102 if (!url) {
103 if (!el.href) {
104 return;
105 } // eslint-disable-next-line
106
107
108 url = el.href.split("?")[0];
109 }
110
111 if (!isUrlRequest(
112 /** @type {string} */
113 url)) {
114 return;
115 }
116
117 if (el.isLoaded === false) {
118 // We seem to be about to replace a css link that hasn't loaded yet.
119 // We're probably changing the same file more than once.
120 return;
121 }
122
123 if (!url || !(url.indexOf(".css") > -1)) {
124 return;
125 } // eslint-disable-next-line no-param-reassign
126
127
128 el.visited = true;
129 var newEl = el.cloneNode();
130 newEl.isLoaded = false;
131 newEl.addEventListener("load", function () {
132 if (newEl.isLoaded) {
133 return;
134 }
135
136 newEl.isLoaded = true;
137 el.parentNode.removeChild(el);
138 });
139 newEl.addEventListener("error", function () {
140 if (newEl.isLoaded) {
141 return;
142 }
143
144 newEl.isLoaded = true;
145 el.parentNode.removeChild(el);
146 });
147 newEl.href = "".concat(url, "?").concat(Date.now());
148
149 if (el.nextSibling) {
150 el.parentNode.insertBefore(newEl, el.nextSibling);
151 } else {
152 el.parentNode.appendChild(newEl);
153 }
154}
155/**
156 * @param {string} href
157 * @param {TODO} src
158 * @returns {TODO}
159 */
160
161
162function getReloadUrl(href, src) {
163 var ret; // eslint-disable-next-line no-param-reassign
164
165 href = normalizeUrl(href);
166 src.some(
167 /**
168 * @param {string} url
169 */
170 // eslint-disable-next-line array-callback-return
171 function (url) {
172 if (href.indexOf(src) > -1) {
173 ret = url;
174 }
175 });
176 return ret;
177}
178/**
179 * @param {string} [src]
180 * @returns {boolean}
181 */
182
183
184function reloadStyle(src) {
185 if (!src) {
186 return false;
187 }
188
189 var elements = document.querySelectorAll("link");
190 var loaded = false;
191 forEach.call(elements, function (el) {
192 if (!el.href) {
193 return;
194 }
195
196 var url = getReloadUrl(el.href, src);
197
198 if (!isUrlRequest(url)) {
199 return;
200 }
201
202 if (el.visited === true) {
203 return;
204 }
205
206 if (url) {
207 updateCss(el, url);
208 loaded = true;
209 }
210 });
211 return loaded;
212}
213
214function reloadAll() {
215 var elements = document.querySelectorAll("link");
216 forEach.call(elements, function (el) {
217 if (el.visited === true) {
218 return;
219 }
220
221 updateCss(el);
222 });
223}
224/**
225 * @param {string} url
226 * @returns {boolean}
227 */
228
229
230function isUrlRequest(url) {
231 // An URL is not an request if
232 // It is not http or https
233 if (!/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)) {
234 return false;
235 }
236
237 return true;
238}
239/**
240 * @param {TODO} moduleId
241 * @param {TODO} options
242 * @returns {TODO}
243 */
244
245
246module.exports = function (moduleId, options) {
247 if (noDocument) {
248 console.log("no window.document found, will not HMR CSS");
249 return noop;
250 }
251
252 var getScriptSrc = getCurrentScriptUrl(moduleId);
253
254 function update() {
255 var src = getScriptSrc(options.filename);
256 var reloaded = reloadStyle(src);
257
258 if (options.locals) {
259 console.log("[HMR] Detected local css modules. Reload all css");
260 reloadAll();
261 return;
262 }
263
264 if (reloaded) {
265 console.log("[HMR] css reload %s", src.join(" "));
266 } else {
267 console.log("[HMR] Reload all css");
268 reloadAll();
269 }
270 }
271
272 return debounce(update, 50);
273};
\No newline at end of file