diff --git a/CHANGELOG.md b/CHANGELOG.md index 14614d20..40fcc083 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log +## 3.17.4 (2024-08-16) + +- fix: when using integrity occurs ERROR in RealContentHashPlugin in serv/watch mode after changes in dynamic imported JS files or after adding new import file + ## 3.17.3 (2024-08-09) - fix: in dev mode imports SCSS in JS when in the same file is inlined another SCSS file via `?inline` query, #102 diff --git a/package.json b/package.json index 5905d32b..6a5c9f20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "html-bundler-webpack-plugin", - "version": "3.17.3", + "version": "3.17.4", "description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.", "keywords": [ "html", diff --git a/src/Plugin/Extras/Integrity.js b/src/Plugin/Extras/Integrity.js index 0aded9f6..f0a33d69 100644 --- a/src/Plugin/Extras/Integrity.js +++ b/src/Plugin/Extras/Integrity.js @@ -27,13 +27,17 @@ class Integrity { this.options = options; this.chunkChildChunksMap = new WeakMap(); - this.referencePlaceholders = new Map(); - this.placeholderByChunkId = new Map(); this.chunkByChunkId = new Map(); + this.templateByChunkId = new Map(); + this.placeholderByChunkId = new Map(); + this.Template = null; } apply(compiler) { const { pluginName } = this; + const { Template } = compiler.webpack; + + this.Template = Template; compiler.hooks.afterPlugins.tap(pluginName, (compiler) => { compiler.hooks.thisCompilation.tap({ name: pluginName, stage: -10000 }, (compilation) => { @@ -69,12 +73,12 @@ class Integrity { this.isRealContentHash = this.options.isRealContentHash(); // dynamically import a JS file - mainTemplate.hooks.jsonpScript.tap(pluginName, (source) => this.addReference('script', source)); - mainTemplate.hooks.linkPreload.tap(pluginName, (source) => this.addReference('link', source)); - mainTemplate.hooks.localVars.tap(pluginName, this.setReferencePlaceholder.bind(this)); + mainTemplate.hooks.jsonpScript.tap(pluginName, (source) => this.getTemplateByTag('script', source)); + mainTemplate.hooks.linkPreload.tap(pluginName, (source) => this.getTemplateByTag('link', source)); + mainTemplate.hooks.localVars.tap(pluginName, this.getTemplateByChunk.bind(this)); compilation.hooks.beforeRuntimeRequirements.tap(pluginName, () => { - this.placeholderByChunkId.clear(); + this.templateByChunkId.clear(); }); compilation.hooks.processAssets.tap( @@ -111,6 +115,9 @@ class Integrity { return chunk ? Integrity.getIntegrity(this.compilation, content[0], chunk) : undefined; } + /** + * @param {Object} assets + */ processAssets(assets) { const { compilation, isRealContentHash } = this; const { compiler } = compilation; @@ -122,11 +129,7 @@ class Integrity { continue; } - let childChunks = this.chunkChildChunksMap.get(chunk); - if (!childChunks) { - childChunks = chunk.getAllAsyncChunks(); - this.chunkChildChunksMap.set(chunk, childChunks); - } + const childChunks = this.getChildChunks(chunk); for (const childChunk of childChunks) { if (childChunk.id == null) { @@ -134,6 +137,7 @@ class Integrity { } // TODO: find the use case when childChunk.files.size > 1 + // the size of childChunk.files is always 1 const childChunkFile = [...childChunk.files][0]; const placeholder = this.placeholderByChunkId.get(childChunk.id); @@ -143,16 +147,22 @@ class Integrity { childChunkFile, (source) => source, (assetInfo) => { - return assetInfo - ? { - ...assetInfo, - contenthash: Array.isArray(assetInfo.contenthash) - ? [...new Set([...assetInfo.contenthash, placeholder])] - : assetInfo.contenthash - ? [...new Set([assetInfo.contenthash, placeholder])] - : placeholder, - } - : undefined; + if (!assetInfo) { + return undefined; + } + + let contenthash = placeholder; + + if (Array.isArray(assetInfo.contenthash)) { + contenthash = [...new Set([...assetInfo.contenthash, placeholder])]; + } else if (assetInfo.contenthash) { + contenthash = [...new Set([assetInfo.contenthash, placeholder])]; + } + + return { + ...assetInfo, + contenthash, + }; } ); } else { @@ -172,13 +182,28 @@ class Integrity { } /** - * Add the reference of integrity hashes into a tag object. + * @param {Chunk} chunk The webpack chunk. + * @return {Set} The child chunks. + */ + getChildChunks(chunk) { + let childChunks = this.chunkChildChunksMap.get(chunk); + + if (!childChunks) { + childChunks = chunk.getAllAsyncChunks(); + this.chunkChildChunksMap.set(chunk, childChunks); + } + + return childChunks; + } + + /** + * Create the integrity template by the tag. * * @param {string} tagName * @param {string} source * @return {string} */ - addReference = (tagName, source) => { + getTemplateByTag = (tagName, source) => { const { compilation, pluginName } = this; const { Template } = compilation.compiler.webpack; const { crossOriginLoading } = compilation.outputOptions; @@ -191,7 +216,9 @@ class Integrity { }; /** - * Set the placeholder in the hash reference using the hash of a chunk file. + * Create the integrity template by the chunk. + * + * Saves the placeholder in the hash reference using the hash of a chunk file. * When the asset is processed, the placeholder will be replaced * with real integrity hash of the processed asset. * @@ -199,32 +226,32 @@ class Integrity { * @param {Chunk} chunk * @return {string} */ - setReferencePlaceholder(source, chunk) { - const { Template } = this.compilation.compiler.webpack; - - if (this.referencePlaceholders.has(chunk.id)) { - return this.referencePlaceholders.get(chunk.id); + getTemplateByChunk(source, chunk) { + if (this.templateByChunkId.has(chunk.id)) { + return this.templateByChunkId.get(chunk.id); } - const childChunks = chunk.getAllAsyncChunks(); - this.chunkChildChunksMap.set(chunk, childChunks); + const childChunks = this.getChildChunks(chunk); + + if (childChunks.size < 1) { + return source; + } const placeholders = {}; for (const childChunk of childChunks) { - const placeholder = getPlaceholder(childChunk.id); + let placeholder = this.placeholderByChunkId.get(childChunk.id); + if (!placeholder) { + placeholder = getPlaceholder(childChunk.id); + this.placeholderByChunkId.set(childChunk.id, placeholder); + } placeholders[childChunk.id] = placeholder; - this.placeholderByChunkId.set(childChunk.id, placeholder); this.chunkByChunkId.set(childChunk.id, childChunk); } - if (Object.keys(placeholders).length > 0) { - const refTemplate = Template.asString([source, `${hashesReference} = ${JSON.stringify(placeholders)};`]); - this.referencePlaceholders.set(chunk.id, refTemplate); - - return refTemplate; - } + const template = this.Template.asString([source, `${hashesReference} = ${JSON.stringify(placeholders)};`]); + this.templateByChunkId.set(chunk.id, template); - return source; + return template; } /** @@ -320,10 +347,10 @@ class Integrity { */ const getPlaceholder = (chunkId) => { // the prefix must be exact 7 chars, the same length as a hash function name, e.g. 'sha256-' - const placeholderPrefix = '___TMP-'; + const prefix = 'xxxxxx-'; const hash = Integrity.computeIntegrity(chunkId); - return placeholderPrefix + hash.slice(placeholderPrefix.length); + return prefix + hash.slice(prefix.length); }; module.exports = Integrity; diff --git a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/index.html b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/index.html index 9101439f..e47257ac 100644 --- a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/index.html +++ b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/index.html @@ -4,7 +4,7 @@ Dev - +

