-
Notifications
You must be signed in to change notification settings - Fork 108
fix: async module in circular dependence #1659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| use std::collections::HashMap; | ||
| use std::collections::{HashMap, HashSet, VecDeque}; | ||
| use std::sync::Arc; | ||
|
|
||
| use swc_core::common::util::take::Take; | ||
|
|
@@ -211,23 +211,61 @@ pub fn mark_async( | |
| ) -> HashMap<ModuleId, Vec<Dependency>> { | ||
| let mut async_deps_by_module_id = HashMap::new(); | ||
| let mut module_graph = context.module_graph.write().unwrap(); | ||
| // TODO: 考虑成环的场景 | ||
|
|
||
| let mut to_visit_queue = module_graph | ||
| .modules() | ||
| .iter() | ||
| .filter_map(|m| { | ||
| m.info | ||
| .as_ref() | ||
| .and_then(|i| if i.is_async { Some(m.id.clone()) } else { None }) | ||
| }) | ||
| .collect::<VecDeque<_>>(); | ||
| let mut visited = HashSet::new(); | ||
|
|
||
| // polluted async to dependants | ||
| while let Some(module_id) = to_visit_queue.pop_front() { | ||
| if visited.contains(&module_id) { | ||
| continue; | ||
| } | ||
|
|
||
| module_graph | ||
| .get_dependents(&module_id) | ||
| .iter() | ||
| .filter_map(|(dependant, dependency)| { | ||
| if !dependency.resolve_type.is_sync_esm() { | ||
| return None; | ||
| } | ||
| if !visited.contains(*dependant) { | ||
| Some((*dependant).clone()) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .collect::<Vec<_>>() | ||
| .iter() | ||
| .for_each(|module_id| { | ||
| let m = module_graph.get_module_mut(module_id).unwrap(); | ||
| m.info.as_mut().unwrap().is_async = true; | ||
|
|
||
|
Comment on lines
+249
to
+250
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 注意:再次使用 第249行的 建议修改如下: -if let Some(m) = module_graph.get_module_mut(module_id) {
- m.info.as_mut().unwrap().is_async = true;
+if let Some(m) = module_graph.get_module_mut(module_id) {
+ if let Some(info) = m.info.as_mut() {
+ info.is_async = true;
+ } else {
+ // 处理info为None的情况,例如记录日志或设置默认值
+ }
+} else {
+ // 处理模块不存在的情况
+}
|
||
| to_visit_queue.push_back(module_id.clone()); | ||
| }); | ||
|
|
||
| visited.insert(module_id.clone()); | ||
| } | ||
|
|
||
| module_ids.iter().for_each(|module_id| { | ||
| let deps = module_graph.get_dependencies_info(module_id); | ||
| let async_deps: Vec<Dependency> = deps | ||
| .into_iter() | ||
| .filter(|(_, dep, is_async)| dep.resolve_type.is_sync_esm() && *is_async) | ||
| .map(|(_, dep, _)| dep.clone()) | ||
| .collect(); | ||
| let module = module_graph.get_module_mut(module_id).unwrap(); | ||
| if let Some(info) = module.info.as_mut() { | ||
| // a module with async deps need to be polluted into async module | ||
| if !info.is_async && !async_deps.is_empty() { | ||
| info.is_async = true; | ||
| } | ||
| if !async_deps.is_empty() { | ||
| async_deps_by_module_id.insert(module_id.clone(), async_deps); | ||
| } | ||
| }); | ||
|
|
||
| async_deps_by_module_id | ||
| } | ||
|
|
||
|
|
@@ -255,8 +293,8 @@ add(1, 2); | |
| "# | ||
| .trim()); | ||
| assert_eq!( | ||
| code, | ||
| r#" | ||
| code, | ||
| r#" | ||
| __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
|
|
@@ -273,7 +311,7 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | |
| asyncResult(); | ||
| }, true); | ||
| "#.trim() | ||
| ); | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -286,8 +324,8 @@ console.log(foo) | |
| "# | ||
| .trim()); | ||
| assert_eq!( | ||
| code, | ||
| r#" | ||
| code, | ||
| r#" | ||
| __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
|
|
@@ -308,8 +346,8 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | |
| asyncResult(); | ||
| }, true); | ||
| "# | ||
| .trim() | ||
| ); | ||
| .trim() | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -321,8 +359,8 @@ add(1, 2); | |
| "# | ||
| .trim()); | ||
| assert_eq!( | ||
| code, | ||
| r#" | ||
| code, | ||
| r#" | ||
| __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
|
|
@@ -340,8 +378,8 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | |
| asyncResult(); | ||
| }, true); | ||
| "# | ||
| .trim() | ||
| ); | ||
| .trim() | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
@@ -374,8 +412,8 @@ const async = require('./miexed_async'); | |
| "# | ||
| .trim()); | ||
| assert_eq!( | ||
| code, | ||
| r#" | ||
| code, | ||
| r#" | ||
| __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
|
|
@@ -393,7 +431,7 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ | |
| asyncResult(); | ||
| }, true); | ||
| "#.trim() | ||
| ); | ||
| ); | ||
| } | ||
|
|
||
| fn run(js_code: &str) -> String { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||
| let x = await new Promise((resolve)=>{ | ||||||||||
| resolve("default") | ||||||||||
| }) | ||||||||||
|
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 建议优化 Promise 的创建方式 当前的 Promise 创建方式过于冗长。对于简单的值,可以使用更简洁的方式。 建议使用以下方式之一: -let x = await new Promise((resolve)=>{
- resolve("default")
-})
+let x = await Promise.resolve("default")或者更简单的: -let x = await new Promise((resolve)=>{
- resolve("default")
-})
+let x = "default"📝 Committable suggestion
Suggested change
|
||||||||||
|
|
||||||||||
| let named = await new Promise((resolve)=>{ | ||||||||||
| resolve("named") | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| export default x; | ||||||||||
|
|
||||||||||
| export {named} | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { listKeys } from "./utils" | ||
|
|
||
| import { named } from "./async" | ||
|
|
||
| export const config = { | ||
| key: "value" | ||
| } | ||
|
|
||
| export function displayConfig() { | ||
| return listKeys() | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,9 @@ | ||||||||||||||||||||||
| const { | ||||||||||||||||||||||
| injectSimpleJest, | ||||||||||||||||||||||
| parseBuildResult, | ||||||||||||||||||||||
| moduleDefinitionOf, | ||||||||||||||||||||||
| } = require("../../../scripts/test-utils"); | ||||||||||||||||||||||
| const { files } = parseBuildResult(__dirname); | ||||||||||||||||||||||
| injectSimpleJest(); | ||||||||||||||||||||||
|
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 建议增加错误处理机制 在解析构建结果时,建议添加错误处理来提高测试的健壮性。 建议修改为: -const { files } = parseBuildResult(__dirname);
+let files;
+try {
+ ({ files } = parseBuildResult(__dirname));
+} catch (error) {
+ console.error('构建结果解析失败:', error);
+ throw error;
+}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| require("./dist/index.js"); | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,6 @@ | ||||||||||||||||||||||||
| import {displayConfig} from "./component"; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| it("should require looped async moule", () => { | ||||||||||||||||||||||||
| expect(displayConfig()).toStrictEqual(["key"]) | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
|
Comment on lines
+3
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 测试用例需要进行一些改进 以下几点建议:
建议按如下方式修改代码: -it("should require looped async moule", () => {
- expect(displayConfig()).toStrictEqual(["key"])
-})
+it("should require looped async module", async () => {
+ try {
+ const result = await displayConfig();
+ expect(result).toStrictEqual(["key"]);
+ } catch (error) {
+ throw new Error(`测试失败: ${error.message}`);
+ }
+});📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "entry": { | ||
| "index": "./index.js" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,9 @@ | ||||||||||||||||
| import {config} from "./component" | ||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification 存在循环依赖问题需要注意 经验证,确实存在循环依赖关系:
这种循环依赖可能会在异步模块加载时造成问题:
建议:
🔗 Analysis chain验证循环依赖关系 由于该文件从 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# 描述:检查是否存在循环依赖
# 预期:查找 component.js 中是否引用了 utils.js
rg -l "import.*from.*['\"]\.\/utils['\"]" "$(dirname $(fd -t f utils.js))/component.js"
Length of output: 341 Script: #!/bin/bash
# 检查 component.js 中是否引用了 utils.js
rg "import.*from.*[\"']./utils[\"']" "$(dirname $(fd -t f utils.js))/component.js"
# 查看 component.js 的完整内容以分析依赖关系
cat "$(dirname $(fd -t f utils.js))/component.js"
Length of output: 612 Script: #!/bin/bash
# 修复路径问题,正确查找和检查文件
fd -t f "component.js" "e2e/fixtures/javascript.async_module_in_loop" -x cat {}
Length of output: 250 |
||||||||||||||||
|
|
||||||||||||||||
| export function listKeys() { | ||||||||||||||||
| if(config){ | ||||||||||||||||
|
|
||||||||||||||||
| return Object.keys(config) | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+4
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 改进代码格式和错误处理 代码存在以下问题:
建议按如下方式修改: - if(config){
-
- return Object.keys(config)
+ if (config && typeof config === 'object') {
+ return Object.keys(config)📝 Committable suggestion
Suggested change
|
||||||||||||||||
| return ["oops"] | ||||||||||||||||
| } | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
注意:
unwrap()可能引发潜在的崩溃在第248行,
let m = module_graph.get_module_mut(module_id).unwrap();中使用了unwrap()。如果get_module_mut返回None,程序将会崩溃。建议添加错误处理以防止这种情况:建议修改如下: