CssLoadingRuntimeModule.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { SyncWaterfallHook } = require("tapable");
  7. /** @import Compilation from "../Compilation" */
  8. const RuntimeGlobals = require("../RuntimeGlobals");
  9. const RuntimeModule = require("../RuntimeModule");
  10. const Template = require("../Template");
  11. const compileBooleanMatcher = require("../util/compileBooleanMatcher");
  12. const createHooksRegistry = require("../util/createHooksRegistry");
  13. const { chunkHasCss } = require("./CssModulesPlugin");
  14. /** @import Chunk, { ChunkId } from "../Chunk" */
  15. /** @import { ReadOnlyRuntimeRequirements } from "../Module" */
  16. const createCompilationHooks = () => ({
  17. /**
  18. * @type {SyncWaterfallHook<[string, Chunk]>}
  19. * @since 5.66.0
  20. */
  21. createStylesheet: new SyncWaterfallHook(["source", "chunk"]),
  22. /**
  23. * @type {SyncWaterfallHook<[string, Chunk]>}
  24. * @since 5.91.0
  25. */
  26. linkPreload: new SyncWaterfallHook(["source", "chunk"]),
  27. /**
  28. * @type {SyncWaterfallHook<[string, Chunk]>}
  29. * @since 5.91.0
  30. */
  31. linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
  32. /**
  33. * @type {SyncWaterfallHook<[string, Chunk]>}
  34. * @since 5.107.0
  35. */
  36. linkInsert: new SyncWaterfallHook(["source", "chunk"])
  37. });
  38. /**
  39. * @typedef {ReturnType<typeof createCompilationHooks>} CssLoadingRuntimeModulePluginHooks
  40. */
  41. class CssLoadingRuntimeModule extends RuntimeModule {
  42. /**
  43. * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements
  44. */
  45. constructor(runtimeRequirements) {
  46. super("css loading", 10);
  47. /** @type {ReadOnlyRuntimeRequirements} */
  48. this._runtimeRequirements = runtimeRequirements;
  49. }
  50. /**
  51. * Generates runtime code for this runtime module.
  52. * @returns {string | null} runtime code
  53. */
  54. generate() {
  55. const { _runtimeRequirements } = this;
  56. const compilation = /** @type {Compilation} */ (this.compilation);
  57. const chunk = /** @type {Chunk} */ (this.chunk);
  58. const {
  59. chunkGraph,
  60. runtimeTemplate,
  61. outputOptions: {
  62. crossOriginLoading,
  63. uniqueName,
  64. chunkLoadTimeout: loadTimeout,
  65. charset,
  66. resourceHints
  67. }
  68. } = compilation;
  69. const dedupePrefetch = Boolean(resourceHints && resourceHints.dedupe);
  70. const fn = RuntimeGlobals.ensureChunkHandlers;
  71. // The predicate the stylesheet is emitted from: an `@import` external gives a
  72. // chunk a css asset without a `CSS_TYPE` module, and it still has to load.
  73. const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasCss);
  74. const hasCssMatcher = compileBooleanMatcher(conditionMap);
  75. const withLoading =
  76. _runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) &&
  77. hasCssMatcher !== false;
  78. /** @type {boolean} */
  79. const withHmr = _runtimeRequirements.has(
  80. RuntimeGlobals.hmrDownloadUpdateHandlers
  81. );
  82. /** @type {Set<ChunkId>} */
  83. const initialChunkIds = new Set();
  84. for (const c of chunk.getAllInitialChunks()) {
  85. if (chunkHasCss(c, chunkGraph)) {
  86. initialChunkIds.add(/** @type {ChunkId} */ (c.id));
  87. }
  88. }
  89. if (!withLoading && !withHmr) {
  90. return null;
  91. }
  92. const environment = compilation.outputOptions.environment;
  93. const isNeutralPlatform = runtimeTemplate.isNeutralPlatform();
  94. const withPrefetch =
  95. this._runtimeRequirements.has(RuntimeGlobals.prefetchChunkHandlers) &&
  96. (environment.document || isNeutralPlatform) &&
  97. chunk.hasChildByOrder(chunkGraph, "prefetch", true, chunkHasCss);
  98. const withPreload =
  99. this._runtimeRequirements.has(RuntimeGlobals.preloadChunkHandlers) &&
  100. (environment.document || isNeutralPlatform) &&
  101. (chunk.hasChildByOrder(chunkGraph, "preload", true, chunkHasCss) ||
  102. // `parser.javascript.dynamicImportCssPreload` — CSS-only preload order.
  103. chunk.hasChildByOrder(chunkGraph, "cssPreload", true, chunkHasCss));
  104. // Under module output each stylesheet is a known file, so the urls are written
  105. // out and read by id rather than built from the chunk id at runtime.
  106. const cssUrls = runtimeTemplate.analyzableCssChunkUrls(
  107. chunk,
  108. chunkGraph,
  109. _runtimeRequirements,
  110. this
  111. );
  112. const { linkPreload, linkPrefetch, createStylesheet, linkInsert } =
  113. CssLoadingRuntimeModule.getCompilationHooks(compilation);
  114. const withFetchPriority = _runtimeRequirements.has(
  115. RuntimeGlobals.hasFetchPriority
  116. );
  117. const stateExpression = withHmr
  118. ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_css`
  119. : undefined;
  120. const code = Template.asString([
  121. "link = document.createElement('link');",
  122. charset ? "link.charset = 'utf-8';" : "",
  123. `if (${RuntimeGlobals.scriptNonce}) {`,
  124. Template.indent(
  125. `link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
  126. ),
  127. "}",
  128. uniqueName
  129. ? 'link.setAttribute("data-webpack", uniqueName + ":" + key);'
  130. : "",
  131. withFetchPriority
  132. ? Template.asString([
  133. "if(fetchPriority) {",
  134. Template.indent(
  135. 'link.setAttribute("fetchpriority", fetchPriority);'
  136. ),
  137. "}"
  138. ])
  139. : "",
  140. "link.setAttribute(loadingAttribute, 1);",
  141. 'link.rel = "stylesheet";',
  142. "link.href = url;",
  143. crossOriginLoading
  144. ? crossOriginLoading === "use-credentials"
  145. ? 'link.crossOrigin = "use-credentials";'
  146. : Template.asString([
  147. "if (link.href.indexOf(window.location.origin + '/') !== 0) {",
  148. Template.indent(
  149. `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
  150. ),
  151. "}"
  152. ])
  153. : ""
  154. ]);
  155. const cst = runtimeTemplate.renderConst();
  156. const lt = runtimeTemplate.renderLet();
  157. const installedChunksObject = `{\n${Template.indent(
  158. Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 0`).join(
  159. ",\n"
  160. )
  161. )}\n}`;
  162. // The url is read through a thunk so a runtime carrying many stylesheets builds
  163. // only the one it loads.
  164. /**
  165. * @param {string} id expression naming the chunk whose stylesheet is wanted
  166. * @returns {string} expression evaluating to its url
  167. */
  168. const cssUrl = (id) =>
  169. cssUrls === undefined || cssUrls === null
  170. ? `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(${id})`
  171. : `cssUrls[${id}]()`;
  172. return Template.asString([
  173. ...(cssUrls
  174. ? [
  175. `${cst} cssUrls = {\n${Template.indent(
  176. Array.from(
  177. cssUrls,
  178. ([id, url]) =>
  179. `${JSON.stringify(String(id))}: ${runtimeTemplate.returningFunction(url)}`
  180. ).join(",\n")
  181. )}\n};`
  182. ]
  183. : []),
  184. "// object to store loaded and loading chunks",
  185. "// undefined = chunk not loaded, null = chunk preloaded/prefetched",
  186. "// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded",
  187. `${cst} installedChunks = ${
  188. stateExpression
  189. ? runtimeTemplate.assignOr(stateExpression, installedChunksObject)
  190. : installedChunksObject
  191. };`,
  192. "",
  193. uniqueName
  194. ? `${cst} uniqueName = ${JSON.stringify(
  195. runtimeTemplate.outputOptions.uniqueName
  196. )};`
  197. : "// data-webpack is not used as build has no uniqueName",
  198. withLoading || withHmr
  199. ? Template.asString([
  200. `${cst} loadingAttribute = "data-webpack-loading";`,
  201. `${cst} loadStylesheet = ${runtimeTemplate.basicFunction(
  202. `chunkId, url, done${
  203. withFetchPriority ? ", fetchPriority" : ""
  204. }${withHmr ? ", hmr" : ""}`,
  205. [
  206. `${lt} link, needAttach, key = "chunk-" + chunkId;`,
  207. withHmr ? "if(!hmr) {" : "",
  208. `${cst} links = document.getElementsByTagName("link");`,
  209. "for(var i = 0; i < links.length; i++) {",
  210. Template.indent([
  211. `${cst} l = links[i];`,
  212. `if(l.rel == "stylesheet" && (${
  213. withHmr
  214. ? 'l.href.startsWith(url) || l.getAttribute("href").startsWith(url)'
  215. : 'l.href == url || l.getAttribute("href") == url'
  216. }${
  217. uniqueName
  218. ? ' || l.getAttribute("data-webpack") == uniqueName + ":" + key'
  219. : ""
  220. })) { link = l; break; }`
  221. ]),
  222. "}",
  223. "if(!done) return link;",
  224. withHmr ? "}" : "",
  225. "if(!link) {",
  226. Template.indent([
  227. "needAttach = true;",
  228. createStylesheet.call(code, /** @type {Chunk} */ (this.chunk))
  229. ]),
  230. "}",
  231. `${lt} timeout;`,
  232. `${cst} onLinkComplete = ${runtimeTemplate.basicFunction(
  233. "prev, event",
  234. Template.asString([
  235. "link.onerror = link.onload = null;",
  236. "link.removeAttribute(loadingAttribute);",
  237. "clearTimeout(timeout);",
  238. 'if(event && event.type != "load") link.parentNode.removeChild(link)',
  239. "done(event);",
  240. "if(prev) return prev(event);"
  241. ])
  242. )};`,
  243. "if(link.getAttribute(loadingAttribute)) {",
  244. Template.indent([
  245. `timeout = setTimeout(onLinkComplete.bind(null, undefined, { type: 'timeout', target: link }), ${loadTimeout});`,
  246. "link.onerror = onLinkComplete.bind(null, link.onerror);",
  247. "link.onload = onLinkComplete.bind(null, link.onload);"
  248. ]),
  249. "} else onLinkComplete(undefined, { type: 'load', target: link });", // We assume any existing stylesheet is render blocking
  250. withHmr && withFetchPriority
  251. ? 'if (hmr && hmr.getAttribute("fetchpriority")) link.setAttribute("fetchpriority", hmr.getAttribute("fetchpriority"));'
  252. : "",
  253. linkInsert.call(
  254. withHmr
  255. ? Template.asString([
  256. "if (hmr) {",
  257. Template.indent(
  258. "hmr.parentNode.insertBefore(link, hmr);"
  259. ),
  260. "} else if (needAttach) {",
  261. Template.indent("document.head.appendChild(link);"),
  262. "}"
  263. ])
  264. : Template.asString([
  265. "if (needAttach) {",
  266. Template.indent("document.head.appendChild(link);"),
  267. "}"
  268. ]),
  269. /** @type {Chunk} */ (this.chunk)
  270. ),
  271. "return link;"
  272. ]
  273. )};`
  274. ])
  275. : "",
  276. withLoading
  277. ? Template.asString([
  278. `${fn}.css = ${runtimeTemplate.basicFunction(
  279. `chunkId, promises${withFetchPriority ? " , fetchPriority" : ""}`,
  280. [
  281. "// css chunk loading",
  282. `${lt} installedChunkData = ${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;`,
  283. 'if(installedChunkData !== 0) { // 0 means "already installed".',
  284. Template.indent([
  285. "",
  286. '// a Promise means "currently loading".',
  287. "if(installedChunkData) {",
  288. Template.indent(["promises.push(installedChunkData[2]);"]),
  289. "} else {",
  290. Template.indent([
  291. hasCssMatcher === true
  292. ? "if(true) { // all chunks have CSS"
  293. : `if(${hasCssMatcher("chunkId")}) {`,
  294. Template.indent([
  295. "// setup Promise in chunk cache",
  296. `${cst} promise = new Promise(${runtimeTemplate.expressionFunction(
  297. "installedChunkData = installedChunks[chunkId] = [resolve, reject]",
  298. "resolve, reject"
  299. )});`,
  300. "promises.push(installedChunkData[2] = promise);",
  301. "",
  302. "// start chunk loading",
  303. `${cst} url = ${cssUrl("chunkId")};`,
  304. "// create error before stack unwound to get useful stacktrace later",
  305. `${cst} error = new Error();`,
  306. `${cst} loadingEnded = ${runtimeTemplate.basicFunction(
  307. "event",
  308. [
  309. `if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId)) {`,
  310. Template.indent([
  311. "installedChunkData = installedChunks[chunkId];",
  312. "if(installedChunkData !== 0) installedChunks[chunkId] = undefined;",
  313. "if(installedChunkData) {",
  314. Template.indent([
  315. 'if(event.type !== "load") {',
  316. Template.indent([
  317. `${cst} errorType = event && event.type;`,
  318. `${cst} realHref = event && event.target && event.target.href;`,
  319. "error.message = 'Loading css chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realHref + ')';",
  320. "error.name = 'ChunkLoadError';",
  321. "error.type = errorType;",
  322. "error.request = realHref;",
  323. "error.event = event;",
  324. "installedChunkData[1](error);"
  325. ]),
  326. "} else {",
  327. Template.indent([
  328. "installedChunks[chunkId] = 0;",
  329. "installedChunkData[0]();"
  330. ]),
  331. "}"
  332. ]),
  333. "}"
  334. ]),
  335. "}"
  336. ]
  337. )};`,
  338. isNeutralPlatform
  339. ? "if (typeof document !== 'undefined') {"
  340. : "",
  341. Template.indent([
  342. `loadStylesheet(chunkId, url, loadingEnded${
  343. withFetchPriority ? ", fetchPriority" : ""
  344. });`
  345. ]),
  346. isNeutralPlatform
  347. ? Template.asString([
  348. "} else {",
  349. Template.indent([
  350. // no DOM (Node SSR): read the emitted CSS via dynamic import('fs') (works on every node), collect it; never reject on a missing file
  351. `Promise.all([import('fs'), import('url')]).then(${runtimeTemplate.basicFunction(
  352. "[{ readFile }, { URL }]",
  353. [
  354. `readFile(${runtimeTemplate.importMetaUrl("url")}, 'utf8', ${runtimeTemplate.basicFunction(
  355. "err, content",
  356. [
  357. `if (!err) ${runtimeTemplate.cssServerStyleRegistry()}["chunk-" + chunkId] = content;`,
  358. "loadingEnded({ type: 'load' });"
  359. ]
  360. )});`
  361. ]
  362. )});`
  363. ]),
  364. "}"
  365. ])
  366. : ""
  367. ]),
  368. "} else installedChunks[chunkId] = 0;"
  369. ]),
  370. "}"
  371. ]),
  372. "}"
  373. ]
  374. )};`
  375. ])
  376. : "// no chunk loading",
  377. "",
  378. withPrefetch && hasCssMatcher !== false
  379. ? `${
  380. RuntimeGlobals.prefetchChunkHandlers
  381. }.s = ${runtimeTemplate.basicFunction("chunkId", [
  382. `if((!${
  383. RuntimeGlobals.hasOwnProperty
  384. }(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${
  385. hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")
  386. }) {`,
  387. Template.indent([
  388. "installedChunks[chunkId] = null;",
  389. // prefetch is a browser-only resource hint; no-op without a DOM (e.g. node side of a universal build)
  390. isNeutralPlatform
  391. ? "if (typeof document === 'undefined') return;"
  392. : "",
  393. linkPrefetch.call(
  394. Template.asString([
  395. `${cst} link = document.createElement('link');`,
  396. charset ? "link.charset = 'utf-8';" : "",
  397. crossOriginLoading
  398. ? `link.crossOrigin = ${JSON.stringify(
  399. crossOriginLoading
  400. )};`
  401. : "",
  402. `if (${RuntimeGlobals.scriptNonce}) {`,
  403. Template.indent(
  404. `link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
  405. ),
  406. "}",
  407. 'link.rel = "prefetch";',
  408. 'link.as = "style";',
  409. `link.href = ${cssUrl("chunkId")};`
  410. ]),
  411. chunk
  412. ),
  413. dedupePrefetch
  414. ? Template.asString([
  415. // Chrome re-requests a resource when a prefetch link is added
  416. // after it was already (pre)loaded via markup; skip in that case.
  417. `${cst} links = document.getElementsByTagName("link");`,
  418. `for(${lt} i = 0; i < links.length; i++) {`,
  419. Template.indent([
  420. `${cst} l = links[i];`,
  421. 'if(l.href === link.href && (l.rel === "prefetch" || l.rel === "preload")) return;'
  422. ]),
  423. "}"
  424. ])
  425. : "",
  426. "document.head.appendChild(link);"
  427. ]),
  428. "}"
  429. ])};`
  430. : "// no prefetching",
  431. "",
  432. withPreload && hasCssMatcher !== false
  433. ? `${
  434. RuntimeGlobals.preloadChunkHandlers
  435. }.s = ${runtimeTemplate.basicFunction("chunkId", [
  436. `if((!${
  437. RuntimeGlobals.hasOwnProperty
  438. }(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${
  439. hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")
  440. }) {`,
  441. Template.indent([
  442. "installedChunks[chunkId] = null;",
  443. // preload is a browser-only resource hint; no-op without a DOM (e.g. node side of a universal build)
  444. isNeutralPlatform
  445. ? "if (typeof document === 'undefined') return;"
  446. : "",
  447. linkPreload.call(
  448. Template.asString([
  449. `${cst} link = document.createElement('link');`,
  450. charset ? "link.charset = 'utf-8';" : "",
  451. `if (${RuntimeGlobals.scriptNonce}) {`,
  452. Template.indent(
  453. `link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
  454. ),
  455. "}",
  456. 'link.rel = "preload";',
  457. 'link.as = "style";',
  458. `link.href = ${cssUrl("chunkId")};`,
  459. crossOriginLoading
  460. ? crossOriginLoading === "use-credentials"
  461. ? 'link.crossOrigin = "use-credentials";'
  462. : Template.asString([
  463. "if (link.href.indexOf(window.location.origin + '/') !== 0) {",
  464. Template.indent(
  465. `link.crossOrigin = ${JSON.stringify(
  466. crossOriginLoading
  467. )};`
  468. ),
  469. "}"
  470. ])
  471. : ""
  472. ]),
  473. chunk
  474. ),
  475. "document.head.appendChild(link);"
  476. ]),
  477. "}"
  478. ])};`
  479. : "// no preloaded",
  480. withHmr
  481. ? Template.asString([
  482. `${cst} oldTags = [];`,
  483. `${cst} newTags = [];`,
  484. `${cst} applyHandler = ${runtimeTemplate.basicFunction("options", [
  485. `return { ${runtimeTemplate.method("dispose", "", [
  486. "while(oldTags.length) {",
  487. Template.indent([
  488. `${cst} oldTag = oldTags.pop();`,
  489. `if(${runtimeTemplate.optionalChaining("oldTag", "parentNode")}) oldTag.parentNode.removeChild(oldTag);`
  490. ]),
  491. "}"
  492. ])}, ${runtimeTemplate.method("apply", "", [
  493. "while(newTags.length) {",
  494. Template.indent([
  495. `${cst} newTag = newTags.pop();`,
  496. "newTag.sheet.disabled = false"
  497. ]),
  498. "}"
  499. ])} };`
  500. ])}`,
  501. `${cst} cssTextKey = ${runtimeTemplate.returningFunction(
  502. `Array.from(link.sheet.cssRules, ${runtimeTemplate.returningFunction(
  503. "r.cssText",
  504. "r"
  505. )}).join()`,
  506. "link"
  507. )};`,
  508. `${
  509. RuntimeGlobals.hmrDownloadUpdateHandlers
  510. }.css = ${runtimeTemplate.basicFunction(
  511. "chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList, css",
  512. [
  513. isNeutralPlatform
  514. ? Template.asString([
  515. "if (typeof document === 'undefined') {",
  516. Template.indent([
  517. // node SSR: refresh the server style registry from the re-emitted CSS instead of touching the DOM
  518. `${cst} cssRemovedChunks = css && css.r;`,
  519. `${cst} registry = ${runtimeTemplate.cssServerStyleRegistry()};`,
  520. `chunkIds.forEach(${runtimeTemplate.basicFunction(
  521. "chunkId",
  522. [
  523. `${cst} key = "chunk-" + chunkId;`,
  524. `if(${runtimeTemplate.optionalChaining(
  525. "cssRemovedChunks",
  526. "indexOf(chunkId)"
  527. )} >= 0) { delete registry[key]; return; }`,
  528. `${cst} url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(chunkId);`,
  529. `promises.push(Promise.all([import('fs'), import('url')]).then(${runtimeTemplate.basicFunction(
  530. "[{ readFile }, { URL }]",
  531. [
  532. `return new Promise(${runtimeTemplate.basicFunction(
  533. "resolve",
  534. [
  535. // best-effort: a non-file publicPath (e.g. a CDN) can't be read from disk, so skip
  536. "try {",
  537. Template.indent([
  538. `readFile(${runtimeTemplate.importMetaUrl("url")}, 'utf8', ${runtimeTemplate.basicFunction(
  539. "err, content",
  540. [
  541. "if (!err) registry[key] = content;",
  542. "resolve();"
  543. ]
  544. )});`
  545. ]),
  546. "} catch (e) { resolve(); }"
  547. ]
  548. )});`
  549. ]
  550. )}));`
  551. ]
  552. )});`,
  553. "return;"
  554. ]),
  555. "}"
  556. ])
  557. : "",
  558. "applyHandlers.push(applyHandler);",
  559. "// Read CSS removed chunks from update manifest",
  560. `${cst} cssRemovedChunks = css && css.r;`,
  561. `chunkIds.forEach(${runtimeTemplate.basicFunction("chunkId", [
  562. `${cst} url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkCssFilename}(chunkId);`,
  563. `${cst} oldTag = loadStylesheet(chunkId, url);`,
  564. `if(!oldTag && !${withHmr} ) return;`,
  565. "// Skip if CSS was removed",
  566. `if(${runtimeTemplate.optionalChaining(
  567. "cssRemovedChunks",
  568. "indexOf(chunkId)"
  569. )} >= 0) {`,
  570. Template.indent(["oldTags.push(oldTag);", "return;"]),
  571. "}",
  572. "",
  573. "// create error before stack unwound to get useful stacktrace later",
  574. `${cst} error = new Error();`,
  575. `promises.push(new Promise(${runtimeTemplate.basicFunction(
  576. "resolve, reject",
  577. [
  578. `${cst} link = loadStylesheet(chunkId, url + (url.indexOf("?") < 0 ? "?" : "&") + "hmr=" + Date.now(), ${runtimeTemplate.basicFunction(
  579. "event",
  580. [
  581. 'if(event.type !== "load") {',
  582. Template.indent([
  583. `${cst} errorType = event && event.type;`,
  584. `${cst} realHref = event && event.target && event.target.href;`,
  585. "error.message = 'Loading css hot update chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realHref + ')';",
  586. "error.name = 'ChunkLoadError';",
  587. "error.type = errorType;",
  588. "error.request = realHref;",
  589. "error.event = event;",
  590. "reject(error);"
  591. ]),
  592. "} else {",
  593. Template.indent([
  594. "try { if(cssTextKey(oldTag) == cssTextKey(link)) { if(link.parentNode) link.parentNode.removeChild(link); return resolve(); } } catch(e) {}",
  595. "link.sheet.disabled = true;",
  596. "oldTags.push(oldTag);",
  597. "newTags.push(link);",
  598. "resolve();"
  599. ]),
  600. "}"
  601. ]
  602. )}, ${withFetchPriority ? "undefined," : ""} oldTag);`
  603. ]
  604. )}));`
  605. ])});`
  606. ]
  607. )}`
  608. ])
  609. : "// no hmr"
  610. ]);
  611. }
  612. }
  613. CssLoadingRuntimeModule.getCompilationHooks = createHooksRegistry(
  614. createCompilationHooks
  615. );
  616. module.exports = CssLoadingRuntimeModule;