Hello World!

diff --git a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js similarity index 99% rename from test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js rename to test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js index 3b4cea96..38ad87e0 100644 --- a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js +++ b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js @@ -363,4 +363,4 @@ console.log('>> main'); /******/ })() ; -//# sourceMappingURL=main.6180b06e.js.map \ No newline at end of file +//# sourceMappingURL=main.b2802b44.js.map \ No newline at end of file diff --git a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js.map b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js.map similarity index 97% rename from test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js.map rename to test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js.map index 71790f6b..b1058534 100644 --- a/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.6180b06e.js.map +++ b/test/cases/integrity-dynamic-chunks-import-assets-dev/expected/main.b2802b44.js.map @@ -1 +1 @@ -{"version":3,"file":"main.6180b06e.js","mappings":";;;;;;;;;AAAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;UAEA;UACA;;;;;WCzBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,sDAAsD;WACtD,sCAAsC,iEAAiE;WACvG;WACA;WACA;WACA;WACA;WACA;;;;;WCzBA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,EAAE;WACF;;;;;WCRA;WACA;WACA;WACA;WACA;;;;;WCJA;WACA;WACA;WACA;WACA,GAAG;WACH;WACA;WACA,CAAC;;;;;WCPD;;;;;WCAA;WACA;WACA;WACA;WACA,uBAAuB,4BAA4B;WACnD;WACA;WACA;WACA,iBAAiB,oBAAoB;WACrC;WACA,sCAAsC,YAAY;WAClD;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mEAAmE,iCAAiC;WACpG;WACA;WACA;WACA;;;;;WC9CA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;WCNA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;UCjBA,iCAAiC;;;;WCDjC;;WAEA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA,iCAAiC;;WAEjC;WACA;WACA;WACA,KAAK;WACL,eAAe;WACf;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,MAAM,qBAAqB;WAC3B;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA;WACA;;;;;;;;;;;;;;ACrFA;AACA;AACA,eAAe,yBAAyB;AACxC;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED,mDAAmD,iBAAiB;;AAEpE,0IAAiB;AACjB;AACA;AACA,cAAc,8BAA8B;AAC5C;AACA;AACA,oCAAoC,KAAK;AACzC,6BAA6B,IAAI,8BAA8B,UAAU,iCAAiC,YAAY;AACtH;AACA,yBAAyB,IAAI;AAC7B,KAAK;AACL,GAAG;AACH;AACA;AACA,GAAG;;AAEmB;AACtB,uIAAgB;AAChB,uIAAgB;;AAEhB","sources":["webpack:///./src/styles.css?9f40","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/create fake namespace object","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/ensure chunk","webpack:///webpack/runtime/get javascript chunk filename","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///webpack/runtime/load script","webpack:///webpack/runtime/make namespace object","webpack:///webpack/runtime/publicPath","webpack:///webpack/runtime/compat","webpack:///webpack/runtime/jsonp chunk loading","webpack:///./src/main.js"],"sourcesContent":["/* extracted by HTMLBundler CSSLoader */","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + chunkId + \".js\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","var inProgress = {};\n// data-webpack is not used as build has no uniqueName\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\n\n\t\tscript.src = url;\n\t\tif (script.src.indexOf(window.location.origin + '/') !== 0) {\n\t\t\tscript.crossOrigin = \"anonymous\";\n\t\t}\n\t\tscript.integrity = __webpack_require__.integrity[chunkId];\n\t\tscript.crossOrigin = \"anonymous\";\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","\n__webpack_require__.integrity = {\"src_chunk_js\":\"___TMP-hLC7y3Ty038R7q2tY7lsphTh3d4xh/tALBgrMbHYLH3OqJtgTaOwBCOAakzch4Xf\",\"src_1_js\":\"___TMP-HnFg7dhitubTs98x6vO3+PUWjys0y60R0St1YMrnuMtve+dtQblk88a23ZDiZbwA\",\"src_2_js\":\"___TMP-R5o+V12ouhkDGiMgErfrgDciKfJUtbomS9p9e/sPzMOrxe4ZMckmQMMe3Z8CkDoa\"};","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t\"main\": 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunk\"] = self[\"webpackChunk\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","const scriptsWithIntegrity = [];\nconst observer = new MutationObserver((mutations) => {\n for (const { addedNodes: nodes = [] } of mutations) {\n for (const node of nodes) {\n if (node.nodeName === 'SCRIPT' && !!node.getAttribute('integrity')) {\n scriptsWithIntegrity.push(node);\n }\n }\n }\n});\n\nobserver.observe(document.querySelector('head'), { childList: true });\n\nimport('./chunk')\n .then(() => {\n scriptsWithIntegrity.forEach((script) => {\n const { src, integrity, crossOrigin } = script;\n let file = src.split('/').pop();\n const elm = document.createElement('p');\n const msg = `Dynamic chunk \"${file}\" is loaded!`;\n elm.innerHTML = `

