From df18cf4b9ff33b096a5ca094bea537b00c06add5 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 10 Mar 2023 11:31:53 +0100 Subject: [PATCH 1/2] fix HMR when module evaluation throws update snapshots --- crates/turbopack-ecmascript/js/src/runtime.js | 48 ++++++++++++++++--- ...ot_basic_async_chunk_input_index_580957.js | 48 ++++++++++++++++--- ...ot_basic_async_chunk_input_index_da3760.js | 48 ++++++++++++++++--- ...apshot_basic_chunked_input_index_1d9ecb.js | 48 ++++++++++++++++--- ...apshot_basic_chunked_input_index_e8535e.js | 48 ++++++++++++++++--- ...apshot_basic_shebang_input_index_718090.js | 48 ++++++++++++++++--- ...apshot_basic_shebang_input_index_a8bbcc.js | 48 ++++++++++++++++--- ...shot_comptime_define_input_index_68d56d.js | 48 ++++++++++++++++--- ...shot_comptime_define_input_index_f522f3.js | 48 ++++++++++++++++--- ..._absolute-uri-import_input_index_24fbf6.js | 48 ++++++++++++++++--- ..._absolute-uri-import_input_index_73a15e.js | 48 ++++++++++++++++--- ...sts_snapshot_css_css_input_index_47659c.js | 48 ++++++++++++++++--- ...sts_snapshot_css_css_input_index_c93332.js | 48 ++++++++++++++++--- ...shot_emotion_emotion_input_index_549658.js | 48 ++++++++++++++++--- ...shot_emotion_emotion_input_index_d9fc1b.js | 48 ++++++++++++++++--- ...s_tests_snapshot_env_env_input_93aa94._.js | 48 ++++++++++++++++--- ...s_tests_snapshot_env_env_input_bb8ee7._.js | 48 ++++++++++++++++--- ...entrry_runtime_entry_input_index_09dc6c.js | 48 ++++++++++++++++--- ...entrry_runtime_entry_input_index_19edf2.js | 48 ++++++++++++++++--- ...shot_example_example_input_index_0a08ce.js | 48 ++++++++++++++++--- ...shot_example_example_input_index_29e0b9.js | 48 ++++++++++++++++--- ...ot_export-alls_cjs-2_input_index_d5f22a.js | 48 ++++++++++++++++--- ...ot_export-alls_cjs-2_input_index_d9c332.js | 48 ++++++++++++++++--- ...port-alls_cjs-script_input_index_b23628.js | 48 ++++++++++++++++--- ...port-alls_cjs-script_input_index_ed5b85.js | 48 ++++++++++++++++--- ...shot_import-meta_cjs_input_index_3e3da0.js | 48 ++++++++++++++++--- ...shot_import-meta_cjs_input_index_746b39.js | 48 ++++++++++++++++--- ...rt-meta_esm-multiple_input_index_090618.js | 48 ++++++++++++++++--- ...rt-meta_esm-multiple_input_index_8687d1.js | 48 ++++++++++++++++--- ...ort-meta_esm-mutable_input_index_3487d2.js | 48 ++++++++++++++++--- ...ort-meta_esm-mutable_input_index_dc37f8.js | 48 ++++++++++++++++--- ...port-meta_esm-object_input_index_040a52.js | 48 ++++++++++++++++--- ...port-meta_esm-object_input_index_73c2df.js | 48 ++++++++++++++++--- ...shot_import-meta_esm_input_index_c633a8.js | 48 ++++++++++++++++--- ...shot_import-meta_esm_input_index_fe8e61.js | 48 ++++++++++++++++--- ...shot_import-meta_url_input_index_5f69bf.js | 48 ++++++++++++++++--- ...shot_import-meta_url_input_index_f01bae.js | 48 ++++++++++++++++--- ...shot_imports_dynamic_input_index_56419a.js | 48 ++++++++++++++++--- ...shot_imports_dynamic_input_index_6f9eb3.js | 48 ++++++++++++++++--- ...napshot_imports_json_input_index_881b1b.js | 48 ++++++++++++++++--- ...napshot_imports_json_input_index_e460e9.js | 48 ++++++++++++++++--- ...ts_resolve_error_cjs_input_index_707ee1.js | 48 ++++++++++++++++--- ...ts_resolve_error_cjs_input_index_fb56eb.js | 48 ++++++++++++++++--- ...ts_resolve_error_esm_input_index_af6491.js | 48 ++++++++++++++++--- ...ts_resolve_error_esm_input_index_ee6078.js | 48 ++++++++++++++++--- ...s_static-and-dynamic_input_index_507785.js | 48 ++++++++++++++++--- ...s_static-and-dynamic_input_index_899ad5.js | 48 ++++++++++++++++--- ...pshot_imports_static_input_index_82c953.js | 48 ++++++++++++++++--- ...pshot_imports_static_input_index_9fc270.js | 48 ++++++++++++++++--- ...de_protocol_external_input_index_4e764f.js | 48 ++++++++++++++++--- ...de_protocol_external_input_index_69be78.js | 48 ++++++++++++++++--- ...ts_styled_components_input_index_01a621.js | 48 ++++++++++++++++--- ...ts_styled_components_input_index_0496ed.js | 48 ++++++++++++++++--- ...nsforms_input_packages_app_index_2a30e9.js | 48 ++++++++++++++++--- ...nsforms_input_packages_app_index_38ad57.js | 48 ++++++++++++++++--- ...ransforms_preset_env_input_index_097653.js | 48 ++++++++++++++++--- ...ransforms_preset_env_input_index_62f043.js | 48 ++++++++++++++++--- ...ipt_jsconfig-baseurl_input_index_18c34e.js | 48 ++++++++++++++++--- ...ipt_jsconfig-baseurl_input_index_3d21b6.js | 48 ++++++++++++++++--- ...sconfig-baseurl_input_index.ts_540b0c._.js | 48 ++++++++++++++++--- ...sconfig-baseurl_input_index.ts_814f4c._.js | 48 ++++++++++++++++--- 61 files changed, 2501 insertions(+), 427 deletions(-) diff --git a/crates/turbopack-ecmascript/js/src/runtime.js b/crates/turbopack-ecmascript/js/src/runtime.js index c3a79272dcc73..d101d27515ed6 100644 --- a/crates/turbopack-ecmascript/js/src/runtime.js +++ b/crates/turbopack-ecmascript/js/src/runtime.js @@ -311,9 +311,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -344,7 +345,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -618,6 +619,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -628,8 +630,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -660,7 +671,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -682,6 +692,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -696,8 +707,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -710,7 +726,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -811,8 +832,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1213,6 +1241,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1221,8 +1251,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js index e0b0b4edce2c7..f479196c8411f 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js +++ b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js @@ -440,9 +440,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -473,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -747,6 +748,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -757,8 +759,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -789,7 +800,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -811,6 +821,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -825,8 +836,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -839,7 +855,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -940,8 +961,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1342,6 +1370,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1350,8 +1380,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js index 9fdda376a123b..b12bc23e18949 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js +++ b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js @@ -440,9 +440,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -473,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -747,6 +748,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -757,8 +759,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -789,7 +800,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -811,6 +821,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -825,8 +836,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -839,7 +855,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -940,8 +961,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1342,6 +1370,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1350,8 +1380,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js index 87538d9dfddf4..39b7308cfaa22 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js +++ b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js @@ -427,9 +427,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -460,7 +461,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -734,6 +735,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -744,8 +746,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -776,7 +787,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -798,6 +808,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -812,8 +823,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -826,7 +842,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -927,8 +948,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1329,6 +1357,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1337,8 +1367,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js index 5c8ce976d6d6e..4a52bc0950890 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js +++ b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js @@ -427,9 +427,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -460,7 +461,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -734,6 +735,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -744,8 +746,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -776,7 +787,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -798,6 +808,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -812,8 +823,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -826,7 +842,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -927,8 +948,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1329,6 +1357,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1337,8 +1367,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js index 2f0b46a068428..df6fc428d01fe 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js +++ b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js @@ -427,9 +427,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -460,7 +461,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -734,6 +735,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -744,8 +746,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -776,7 +787,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -798,6 +808,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -812,8 +823,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -826,7 +842,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -927,8 +948,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1329,6 +1357,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1337,8 +1367,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js index 859359be317e8..f86c776d5bcf6 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js +++ b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js @@ -427,9 +427,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -460,7 +461,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -734,6 +735,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -744,8 +746,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -776,7 +787,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -798,6 +808,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -812,8 +823,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -826,7 +842,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -927,8 +948,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1329,6 +1357,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1337,8 +1367,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js index caa4327b5aaa7..8a30b74499b32 100644 --- a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js +++ b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js @@ -448,9 +448,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -481,7 +482,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -755,6 +756,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -765,8 +767,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -797,7 +808,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -819,6 +829,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -833,8 +844,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -847,7 +863,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -948,8 +969,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1350,6 +1378,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1358,8 +1388,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js index 8c6e09f077940..04fc3fda5b6ed 100644 --- a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js +++ b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js @@ -448,9 +448,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -481,7 +482,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -755,6 +756,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -765,8 +767,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -797,7 +808,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -819,6 +829,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -833,8 +844,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -847,7 +863,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -948,8 +969,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1350,6 +1378,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1358,8 +1388,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js index 57fabe450701b..08e72cd88a1e0 100644 --- a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js +++ b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js index f8e58e7d4359c..3aa14ded22c01 100644 --- a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js +++ b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js index 921e31bf882d5..f78ef11588d51 100644 --- a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js +++ b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js @@ -442,9 +442,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -475,7 +476,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -749,6 +750,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -759,8 +761,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -791,7 +802,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -813,6 +823,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -827,8 +838,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -841,7 +857,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -942,8 +963,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1344,6 +1372,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1352,8 +1382,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js index fc083bccefb2a..e8964e7f2c413 100644 --- a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js +++ b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js @@ -442,9 +442,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -475,7 +476,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -749,6 +750,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -759,8 +761,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -791,7 +802,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -813,6 +823,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -827,8 +838,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -841,7 +857,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -942,8 +963,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1344,6 +1372,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1352,8 +1382,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js index de4295390dbd8..f925e2cc8afae 100644 --- a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js +++ b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js @@ -447,9 +447,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -480,7 +481,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -754,6 +755,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -764,8 +766,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -796,7 +807,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -818,6 +828,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -832,8 +843,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -846,7 +862,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -947,8 +968,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1349,6 +1377,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1357,8 +1387,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js index e14fe60721bea..5b9cb1c1c80a3 100644 --- a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js +++ b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js @@ -447,9 +447,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -480,7 +481,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -754,6 +755,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -764,8 +766,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -796,7 +807,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -818,6 +828,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -832,8 +843,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -846,7 +862,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -947,8 +968,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1349,6 +1377,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1357,8 +1387,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js index 12a7d91bc63cb..e6996bc9def11 100644 --- a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js +++ b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js @@ -433,9 +433,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -466,7 +467,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -740,6 +741,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -750,8 +752,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -782,7 +793,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -804,6 +814,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -818,8 +829,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -832,7 +848,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -933,8 +954,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1335,6 +1363,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1343,8 +1373,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js index 705d5236fb2de..cb6be6f6470de 100644 --- a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js +++ b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js @@ -433,9 +433,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -466,7 +467,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -740,6 +741,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -750,8 +752,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -782,7 +793,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -804,6 +814,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -818,8 +829,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -832,7 +848,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -933,8 +954,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1335,6 +1363,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1343,8 +1373,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js index f164e68f00b3a..8a1e0dc1d7a0a 100644 --- a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js +++ b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js index 5d08d899032d8..b8e1a95735053 100644 --- a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js +++ b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js index f455e8348de0c..c513de175d79d 100644 --- a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js +++ b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js index 24df4ed2ffeae..940b23991e4f8 100644 --- a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js +++ b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js @@ -424,9 +424,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -457,7 +458,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -731,6 +732,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -741,8 +743,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -773,7 +784,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -795,6 +805,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -809,8 +820,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -823,7 +839,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -924,8 +945,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1326,6 +1354,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1334,8 +1364,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js index 7f2e473ad04b4..64227a483f557 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js @@ -450,9 +450,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -483,7 +484,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -757,6 +758,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -767,8 +769,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -799,7 +810,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -821,6 +831,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -835,8 +846,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -849,7 +865,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -950,8 +971,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1352,6 +1380,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1360,8 +1390,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js index 80ebccc733b25..89db45d9eb64e 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js @@ -450,9 +450,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -483,7 +484,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -757,6 +758,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -767,8 +769,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -799,7 +810,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -821,6 +831,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -835,8 +846,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -849,7 +865,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -950,8 +971,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1352,6 +1380,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1360,8 +1390,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js index c6a9c61c81555..50135da3dc01d 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js @@ -445,9 +445,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -478,7 +479,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -752,6 +753,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -762,8 +764,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -794,7 +805,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -816,6 +826,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -830,8 +841,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -844,7 +860,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -945,8 +966,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1347,6 +1375,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1355,8 +1385,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js index 427ad1af3099c..6d7b95dbd8543 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js @@ -445,9 +445,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -478,7 +479,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -752,6 +753,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -762,8 +764,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -794,7 +805,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -816,6 +826,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -830,8 +841,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -844,7 +860,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -945,8 +966,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1347,6 +1375,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1355,8 +1385,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js index feb312b7a39ea..29c04156d2236 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js index 4b26fe5e75923..9c579f30b0dec 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js index 3fa1e091e0624..39389a08c6730 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js @@ -442,9 +442,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -475,7 +476,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -749,6 +750,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -759,8 +761,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -791,7 +802,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -813,6 +823,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -827,8 +838,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -841,7 +857,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -942,8 +963,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1344,6 +1372,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1352,8 +1382,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js index 66c8598f008a0..90ea5026d2e72 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js @@ -442,9 +442,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -475,7 +476,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -749,6 +750,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -759,8 +761,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -791,7 +802,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -813,6 +823,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -827,8 +838,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -841,7 +857,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -942,8 +963,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1344,6 +1372,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1352,8 +1382,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js index 36b2b517b8d5d..01140b60a357a 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js index a7ba9eda1bf26..c477a547f8350 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js index 2edbd0f88b040..b6f75f54c50c3 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js index efd0eaed44f06..ada65911045c1 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js index eccf4fc573d09..015512eac94ec 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js index e1cbbc1ecc5de..1a58ae8dcc085 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js @@ -435,9 +435,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -468,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -742,6 +743,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -752,8 +754,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -784,7 +795,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -806,6 +816,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -820,8 +831,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -834,7 +850,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -935,8 +956,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1337,6 +1365,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1345,8 +1375,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js index 1ef6e29879a30..5344c900a6bcf 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js @@ -441,9 +441,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -474,7 +475,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -748,6 +749,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -758,8 +760,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -790,7 +801,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -812,6 +822,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -826,8 +837,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -840,7 +856,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -941,8 +962,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1343,6 +1371,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1351,8 +1381,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js index 17a3d628c9ef7..92df15fec15e1 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js @@ -441,9 +441,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -474,7 +475,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -748,6 +749,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -758,8 +760,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -790,7 +801,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -812,6 +822,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -826,8 +837,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -840,7 +856,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -941,8 +962,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1343,6 +1371,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1351,8 +1381,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js index 64bd0e4b685ea..9c7f79f3e9126 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js +++ b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js @@ -438,9 +438,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -471,7 +472,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -745,6 +746,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -755,8 +757,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -787,7 +798,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -809,6 +819,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -823,8 +834,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -837,7 +853,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -938,8 +959,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1340,6 +1368,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1348,8 +1378,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js index 6780519082913..1bffa2ffa30d1 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js +++ b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js @@ -438,9 +438,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -471,7 +472,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -745,6 +746,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -755,8 +757,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -787,7 +798,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -809,6 +819,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -823,8 +834,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -837,7 +853,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -938,8 +959,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1340,6 +1368,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1348,8 +1378,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js index 9854d9a96d405..726bdf1e3652a 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js +++ b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js index 5a953d3117a30..2746b4bea7834 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js +++ b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js index 998ffda23838d..2ca35fccd8615 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js @@ -429,9 +429,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -462,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -736,6 +737,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -746,8 +748,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -778,7 +789,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -800,6 +810,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -814,8 +825,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -828,7 +844,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -929,8 +950,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1331,6 +1359,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1339,8 +1369,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js index 751eb513a337a..7640ea84442b2 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js @@ -429,9 +429,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -462,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -736,6 +737,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -746,8 +748,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -778,7 +789,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -800,6 +810,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -814,8 +825,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -828,7 +844,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -929,8 +950,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1331,6 +1359,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1339,8 +1369,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js index fbd972f8eaf33..3b2415a92fc9c 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js @@ -432,9 +432,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -465,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -739,6 +740,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -749,8 +751,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -781,7 +792,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -803,6 +813,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -817,8 +828,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -831,7 +847,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -932,8 +953,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1334,6 +1362,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1342,8 +1372,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js index 0bd9d2b984d05..c17b2bdfe2779 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js @@ -432,9 +432,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -465,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -739,6 +740,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -749,8 +751,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -781,7 +792,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -803,6 +813,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -817,8 +828,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -831,7 +847,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -932,8 +953,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1334,6 +1362,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1342,8 +1372,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js index 50974ed6615cc..c810ca4e03b8d 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js @@ -450,9 +450,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -483,7 +484,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -757,6 +758,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -767,8 +769,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -799,7 +810,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -821,6 +831,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -835,8 +846,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -849,7 +865,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -950,8 +971,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1352,6 +1380,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1360,8 +1390,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js index d535883079762..d334829253b8f 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js @@ -450,9 +450,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -483,7 +484,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -757,6 +758,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -767,8 +769,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -799,7 +810,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -821,6 +831,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -835,8 +846,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -849,7 +865,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -950,8 +971,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1352,6 +1380,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1360,8 +1390,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js index e04824a94c61e..5b6007f10bf9e 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js @@ -431,9 +431,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -464,7 +465,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -738,6 +739,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -748,8 +750,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -780,7 +791,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -802,6 +812,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -816,8 +827,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -830,7 +846,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -931,8 +952,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1333,6 +1361,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1341,8 +1371,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js index 302f4069e0977..44a826d909e99 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js @@ -431,9 +431,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -464,7 +465,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -738,6 +739,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -748,8 +750,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -780,7 +791,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -802,6 +812,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -816,8 +827,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -830,7 +846,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -931,8 +952,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1333,6 +1361,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1341,8 +1371,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js index d45d4e066208d..ae054e511c05d 100644 --- a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js +++ b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js @@ -426,9 +426,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -459,7 +460,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -733,6 +734,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -743,8 +745,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -775,7 +786,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -797,6 +807,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -811,8 +822,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -825,7 +841,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -926,8 +947,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1328,6 +1356,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1336,8 +1366,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js index e9dc42858f45e..cd0e943fc46fa 100644 --- a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js +++ b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js @@ -426,9 +426,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -459,7 +460,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -733,6 +734,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -743,8 +745,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -775,7 +786,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -797,6 +807,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -811,8 +822,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -825,7 +841,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -926,8 +947,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1328,6 +1356,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1336,8 +1366,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js index 21dac7303a59e..d895c6e5a344e 100644 --- a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js +++ b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js @@ -433,9 +433,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -466,7 +467,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -740,6 +741,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -750,8 +752,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -782,7 +793,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -804,6 +814,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -818,8 +829,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -832,7 +848,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -933,8 +954,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1335,6 +1363,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1343,8 +1373,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js index 5885493148e28..39f0439d86b25 100644 --- a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js +++ b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js @@ -433,9 +433,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -466,7 +467,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -740,6 +741,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -750,8 +752,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -782,7 +793,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -804,6 +814,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -818,8 +829,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -832,7 +848,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -933,8 +954,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1335,6 +1363,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1343,8 +1373,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js index 7a6da67f32bab..704649a86267a 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js @@ -429,9 +429,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -462,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -736,6 +737,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -746,8 +748,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -778,7 +789,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -800,6 +810,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -814,8 +825,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -828,7 +844,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -929,8 +950,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1331,6 +1359,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1339,8 +1369,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js index 814ac88c0b810..a2a86e7eb6074 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js @@ -429,9 +429,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -462,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -736,6 +737,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -746,8 +748,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -778,7 +789,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -800,6 +810,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -814,8 +825,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -828,7 +844,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -929,8 +950,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1331,6 +1359,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1339,8 +1369,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js index f115ae0243411..8f46cc494685a 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js @@ -432,9 +432,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -465,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -739,6 +740,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -749,8 +751,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -781,7 +792,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -803,6 +813,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -817,8 +828,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -831,7 +847,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -932,8 +953,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1334,6 +1362,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1342,8 +1372,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js index 6a3d444ee3e44..2d798c7aea063 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js @@ -432,9 +432,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -465,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -739,6 +740,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -749,8 +751,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -781,7 +792,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -803,6 +813,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -817,8 +828,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -831,7 +847,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -932,8 +953,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1334,6 +1362,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1342,8 +1372,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js index 179cdd95badb6..6a994d4c0a7b6 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js index 41f37160094ff..3e460189d7766 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js index 656451ea1381c..3d958ffa02c31 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } diff --git a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js index fdd957f8758c5..bd4d55f251f9a 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js @@ -439,9 +439,10 @@ const SourceType = { * @param {ModuleId} id * @param {SourceType} sourceType * @param {ModuleId} [sourceId] + * @param {ModuleId[]} [parents] * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId) { +function instantiateModule(id, sourceType, sourceId, parents) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, @@ -472,7 +473,7 @@ function instantiateModule(id, sourceType, sourceId) { exports: {}, loaded: false, id, - parents: [], + parents: parents || [], children: [], interopNamespace: undefined, hot, @@ -746,6 +747,7 @@ function updateChunksPhase(chunksAddedModules, chunksDeletedModules) { /** * @param {Iterable} outdatedModules * @param {Set} disposedModules + * @return {{ outdatedModuleParents: Map> }} */ function disposePhase(outdatedModules, disposedModules) { for (const moduleId of outdatedModules) { @@ -756,8 +758,17 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + const outdatedModuleParents = new Map(); + for (const moduleId of outdatedModules) { + const oldModule = moduleCache[moduleId]; + outdatedModuleParents.set(moduleId, oldModule?.parents); + delete moduleCache[moduleId]; + } + // TODO(alexkirsz) Dependencies: remove outdated dependency from module // children. + + return { outdatedModuleParents }; } /** @@ -788,7 +799,6 @@ function disposeModule(moduleId, mode) { // module is still importing other modules. module.hot.active = false; - delete moduleCache[module.id]; moduleHotState.delete(module); // TODO(alexkirsz) Dependencies: delete the module from outdated deps. @@ -810,6 +820,7 @@ function disposeModule(moduleId, mode) { switch (mode) { case "clear": + delete moduleCache[module.id]; moduleHotData.delete(module.id); break; case "replace": @@ -824,8 +835,13 @@ function disposeModule(moduleId, mode) { * * @param {{ moduleId: ModuleId, errorHandler: true | Function }[]} outdatedSelfAcceptedModules * @param {Map} newModuleFactories + * @param {Map>} outdatedModuleParents */ -function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { +function applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents +) { // Update module factories. for (const [moduleId, factory] of newModuleFactories.entries()) { moduleFactories[moduleId] = factory; @@ -838,7 +854,12 @@ function applyPhase(outdatedSelfAcceptedModules, newModuleFactories) { // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule(moduleId, SourceType.Update); + instantiateModule( + moduleId, + SourceType.Update, + undefined, + outdatedModuleParents.get(moduleId) + ); } catch (err) { if (typeof errorHandler === "function") { try { @@ -939,8 +960,15 @@ function applyEcmascriptMergedUpdate(chunkPath, update) { const outdatedSelfAcceptedModules = computeOutdatedSelfAcceptedModules(outdatedModules); const { disposedModules } = updateChunksPhase(chunksAdded, chunksDeleted); - disposePhase(outdatedModules, disposedModules); - applyPhase(outdatedSelfAcceptedModules, newModuleFactories); + const { outdatedModuleParents } = disposePhase( + outdatedModules, + disposedModules + ); + applyPhase( + outdatedSelfAcceptedModules, + newModuleFactories, + outdatedModuleParents + ); } /** @@ -1341,6 +1369,8 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); + const disposedModules = []; + for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1349,8 +1379,12 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); + disposedModules.push(moduleId); } } + for (const moduleId of disposedModules) { + delete moduleCache[moduleId]; + } return true; } From fbc9031993298f12a894cf82af9551c56845c091 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 10 Mar 2023 12:44:25 +0100 Subject: [PATCH 2/2] review --- crates/turbopack-ecmascript/js/src/runtime.js | 100 +++++++++--------- .../turbopack-ecmascript/js/types/index.d.ts | 30 ++++++ ...ot_basic_async_chunk_input_index_580957.js | 100 +++++++++--------- ...ot_basic_async_chunk_input_index_da3760.js | 100 +++++++++--------- ...apshot_basic_chunked_input_index_1d9ecb.js | 100 +++++++++--------- ...apshot_basic_chunked_input_index_e8535e.js | 100 +++++++++--------- ...apshot_basic_shebang_input_index_718090.js | 100 +++++++++--------- ...apshot_basic_shebang_input_index_a8bbcc.js | 100 +++++++++--------- ...shot_comptime_define_input_index_68d56d.js | 100 +++++++++--------- ...shot_comptime_define_input_index_f522f3.js | 100 +++++++++--------- ..._absolute-uri-import_input_index_24fbf6.js | 100 +++++++++--------- ..._absolute-uri-import_input_index_73a15e.js | 100 +++++++++--------- ...sts_snapshot_css_css_input_index_47659c.js | 100 +++++++++--------- ...sts_snapshot_css_css_input_index_c93332.js | 100 +++++++++--------- ...shot_emotion_emotion_input_index_549658.js | 100 +++++++++--------- ...shot_emotion_emotion_input_index_d9fc1b.js | 100 +++++++++--------- ...s_tests_snapshot_env_env_input_93aa94._.js | 100 +++++++++--------- ...s_tests_snapshot_env_env_input_bb8ee7._.js | 100 +++++++++--------- ...entrry_runtime_entry_input_index_09dc6c.js | 100 +++++++++--------- ...entrry_runtime_entry_input_index_19edf2.js | 100 +++++++++--------- ...shot_example_example_input_index_0a08ce.js | 100 +++++++++--------- ...shot_example_example_input_index_29e0b9.js | 100 +++++++++--------- ...ot_export-alls_cjs-2_input_index_d5f22a.js | 100 +++++++++--------- ...ot_export-alls_cjs-2_input_index_d9c332.js | 100 +++++++++--------- ...port-alls_cjs-script_input_index_b23628.js | 100 +++++++++--------- ...port-alls_cjs-script_input_index_ed5b85.js | 100 +++++++++--------- ...shot_import-meta_cjs_input_index_3e3da0.js | 100 +++++++++--------- ...shot_import-meta_cjs_input_index_746b39.js | 100 +++++++++--------- ...rt-meta_esm-multiple_input_index_090618.js | 100 +++++++++--------- ...rt-meta_esm-multiple_input_index_8687d1.js | 100 +++++++++--------- ...ort-meta_esm-mutable_input_index_3487d2.js | 100 +++++++++--------- ...ort-meta_esm-mutable_input_index_dc37f8.js | 100 +++++++++--------- ...port-meta_esm-object_input_index_040a52.js | 100 +++++++++--------- ...port-meta_esm-object_input_index_73c2df.js | 100 +++++++++--------- ...shot_import-meta_esm_input_index_c633a8.js | 100 +++++++++--------- ...shot_import-meta_esm_input_index_fe8e61.js | 100 +++++++++--------- ...shot_import-meta_url_input_index_5f69bf.js | 100 +++++++++--------- ...shot_import-meta_url_input_index_f01bae.js | 100 +++++++++--------- ...shot_imports_dynamic_input_index_56419a.js | 100 +++++++++--------- ...shot_imports_dynamic_input_index_6f9eb3.js | 100 +++++++++--------- ...napshot_imports_json_input_index_881b1b.js | 100 +++++++++--------- ...napshot_imports_json_input_index_e460e9.js | 100 +++++++++--------- ...ts_resolve_error_cjs_input_index_707ee1.js | 100 +++++++++--------- ...ts_resolve_error_cjs_input_index_fb56eb.js | 100 +++++++++--------- ...ts_resolve_error_esm_input_index_af6491.js | 100 +++++++++--------- ...ts_resolve_error_esm_input_index_ee6078.js | 100 +++++++++--------- ...s_static-and-dynamic_input_index_507785.js | 100 +++++++++--------- ...s_static-and-dynamic_input_index_899ad5.js | 100 +++++++++--------- ...pshot_imports_static_input_index_82c953.js | 100 +++++++++--------- ...pshot_imports_static_input_index_9fc270.js | 100 +++++++++--------- ...de_protocol_external_input_index_4e764f.js | 100 +++++++++--------- ...de_protocol_external_input_index_69be78.js | 100 +++++++++--------- ...ts_styled_components_input_index_01a621.js | 100 +++++++++--------- ...ts_styled_components_input_index_0496ed.js | 100 +++++++++--------- ...nsforms_input_packages_app_index_2a30e9.js | 100 +++++++++--------- ...nsforms_input_packages_app_index_38ad57.js | 100 +++++++++--------- ...ransforms_preset_env_input_index_097653.js | 100 +++++++++--------- ...ransforms_preset_env_input_index_62f043.js | 100 +++++++++--------- ...ipt_jsconfig-baseurl_input_index_18c34e.js | 100 +++++++++--------- ...ipt_jsconfig-baseurl_input_index_3d21b6.js | 100 +++++++++--------- ...sconfig-baseurl_input_index.ts_540b0c._.js | 100 +++++++++--------- ...sconfig-baseurl_input_index.ts_814f4c._.js | 100 +++++++++--------- 62 files changed, 3080 insertions(+), 3050 deletions(-) diff --git a/crates/turbopack-ecmascript/js/src/runtime.js b/crates/turbopack-ecmascript/js/src/runtime.js index d101d27515ed6..e05ada757ef39 100644 --- a/crates/turbopack-ecmascript/js/src/runtime.js +++ b/crates/turbopack-ecmascript/js/src/runtime.js @@ -8,6 +8,11 @@ /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -286,49 +291,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -345,7 +335,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -353,13 +343,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -446,7 +442,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -630,6 +629,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -649,6 +650,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -726,12 +734,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1241,8 +1247,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1251,12 +1255,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1268,7 +1268,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-ecmascript/js/types/index.d.ts b/crates/turbopack-ecmascript/js/types/index.d.ts index e2ae613e9e1c8..cdca7d692addf 100644 --- a/crates/turbopack-ecmascript/js/types/index.d.ts +++ b/crates/turbopack-ecmascript/js/types/index.d.ts @@ -33,6 +33,36 @@ interface Module { interopNamespace?: EsmInteropNamespace; } +enum SourceType { + /** + * The module was instantiated because it was included in an evaluated chunk's + * runtime. + */ + Runtime = 0, + /** + * The module was instantiated because a parent module imported it. + */ + Parent = 1, + /** + * The module was instantiated because it was included in a chunk's hot module + * update. + */ + Update = 2, +} + +type SourceInfo = + | { + type: SourceType.Runtime; + } + | { + type: SourceType.Parent; + parentId: ModuleId; + } + | { + type: SourceType.Update; + parents?: ModuleId[]; + }; + type ModuleCache = Record; type CommonJsRequire = (moduleId: ModuleId) => Exports; diff --git a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js index f479196c8411f..893f27e1bb412 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js +++ b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_580957.js @@ -137,6 +137,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -415,49 +420,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -474,7 +464,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -482,13 +472,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -575,7 +571,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -759,6 +758,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -778,6 +779,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -855,12 +863,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1370,8 +1376,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1380,12 +1384,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1397,7 +1397,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js index b12bc23e18949..0a4a209b25a5c 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js +++ b/crates/turbopack-tests/tests/snapshot/basic/async_chunk/output/crates_turbopack-tests_tests_snapshot_basic_async_chunk_input_index_da3760.js @@ -137,6 +137,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -415,49 +420,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -474,7 +464,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -482,13 +472,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -575,7 +571,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -759,6 +758,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -778,6 +779,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -855,12 +863,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1370,8 +1376,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1380,12 +1384,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1397,7 +1397,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js index 39b7308cfaa22..b48bfdb34c2ba 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js +++ b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_1d9ecb.js @@ -124,6 +124,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -402,49 +407,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -461,7 +451,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -469,13 +459,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -562,7 +558,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -746,6 +745,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -765,6 +766,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -842,12 +850,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1357,8 +1363,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1367,12 +1371,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1384,7 +1384,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js index 4a52bc0950890..a17910f315be5 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js +++ b/crates/turbopack-tests/tests/snapshot/basic/chunked/output/crates_turbopack-tests_tests_snapshot_basic_chunked_input_index_e8535e.js @@ -124,6 +124,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -402,49 +407,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -461,7 +451,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -469,13 +459,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -562,7 +558,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -746,6 +745,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -765,6 +766,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -842,12 +850,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1357,8 +1363,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1367,12 +1371,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1384,7 +1384,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js index df6fc428d01fe..80b712acfeba4 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js +++ b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_718090.js @@ -124,6 +124,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -402,49 +407,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -461,7 +451,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -469,13 +459,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -562,7 +558,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -746,6 +745,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -765,6 +766,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -842,12 +850,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1357,8 +1363,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1367,12 +1371,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1384,7 +1384,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js index f86c776d5bcf6..6753e6e4045c8 100644 --- a/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js +++ b/crates/turbopack-tests/tests/snapshot/basic/shebang/output/crates_turbopack-tests_tests_snapshot_basic_shebang_input_index_a8bbcc.js @@ -124,6 +124,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -402,49 +407,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -461,7 +451,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -469,13 +459,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -562,7 +558,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -746,6 +745,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -765,6 +766,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -842,12 +850,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1357,8 +1363,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1367,12 +1371,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1384,7 +1384,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js index 8a30b74499b32..1bc5be6598c9a 100644 --- a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js +++ b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_68d56d.js @@ -145,6 +145,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -423,49 +428,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -482,7 +472,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -490,13 +480,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -583,7 +579,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -767,6 +766,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -786,6 +787,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -863,12 +871,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1378,8 +1384,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1388,12 +1392,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1405,7 +1405,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js index 04fc3fda5b6ed..e2b81c0c5701b 100644 --- a/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js +++ b/crates/turbopack-tests/tests/snapshot/comptime/define/output/crates_turbopack-tests_tests_snapshot_comptime_define_input_index_f522f3.js @@ -145,6 +145,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -423,49 +428,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -482,7 +472,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -490,13 +480,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -583,7 +579,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -767,6 +766,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -786,6 +787,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -863,12 +871,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1378,8 +1384,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1388,12 +1392,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1405,7 +1405,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js index 08e72cd88a1e0..f27e19d3b2048 100644 --- a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js +++ b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_24fbf6.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js index 3aa14ded22c01..513f63f61c203 100644 --- a/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js +++ b/crates/turbopack-tests/tests/snapshot/css/absolute-uri-import/output/crates_turbopack-tests_tests_snapshot_css_absolute-uri-import_input_index_73a15e.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js index f78ef11588d51..ffce639f8c01c 100644 --- a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js +++ b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_47659c.js @@ -139,6 +139,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -417,49 +422,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -476,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -484,13 +474,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -577,7 +573,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -761,6 +760,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -780,6 +781,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -857,12 +865,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1372,8 +1378,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1382,12 +1386,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1399,7 +1399,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js index e8964e7f2c413..a51710ea42d26 100644 --- a/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js +++ b/crates/turbopack-tests/tests/snapshot/css/css/output/crates_turbopack-tests_tests_snapshot_css_css_input_index_c93332.js @@ -139,6 +139,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -417,49 +422,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -476,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -484,13 +474,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -577,7 +573,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -761,6 +760,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -780,6 +781,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -857,12 +865,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1372,8 +1378,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1382,12 +1386,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1399,7 +1399,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js index f925e2cc8afae..e3b5ac62623bd 100644 --- a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js +++ b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_549658.js @@ -144,6 +144,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -422,49 +427,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -481,7 +471,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -489,13 +479,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -582,7 +578,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -766,6 +765,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -785,6 +786,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -862,12 +870,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1377,8 +1383,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1387,12 +1391,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1404,7 +1404,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js index 5b9cb1c1c80a3..5509601234336 100644 --- a/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js +++ b/crates/turbopack-tests/tests/snapshot/emotion/emotion/output/crates_turbopack-tests_tests_snapshot_emotion_emotion_input_index_d9fc1b.js @@ -144,6 +144,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -422,49 +427,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -481,7 +471,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -489,13 +479,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -582,7 +578,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -766,6 +765,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -785,6 +786,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -862,12 +870,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1377,8 +1383,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1387,12 +1391,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1404,7 +1404,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js index e6996bc9def11..bf4c038d3f814 100644 --- a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js +++ b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_93aa94._.js @@ -130,6 +130,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -408,49 +413,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -467,7 +457,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -475,13 +465,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -568,7 +564,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -752,6 +751,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -771,6 +772,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -848,12 +856,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1363,8 +1369,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1373,12 +1377,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1390,7 +1390,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js index cb6be6f6470de..7eaa25d0432e6 100644 --- a/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js +++ b/crates/turbopack-tests/tests/snapshot/env/env/output/crates_turbopack-tests_tests_snapshot_env_env_input_bb8ee7._.js @@ -130,6 +130,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -408,49 +413,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -467,7 +457,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -475,13 +465,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -568,7 +564,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -752,6 +751,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -771,6 +772,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -848,12 +856,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1363,8 +1369,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1373,12 +1377,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1390,7 +1390,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js index 8a1e0dc1d7a0a..b0227fabf08c6 100644 --- a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js +++ b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_09dc6c.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js index b8e1a95735053..5801496ed2db1 100644 --- a/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js +++ b/crates/turbopack-tests/tests/snapshot/evaluated_entrry/runtime_entry/output/a587c_tests_snapshot_evaluated_entrry_runtime_entry_input_index_19edf2.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js index c513de175d79d..08c91dcb542dc 100644 --- a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js +++ b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_0a08ce.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js index 940b23991e4f8..f448d3786ac4f 100644 --- a/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js +++ b/crates/turbopack-tests/tests/snapshot/example/example/output/crates_turbopack-tests_tests_snapshot_example_example_input_index_29e0b9.js @@ -121,6 +121,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -399,49 +404,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -458,7 +448,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -466,13 +456,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -559,7 +555,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -743,6 +742,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -762,6 +763,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -839,12 +847,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1354,8 +1360,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1364,12 +1368,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1381,7 +1381,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js index 64227a483f557..892a93d5b8971 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d5f22a.js @@ -147,6 +147,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -425,49 +430,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -484,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -492,13 +482,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -585,7 +581,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -769,6 +768,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -788,6 +789,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -865,12 +873,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1380,8 +1386,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1390,12 +1394,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1407,7 +1407,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js index 89db45d9eb64e..cce93905ac6d3 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-2/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-2_input_index_d9c332.js @@ -147,6 +147,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -425,49 +430,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -484,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -492,13 +482,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -585,7 +581,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -769,6 +768,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -788,6 +789,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -865,12 +873,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1380,8 +1386,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1390,12 +1394,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1407,7 +1407,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js index 50135da3dc01d..7e94da68980f8 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_b23628.js @@ -142,6 +142,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -420,49 +425,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -479,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -487,13 +477,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -580,7 +576,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -764,6 +763,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -783,6 +784,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -860,12 +868,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1375,8 +1381,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1385,12 +1389,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1402,7 +1402,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js index 6d7b95dbd8543..91e8a7eef5a52 100644 --- a/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js +++ b/crates/turbopack-tests/tests/snapshot/export-alls/cjs-script/output/crates_turbopack-tests_tests_snapshot_export-alls_cjs-script_input_index_ed5b85.js @@ -142,6 +142,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -420,49 +425,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -479,7 +469,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -487,13 +477,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -580,7 +576,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -764,6 +763,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -783,6 +784,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -860,12 +868,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1375,8 +1381,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1385,12 +1389,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1402,7 +1402,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js index 29c04156d2236..27cd56b0072cc 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_3e3da0.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js index 9c579f30b0dec..db5154ab5d9b0 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/cjs/output/crates_turbopack-tests_tests_snapshot_import-meta_cjs_input_index_746b39.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js index 39389a08c6730..bc978e207d4fb 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_090618.js @@ -139,6 +139,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -417,49 +422,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -476,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -484,13 +474,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -577,7 +573,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -761,6 +760,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -780,6 +781,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -857,12 +865,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1372,8 +1378,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1382,12 +1386,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1399,7 +1399,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js index 90ea5026d2e72..2f5d9ccac8b44 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-multiple/output/79fb1_turbopack-tests_tests_snapshot_import-meta_esm-multiple_input_index_8687d1.js @@ -139,6 +139,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -417,49 +422,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -476,7 +466,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -484,13 +474,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -577,7 +573,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -761,6 +760,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -780,6 +781,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -857,12 +865,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1372,8 +1378,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1382,12 +1386,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1399,7 +1399,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js index 01140b60a357a..b3f1306e6e8b2 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_3487d2.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js index c477a547f8350..2fe93ce5d3b4d 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-mutable/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-mutable_input_index_dc37f8.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js index b6f75f54c50c3..bf7611db855ec 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_040a52.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js index ada65911045c1..f52899b89e18e 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm-object/output/crates_turbopack-tests_tests_snapshot_import-meta_esm-object_input_index_73c2df.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js index 015512eac94ec..f737649f4af71 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_c633a8.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js index 1a58ae8dcc085..3179d8ad87a2f 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/esm/output/crates_turbopack-tests_tests_snapshot_import-meta_esm_input_index_fe8e61.js @@ -132,6 +132,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -410,49 +415,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -469,7 +459,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -477,13 +467,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -570,7 +566,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -754,6 +753,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -773,6 +774,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -850,12 +858,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1365,8 +1371,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1375,12 +1379,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1392,7 +1392,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js index 5344c900a6bcf..7625acff714d2 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_5f69bf.js @@ -138,6 +138,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -416,49 +421,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -475,7 +465,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -483,13 +473,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -576,7 +572,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -760,6 +759,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -779,6 +780,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -856,12 +864,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1371,8 +1377,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1381,12 +1385,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1398,7 +1398,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js index 92df15fec15e1..5385a8d5dbec9 100644 --- a/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js +++ b/crates/turbopack-tests/tests/snapshot/import-meta/url/output/crates_turbopack-tests_tests_snapshot_import-meta_url_input_index_f01bae.js @@ -138,6 +138,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -416,49 +421,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -475,7 +465,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -483,13 +473,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -576,7 +572,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -760,6 +759,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -779,6 +780,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -856,12 +864,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1371,8 +1377,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1381,12 +1385,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1398,7 +1398,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js index 9c7f79f3e9126..022b718bddf9e 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js +++ b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_56419a.js @@ -135,6 +135,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -413,49 +418,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -472,7 +462,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -480,13 +470,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -573,7 +569,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -757,6 +756,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -776,6 +777,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -853,12 +861,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1368,8 +1374,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1378,12 +1382,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1395,7 +1395,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js index 1bffa2ffa30d1..81ecadf11a479 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js +++ b/crates/turbopack-tests/tests/snapshot/imports/dynamic/output/crates_turbopack-tests_tests_snapshot_imports_dynamic_input_index_6f9eb3.js @@ -135,6 +135,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -413,49 +418,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -472,7 +462,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -480,13 +470,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -573,7 +569,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -757,6 +756,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -776,6 +777,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -853,12 +861,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1368,8 +1374,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1378,12 +1382,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1395,7 +1395,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js index 726bdf1e3652a..67585a0037893 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js +++ b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_881b1b.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js index 2746b4bea7834..f5932a3fe3000 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js +++ b/crates/turbopack-tests/tests/snapshot/imports/json/output/crates_turbopack-tests_tests_snapshot_imports_json_input_index_e460e9.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js index 2ca35fccd8615..f5cb42e6bda09 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_707ee1.js @@ -126,6 +126,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -404,49 +409,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -463,7 +453,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -471,13 +461,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -564,7 +560,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -748,6 +747,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -767,6 +768,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -844,12 +852,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1359,8 +1365,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1369,12 +1373,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1386,7 +1386,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js index 7640ea84442b2..d04fc3d0911e6 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_cjs/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_cjs_input_index_fb56eb.js @@ -126,6 +126,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -404,49 +409,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -463,7 +453,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -471,13 +461,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -564,7 +560,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -748,6 +747,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -767,6 +768,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -844,12 +852,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1359,8 +1365,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1369,12 +1373,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1386,7 +1386,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js index 3b2415a92fc9c..eee1237246023 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_af6491.js @@ -129,6 +129,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -407,49 +412,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -466,7 +456,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -474,13 +464,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -567,7 +563,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -751,6 +750,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -770,6 +771,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -847,12 +855,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1362,8 +1368,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1372,12 +1376,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1389,7 +1389,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js index c17b2bdfe2779..1fc87264b3d28 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js +++ b/crates/turbopack-tests/tests/snapshot/imports/resolve_error_esm/output/79fb1_turbopack-tests_tests_snapshot_imports_resolve_error_esm_input_index_ee6078.js @@ -129,6 +129,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -407,49 +412,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -466,7 +456,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -474,13 +464,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -567,7 +563,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -751,6 +750,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -770,6 +771,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -847,12 +855,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1362,8 +1368,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1372,12 +1376,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1389,7 +1389,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js index c810ca4e03b8d..c330b325fac8f 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_507785.js @@ -147,6 +147,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -425,49 +430,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -484,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -492,13 +482,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -585,7 +581,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -769,6 +768,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -788,6 +789,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -865,12 +873,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1380,8 +1386,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1390,12 +1394,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1407,7 +1407,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js index d334829253b8f..3a8889857f460 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static-and-dynamic/output/79fb1_turbopack-tests_tests_snapshot_imports_static-and-dynamic_input_index_899ad5.js @@ -147,6 +147,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -425,49 +430,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -484,7 +474,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -492,13 +482,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -585,7 +581,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -769,6 +768,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -788,6 +789,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -865,12 +873,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1380,8 +1386,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1390,12 +1394,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1407,7 +1407,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js index 5b6007f10bf9e..be57979d0f1d0 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_82c953.js @@ -128,6 +128,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -406,49 +411,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -465,7 +455,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -473,13 +463,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -566,7 +562,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -750,6 +749,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -769,6 +770,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -846,12 +854,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1361,8 +1367,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1371,12 +1375,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1388,7 +1388,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js index 44a826d909e99..64142b73c49b0 100644 --- a/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js +++ b/crates/turbopack-tests/tests/snapshot/imports/static/output/crates_turbopack-tests_tests_snapshot_imports_static_input_index_9fc270.js @@ -128,6 +128,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -406,49 +411,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -465,7 +455,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -473,13 +463,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -566,7 +562,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -750,6 +749,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -769,6 +770,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -846,12 +854,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1361,8 +1367,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1371,12 +1375,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1388,7 +1388,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js index ae054e511c05d..5128df21b244e 100644 --- a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js +++ b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_4e764f.js @@ -123,6 +123,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -401,49 +406,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -460,7 +450,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -468,13 +458,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -561,7 +557,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -745,6 +744,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -764,6 +765,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -841,12 +849,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1356,8 +1362,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1366,12 +1370,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1383,7 +1383,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js index cd0e943fc46fa..114c81ae387f2 100644 --- a/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js +++ b/crates/turbopack-tests/tests/snapshot/node/node_protocol_external/output/79fb1_turbopack-tests_tests_snapshot_node_node_protocol_external_input_index_69be78.js @@ -123,6 +123,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -401,49 +406,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -460,7 +450,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -468,13 +458,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -561,7 +557,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -745,6 +744,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -764,6 +765,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -841,12 +849,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1356,8 +1362,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1366,12 +1370,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1383,7 +1383,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js index d895c6e5a344e..d662d145fb694 100644 --- a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js +++ b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_01a621.js @@ -130,6 +130,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -408,49 +413,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -467,7 +457,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -475,13 +465,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -568,7 +564,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -752,6 +751,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -771,6 +772,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -848,12 +856,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1363,8 +1369,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1373,12 +1377,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1390,7 +1390,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js index 39f0439d86b25..9cc7acc4a5a75 100644 --- a/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js +++ b/crates/turbopack-tests/tests/snapshot/styled_components/styled_components/output/a587c_tests_snapshot_styled_components_styled_components_input_index_0496ed.js @@ -130,6 +130,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -408,49 +413,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -467,7 +457,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -475,13 +465,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -568,7 +564,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -752,6 +751,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -771,6 +772,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -848,12 +856,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1363,8 +1369,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1373,12 +1377,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1390,7 +1390,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js index 704649a86267a..4bae1e998b33f 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_2a30e9.js @@ -126,6 +126,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -404,49 +409,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -463,7 +453,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -471,13 +461,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -564,7 +560,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -748,6 +747,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -767,6 +768,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -844,12 +852,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1359,8 +1365,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1369,12 +1373,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1386,7 +1386,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js index a2a86e7eb6074..abaaf9b716e3d 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/mono_transforms/output/a587c_tests_snapshot_swc_transforms_mono_transforms_input_packages_app_index_38ad57.js @@ -126,6 +126,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -404,49 +409,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -463,7 +453,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -471,13 +461,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -564,7 +560,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -748,6 +747,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -767,6 +768,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -844,12 +852,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1359,8 +1365,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1369,12 +1373,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1386,7 +1386,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js index 8f46cc494685a..2f912a29dc957 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_097653.js @@ -129,6 +129,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -407,49 +412,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -466,7 +456,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -474,13 +464,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -567,7 +563,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -751,6 +750,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -770,6 +771,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -847,12 +855,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1362,8 +1368,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1372,12 +1376,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1389,7 +1389,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js index 2d798c7aea063..d5f1d5c3b154b 100644 --- a/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js +++ b/crates/turbopack-tests/tests/snapshot/swc_transforms/preset_env/output/79fb1_turbopack-tests_tests_snapshot_swc_transforms_preset_env_input_index_62f043.js @@ -129,6 +129,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -407,49 +412,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -466,7 +456,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -474,13 +464,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -567,7 +563,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -751,6 +750,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -770,6 +771,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -847,12 +855,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1362,8 +1368,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1372,12 +1376,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1389,7 +1389,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js index 6a994d4c0a7b6..a2e62295a87d9 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_18c34e.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js index 3e460189d7766..c1b8fc78caf79 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/jsconfig-baseurl/output/79fb1_turbopack-tests_tests_snapshot_typescript_jsconfig-baseurl_input_index_3d21b6.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js index 3d958ffa02c31..b56c52fecaa14 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_540b0c._.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /** diff --git a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js index bd4d55f251f9a..051f33db6a849 100644 --- a/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js +++ b/crates/turbopack-tests/tests/snapshot/typescript/tsconfig-baseurl/output/a587c_tests_snapshot_typescript_tsconfig-baseurl_input_index.ts_814f4c._.js @@ -136,6 +136,11 @@ const BACKEND = { /** @typedef {import('../types').GetFirstModuleChunk} GetFirstModuleChunk */ /** @typedef {import('../types').Module} Module */ +/** @typedef {import('../types').SourceInfo} SourceInfo */ +/** @typedef {import('../types').SourceType} SourceType */ +/** @typedef {import('../types').SourceType.Runtime} SourceTypeRuntime */ +/** @typedef {import('../types').SourceType.Parent} SourceTypeParent */ +/** @typedef {import('../types').SourceType.Update} SourceTypeUpdate */ /** @typedef {import('../types').Exports} Exports */ /** @typedef {import('../types').EsmInteropNamespace} EsmInteropNamespace */ /** @typedef {import('../types').Runnable} Runnable */ @@ -414,49 +419,34 @@ function getOrCreateChunkLoader(chunkPath, from) { return chunkLoader; } -/** - * @enum {number} - */ -const SourceType = { - /** - * The module was instantiated because it was included in an evaluated chunk's - * runtime. - */ - Runtime: 0, - /** - * The module was instantiated because a parent module imported it. - */ - Parent: 1, - /** - * The module was instantiated because it was included in a chunk's hot module - * update. - */ - Update: 2, -}; +/** @type {SourceTypeRuntime} */ +const SourceTypeRuntime = 0; +/** @type {SourceTypeParent} */ +const SourceTypeParent = 1; +/** @type {SourceTypeUpdate} */ +const SourceTypeUpdate = 2; /** * * @param {ModuleId} id - * @param {SourceType} sourceType - * @param {ModuleId} [sourceId] - * @param {ModuleId[]} [parents] + * @param {SourceInfo} source * @returns {Module} */ -function instantiateModule(id, sourceType, sourceId, parents) { +function instantiateModule(id, source) { const moduleFactory = moduleFactories[id]; if (typeof moduleFactory !== "function") { // This can happen if modules incorrectly handle HMR disposes/updates, // e.g. when they keep a `setTimeout` around which still executes old code // and contains e.g. a `require("something")` call. let instantiationReason; - switch (sourceType) { - case SourceType.Runtime: + switch (source.type) { + case SourceTypeRuntime: instantiationReason = "as a runtime entry"; break; - case SourceType.Parent: - instantiationReason = `because it was required from module ${sourceId}`; + case SourceTypeParent: + instantiationReason = `because it was required from module ${source.parentId}`; break; - case SourceType.Update: + case SourceTypeUpdate: instantiationReason = "because of an HMR update"; break; } @@ -473,7 +463,7 @@ function instantiateModule(id, sourceType, sourceId, parents) { exports: {}, loaded: false, id, - parents: parents || [], + parents: undefined, children: [], interopNamespace: undefined, hot, @@ -481,13 +471,19 @@ function instantiateModule(id, sourceType, sourceId, parents) { moduleCache[id] = module; moduleHotState.set(module, hotState); - if (sourceType === SourceType.Runtime) { - runtimeModules.add(id); - } else if (sourceType === SourceType.Parent) { - module.parents.push(sourceId); - - // No need to add this module as a child of the parent module here, this - // has already been taken care of in `getOrInstantiateModuleFromParent`. + switch (source.type) { + case SourceTypeRuntime: + runtimeModules.add(id); + module.parents = []; + break; + case SourceTypeParent: + // No need to add this module as a child of the parent module here, this + // has already been taken care of in `getOrInstantiateModuleFromParent`. + module.parents = [source.parentId]; + break; + case SourceTypeUpdate: + module.parents = source.parents || []; + break; } runModuleExecutionHooks(module, () => { @@ -574,7 +570,10 @@ function getOrInstantiateModuleFromParent(id, sourceModule) { return module; } - return instantiateModule(id, SourceType.Parent, sourceModule.id); + return instantiateModule(id, { + type: SourceTypeParent, + parentId: sourceModule.id, + }); } /** @@ -758,6 +757,8 @@ function disposePhase(outdatedModules, disposedModules) { disposeModule(moduleId, "clear"); } + // Removing modules from the module cache is a separate step. + // We also want to keep track of previous parents of the outdated modules. const outdatedModuleParents = new Map(); for (const moduleId of outdatedModules) { const oldModule = moduleCache[moduleId]; @@ -777,6 +778,13 @@ function disposePhase(outdatedModules, disposedModules) { * Returns the persistent hot data that should be kept for the next module * instance. * + * NOTE: mode = "replace" will not remove modules from the moduleCache. + * This must be done in a separate step afterwards. + * This is important because all modules need to be diposed to update the + * parent/child relationships before they are actually removed from the moduleCache. + * If this would be done in this method, following disposeModulecalls won't find + * the module from the module id in the cache. + * * @param {ModuleId} moduleId * @param {"clear" | "replace"} mode */ @@ -854,12 +862,10 @@ function applyPhase( // Re-instantiate all outdated self-accepted modules. for (const { moduleId, errorHandler } of outdatedSelfAcceptedModules) { try { - instantiateModule( - moduleId, - SourceType.Update, - undefined, - outdatedModuleParents.get(moduleId) - ); + instantiateModule(moduleId, { + type: SourceTypeUpdate, + parents: outdatedModuleParents.get(moduleId), + }); } catch (err) { if (typeof errorHandler === "function") { try { @@ -1369,8 +1375,6 @@ function disposeChunk(chunkPath) { } chunkModules.delete(chunkPath); - const disposedModules = []; - for (const moduleId of chunkModules) { const moduleChunks = moduleChunksMap.get(moduleId); moduleChunks.delete(chunkPath); @@ -1379,12 +1383,8 @@ function disposeChunk(chunkPath) { if (noRemainingChunks) { moduleChunksMap.delete(moduleId); disposeModule(moduleId, "clear"); - disposedModules.push(moduleId); } } - for (const moduleId of disposedModules) { - delete moduleCache[moduleId]; - } return true; } @@ -1396,7 +1396,7 @@ function disposeChunk(chunkPath) { * @returns {Module} */ function instantiateRuntimeModule(moduleId) { - return instantiateModule(moduleId, SourceType.Runtime); + return instantiateModule(moduleId, { type: SourceTypeRuntime }); } /**