Skip to content

Commit 6fafa25

Browse files
wre232114wumingliang
andauthored
fix(v2): #2262 HMR does not work with Qiankun framework (#2263)
* fix(v2): #2262 HMR does not work with Qiankun framework * chore: update snapshot --------- Co-authored-by: wumingliang <wumingliang.9801@bytedance.com>
1 parent 5130d46 commit 6fafa25

File tree

12 files changed

+228
-18
lines changed

12 files changed

+228
-18
lines changed

.changeset/thick-trees-collect.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@farmfe/runtime-plugin-hmr": patch
3+
"@farmfe/core": patch
4+
---
5+
6+
Fix(v2) #2262. HMR does not work with Qiankun framework

crates/compiler/tests/fixtures/library/browser/external/output.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//index.js:
22
(function(){const __farm_internal_module_system__ = {};
33
function initModuleSystem() {
4-
console.log('module-system-helper.ts');
4+
console.log('dynamic-import.ts');
55
}
66
function initModuleSystem$1() {
7+
console.log('module-system-helper.ts');
8+
}
9+
function initModuleSystem$2() {
710
console.log('module-helper.ts');
811
}
912
initModuleSystem(__farm_internal_module_system__);
1013
initModuleSystem$1(__farm_internal_module_system__);
14+
initModuleSystem$2(__farm_internal_module_system__);
1115
}());window['__farm_default_namespace__'].m.se({
1216
"node:fs": window['node:fs'] || {}
1317
});