${msg}

integrity: ${integrity}
crossOrigin: ${crossOrigin}
`;\n document.body.append(elm);\n console.log(`--> ${msg}`, script);\n });\n })\n .catch((e) => {\n console.log('import chunk error: ', e);\n });\n\nimport './styles.css';\nimport('./1.js');\nimport('./2.js');\n\nconsole.log('>> main');\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"main.b2802b44.js","mappings":";;;;;;;;;AAAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;UAEA;UACA;;;;;WCzBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,sDAAsD;WACtD,sCAAsC,iEAAiE;WACvG;WACA;WACA;WACA;WACA;WACA;;;;;WCzBA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,EAAE;WACF;;;;;WCRA;WACA;WACA;WACA;WACA;;;;;WCJA;WACA;WACA;WACA;WACA,GAAG;WACH;WACA;WACA,CAAC;;;;;WCPD;;;;;WCAA;WACA;WACA;WACA;WACA,uBAAuB,4BAA4B;WACnD;WACA;WACA;WACA,iBAAiB,oBAAoB;WACrC;WACA,sCAAsC,YAAY;WAClD;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mEAAmE,iCAAiC;WACpG;WACA;WACA;WACA;;;;;WC9CA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;WCNA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;UCjBA,iCAAiC;;;;WCDjC;;WAEA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA,iCAAiC;;WAEjC;WACA;WACA;WACA,KAAK;WACL,eAAe;WACf;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,MAAM,qBAAqB;WAC3B;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA;WACA;;;;;;;;;;;;;;ACrFA;AACA;AACA,eAAe,yBAAyB;AACxC;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED,mDAAmD,iBAAiB;;AAEpE,0IAAiB;AACjB;AACA;AACA,cAAc,8BAA8B;AAC5C;AACA;AACA,oCAAoC,KAAK;AACzC,6BAA6B,IAAI,8BAA8B,UAAU,iCAAiC,YAAY;AACtH;AACA,yBAAyB,IAAI;AAC7B,KAAK;AACL,GAAG;AACH;AACA;AACA,GAAG;;AAEmB;AACtB,uIAAgB;AAChB,uIAAgB;;AAEhB","sources":["webpack:///./src/styles.css?9f40","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/create fake namespace object","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/ensure chunk","webpack:///webpack/runtime/get javascript chunk filename","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///webpack/runtime/load script","webpack:///webpack/runtime/make namespace object","webpack:///webpack/runtime/publicPath","webpack:///webpack/runtime/compat","webpack:///webpack/runtime/jsonp chunk loading","webpack:///./src/main.js"],"sourcesContent":["/* extracted by HTMLBundler CSSLoader */","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"\" + chunkId + \".js\";\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","var inProgress = {};\n// data-webpack is not used as build has no uniqueName\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\n\n\t\tscript.src = url;\n\t\tif (script.src.indexOf(window.location.origin + '/') !== 0) {\n\t\t\tscript.crossOrigin = \"anonymous\";\n\t\t}\n\t\tscript.integrity = __webpack_require__.integrity[chunkId];\n\t\tscript.crossOrigin = \"anonymous\";\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src;\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) {\n\t\t\tvar i = scripts.length - 1;\n\t\t\twhile (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src;\n\t\t}\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","\n__webpack_require__.integrity = {\"src_chunk_js\":\"xxxxxx-hLC7y3Ty038R7q2tY7lsphTh3d4xh/tALBgrMbHYLH3OqJtgTaOwBCOAakzch4Xf\",\"src_1_js\":\"xxxxxx-HnFg7dhitubTs98x6vO3+PUWjys0y60R0St1YMrnuMtve+dtQblk88a23ZDiZbwA\",\"src_2_js\":\"xxxxxx-R5o+V12ouhkDGiMgErfrgDciKfJUtbomS9p9e/sPzMOrxe4ZMckmQMMe3Z8CkDoa\"};","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t\"main\": 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunk\"] = self[\"webpackChunk\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","const scriptsWithIntegrity = [];\nconst observer = new MutationObserver((mutations) => {\n for (const { addedNodes: nodes = [] } of mutations) {\n for (const node of nodes) {\n if (node.nodeName === 'SCRIPT' && !!node.getAttribute('integrity')) {\n scriptsWithIntegrity.push(node);\n }\n }\n }\n});\n\nobserver.observe(document.querySelector('head'), { childList: true });\n\nimport('./chunk')\n .then(() => {\n scriptsWithIntegrity.forEach((script) => {\n const { src, integrity, crossOrigin } = script;\n let file = src.split('/').pop();\n const elm = document.createElement('p');\n const msg = `Dynamic chunk \"${file}\" is loaded!`;\n elm.innerHTML = `

