diff --git a/lib/Compilation.js b/lib/Compilation.js index 9ed6f297b0d..bdbc0e541a7 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -12,7 +12,8 @@ const { SyncBailHook, SyncWaterfallHook, AsyncSeriesHook, - AsyncSeriesBailHook + AsyncSeriesBailHook, + AsyncParallelHook } = require("tapable"); const util = require("util"); const { CachedSource } = require("webpack-sources"); @@ -183,6 +184,7 @@ const { isSourceEqual } = require("./util/source"); /** * @typedef {Object} ExecuteModuleResult * @property {any} exports + * @property {boolean} cacheable * @property {Map} assets * @property {LazySet} fileDependencies * @property {LazySet} contextDependencies @@ -193,7 +195,8 @@ const { isSourceEqual } = require("./util/source"); /** * @typedef {Object} ExecuteModuleArgument * @property {Module} module - * @property {object} moduleObject + * @property {{ id: string, exports: any, loaded: boolean }=} moduleObject + * @property {any} preparedInfo * @property {CodeGenerationResult} codeGenerationResult */ @@ -202,7 +205,7 @@ const { isSourceEqual } = require("./util/source"); * @property {Map} assets * @property {Chunk} chunk * @property {ChunkGraph} chunkGraph - * @property {Function} __webpack_require__ + * @property {function(string): any=} __webpack_require__ */ /** @@ -574,6 +577,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si /** @type {SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */ executeModule: new SyncHook(["options", "context"]), + /** @type {AsyncParallelHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */ + prepareModuleExecution: new AsyncParallelHook(["options", "context"]), /** @type {AsyncSeriesHook<[Iterable]>} */ finishModules: new AsyncSeriesHook(["modules"]), @@ -2833,7 +2838,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o } // attach runtime module - module.attach(this, chunk); + module.attach(this, chunk, chunkGraph); // Setup internals const exportsInfo = this.moduleGraph.getExportsInfo(module); @@ -4030,6 +4035,7 @@ This prevents using hashes of each other and should be avoided.`); const entrypoint = new Entrypoint({ runtime, + chunkLoading: "none", ...options.entryOptions }); chunkGraph.connectChunkAndEntryModule(chunk, module, entrypoint); @@ -4039,13 +4045,9 @@ This prevents using hashes of each other and should be avoided.`); const chunks = new Set([chunk]); - /** @type {Map} */ - const modulesById = new Map(); - // Assign ids to modules and modules to the chunk for (const module of modules) { const id = module.identifier(); - modulesById.set(id, module); chunkGraph.setModuleId(module, id); chunkGraph.connectChunkAndModule(chunk, module); } @@ -4125,6 +4127,7 @@ This prevents using hashes of each other and should be avoided.`); // Hash runtime modules for (const module of runtimeModules) { + modules.add(module); this._createModuleHash( module, chunkGraph, @@ -4141,7 +4144,11 @@ This prevents using hashes of each other and should be avoided.`); if (err) return callback(err); reportErrors(); - let exports; + /** @type {Map} */ + const moduleArgumentsMap = new Map(); + /** @type {Map} */ + const moduleArgumentsById = new Map(); + /** @type {ExecuteModuleResult["fileDependencies"]} */ const fileDependencies = new LazySet(); /** @type {ExecuteModuleResult["contextDependencies"]} */ @@ -4150,43 +4157,47 @@ This prevents using hashes of each other and should be avoided.`); const missingDependencies = new LazySet(); /** @type {ExecuteModuleResult["buildDependencies"]} */ const buildDependencies = new LazySet(); + /** @type {ExecuteModuleResult["assets"]} */ const assets = new Map(); - try { - const { - strictModuleErrorHandling, - strictModuleExceptionHandling - } = this.outputOptions; - const moduleCache = new Map(); - const __webpack_require__ = id => { - const module = modulesById.get(id); - return __webpack_require_module__(module, id); - }; - /** - * - * @param {Module} module the module - * @param {string} id id - * @returns {any} exports - */ - const __webpack_require_module__ = (module, id) => { - const cached = moduleCache.get(module); - if (cached !== undefined) { - if (cached.error) throw cached.error; - return cached.exports; - } - const moduleObject = { - id, - exports: {}, - loaded: false, - error: undefined + + let cacheable = true; + + /** @type {ExecuteModuleContext} */ + const context = { + assets, + __webpack_require__: undefined, + chunk, + chunkGraph + }; + + // Prepare execution + asyncLib.eachLimit( + modules, + 10, + (module, callback) => { + const codeGenerationResult = codeGenerationResults.get( + module, + runtime + ); + /** @type {ExecuteModuleArgument} */ + const moduleArgument = { + module, + codeGenerationResult, + preparedInfo: undefined, + moduleObject: undefined }; - this.buildTimeExecutedModules.add(module); + moduleArgumentsMap.set(module, moduleArgument); + moduleArgumentsById.set(module.identifier(), moduleArgument); module.addCacheDependencies( fileDependencies, contextDependencies, missingDependencies, buildDependencies ); + if (module.buildInfo.cacheable === false) { + cacheable = false; + } if (module.buildInfo && module.buildInfo.assets) { const { assets: moduleAssets, assetsInfo } = module.buildInfo; for (const assetName of Object.keys(moduleAssets)) { @@ -4196,73 +4207,122 @@ This prevents using hashes of each other and should be avoided.`); }); } } + this.hooks.prepareModuleExecution.callAsync( + moduleArgument, + context, + callback + ); + }, + err => { + if (err) return callback(err); + + let exports; try { - moduleCache.set(module, moduleObject); - const codeGenerationResult = codeGenerationResults.get( - module, - runtime - ); - tryRunOrWebpackError( - () => - this.hooks.executeModule.call( - { - codeGenerationResult, - module, - moduleObject - }, - context - ), - "Compilation.hooks.executeModule" - ); - moduleObject.loaded = true; - return moduleObject.exports; - } catch (e) { - if (strictModuleExceptionHandling) { - moduleCache.delete(module); - } else if (strictModuleErrorHandling) { - moduleObject.error = e; + const { + strictModuleErrorHandling, + strictModuleExceptionHandling + } = this.outputOptions; + const __webpack_require__ = id => { + const cached = moduleCache[id]; + if (cached !== undefined) { + if (cached.error) throw cached.error; + return cached.exports; + } + const moduleArgument = moduleArgumentsById.get(id); + return __webpack_require_module__(moduleArgument, id); + }; + const interceptModuleExecution = (__webpack_require__[ + RuntimeGlobals.interceptModuleExecution.replace( + "__webpack_require__.", + "" + ) + ] = []); + const moduleCache = (__webpack_require__[ + RuntimeGlobals.moduleCache.replace( + "__webpack_require__.", + "" + ) + ] = {}); + + context.__webpack_require__ = __webpack_require__; + + /** + * @param {ExecuteModuleArgument} moduleArgument the module argument + * @param {string=} id id + * @returns {any} exports + */ + const __webpack_require_module__ = (moduleArgument, id) => { + var execOptions = { + id, + module: { + id, + exports: {}, + loaded: false, + error: undefined + }, + require: __webpack_require__ + }; + interceptModuleExecution.forEach(handler => + handler(execOptions) + ); + const module = moduleArgument.module; + this.buildTimeExecutedModules.add(module); + const moduleObject = execOptions.module; + moduleArgument.moduleObject = moduleObject; + try { + if (id) moduleCache[id] = moduleObject; + + tryRunOrWebpackError( + () => + this.hooks.executeModule.call( + moduleArgument, + context + ), + "Compilation.hooks.executeModule" + ); + moduleObject.loaded = true; + return moduleObject.exports; + } catch (e) { + if (strictModuleExceptionHandling) { + if (id) delete moduleCache[id]; + } else if (strictModuleErrorHandling) { + moduleObject.error = e; + } + if (!e.module) e.module = module; + throw e; + } + }; + + for (const runtimeModule of chunkGraph.getChunkRuntimeModulesInOrder( + chunk + )) { + __webpack_require_module__( + moduleArgumentsMap.get(runtimeModule) + ); } - if (!e.module) e.module = module; - throw e; + exports = __webpack_require__(module.identifier()); + } catch (e) { + const err = new WebpackError( + `Execution of module code from module graph (${module.readableIdentifier( + this.requestShortener + )}) failed: ${e.message}` + ); + err.stack = e.stack; + err.module = e.module; + return callback(err); } - }; - /** @type {ExecuteModuleContext} */ - const context = { - assets, - __webpack_require__, - chunk, - chunkGraph - }; - - for (const runtimeModule of chunkGraph.getChunkRuntimeModulesInOrder( - chunk - )) { - __webpack_require_module__( - runtimeModule, - runtimeModule.identifier() - ); + callback(null, { + exports, + assets, + cacheable, + fileDependencies, + contextDependencies, + missingDependencies, + buildDependencies + }); } - exports = __webpack_require__(module.identifier()); - } catch (e) { - const err = new WebpackError( - `Execution of module code from module graph (${module.readableIdentifier( - this.requestShortener - )}) failed: ${e.message}` - ); - err.stack = e.stack; - err.module = e.module; - return callback(err); - } - - callback(null, { - exports, - assets, - fileDependencies, - contextDependencies, - missingDependencies, - buildDependencies - }); + ); }); }); } diff --git a/lib/RuntimeModule.js b/lib/RuntimeModule.js index a0c21e287da..2788eda0f7d 100644 --- a/lib/RuntimeModule.js +++ b/lib/RuntimeModule.js @@ -41,6 +41,8 @@ class RuntimeModule extends Module { this.compilation = undefined; /** @type {Chunk} */ this.chunk = undefined; + /** @type {ChunkGraph} */ + this.chunkGraph = undefined; this.fullHash = false; /** @type {string} */ this._cachedGeneratedCode = undefined; @@ -49,11 +51,13 @@ class RuntimeModule extends Module { /** * @param {Compilation} compilation the compilation * @param {Chunk} chunk the chunk + * @param {ChunkGraph} chunkGraph the chunk graph * @returns {void} */ - attach(compilation, chunk) { + attach(compilation, chunk, chunkGraph = compilation.chunkGraph) { this.compilation = compilation; this.chunk = chunk; + this.chunkGraph = chunkGraph; } /** diff --git a/lib/asset/AssetModulesPlugin.js b/lib/asset/AssetModulesPlugin.js index e46aa945c30..d708b46f2fb 100644 --- a/lib/asset/AssetModulesPlugin.js +++ b/lib/asset/AssetModulesPlugin.js @@ -177,7 +177,7 @@ class AssetModulesPlugin { return result; }); - compilation.hooks.executeModule.tap( + compilation.hooks.prepareModuleExecution.tap( "AssetModulesPlugin", (options, context) => { const { codeGenerationResult } = options; diff --git a/lib/container/RemoteRuntimeModule.js b/lib/container/RemoteRuntimeModule.js index a1dc075439f..90948f2737c 100644 --- a/lib/container/RemoteRuntimeModule.js +++ b/lib/container/RemoteRuntimeModule.js @@ -20,7 +20,8 @@ class RemoteRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { runtimeTemplate, chunkGraph, moduleGraph } = this.compilation; + const { compilation, chunkGraph } = this; + const { runtimeTemplate, moduleGraph } = compilation; const chunkToRemotesMapping = {}; const idToExternalAndNameMapping = {}; for (const chunk of this.chunk.getAllAsyncChunks()) { diff --git a/lib/dependencies/LoaderPlugin.js b/lib/dependencies/LoaderPlugin.js index c0a3de7b35d..9386bb3efb7 100644 --- a/lib/dependencies/LoaderPlugin.js +++ b/lib/dependencies/LoaderPlugin.js @@ -214,6 +214,8 @@ class LoaderPlugin { for (const d of result.buildDependencies) { loaderContext.addBuildDependency(d); } + if (result.cacheable === false) + loaderContext.cacheable(false); for (const [name, { source, info }] of result.assets) { const { buildInfo } = loaderContext._module; if (!buildInfo.assets) { diff --git a/lib/javascript/JavascriptModulesPlugin.js b/lib/javascript/JavascriptModulesPlugin.js index 7b93fc878c6..2971f5b8a29 100644 --- a/lib/javascript/JavascriptModulesPlugin.js +++ b/lib/javascript/JavascriptModulesPlugin.js @@ -52,6 +52,17 @@ const chunkHasJs = (chunk, chunkGraph) => { : false; }; +const printGeneratedCodeForStack = (module, code) => { + const lines = code.split("\n"); + const n = `${lines.length}`.length; + return `\n\nGenerated code for ${module.identifier()}\n${lines + .map((line, i, lines) => { + const iStr = `${i + 1}`; + return `${" ".repeat(n - iStr.length)}${iStr} | ${line}`; + }) + .join("\n")}`; +}; + /** * @typedef {Object} RenderContext * @property {Chunk} chunk the chunk @@ -397,12 +408,17 @@ class JavascriptModulesPlugin { lineOffset: -1 } ); - fn.call( - moduleObject.exports, - moduleObject, - moduleObject.exports, - context.__webpack_require__ - ); + try { + fn.call( + moduleObject.exports, + moduleObject, + moduleObject.exports, + context.__webpack_require__ + ); + } catch (e) { + e.stack += printGeneratedCodeForStack(options.module, code); + throw e; + } } ); compilation.hooks.executeModule.tap( @@ -410,7 +426,8 @@ class JavascriptModulesPlugin { (options, context) => { const source = options.codeGenerationResult.sources.get("runtime"); if (source === undefined) return; - const code = source.source(); + let code = source.source(); + if (typeof code !== "string") code = code.toString(); const fn = vm.runInThisContext( `(function(__webpack_require__) {\n${code}\n/**/})`, @@ -419,7 +436,12 @@ class JavascriptModulesPlugin { lineOffset: -1 } ); - fn.call(null, context.__webpack_require__); + try { + fn.call(null, context.__webpack_require__); + } catch (e) { + e.stack += printGeneratedCodeForStack(options.module, code); + throw e; + } } ); } diff --git a/lib/node/ReadFileChunkLoadingRuntimeModule.js b/lib/node/ReadFileChunkLoadingRuntimeModule.js index 827b689248b..97492b93981 100644 --- a/lib/node/ReadFileChunkLoadingRuntimeModule.js +++ b/lib/node/ReadFileChunkLoadingRuntimeModule.js @@ -25,8 +25,8 @@ class ReadFileChunkLoadingRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { chunk } = this; - const { chunkGraph, runtimeTemplate } = this.compilation; + const { chunkGraph, chunk } = this; + const { runtimeTemplate } = this.compilation; const fn = RuntimeGlobals.ensureChunkHandlers; const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI); const withExternalInstallChunk = this.runtimeRequirements.has( diff --git a/lib/node/RequireChunkLoadingRuntimeModule.js b/lib/node/RequireChunkLoadingRuntimeModule.js index 2e7ca9213f8..699d9d0051b 100644 --- a/lib/node/RequireChunkLoadingRuntimeModule.js +++ b/lib/node/RequireChunkLoadingRuntimeModule.js @@ -25,8 +25,8 @@ class RequireChunkLoadingRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { chunk } = this; - const { chunkGraph, runtimeTemplate } = this.compilation; + const { chunkGraph, chunk } = this; + const { runtimeTemplate } = this.compilation; const fn = RuntimeGlobals.ensureChunkHandlers; const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI); const withExternalInstallChunk = this.runtimeRequirements.has( diff --git a/lib/runtime/CompatRuntimeModule.js b/lib/runtime/CompatRuntimeModule.js index d4c627d1586..ed9d9aff984 100644 --- a/lib/runtime/CompatRuntimeModule.js +++ b/lib/runtime/CompatRuntimeModule.js @@ -19,9 +19,8 @@ class CompatRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { chunk, compilation } = this; + const { chunkGraph, chunk, compilation } = this; const { - chunkGraph, runtimeTemplate, mainTemplate, moduleTemplates, diff --git a/lib/runtime/GetChunkFilenameRuntimeModule.js b/lib/runtime/GetChunkFilenameRuntimeModule.js index 203c296f2af..632407264d3 100644 --- a/lib/runtime/GetChunkFilenameRuntimeModule.js +++ b/lib/runtime/GetChunkFilenameRuntimeModule.js @@ -39,6 +39,7 @@ class GetChunkFilenameRuntimeModule extends RuntimeModule { const { global, chunk, + chunkGraph, contentType, getFilenameForChunk, allChunks, @@ -90,12 +91,12 @@ class GetChunkFilenameRuntimeModule extends RuntimeModule { for (const c of chunk.getAllAsyncChunks()) { addChunk(c); } - const includeEntries = compilation.chunkGraph + const includeEntries = chunkGraph .getTreeRuntimeRequirements(chunk) .has(RuntimeGlobals.ensureChunkIncludeEntries); if (includeEntries) { includedChunksMessages.push("sibling chunks for the entrypoint"); - for (const c of compilation.chunkGraph.getChunkEntryDependentChunksIterable( + for (const c of chunkGraph.getChunkEntryDependentChunksIterable( chunk )) { addChunk(c); diff --git a/lib/runtime/RuntimeIdRuntimeModule.js b/lib/runtime/RuntimeIdRuntimeModule.js index 7e046eb6e8f..ca2313c7de5 100644 --- a/lib/runtime/RuntimeIdRuntimeModule.js +++ b/lib/runtime/RuntimeIdRuntimeModule.js @@ -16,8 +16,7 @@ class RuntimeIdRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { chunk, compilation } = this; - const { chunkGraph } = compilation; + const { chunkGraph, chunk } = this; const runtime = chunk.runtime; if (typeof runtime !== "string") throw new Error("RuntimeIdRuntimeModule must be in a single runtime"); diff --git a/lib/runtime/StartupChunkDependenciesRuntimeModule.js b/lib/runtime/StartupChunkDependenciesRuntimeModule.js index efcc1c22b33..5097b4d681f 100644 --- a/lib/runtime/StartupChunkDependenciesRuntimeModule.js +++ b/lib/runtime/StartupChunkDependenciesRuntimeModule.js @@ -19,8 +19,8 @@ class StartupChunkDependenciesRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { chunk, compilation } = this; - const { chunkGraph, runtimeTemplate } = compilation; + const { chunkGraph, chunk, compilation } = this; + const { runtimeTemplate } = compilation; const chunkIds = Array.from( chunkGraph.getChunkEntryDependentChunksIterable(chunk) ).map(chunk => { diff --git a/lib/sharing/ConsumeSharedRuntimeModule.js b/lib/sharing/ConsumeSharedRuntimeModule.js index 3dbf4f3f3e4..180c1ae0a9c 100644 --- a/lib/sharing/ConsumeSharedRuntimeModule.js +++ b/lib/sharing/ConsumeSharedRuntimeModule.js @@ -30,11 +30,8 @@ class ConsumeSharedRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { - runtimeTemplate, - chunkGraph, - codeGenerationResults - } = this.compilation; + const { compilation, chunkGraph } = this; + const { runtimeTemplate, codeGenerationResults } = compilation; const chunkToModuleMapping = {}; /** @type {Map} */ const moduleIdToSourceMapping = new Map(); diff --git a/lib/sharing/ShareRuntimeModule.js b/lib/sharing/ShareRuntimeModule.js index d5a36b5d338..3d9f7bd5d84 100644 --- a/lib/sharing/ShareRuntimeModule.js +++ b/lib/sharing/ShareRuntimeModule.js @@ -22,12 +22,12 @@ class ShareRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { + const { compilation, chunkGraph } = this; const { runtimeTemplate, - chunkGraph, codeGenerationResults, outputOptions: { uniqueName } - } = this.compilation; + } = compilation; /** @type {Map>>} */ const initCodePerScope = new Map(); for (const chunk of this.chunk.getAllReferencedChunks()) { diff --git a/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js b/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js index f55f917f47c..274251babea 100644 --- a/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +++ b/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js @@ -200,8 +200,8 @@ class WasmChunkLoadingRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { compilation, chunk, mangleImports } = this; - const { chunkGraph, moduleGraph, outputOptions } = compilation; + const { chunkGraph, compilation, chunk, mangleImports } = this; + const { moduleGraph, outputOptions } = compilation; const fn = RuntimeGlobals.ensureChunkHandlers; const wasmModules = getAllWasmModules(moduleGraph, chunkGraph, chunk); const declarations = []; diff --git a/lib/web/JsonpChunkLoadingRuntimeModule.js b/lib/web/JsonpChunkLoadingRuntimeModule.js index 726c00f84d3..a19c413a81a 100644 --- a/lib/web/JsonpChunkLoadingRuntimeModule.js +++ b/lib/web/JsonpChunkLoadingRuntimeModule.js @@ -55,10 +55,9 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule { * @returns {string} runtime code */ generate() { - const { compilation, chunk } = this; + const { chunkGraph, compilation, chunk } = this; const { runtimeTemplate, - chunkGraph, outputOptions: { globalObject, chunkLoadingGlobal, diff --git a/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js b/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js index c43ed5c742d..77c26863b10 100644 --- a/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +++ b/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js @@ -27,8 +27,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { generate() { const { chunk, + chunkGraph, compilation: { - chunkGraph, runtimeTemplate, outputOptions: { globalObject, chunkLoadingGlobal, hotUpdateGlobal } } diff --git a/test/hotCases/loader-import-module/css/colors.js b/test/hotCases/loader-import-module/css/colors.js new file mode 100644 index 00000000000..61cfd6f0cbb --- /dev/null +++ b/test/hotCases/loader-import-module/css/colors.js @@ -0,0 +1,5 @@ +export const color = "#f00"; +--- +export const color = "#0f0"; +--- +export const color = "#0f0"; diff --git a/test/hotCases/loader-import-module/css/file.jpg b/test/hotCases/loader-import-module/css/file.jpg new file mode 100644 index 00000000000..fe5c6eefa79 Binary files /dev/null and b/test/hotCases/loader-import-module/css/file.jpg differ diff --git a/test/hotCases/loader-import-module/css/file.png b/test/hotCases/loader-import-module/css/file.png new file mode 100644 index 00000000000..fb53b9dedd3 Binary files /dev/null and b/test/hotCases/loader-import-module/css/file.png differ diff --git a/test/hotCases/loader-import-module/css/index.js b/test/hotCases/loader-import-module/css/index.js new file mode 100644 index 00000000000..1383ac03598 --- /dev/null +++ b/test/hotCases/loader-import-module/css/index.js @@ -0,0 +1,26 @@ +import stylesheet from "./stylesheet.css.js"; + +it("should be able to use build-time code with HMR", done => { + expect(stylesheet).toBe( + 'body { background: url("https://test.cases/path/assets/file.png"); color: #f00; }' + ); + NEXT( + require("../../update")(done, true, stats => { + expect(stylesheet).toBe( + 'body { background: url("https://test.cases/path/assets/file.png"); color: #0f0; }' + ); + NEXT( + require("../../update")(done, true, stats => { + expect(stylesheet).toBe( + 'body { background: url("https://test.cases/path/assets/file.jpg"); color: #0f0; }' + ); + done(); + }) + ); + }) + ); +}); + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept("./stylesheet.css.js"); +} diff --git a/test/hotCases/loader-import-module/css/loader.js b/test/hotCases/loader-import-module/css/loader.js new file mode 100644 index 00000000000..927bbc669c6 --- /dev/null +++ b/test/hotCases/loader-import-module/css/loader.js @@ -0,0 +1,6 @@ +exports.pitch = async function (remaining) { + const result = await this.importModule( + this.resourcePath + ".webpack[javascript/auto]" + "!=!" + remaining + ); + return (result.default || result)(); +}; diff --git a/test/hotCases/loader-import-module/css/stylesheet.css.js b/test/hotCases/loader-import-module/css/stylesheet.css.js new file mode 100644 index 00000000000..dec22b62b3c --- /dev/null +++ b/test/hotCases/loader-import-module/css/stylesheet.css.js @@ -0,0 +1,29 @@ +import { color } from "./colors.js"; +export default () => + `body { background: url("${ + new URL("./file.png", import.meta.url).href + }"); color: ${color}; }`; + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept("./colors.js"); +} +--- +import { color } from "./colors.js"; +export default () => + `body { background: url("${ + new URL("./file.png", import.meta.url).href + }"); color: ${color}; }`; + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept("./colors.js"); +} +--- +import { color } from "./colors.js"; +export default () => + `body { background: url("${ + new URL("./file.jpg", import.meta.url).href + }"); color: ${color}; }`; + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept("./colors.js"); +} diff --git a/test/hotCases/loader-import-module/css/webpack.config.js b/test/hotCases/loader-import-module/css/webpack.config.js new file mode 100644 index 00000000000..d142ebdf35b --- /dev/null +++ b/test/hotCases/loader-import-module/css/webpack.config.js @@ -0,0 +1,39 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + module: { + generator: { + asset: { + filename: "assets/[name][ext]" + } + }, + rules: [ + { + test: /\.css\.js$/, + use: "./loader", + type: "asset/source" + } + ] + }, + experiments: { + executeModule: true + }, + plugins: [ + compiler => + compiler.hooks.done.tap("test case", stats => { + const png = stats.compilation.getAsset("assets/file.png"); + const jpg = stats.compilation.getAsset("assets/file.jpg"); + if (png) { + expect(jpg).toBe(undefined); + expect(png).toHaveProperty( + "info", + expect.objectContaining({ sourceFilename: "file.png" }) + ); + } else { + expect(jpg).toHaveProperty( + "info", + expect.objectContaining({ sourceFilename: "file.jpg" }) + ); + } + }) + ] +}; diff --git a/types.d.ts b/types.d.ts index 54414e647da..476139698cf 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1251,6 +1251,9 @@ declare class Compilation { [(string[] | ReferencedExport)[], Dependency, RuntimeSpec] >; executeModule: SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>; + prepareModuleExecution: AsyncParallelHook< + [ExecuteModuleArgument, ExecuteModuleContext] + >; finishModules: AsyncSeriesHook<[Iterable]>; finishRebuildingModule: AsyncSeriesHook<[Module]>; unseal: SyncHook<[]>; @@ -3137,20 +3140,22 @@ declare class EvalSourceMapDevToolPlugin { } declare interface ExecuteModuleArgument { module: Module; - moduleObject: object; + moduleObject?: { id: string; exports: any; loaded: boolean }; + preparedInfo: any; codeGenerationResult: CodeGenerationResult; } declare interface ExecuteModuleContext { assets: Map; chunk: Chunk; chunkGraph: ChunkGraph; - __webpack_require__: Function; + __webpack_require__?: (arg0: string) => any; } declare interface ExecuteModuleOptions { entryOptions?: EntryOptions; } declare interface ExecuteModuleResult { exports: any; + cacheable: boolean; assets: Map; fileDependencies: LazySet; contextDependencies: LazySet; @@ -9358,8 +9363,9 @@ declare class RuntimeModule extends Module { stage: number; compilation: Compilation; chunk: Chunk; + chunkGraph: ChunkGraph; fullHash: boolean; - attach(compilation: Compilation, chunk: Chunk): void; + attach(compilation: Compilation, chunk: Chunk, chunkGraph?: ChunkGraph): void; generate(): string; getGeneratedCode(): string; shouldIsolate(): boolean;