crates/compiler/tests/fixtures/library/browser/normal/output.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//index.js:
22
(function(){const __farm_internal_module_system__ = {};
33
function initModuleSystem() {
4-
console.log('module-system-helper.ts');
4+
console.log('dynamic-import.ts');
55
}
66
function initModuleSystem$1() {
7+
console.log('module-system-helper.ts');
8+
}
9+
function initModuleSystem$2() {
710
console.log('module-helper.ts');
811
}
912
initModuleSystem(__farm_internal_module_system__);
1013
initModuleSystem$1(__farm_internal_module_system__);
14+
initModuleSystem$2(__farm_internal_module_system__);
1115
}());(function(moduleSystem, modules) {
1216
for(var moduleId in modules){
1317
var module = modules[moduleId];

crates/compiler/tests/fixtures/library/external/multiple-export-all/output.browser.js

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,21 +86,176 @@ function farmRegister(id, module) {
8686
__farm_internal_modules__[id] = module;
8787
return ()=>farmRequire$1(id);
8888
}
89-
; // module_id: @farmfe/runtime/src/modules/module-system-helper.ts.farm-runtime
89+
; // module_id: @farmfe/runtime/src/modules/dynamic-import.ts.farm-runtime
90+
let dynamicResources = [];
91+
// dynamic module entry and resources map
92+
let dynamicModuleResourcesMap = {};
93+
const loadedResources = {};
94+
const loadingResources = {};
95+
// available public paths, when loading resources, we will try each publicPath until it is available, this is so called `resource loading retry`
96+
const publicPaths = [];
9097
let moduleSystem;
9198
function initModuleSystem(ms) {
9299
moduleSystem = ms;
93-
moduleSystem.u = updateModule;
94-
moduleSystem.e = deleteModule;
95-
moduleSystem.a = clearCache;
100+
moduleSystem.pp = publicPaths;
101+
moduleSystem.d = dynamicImport;
102+
moduleSystem.sp = setPublicPaths;
103+
moduleSystem.si = setInitialLoadedResources;
104+
moduleSystem.sd = setDynamicModuleResourcesMap;
105+
moduleSystem.l = loadDynamicResourcesOnly;
106+
}
107+
function requireDynamicModule(id) {
108+
const exports = moduleSystem.r(id);
109+
// if the module is async, return the default export, the default export should be a promise
110+
return exports.__farm_async ? exports.default : Promise.resolve(exports);
111+
}
112+
function dynamicImport(id) {
113+
if (moduleSystem.m()[id] && !dynamicModuleResourcesMap[id]) {
114+
return requireDynamicModule(id);
115+
}
116+
return loadDynamicResources(id);
117+
}
118+
function loadDynamicResources(id, force = false) {
119+
const resources = dynamicModuleResourcesMap[id].map((index)=>dynamicResources[index]);
120+
return loadDynamicResourcesOnly(id, force).then(()=>{
121+
// Do not require the module if all the resources are not js resources
122+
if (resources.every((resource)=>resource.type !== 0)) {
123+
return;
124+
}
125+
if (!moduleSystem.m()[id]) {
126+
throw new Error(`Dynamic imported module "${id}" is not registered.`);
127+
}
128+
return requireDynamicModule(id);
129+
}).catch((err)=>{
130+
console.error(`[Farm] Error loading dynamic module "${id}"`, err);
131+
throw err;
132+
});
133+
}
134+
function loadDynamicResourcesOnly(id, force = false) {
135+
const resources = dynamicModuleResourcesMap[id].map((index)=>dynamicResources[index]);
136+
if (!moduleSystem.m()[id] && (!resources || resources.length === 0)) {
137+
throw new Error(`Dynamic imported module "${id}" does not belong to any resource`);
138+
}
139+
// force reload resources
140+
if (force) {
141+
moduleSystem.a(id);
142+
}
143+
// loading all required resources, and return the exports of the entry module
144+
return Promise.all(resources.map((resource)=>{
145+
if (force) {
146+
const resourceLoaded = isResourceLoaded(resource.path);
147+
setLoadedResource(resource.path, false);
148+
if (resourceLoaded) {
149+
return load(resource, `?t=${Date.now()}`);
150+
}
151+
}
152+
return load(resource);
153+
}));
154+
}
155+
function load(resource, query) {
156+
{
157+
if (loadedResources[resource.path] && !query) {
158+
// Skip inject Promise polyfill for runtime
159+
return Promise.resolve();
160+
} else if (loadingResources[resource.path]) {
161+
if (query) {
162+
loadingResources[resource.path] = loadingResources[resource.path].then(()=>loadResource(resource, 0, query));
163+
}
164+
return loadingResources[resource.path];
165+
}
166+
return loadResource(resource, 0, query);
167+
}
168+
}
169+
function loadResource(resource, index, query) {
170+
const publicPath = publicPaths[index];
171+
const url = `${publicPath.endsWith('/') ? publicPath.slice(0, -1) : publicPath}/${resource.path}${query || ''}`;
172+
let promise = Promise.resolve();
173+
if (resource.type === 0) {
174+
promise = loadScript(url);
175+
} else if (resource.type === 1) {
176+
promise = loadLink(url);
177+
}
178+
loadingResources[resource.path] = promise;
179+
promise.then(()=>{
180+
loadedResources[resource.path] = true;
181+
loadingResources[resource.path] = null;
182+
}).catch((e)=>{
183+
console.warn(`[Farm] Failed to load resource "${url}" using publicPath: ${publicPaths[index]}`);
184+
index++;
185+
if (index < publicPaths.length) {
186+
return loadResource(resource, index);
187+
} else {
188+
loadingResources[resource.path] = null;
189+
throw new Error(`[Farm] Failed to load resource: "${resource.path}, type: ${resource.type}". ${e}`);
190+
}
191+
});
192+
return promise;
193+
}
194+
function loadScript(path) {
195+
return new Promise((resolve, reject)=>{
196+
const script = document.createElement('script');
197+
script.src = path;
198+
document.body.appendChild(script);
199+
script.onload = ()=>{
200+
resolve();
201+
};
202+
script.onerror = (e)=>{
203+
reject(e);
204+
};
205+
});
206+
}
207+
function loadLink(path) {
208+
return new Promise((resolve, reject)=>{
209+
const link = document.createElement('link');
210+
link.rel = 'stylesheet';
211+
link.href = path;
212+
document.head.appendChild(link);
213+
link.onload = ()=>{
214+
resolve();
215+
};
216+
link.onerror = (e)=>{
217+
reject(e);
218+
};
219+
});
220+
}
221+
function setLoadedResource(path, loaded = true) {
222+
loadedResources[path] = loaded;
223+
}
224+
function isResourceLoaded(path) {
225+
return loadedResources[path];
226+
}
227+
// The public paths are injected during compile time
228+
function setPublicPaths(p) {
229+
for(const key in p){
230+
publicPaths[key] = p[key];
231+
}
232+
}
233+
function setInitialLoadedResources(resources) {
234+
resources.forEach((resource)=>{
235+
setLoadedResource(resource);
236+
});
237+
}
238+
// These two methods are used to support dynamic module loading, the dynamic module info is collected by the compiler and injected during compile time
239+
// This method can also be called during runtime to add new dynamic modules
240+
function setDynamicModuleResourcesMap(dr, dmp) {
241+
dynamicResources = dr;
242+
dynamicModuleResourcesMap = dmp;
243+
}
244+
; // module_id: @farmfe/runtime/src/modules/module-system-helper.ts.farm-runtime
245+
let moduleSystem$1;
246+
function initModuleSystem$1(ms) {
247+
moduleSystem$1 = ms;
248+
moduleSystem$1.u = updateModule;
249+
moduleSystem$1.e = deleteModule;
250+
moduleSystem$1.a = clearCache;
96251
}
97252
function updateModule(moduleId, init) {
98-
const modules = moduleSystem.m();
253+
const modules = moduleSystem$1.m();
99254
modules[moduleId] = init;
100255
clearCache(moduleId);
101256
}
102257
function deleteModule(moduleId) {
103-
const modules = moduleSystem.m();
258+
const modules = moduleSystem$1.m();
104259
if (modules[moduleId]) {
105260
clearCache(moduleId);
106261
delete modules[moduleId];
@@ -110,7 +265,7 @@ function deleteModule(moduleId) {
110265
}
111266
}
112267
function clearCache(moduleId) {
113-
const cache = moduleSystem.c();
268+
const cache = moduleSystem$1.c();
114269
if (cache[moduleId]) {
115270
delete cache[moduleId];
116271
return true;
@@ -119,7 +274,7 @@ function clearCache(moduleId) {
119274
}
120275
}
121276
; // module_id: @farmfe/runtime/src/modules/module-helper.ts.farm-runtime
122-
function initModuleSystem$1(ms) {
277+
function initModuleSystem$2(ms) {
123278
const farmRequire = ms.r;
124279
{
125280
farmRequire.o = exportByDefineProperty;
@@ -239,6 +394,7 @@ function importDefault(v) {
239394
; // module_id: @farmfe/runtime
240395
initModuleSystem(__farm_internal_module_system__);
241396
initModuleSystem$1(__farm_internal_module_system__);
397+
initModuleSystem$2(__farm_internal_module_system__);
242398
}());window['__farm_default_namespace__'].m.se({
243399
"/external/bar": window['/external/bar'] || {},
244400
"/external/zoo": window['/external/zoo'] || {}

crates/compiler/tests/fixtures/tree_shake/changed_ast/output.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//index.js:
22
(function(){const __farm_internal_module_system__ = {};
33
function initModuleSystem() {
4-
console.log('module-system-helper.ts');
4+
console.log('dynamic-import.ts');
55
}
66
function initModuleSystem$1() {
7+
console.log('module-system-helper.ts');
8+
}
9+
function initModuleSystem$2() {
710
console.log('module-helper.ts');
811
}
912
initModuleSystem(__farm_internal_module_system__);
1013
initModuleSystem$1(__farm_internal_module_system__);
14+
initModuleSystem$2(__farm_internal_module_system__);
1115
}());(function(moduleSystem, modules) {
1216
for(var moduleId in modules){
1317
var module = modules[moduleId];

crates/compiler/tests/fixtures/tree_shake_development/css/output.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//index.js:
22
(function(){const __farm_internal_module_system__ = {};
33
function initModuleSystem() {
4-
console.log('module-system-helper.ts');
4+
console.log('dynamic-import.ts');
55
}
66
function initModuleSystem$1() {
7+
console.log('module-system-helper.ts');
8+
}
9+
function initModuleSystem$2() {
710
console.log('module-helper.ts');
811
}
912
initModuleSystem(__farm_internal_module_system__);
1013
initModuleSystem$1(__farm_internal_module_system__);
14+
initModuleSystem$2(__farm_internal_module_system__);
1115
}());(function(moduleSystem, modules) {
1216
for(var moduleId in modules){
1317
var module = modules[moduleId];

crates/compiler/tests/fixtures/tree_shake_development/import_meta_hot/output.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//index.js:
22
(function(){const __farm_internal_module_system__ = {};
33
function initModuleSystem() {
4-
console.log('module-system-helper.ts');
4+
console.log('dynamic-import.ts');
55
}
66
function initModuleSystem$1() {
7+
console.log('module-system-helper.ts');
8+
}
9+
function initModuleSystem$2() {
710
console.log('module-helper.ts');
811
}
912
initModuleSystem(__farm_internal_module_system__);
1013
initModuleSystem$1(__farm_internal_module_system__);
14+
initModuleSystem$2(__farm_internal_module_system__);
1115
}());(function(moduleSystem, modules) {
1216
for(var moduleId in modules){
1317
var module = modules[moduleId];

crates/core/src/module/meta_data/script/feature_flag.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use farmfe_macro_cache_item::cache_item;
22

3+
use crate::HashSet;
4+
35
pub const FARM_ENABLE_TOP_LEVEL_AWAIT: &str = "__FARM_ENABLE_TOP_LEVEL_AWAIT__";
46
pub const FARM_ENABLE_EXPORT_HELPER: &str = "__FARM_ENABLE_EXPORT_HELPER__";
57
pub const FARM_ENABLE_EXPORT_ALL_HELPER: &str = "__FARM_ENABLE_EXPORT_ALL_HELPER__";
@@ -43,4 +45,18 @@ impl FeatureFlag {
4345
FeatureFlag::ImportNamed => "ImportStatement",
4446
}
4547
}
48+
49+
pub fn all() -> HashSet<Self> {
50+
HashSet::from_iter([
51+
Self::DynamicImport,
52+
Self::TopLevelAwait,
53+
Self::ImportDefault,
54+
Self::ImportNamespace,
55+
Self::ExportAll,
56+
Self::ExportFrom,
57+
Self::ExportStatement,
58+
Self::ImportStatement,
59+
Self::ImportNamed,
60+
])
61+
}
4662
}

crates/plugin_runtime/src/handle_runtime_modules.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22

33
use farmfe_core::{
4+
config::{Mode, TargetEnv},
45
context::CompilationContext,
56
module::{
67
meta_data::script::feature_flag::FeatureFlag,
@@ -299,7 +300,17 @@ pub fn transform_normal_runtime_inputs_to_dynamic_entries(
299300
}
300301
}
301302

302-
pub fn get_all_feature_flags(module_graph: &ModuleGraph) -> HashSet<FeatureFlag> {
303+
pub fn get_all_feature_flags(
304+
module_graph: &ModuleGraph,
305+
context: &CompilationContext,
306+
) -> HashSet<FeatureFlag> {
307+
// enable all features in development mode
308+
if matches!(context.config.mode, Mode::Development)
309+
&& context.config.output.target_env != TargetEnv::Library
310+
{
311+
return FeatureFlag::all();
312+
}
313+
303314
let mut all_features_flags = HashSet::default();
304315

305316
for module in module_graph.modules() {

crates/plugin_runtime/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ impl Plugin for FarmPluginRuntime {
261261
module_graph: &mut farmfe_core::module::module_graph::ModuleGraph,
262262
context: &Arc<CompilationContext>,
263263
) -> farmfe_core::error::Result<Option<()>> {
264-
let all_feature_flags = get_all_feature_flags(module_graph);
264+
let all_feature_flags = get_all_feature_flags(module_graph, context);
265265
transform_normal_runtime_inputs_to_dynamic_entries(module_graph, &all_feature_flags, context);
266266
// remove unused runtime features that controlled by feature guard like `if (__FARM_TARGET_ENV__)`
267267
// note that this must be called before insert_runtime_modules cause insert_runtime_modules will remove dynamic entries

0 commit comments

Comments
 (0)