${msg}

integrity: ${integrity}
crossOrigin: ${crossOrigin}
`;\n document.body.append(elm);\n console.log(`--> ${msg}`, script);\n });\n })\n .catch((e) => {\n console.log('import chunk error: ', e);\n });\n\nimport './styles.css';\nimport('./1.js');\nimport('./2.js');\n\nconsole.log('>> main');\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/test/manual/watch-integrity-import-js/src/main.js b/test/manual/watch-integrity-import-js/src/main.js index cd825df5..951034a8 100644 --- a/test/manual/watch-integrity-import-js/src/main.js +++ b/test/manual/watch-integrity-import-js/src/main.js @@ -1,7 +1,31 @@ -import str from './module'; +const scriptsWithIntegrity = []; +const observer = new MutationObserver((mutations) => { + for (const { addedNodes: nodes = [] } of mutations) { + for (const node of nodes) { + if (node.nodeName === 'SCRIPT' && !!node.getAttribute('integrity')) { + scriptsWithIntegrity.push(node); + } + } + } +}); -// test in serv mode: add new import JS file -// import str2 from './module-new'; // <= uncomment -// console.log('>> main', { str2 }); // <= uncomment +observer.observe(document.querySelector('head'), { childList: true }); -console.log('>> main', { str }); +// dynamic load module +import('./module') + .then(() => { + scriptsWithIntegrity.forEach((script) => { + const { src, integrity, crossOrigin } = script; + let file = src.split('/').pop(); + const elm = document.createElement('p'); + const msg = `Dynamic chunk "${file}" is loaded!`; + elm.innerHTML = `

