diff --git a/src/index.js b/src/index.js index 51809608..d2a19637 100644 --- a/src/index.js +++ b/src/index.js @@ -15,6 +15,7 @@ const { compareModulesByIdentifier, getUndoPath, BASE_URI, + compileBooleanMatcher, } = require("./utils"); /** @typedef {import("schema-utils/declarations/validate").Schema} Schema */ @@ -105,6 +106,8 @@ const CODE_GENERATION_RESULT = { /** * @typedef {Object} MiniCssExtractPluginCompilationHooks * @property {import("tapable").SyncWaterfallHook<[string, VarNames], string>} beforeTagInsert + * @property {SyncWaterfallHook<[string, Chunk]>} linkPreload + * @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch */ /** @@ -526,7 +529,8 @@ class MiniCssExtractPlugin { /** * Returns all hooks for the given compilation - * @param {Compilation} compilation + * @param {Compilation} compilation the compilation + * @returns {MiniCssExtractPluginCompilationHooks} hooks */ static getCompilationHooks(compilation) { let hooks = compilationHooksMap.get(compilation); @@ -537,6 +541,8 @@ class MiniCssExtractPlugin { ["source", "varNames"], "string" ), + linkPreload: new SyncWaterfallHook(["source", "chunk"]), + linkPrefetch: new SyncWaterfallHook(["source", "chunk"]), }; compilationHooksMap.set(compilation, hooks); } @@ -841,6 +847,20 @@ class MiniCssExtractPlugin { return obj; }; + /** + * @param {Chunk} chunk chunk + * @param {ChunkGraph} chunkGraph chunk graph + * @returns {boolean} true, when the chunk has css + */ + function chunkHasCss(chunk, chunkGraph) { + // this function replace: + // const chunkHasCss = require("webpack/lib/css/CssModulesPlugin").chunkHasCss; + return !!chunkGraph.getChunkModulesIterableBySourceType( + chunk, + "css/mini-extract" + ); + } + class CssLoadingRuntimeModule extends RuntimeModule { /** * @param {Set} runtimeRequirements @@ -854,7 +874,7 @@ class MiniCssExtractPlugin { } generate() { - const { chunk, runtimeRequirements } = this; + const { chunkGraph, chunk, runtimeRequirements } = this; const { runtimeTemplate, outputOptions: { crossOriginLoading }, @@ -863,7 +883,6 @@ class MiniCssExtractPlugin { /** @type {Chunk} */ (chunk), /** @type {Compilation} */ (this.compilation) ); - const withLoading = runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) && Object.keys(chunkMap).length > 0; @@ -874,6 +893,20 @@ class MiniCssExtractPlugin { if (!withLoading && !withHmr) { return ""; } + + const conditionMap = /** @type {ChunkGraph} */ ( + chunkGraph + ).getChunkConditionMap(/** @type {Chunk} */ (chunk), chunkHasCss); + const hasCssMatcher = compileBooleanMatcher(conditionMap); + const withPrefetch = runtimeRequirements.has( + RuntimeGlobals.prefetchChunkHandlers + ); + const withPreload = runtimeRequirements.has( + RuntimeGlobals.preloadChunkHandlers + ); + const { linkPreload, linkPrefetch } = + MiniCssExtractPlugin.getCompilationHooks(compilation); + return Template.asString([ 'if (typeof document === "undefined") return;', `var createStylesheet = ${runtimeTemplate.basicFunction( @@ -1088,6 +1121,87 @@ class MiniCssExtractPlugin { )}`, ]) : "// no hmr", + "", + withPrefetch && hasCssMatcher !== false + ? `${ + RuntimeGlobals.prefetchChunkHandlers + }.miniCss = ${runtimeTemplate.basicFunction("chunkId", [ + `if((!${ + RuntimeGlobals.hasOwnProperty + }(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${ + hasCssMatcher === true ? "true" : hasCssMatcher("chunkId") + }) {`, + Template.indent([ + "installedCssChunks[chunkId] = null;", + linkPrefetch.call( + Template.asString([ + "var link = document.createElement('link');", + crossOriginLoading + ? `link.crossOrigin = ${JSON.stringify( + crossOriginLoading + )};` + : "", + `if (${RuntimeGlobals.scriptNonce}) {`, + Template.indent( + `link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});` + ), + "}", + 'link.rel = "prefetch";', + 'link.as = "style";', + `link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`, + ]), + /** @type {Chunk} */ (chunk) + ), + "document.head.appendChild(link);", + ]), + "}", + ])};` + : "// no prefetching", + "", + withPreload && hasCssMatcher !== false + ? `${ + RuntimeGlobals.preloadChunkHandlers + }.miniCss = ${runtimeTemplate.basicFunction("chunkId", [ + `if((!${ + RuntimeGlobals.hasOwnProperty + }(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${ + hasCssMatcher === true ? "true" : hasCssMatcher("chunkId") + }) {`, + Template.indent([ + "installedCssChunks[chunkId] = null;", + linkPreload.call( + Template.asString([ + "var link = document.createElement('link');", + "link.charset = 'utf-8';", + `if (${RuntimeGlobals.scriptNonce}) {`, + Template.indent( + `link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});` + ), + "}", + 'link.rel = "preload";', + 'link.as = "style";', + `link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`, + crossOriginLoading + ? crossOriginLoading === "use-credentials" + ? 'link.crossOrigin = "use-credentials";' + : Template.asString([ + "if (link.href.indexOf(window.location.origin + '/') !== 0) {", + Template.indent( + `link.crossOrigin = ${JSON.stringify( + crossOriginLoading + )};` + ), + "}", + ]) + : "", + ]), + /** @type {Chunk} */ (chunk) + ), + "document.head.appendChild(link);", + ]), + "}", + ])};` + : "// no preloaded", ]); } } @@ -1149,6 +1263,12 @@ class MiniCssExtractPlugin { compilation.hooks.runtimeRequirementInTree .for(RuntimeGlobals.hmrDownloadUpdateHandlers) .tap(pluginName, handler); + compilation.hooks.runtimeRequirementInTree + .for(RuntimeGlobals.prefetchChunkHandlers) + .tap(pluginName, handler); + compilation.hooks.runtimeRequirementInTree + .for(RuntimeGlobals.preloadChunkHandlers) + .tap(pluginName, handler); }); } diff --git a/src/utils.js b/src/utils.js index 68ab5f08..fc1c238f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -214,6 +214,265 @@ function stringifyLocal(value) { return typeof value === "function" ? value.toString() : JSON.stringify(value); } +/** + * @param {string} str string + * @returns {string} string + */ +const toSimpleString = (str) => { + if (`${+str}` === str) { + return str; + } + return JSON.stringify(str); +}; + +/** + * @param {string} str string + * @returns {string} quoted meta + */ +const quoteMeta = (str) => str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&"); + +/** + * @param {Array} items items + * @returns {string} common prefix + */ +const getCommonPrefix = (items) => { + let prefix = items[0]; + + for (let i = 1; i < items.length; i++) { + const item = items[i]; + for (let p = 0; p < prefix.length; p++) { + if (item[p] !== prefix[p]) { + prefix = prefix.slice(0, p); + break; + } + } + } + + return prefix; +}; + +/** + * @param {Array} items items + * @returns {string} common suffix + */ +const getCommonSuffix = (items) => { + let suffix = items[0]; + + for (let i = 1; i < items.length; i++) { + const item = items[i]; + for (let p = item.length - 1, s = suffix.length - 1; s >= 0; p--, s--) { + if (item[p] !== suffix[s]) { + suffix = suffix.slice(s + 1); + break; + } + } + } + + return suffix; +}; + +/** + * @param {Set} itemsSet items set + * @param {(str: string) => string | false} getKey get key function + * @param {(str: Array) => boolean} condition condition + * @returns {Array>} list of common items + */ +const popCommonItems = (itemsSet, getKey, condition) => { + /** @type {Map>} */ + const map = new Map(); + + for (const item of itemsSet) { + const key = getKey(item); + if (key) { + let list = map.get(key); + if (list === undefined) { + /** @type {Array} */ + list = []; + map.set(key, list); + } + list.push(item); + } + } + + /** @type {Array>} */ + const result = []; + + for (const list of map.values()) { + if (condition(list)) { + for (const item of list) { + itemsSet.delete(item); + } + result.push(list); + } + } + + return result; +}; + +/** + * @param {Array} itemsArr array of items + * @returns {string} regexp + */ +const itemsToRegexp = (itemsArr) => { + if (itemsArr.length === 1) { + return quoteMeta(itemsArr[0]); + } + + /** @type {Array} */ + const finishedItems = []; + + // merge single char items: (a|b|c|d|ef) => ([abcd]|ef) + let countOfSingleCharItems = 0; + + for (const item of itemsArr) { + if (item.length === 1) { + // eslint-disable-next-line no-plusplus + countOfSingleCharItems++; + } + } + + // special case for only single char items + if (countOfSingleCharItems === itemsArr.length) { + return `[${quoteMeta(itemsArr.sort().join(""))}]`; + } + + const items = new Set(itemsArr.sort()); + + if (countOfSingleCharItems > 2) { + let singleCharItems = ""; + for (const item of items) { + if (item.length === 1) { + singleCharItems += item; + items.delete(item); + } + } + finishedItems.push(`[${quoteMeta(singleCharItems)}]`); + } + + // special case for 2 items with common prefix/suffix + if (finishedItems.length === 0 && items.size === 2) { + const prefix = getCommonPrefix(itemsArr); + const suffix = getCommonSuffix( + itemsArr.map((item) => item.slice(prefix.length)) + ); + + if (prefix.length > 0 || suffix.length > 0) { + return `${quoteMeta(prefix)}${itemsToRegexp( + itemsArr.map((i) => i.slice(prefix.length, -suffix.length || undefined)) + )}${quoteMeta(suffix)}`; + } + } + + // special case for 2 items with common suffix + if (finishedItems.length === 0 && items.size === 2) { + /** @type {Iterator} */ + const it = items[Symbol.iterator](); + const a = it.next().value; + const b = it.next().value; + if (a.length > 0 && b.length > 0 && a.slice(-1) === b.slice(-1)) { + return `${itemsToRegexp([a.slice(0, -1), b.slice(0, -1)])}${quoteMeta( + a.slice(-1) + )}`; + } + } + + // find common prefix: (a1|a2|a3|a4|b5) => (a(1|2|3|4)|b5) + const prefixed = popCommonItems( + items, + (item) => (item.length >= 1 ? item[0] : false), + (list) => { + if (list.length >= 3) return true; + if (list.length <= 1) return false; + return list[0][1] === list[1][1]; + } + ); + for (const prefixedItems of prefixed) { + const prefix = getCommonPrefix(prefixedItems); + finishedItems.push( + `${quoteMeta(prefix)}${itemsToRegexp( + prefixedItems.map((i) => i.slice(prefix.length)) + )}` + ); + } + + // find common suffix: (a1|b1|c1|d1|e2) => ((a|b|c|d)1|e2) + const suffixed = popCommonItems( + items, + (item) => (item.length >= 1 ? item.slice(-1) : false), + (list) => { + if (list.length >= 3) return true; + if (list.length <= 1) return false; + return list[0].slice(-2) === list[1].slice(-2); + } + ); + for (const suffixedItems of suffixed) { + const suffix = getCommonSuffix(suffixedItems); + finishedItems.push( + `${itemsToRegexp( + suffixedItems.map((i) => i.slice(0, -suffix.length)) + )}${quoteMeta(suffix)}` + ); + } + + // TODO further optimize regexp, i. e. + // use ranges: (1|2|3|4|a) => [1-4a] + const conditional = finishedItems.concat(Array.from(items, quoteMeta)); + if (conditional.length === 1) return conditional[0]; + return `(${conditional.join("|")})`; +}; + +/** + * @param {string[]} positiveItems positive items + * @param {string[]} negativeItems negative items + * @returns {function(string): string} a template function to determine the value at runtime + */ +const compileBooleanMatcherFromLists = (positiveItems, negativeItems) => { + if (positiveItems.length === 0) { + return () => "false"; + } + + if (negativeItems.length === 0) { + return () => "true"; + } + + if (positiveItems.length === 1) { + return (value) => `${toSimpleString(positiveItems[0])} == ${value}`; + } + + if (negativeItems.length === 1) { + return (value) => `${toSimpleString(negativeItems[0])} != ${value}`; + } + + const positiveRegexp = itemsToRegexp(positiveItems); + const negativeRegexp = itemsToRegexp(negativeItems); + + if (positiveRegexp.length <= negativeRegexp.length) { + return (value) => `/^${positiveRegexp}$/.test(${value})`; + } + + return (value) => `!/^${negativeRegexp}$/.test(${value})`; +}; + +// TODO simplify in the next major release and use it from webpack +/** + * @param {Record} map value map + * @returns {boolean|(function(string): string)} true/false, when unconditionally true/false, or a template function to determine the value at runtime + */ +const compileBooleanMatcher = (map) => { + const positiveItems = Object.keys(map).filter((i) => map[i]); + const negativeItems = Object.keys(map).filter((i) => !map[i]); + + if (positiveItems.length === 0) { + return false; + } + + if (negativeItems.length === 0) { + return true; + } + + return compileBooleanMatcherFromLists(positiveItems, negativeItems); +}; + module.exports = { trueFn, findModuleById, @@ -227,4 +486,5 @@ module.exports = { stringifyRequest, stringifyLocal, getUndoPath, + compileBooleanMatcher, }; diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/0.bb74e774207d1a909943.css b/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/0.ec6b19ad9e5afccf41ed.css similarity index 100% rename from test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/0.bb74e774207d1a909943.css rename to test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/0.ec6b19ad9e5afccf41ed.css diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/bb74e774207d1a909943.css b/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/ec6b19ad9e5afccf41ed.css similarity index 100% rename from test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/bb74e774207d1a909943.css rename to test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/ec6b19ad9e5afccf41ed.css diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/main.js b/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/main.js index e288a56b..699df42d 100644 --- a/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/main.js +++ b/test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/main.js @@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__); /******/ /******/ /* webpack/runtime/getFullHash */ /******/ (() => { -/******/ __webpack_require__.h = () => ("bb74e774207d1a909943") +/******/ __webpack_require__.h = () => ("ec6b19ad9e5afccf41ed") /******/ })(); /******/ /******/ /* webpack/runtime/global */ @@ -253,6 +253,10 @@ __webpack_require__.r(__webpack_exports__); /******/ }; /******/ /******/ // no hmr +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded /******/ })(); /******/ /******/ /* webpack/runtime/jsonp chunk loading */ diff --git a/test/cases/hmr/expected/main.js b/test/cases/hmr/expected/main.js index 39fc3d64..ff182fd7 100644 --- a/test/cases/hmr/expected/main.js +++ b/test/cases/hmr/expected/main.js @@ -1029,6 +1029,10 @@ __webpack_require__.r(__webpack_exports__); /******/ })); /******/ }); /******/ } +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded /******/ })(); /******/ /******/ /* webpack/runtime/jsonp chunk loading */ diff --git a/test/cases/insert-function/expected/main.js b/test/cases/insert-function/expected/main.js index 0f7f4ea5..bb3fbf19 100644 --- a/test/cases/insert-function/expected/main.js +++ b/test/cases/insert-function/expected/main.js @@ -238,6 +238,10 @@ /******/ }; /******/ /******/ // no hmr +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded /******/ })(); /******/ /******/ /* webpack/runtime/jsonp chunk loading */ diff --git a/test/cases/insert-string/expected/main.js b/test/cases/insert-string/expected/main.js index faf5ef4c..d2550a28 100644 --- a/test/cases/insert-string/expected/main.js +++ b/test/cases/insert-string/expected/main.js @@ -234,6 +234,10 @@ /******/ }; /******/ /******/ // no hmr +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded /******/ })(); /******/ /******/ /* webpack/runtime/jsonp chunk loading */ diff --git a/test/cases/insert-undefined/expected/main.js b/test/cases/insert-undefined/expected/main.js index eef6572b..ae8118ca 100644 --- a/test/cases/insert-undefined/expected/main.js +++ b/test/cases/insert-undefined/expected/main.js @@ -237,6 +237,10 @@ /******/ }; /******/ /******/ // no hmr +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded /******/ })(); /******/ /******/ /* webpack/runtime/jsonp chunk loading */ diff --git a/test/cases/prefetch-preload/a.css b/test/cases/prefetch-preload/a.css new file mode 100644 index 00000000..5451a331 --- /dev/null +++ b/test/cases/prefetch-preload/a.css @@ -0,0 +1,3 @@ +.a { + color: red; +} diff --git a/test/cases/prefetch-preload/b.js b/test/cases/prefetch-preload/b.js new file mode 100644 index 00000000..68eec89d --- /dev/null +++ b/test/cases/prefetch-preload/b.js @@ -0,0 +1,3 @@ +import(/* webpackPrefetch: true, webpackChunkName: "b1" */ "./b1.css"); +import(/* webpackPreload: true, webpackChunkName: "b2" */ "./b2.css"); +import(/* webpackPrefetch: true, webpackChunkName: "b3" */ "./b3"); diff --git a/test/cases/prefetch-preload/b1.css b/test/cases/prefetch-preload/b1.css new file mode 100644 index 00000000..effc583c --- /dev/null +++ b/test/cases/prefetch-preload/b1.css @@ -0,0 +1,3 @@ +.b1 { + color: red; +} diff --git a/test/cases/prefetch-preload/b2.css b/test/cases/prefetch-preload/b2.css new file mode 100644 index 00000000..e0331e1a --- /dev/null +++ b/test/cases/prefetch-preload/b2.css @@ -0,0 +1,3 @@ +.b2 { + color: red; +} diff --git a/test/cases/prefetch-preload/b3.js b/test/cases/prefetch-preload/b3.js new file mode 100644 index 00000000..e69de29b diff --git a/test/cases/prefetch-preload/c.js b/test/cases/prefetch-preload/c.js new file mode 100644 index 00000000..ce96cadc --- /dev/null +++ b/test/cases/prefetch-preload/c.js @@ -0,0 +1,2 @@ +import(/* webpackPreload: true, webpackChunkName: "c1" */ "./c1.css"); +import(/* webpackPreload: true, webpackChunkName: "c2" */ "./c2.css"); diff --git a/test/cases/prefetch-preload/c1.css b/test/cases/prefetch-preload/c1.css new file mode 100644 index 00000000..8725bcb0 --- /dev/null +++ b/test/cases/prefetch-preload/c1.css @@ -0,0 +1,3 @@ +.c1 { + color: red; +} diff --git a/test/cases/prefetch-preload/c2.css b/test/cases/prefetch-preload/c2.css new file mode 100644 index 00000000..a124f6df --- /dev/null +++ b/test/cases/prefetch-preload/c2.css @@ -0,0 +1,3 @@ +.c2 { + color: red; +} diff --git a/test/cases/prefetch-preload/expected/a.css b/test/cases/prefetch-preload/expected/a.css new file mode 100644 index 00000000..98f445d8 --- /dev/null +++ b/test/cases/prefetch-preload/expected/a.css @@ -0,0 +1,4 @@ +.a { + color: red; +} + diff --git a/test/cases/prefetch-preload/expected/b.js b/test/cases/prefetch-preload/expected/b.js new file mode 100644 index 00000000..7b97d9e9 --- /dev/null +++ b/test/cases/prefetch-preload/expected/b.js @@ -0,0 +1,13 @@ +(self["webpackChunk"] = self["webpackChunk"] || []).push([[1],{ + +/***/ 2: +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +__webpack_require__.e(/* import() | b1 */ 2).then(__webpack_require__.bind(__webpack_require__, 4)); +__webpack_require__.e(/* import() | b2 */ 3).then(__webpack_require__.bind(__webpack_require__, 5)); +__webpack_require__.e(/* import() | b3 */ 4).then(__webpack_require__.t.bind(__webpack_require__, 6, 23)); + + +/***/ }) + +}]); \ No newline at end of file diff --git a/test/cases/prefetch-preload/expected/b1.css b/test/cases/prefetch-preload/expected/b1.css new file mode 100644 index 00000000..4ba3041c --- /dev/null +++ b/test/cases/prefetch-preload/expected/b1.css @@ -0,0 +1,4 @@ +.b1 { + color: red; +} + diff --git a/test/cases/prefetch-preload/expected/b2.css b/test/cases/prefetch-preload/expected/b2.css new file mode 100644 index 00000000..3bc21aa1 --- /dev/null +++ b/test/cases/prefetch-preload/expected/b2.css @@ -0,0 +1,4 @@ +.b2 { + color: red; +} + diff --git a/test/cases/prefetch-preload/expected/c.js b/test/cases/prefetch-preload/expected/c.js new file mode 100644 index 00000000..45d60ccc --- /dev/null +++ b/test/cases/prefetch-preload/expected/c.js @@ -0,0 +1,12 @@ +(self["webpackChunk"] = self["webpackChunk"] || []).push([[5],{ + +/***/ 3: +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +__webpack_require__.e(/* import() | c1 */ 6).then(__webpack_require__.bind(__webpack_require__, 7)); +__webpack_require__.e(/* import() | c2 */ 7).then(__webpack_require__.bind(__webpack_require__, 8)); + + +/***/ }) + +}]); \ No newline at end of file diff --git a/test/cases/prefetch-preload/expected/c1.css b/test/cases/prefetch-preload/expected/c1.css new file mode 100644 index 00000000..b10e79dd --- /dev/null +++ b/test/cases/prefetch-preload/expected/c1.css @@ -0,0 +1,4 @@ +.c1 { + color: red; +} + diff --git a/test/cases/prefetch-preload/expected/c2.css b/test/cases/prefetch-preload/expected/c2.css new file mode 100644 index 00000000..a17ea975 --- /dev/null +++ b/test/cases/prefetch-preload/expected/c2.css @@ -0,0 +1,4 @@ +.c2 { + color: red; +} + diff --git a/test/cases/prefetch-preload/expected/main.js b/test/cases/prefetch-preload/expected/main.js new file mode 100644 index 00000000..7fc8edd5 --- /dev/null +++ b/test/cases/prefetch-preload/expected/main.js @@ -0,0 +1,529 @@ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({}); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/chunk loaded */ +/******/ (() => { +/******/ var deferred = []; +/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { +/******/ if(chunkIds) { +/******/ priority = priority || 0; +/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; +/******/ deferred[i] = [chunkIds, fn, priority]; +/******/ return; +/******/ } +/******/ var notFulfilled = Infinity; +/******/ for (var i = 0; i < deferred.length; i++) { +/******/ var [chunkIds, fn, priority] = deferred[i]; +/******/ var fulfilled = true; +/******/ for (var j = 0; j < chunkIds.length; j++) { +/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) { +/******/ chunkIds.splice(j--, 1); +/******/ } else { +/******/ fulfilled = false; +/******/ if(priority < notFulfilled) notFulfilled = priority; +/******/ } +/******/ } +/******/ if(fulfilled) { +/******/ deferred.splice(i--, 1) +/******/ var r = fn(); +/******/ if (r !== undefined) result = r; +/******/ } +/******/ } +/******/ return result; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/chunk prefetch function */ +/******/ (() => { +/******/ __webpack_require__.F = {}; +/******/ __webpack_require__.E = (chunkId) => { +/******/ Object.keys(__webpack_require__.F).map((key) => { +/******/ __webpack_require__.F[key](chunkId); +/******/ }); +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/chunk preload function */ +/******/ (() => { +/******/ __webpack_require__.H = {}; +/******/ __webpack_require__.G = (chunkId) => { +/******/ Object.keys(__webpack_require__.H).map((key) => { +/******/ __webpack_require__.H[key](chunkId); +/******/ }); +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/create fake namespace object */ +/******/ (() => { +/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); +/******/ var leafPrototypes; +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 16: return value when it's Promise-like +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = this(value); +/******/ if(mode & 8) return value; +/******/ if(typeof value === 'object' && value) { +/******/ if((mode & 4) && value.__esModule) return value; +/******/ if((mode & 16) && typeof value.then === 'function') return value; +/******/ } +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ var def = {}; +/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; +/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) { +/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); +/******/ } +/******/ def['default'] = () => (value); +/******/ __webpack_require__.d(ns, def); +/******/ return ns; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/ensure chunk */ +/******/ (() => { +/******/ __webpack_require__.f = {}; +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __webpack_require__.e = (chunkId) => { +/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { +/******/ __webpack_require__.f[key](chunkId, promises); +/******/ return promises; +/******/ }, [])); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __webpack_require__.u = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + {"0":"a","1":"b","2":"b1","3":"b2","4":"b3","5":"c","6":"c1","7":"c2"}[chunkId] + ".js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get mini-css chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __webpack_require__.miniCssF = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + {"0":"a","2":"b1","3":"b2","6":"c1","7":"c2"}[chunkId] + ".css"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/global */ +/******/ (() => { +/******/ __webpack_require__.g = (function() { +/******/ if (typeof globalThis === 'object') return globalThis; +/******/ try { +/******/ return this || new Function('return this')(); +/******/ } catch (e) { +/******/ if (typeof window === 'object') return window; +/******/ } +/******/ })(); +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/load script */ +/******/ (() => { +/******/ var inProgress = {}; +/******/ // data-webpack is not used as build has no uniqueName +/******/ // loadScript function to load a script via script tag +/******/ __webpack_require__.l = (url, done, key, chunkId) => { +/******/ if(inProgress[url]) { inProgress[url].push(done); return; } +/******/ var script, needAttach; +/******/ if(key !== undefined) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ for(var i = 0; i < scripts.length; i++) { +/******/ var s = scripts[i]; +/******/ if(s.getAttribute("src") == url) { script = s; break; } +/******/ } +/******/ } +/******/ if(!script) { +/******/ needAttach = true; +/******/ script = document.createElement('script'); +/******/ +/******/ script.charset = 'utf-8'; +/******/ script.timeout = 120; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ +/******/ +/******/ script.src = url; +/******/ } +/******/ inProgress[url] = [done]; +/******/ var onScriptComplete = (prev, event) => { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var doneFns = inProgress[url]; +/******/ delete inProgress[url]; +/******/ script.parentNode && script.parentNode.removeChild(script); +/******/ doneFns && doneFns.forEach((fn) => (fn(event))); +/******/ if(prev) return prev(event); +/******/ } +/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); +/******/ script.onerror = onScriptComplete.bind(null, script.onerror); +/******/ script.onload = onScriptComplete.bind(null, script.onload); +/******/ needAttach && document.head.appendChild(script); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/publicPath */ +/******/ (() => { +/******/ var scriptUrl; +/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + ""; +/******/ var document = __webpack_require__.g.document; +/******/ if (!scriptUrl && document) { +/******/ if (document.currentScript) +/******/ scriptUrl = document.currentScript.src; +/******/ if (!scriptUrl) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ if(scripts.length) { +/******/ var i = scripts.length - 1; +/******/ while (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src; +/******/ } +/******/ } +/******/ } +/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration +/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic. +/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser"); +/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/"); +/******/ __webpack_require__.p = scriptUrl; +/******/ })(); +/******/ +/******/ /* webpack/runtime/css loading */ +/******/ (() => { +/******/ if (typeof document === "undefined") return; +/******/ var createStylesheet = (chunkId, fullhref, oldTag, resolve, reject) => { +/******/ var linkTag = document.createElement("link"); +/******/ +/******/ linkTag.rel = "stylesheet"; +/******/ linkTag.type = "text/css"; +/******/ if (__webpack_require__.nc) { +/******/ linkTag.nonce = __webpack_require__.nc; +/******/ } +/******/ var onLinkComplete = (event) => { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var errorType = event && event.type; +/******/ var realHref = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + errorType + ": " + realHref + ")"); +/******/ err.name = "ChunkLoadError"; +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.type = errorType; +/******/ err.request = realHref; +/******/ if (linkTag.parentNode) linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } +/******/ } +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; +/******/ linkTag.href = fullhref; +/******/ +/******/ +/******/ if (oldTag) { +/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling); +/******/ } else { +/******/ document.head.appendChild(linkTag); +/******/ } +/******/ return linkTag; +/******/ }; +/******/ var findStylesheet = (href, fullhref) => { +/******/ var existingLinkTags = document.getElementsByTagName("link"); +/******/ for(var i = 0; i < existingLinkTags.length; i++) { +/******/ var tag = existingLinkTags[i]; +/******/ var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href"); +/******/ if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return tag; +/******/ } +/******/ var existingStyleTags = document.getElementsByTagName("style"); +/******/ for(var i = 0; i < existingStyleTags.length; i++) { +/******/ var tag = existingStyleTags[i]; +/******/ var dataHref = tag.getAttribute("data-href"); +/******/ if(dataHref === href || dataHref === fullhref) return tag; +/******/ } +/******/ }; +/******/ var loadStylesheet = (chunkId) => { +/******/ return new Promise((resolve, reject) => { +/******/ var href = __webpack_require__.miniCssF(chunkId); +/******/ var fullhref = __webpack_require__.p + href; +/******/ if(findStylesheet(href, fullhref)) return resolve(); +/******/ createStylesheet(chunkId, fullhref, null, resolve, reject); +/******/ }); +/******/ } +/******/ // object to store loaded CSS chunks +/******/ var installedCssChunks = { +/******/ 8: 0 +/******/ }; +/******/ +/******/ __webpack_require__.f.miniCss = (chunkId, promises) => { +/******/ var cssChunks = {"0":1,"2":1,"3":1,"6":1,"7":1}; +/******/ if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]); +/******/ else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) { +/******/ promises.push(installedCssChunks[chunkId] = loadStylesheet(chunkId).then(() => { +/******/ installedCssChunks[chunkId] = 0; +/******/ }, (e) => { +/******/ delete installedCssChunks[chunkId]; +/******/ throw e; +/******/ })); +/******/ } +/******/ }; +/******/ +/******/ // no hmr +/******/ +/******/ __webpack_require__.F.miniCss = (chunkId) => { +/******/ if((!__webpack_require__.o(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && !/^[1458]$/.test(chunkId)) { +/******/ installedCssChunks[chunkId] = null; +/******/ var link = document.createElement('link'); +/******/ +/******/ if (__webpack_require__.nc) { +/******/ link.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ link.rel = "prefetch"; +/******/ link.as = "style"; +/******/ link.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId); +/******/ document.head.appendChild(link); +/******/ } +/******/ }; +/******/ +/******/ __webpack_require__.H.miniCss = (chunkId) => { +/******/ if((!__webpack_require__.o(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && !/^[1458]$/.test(chunkId)) { +/******/ installedCssChunks[chunkId] = null; +/******/ var link = document.createElement('link'); +/******/ link.charset = 'utf-8'; +/******/ if (__webpack_require__.nc) { +/******/ link.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ link.rel = "preload"; +/******/ link.as = "style"; +/******/ link.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId); +/******/ document.head.appendChild(link); +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ 8: 0 +/******/ }; +/******/ +/******/ __webpack_require__.f.j = (chunkId, promises) => { +/******/ // JSONP chunk loading for javascript +/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; +/******/ if(installedChunkData !== 0) { // 0 means "already installed". +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunkData) { +/******/ promises.push(installedChunkData[2]); +/******/ } else { +/******/ if(true) { // all chunks have JS +/******/ // setup Promise in chunk cache +/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject])); +/******/ promises.push(installedChunkData[2] = promise); +/******/ +/******/ // start chunk loading +/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId); +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ var loadingEnded = (event) => { +/******/ if(__webpack_require__.o(installedChunks, chunkId)) { +/******/ installedChunkData = installedChunks[chunkId]; +/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined; +/******/ if(installedChunkData) { +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ installedChunkData[1](error); +/******/ } +/******/ } +/******/ }; +/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId); +/******/ } +/******/ } +/******/ } +/******/ }; +/******/ +/******/ __webpack_require__.F.j = (chunkId) => { +/******/ if((!__webpack_require__.o(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && true) { +/******/ installedChunks[chunkId] = null; +/******/ var link = document.createElement('link'); +/******/ +/******/ if (__webpack_require__.nc) { +/******/ link.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ link.rel = "prefetch"; +/******/ link.as = "script"; +/******/ link.href = __webpack_require__.p + __webpack_require__.u(chunkId); +/******/ document.head.appendChild(link); +/******/ } +/******/ }; +/******/ +/******/ __webpack_require__.H.j = (chunkId) => { +/******/ if((!__webpack_require__.o(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && true) { +/******/ installedChunks[chunkId] = null; +/******/ var link = document.createElement('link'); +/******/ +/******/ link.charset = 'utf-8'; +/******/ if (__webpack_require__.nc) { +/******/ link.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ link.rel = "preload"; +/******/ link.as = "script"; +/******/ link.href = __webpack_require__.p + __webpack_require__.u(chunkId); +/******/ document.head.appendChild(link); +/******/ } +/******/ }; +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ +/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0); +/******/ +/******/ // install a JSONP callback for chunk loading +/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { +/******/ var [chunkIds, moreModules, runtime] = data; +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0; +/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { +/******/ for(moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) var result = runtime(__webpack_require__); +/******/ } +/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ installedChunks[chunkId][0](); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ return __webpack_require__.O(result); +/******/ } +/******/ +/******/ var chunkLoadingGlobal = self["webpackChunk"] = self["webpackChunk"] || []; +/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); +/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); +/******/ })(); +/******/ +/******/ /* webpack/runtime/chunk prefetch trigger */ +/******/ (() => { +/******/ var chunkToChildrenMap = { +/******/ "1": [ +/******/ 2, +/******/ 4 +/******/ ] +/******/ }; +/******/ __webpack_require__.f.prefetch = (chunkId, promises) => (Promise.all(promises).then(() => { +/******/ var chunks = chunkToChildrenMap[chunkId]; +/******/ Array.isArray(chunks) && chunks.map(__webpack_require__.E); +/******/ })); +/******/ })(); +/******/ +/******/ /* webpack/runtime/chunk preload trigger */ +/******/ (() => { +/******/ var chunkToChildrenMap = { +/******/ "1": [ +/******/ 3 +/******/ ], +/******/ "5": [ +/******/ 6, +/******/ 7 +/******/ ] +/******/ }; +/******/ __webpack_require__.f.preload = (chunkId) => { +/******/ var chunks = chunkToChildrenMap[chunkId]; +/******/ Array.isArray(chunks) && chunks.map(__webpack_require__.G); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/startup prefetch */ +/******/ (() => { +/******/ __webpack_require__.O(0, [8], () => { +/******/ [0,1,5].map(__webpack_require__.E); +/******/ }, 5); +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +__webpack_require__.e(/* import() | a */ 0).then(__webpack_require__.bind(__webpack_require__, 1)); +__webpack_require__.e(/* import() | b */ 1).then(__webpack_require__.t.bind(__webpack_require__, 2, 23)); +__webpack_require__.e(/* import() | c */ 5).then(__webpack_require__.t.bind(__webpack_require__, 3, 23)); + +__webpack_exports__ = __webpack_require__.O(__webpack_exports__); +/******/ })() +; \ No newline at end of file diff --git a/test/cases/prefetch-preload/index.js b/test/cases/prefetch-preload/index.js new file mode 100644 index 00000000..e1c87500 --- /dev/null +++ b/test/cases/prefetch-preload/index.js @@ -0,0 +1,3 @@ +import(/* webpackPrefetch: true, webpackChunkName: "a" */ "./a.css"); +import(/* webpackPrefetch: true, webpackChunkName: "b" */ "./b"); +import(/* webpackPrefetch: true, webpackChunkName: "c" */ "./c"); diff --git a/test/cases/prefetch-preload/webpack.config.js b/test/cases/prefetch-preload/webpack.config.js new file mode 100644 index 00000000..cf55c08c --- /dev/null +++ b/test/cases/prefetch-preload/webpack.config.js @@ -0,0 +1,18 @@ +import Self from "../../../src"; + +module.exports = { + entry: "./index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, "css-loader"], + }, + ], + }, + plugins: [ + new Self({ + filename: "[name].css", + }), + ], +}; diff --git a/test/manual/index.html b/test/manual/index.html index 3b7d2ca4..8c93cb71 100644 --- a/test/manual/index.html +++ b/test/manual/index.html @@ -23,6 +23,16 @@ background: lightgreen; } + + @@ -97,6 +107,20 @@

+
+

Prefetch CSS: Must be blue and green after click.

+

+ to load + prefetched chunk +

+
+
+

Preload CSS: Must be white, then blue, then green after click.

+

+ to load + preloaded chunk +

+
diff --git a/test/manual/src/index.js b/test/manual/src/index.js index 8f0d1450..76f0dc49 100644 --- a/test/manual/src/index.js +++ b/test/manual/src/index.js @@ -102,3 +102,16 @@ worker.postMessage("test"); worker.addEventListener("message", (event) => { console.log(`Received message from worker: ${event.data}`); }); + +makeButton( + ".prefetch-button", + () => import(/* webpackPrefetch: true */ "./prefetch.css"), + false +); + +makeButton( + ".preload-button", + // eslint-disable-next-line import/extensions + () => import("./preload.js"), + false +); diff --git a/test/manual/src/prefetch.css b/test/manual/src/prefetch.css new file mode 100644 index 00000000..b0439ee6 --- /dev/null +++ b/test/manual/src/prefetch.css @@ -0,0 +1,3 @@ +.prefetch { + background: lightgreen !important; +} diff --git a/test/manual/src/preload.css b/test/manual/src/preload.css new file mode 100644 index 00000000..2742013d --- /dev/null +++ b/test/manual/src/preload.css @@ -0,0 +1,3 @@ +.preload { + background: lightgreen !important; +} diff --git a/test/manual/src/preload.js b/test/manual/src/preload.js new file mode 100644 index 00000000..015a99a7 --- /dev/null +++ b/test/manual/src/preload.js @@ -0,0 +1,7 @@ +async function load() { + setTimeout(() => { + import(/* webpackPreload: true */ "./preload.css"); + }, 1000); +} + +load(); diff --git a/types/index.d.ts b/types/index.d.ts index 0765eb15..0bd721c3 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,7 +14,8 @@ declare class MiniCssExtractPlugin { ): CssDependencyConstructor; /** * Returns all hooks for the given compilation - * @param {Compilation} compilation + * @param {Compilation} compilation the compilation + * @returns {MiniCssExtractPluginCompilationHooks} hooks */ static getCompilationHooks( compilation: Compilation @@ -118,6 +119,8 @@ type MiniCssExtractPluginCompilationHooks = { [string, VarNames], string >; + linkPreload: SyncWaterfallHook<[string, Chunk]>; + linkPrefetch: SyncWaterfallHook<[string, Chunk]>; }; type PluginOptions = { filename?: Required["output"]["filename"]; @@ -255,3 +258,4 @@ type VarNames = { resolve: string; reject: string; }; +import { SyncWaterfallHook } from "tapable";