${msg}

integrity: ${integrity}
crossOrigin: ${crossOrigin}
`; + document.body.append(elm); + console.log(`--> ${msg}`, script); + }); + }) + .catch((e) => { + console.log('import chunk error: ', e); + }); + +console.log('>> main: test 123'); diff --git a/test/manual/watch-integrity-import-js/src/module-new.js b/test/manual/watch-integrity-import-js/src/module-new.js deleted file mode 100644 index a0a9808e..00000000 --- a/test/manual/watch-integrity-import-js/src/module-new.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'New added module: 123'; diff --git a/test/manual/watch-integrity-import-js/src/module.js b/test/manual/watch-integrity-import-js/src/module.js index 3f090786..f9337a03 100644 --- a/test/manual/watch-integrity-import-js/src/module.js +++ b/test/manual/watch-integrity-import-js/src/module.js @@ -1 +1,3 @@ module.exports = 'Test 123'; + +console.log('>> dynamic module test: 123'); diff --git a/test/manual/watch-integrity-import-js/webpack.config.js b/test/manual/watch-integrity-import-js/webpack.config.js index b5d2fa5d..ae793696 100644 --- a/test/manual/watch-integrity-import-js/webpack.config.js +++ b/test/manual/watch-integrity-import-js/webpack.config.js @@ -22,6 +22,13 @@ module.exports = { }), ], + optimization: { + runtimeChunk: { + // add webpack runtime code as a file into html + name: 'runtime', + }, + }, + // enable live reload devServer: { static: {