Skip to content

feat: generate_end with stats#1450

Merged
sorrycc merged 2 commits intomasterfrom
feat/generate-end-stats
Jul 26, 2024
Merged

feat: generate_end with stats#1450
sorrycc merged 2 commits intomasterfrom
feat/generate-end-stats

Conversation

@xusd320
Copy link
Contributor

@xusd320 xusd320 commented Jul 25, 2024

Carry the full stats info in generate_end hook
image
image

Summary by CodeRabbit

  • 新功能

    • 简化了开发服务器的 serve 方法,提升了代码可读性。
    • 加入了新的统计信息生成方法,提供更详细的构建过程数据。
    • 扩展了 JsHooks 接口的 stats 属性,以支持更多构建相关信息。
    • 引入了 TransformOutput 接口,增强了类型定义。
  • 修复

    • 改善了时间处理机制,使用更精确的时间戳记录构建时长。
  • 文档

    • 增加了 TypeScript 类型定义的灵活性,以支持未来扩展。

@xusd320 xusd320 requested review from sorrycc and stormslowly July 25, 2024 06:53
@coderabbitai
Copy link

coderabbitai bot commented Jul 25, 2024

Walkthrough

本次更改涉及多个文件的结构和功能调整,主要集中在简化方法调用、增强统计信息的处理以及改进时间管理。通过去除不必要的回调参数和采用新的数据结构,使得代码更加清晰易读,同时提升了构建过程中的统计信息收集能力。这些更改将有助于提高整体可维护性和功能性。

Changes

文件 更改摘要
crates/binding/src/lib.rs serve 方法调用简化,去掉了不必要的闭包参数。
crates/binding/src/tsfn.rs 更新 generate_end 函数签名,引入更复杂的参数类型以增强构建统计信息处理。
crates/mako/Cargo.toml 新增 chrono 依赖,增强日期和时间处理能力。
crates/mako/src/compiler.rs chrono::Local 替换 std::time::SystemTime,提升时间管理精度,并简化错误处理逻辑。
crates/mako/src/dev/mod.rs 简化 DevServerserve 方法,移除了相关的回调结构,直接处理编译统计信息。
crates/mako/src/generate/mod.rs 更新多个方法返回类型,增强统计信息的返回,内部逻辑也进行了重构。
crates/mako/src/lib.rs stats 模块的可见性改为公共,增强库的可用性。
crates/mako/src/main.rs 简化 run 函数中对 serve 方法的调用,去掉了闭包参数。
crates/mako/src/plugin.rs 更新 PluginGenerateEndParams 结构,增加序列化能力并修改字段类型。
crates/mako/src/stats.rs 添加新方法以生成和打印编译统计信息,更新 StatsJsonMap 以包含更多字段。
crates/mako/src/utils/profile_gui.rs 移除 ProfileAppserve 方法的参数,简化处理方式。
docs/config.md 修改 TypeScript 类型定义,增强 stats 对象的灵活性。
docs/config.zh-CN.md 修改 TypeScript 接口定义,增强 stats 对象的灵活性。
e2e/fixtures/hooks/plugins.config.js 更新 generateEnd 函数签名,新增参数以接收统计信息并增强日志记录能力。
packages/mako/binding.d.ts 添加 TransformOutput 接口,扩展 JsHooksstats 属性以包含更详细的构建过程元数据。

Sequence Diagram(s)

sequenceDiagram
    participant DevServer
    participant Compiler
    participant Stats

    DevServer->>Compiler: invoke serve()
    Compiler->>Stats: collect build stats
    Stats-->>Compiler: return stats
    Compiler->>DevServer: complete serve()
Loading

在草地上跳跃的小兔,
新改动真是让人欢喜。
时间管理如春风,
统计信息更明确。
代码简化如花绽放,
维护更易乐无穷! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@xusd320 xusd320 requested a review from PeachScript July 25, 2024 06:54
@xusd320 xusd320 force-pushed the feat/generate-end-stats branch from 709d4a7 to 336a26e Compare July 25, 2024 06:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post

Actionable comments posted: 3

Outside diff range, codebase verification and nitpick comments (1)
e2e/fixtures/hooks/plugins.config.js (1)

10-13: 请更新 generateEnd 函数的调用

packages/bundler-mako/index.js 文件中,generateEnd 函数仍然使用旧的 (args) 签名。请将其更新为新的 (params) 签名,以确保一致性和正确功能。

  • packages/bundler-mako/index.js:
    generateEnd: (args) => {
      opts.onDevCompileDone(args);
    },
Analysis chain

请确认 generateEnd 函数的调用

新增的 params 参数和 console.dir 日志语句看起来是正确的。

请确保所有对 generateEnd 函数的调用都已更新为使用新的参数签名。

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `generateEnd` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type js -A 5 $'generateEnd'

Length of output: 741


Script:

#!/bin/bash
# Description: Verify the function definition and all usages of `generateEnd` in `packages/bundler-mako/index.js`.

# Test: Search for the function definition and its usages. Expect: All occurrences should match the new signature.
rg --type js -A 5 'generateEnd' packages/bundler-mako/index.js

Length of output: 171

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 5a1b9c4 and 709d4a7.

Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing (15)
  • crates/binding/src/lib.rs (1 hunks)
  • crates/binding/src/tsfn.rs (2 hunks)
  • crates/mako/Cargo.toml (1 hunks)
  • crates/mako/src/compiler.rs (4 hunks)
  • crates/mako/src/dev/mod.rs (10 hunks)
  • crates/mako/src/generate/mod.rs (16 hunks)
  • crates/mako/src/lib.rs (1 hunks)
  • crates/mako/src/main.rs (1 hunks)
  • crates/mako/src/plugin.rs (3 hunks)
  • crates/mako/src/stats.rs (10 hunks)
  • crates/mako/src/utils/profile_gui.rs (1 hunks)
  • docs/config.md (2 hunks)
  • docs/config.zh-CN.md (1 hunks)
  • e2e/fixtures/hooks/plugins.config.js (1 hunks)
  • packages/mako/binding.d.ts (2 hunks)
Files skipped from review due to trivial changes (1)
  • docs/config.md
Additional comments not posted (27)
crates/mako/src/utils/profile_gui.rs (1)

34-34: 请确认 serve 方法的调用

去除参数的 serve 方法调用看起来是正确的。

请确保所有对 serve 方法的调用都已更新为使用新的签名。

Verification successful

请确认 serve 方法的调用

去除参数的 serve 方法调用看起来是正确的。

  • crates/mako/src/main.rs
  • crates/mako/src/utils/profile_gui.rs
  • crates/mako/src/dev/mod.rs

所有对 serve 方法的调用都已更新为使用新的签名。

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all function calls to `serve` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type rust -A 5 $'serve'

Length of output: 21614

crates/mako/src/lib.rs (1)

19-19: 请确认 stats 模块的使用

stats 模块的可见性从私有改为公共看起来是正确的。

请确保所有对 stats 模块的引用都已正确更新。

crates/mako/src/main.rs (1)

99-99: 简化 serve 方法调用

移除闭包参数简化了 serve 方法调用。请确认此更改不会影响开发服务器的预期行为。

Verification successful

简化 serve 方法调用

移除闭包参数简化了 serve 方法调用。确认此更改不会影响开发服务器的预期行为。

  • crates/mako/src/dev/mod.rs 中的 serve 方法定义不包含任何参数。
  • 代码库中没有 serve 方法使用闭包参数的实例。
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the behavior of the DevServer after removing the closure parameter.

# Test: Search for the `serve` method implementation and its usage. Expect: No dependency on the removed closure parameter.
rg --type rust -A 5 $'fn serve'

Length of output: 390


Script:

#!/bin/bash
# Description: Verify if there were any previous definitions or usages of `serve` that included a closure parameter.

# Search for all instances of `serve` in the codebase to ensure no previous definitions or usages included a closure parameter.
rg --type rust 'serve('

Length of output: 88


Script:

#!/bin/bash
# Description: Verify if there were any previous definitions or usages of `serve` that included a closure parameter.

# Corrected search pattern for all instances of `serve(` in the codebase to ensure no previous definitions or usages included a closure parameter.
rg --type rust 'serve\('

Length of output: 442

crates/mako/Cargo.toml (1)

76-76: 添加 chrono 依赖

添加 chrono 依赖增强了日期和时间处理能力。请确认此添加是必要的,并且不会引入任何冲突。

Verification successful

添加 chrono 依赖

添加 chrono 依赖增强了日期和时间处理能力。经过验证,chrono crate 在代码库中的使用是有效的,且主要用于时间戳处理。

  • crates/mako/src/compiler.rs
  • crates/mako/src/stats.rs
  • crates/mako/src/dev/mod.rs
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the `chrono` crate in the codebase.

# Test: Search for the usage of `chrono` crate. Expect: Valid usage in the codebase.
rg --type rust -A 5 $'chrono'

Length of output: 2076

packages/mako/binding.d.ts (2)

19-51: 扩展 JsHooks 接口中的 stats 属性

扩展 stats 属性增强了 JavaScript 钩子的数据信息结构。请确认扩展结构在代码库中正确使用。

Verification successful

扩展 JsHooks 接口中的 stats 属性

扩展 stats 属性增强了 JavaScript 钩子的数据信息结构。通过验证,扩展结构在代码库中的多处使用均正确。

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the expanded `stats` property in the codebase.

# Test: Search for the usage of `stats` property in `JsHooks` interface. Expect: Valid usage in the codebase.
rg --type typescript -A 5 $'stats'

Length of output: 68


Script:

#!/bin/bash
# Description: Verify the usage of the expanded `stats` property in the codebase.

# Test: Search for the usage of `stats` property in `JsHooks` interface. Expect: Valid usage in the codebase.
rg -A 5 'stats'

Length of output: 48435


6-9: 添加 TransformOutput 接口

添加 TransformOutput 接口增强了转换输出的类型定义。请确认此接口在代码库中正确使用。

crates/binding/src/lib.rs (1)

220-220: 简化 serve 方法调用

移除不必要的闭包简化了代码,提高了可读性和可维护性。

crates/mako/src/plugin.rs (2)

6-6: 增加序列化功能和改进时间字段

PluginGenerateEndParams 派生 Serialize 特性增强了数据交换能力,将 time 字段类型更改为 i64 允许更广泛的时间戳范围。

Also applies to: 37-41


281-285: 更新参数名称以提高一致性

generate_end 方法中的参数名称更新为 params 提高了一致性和清晰度。

crates/binding/src/tsfn.rs (2)

18-58: 增强 generate_end 参数结构

generate_end 函数现在具有更复杂的参数结构,允许更全面地处理构建相关事件,提升了功能性和灵活性。


107-111: 简化 generate_end 函数实现

重构简化了代码,通过减少显式属性赋值提高了可维护性。

docs/config.zh-CN.md (1)

496-496: 增强了 stats 对象的灵活性

通过在 stats 对象中添加省略号 (...),允许包含更多动态属性。这提高了接口的扩展性。

crates/mako/src/compiler.rs (5)

20-20: 确认移除 PluginGenerateStats

请确认在其他地方不再需要 PluginGenerateStats,确保其移除不会影响功能。


342-342: 改进了时间处理的精度

使用 chrono::Local::now().timestamp_millis() 代替 std::time::SystemTime 提高了时间处理的精度和一致性。


391-416: 改进了控制流和错误处理

使用 match 语句处理构建结果,提高了代码的可读性并简化了错误处理逻辑。


406-410: 增强了 PluginGenerateEndParams 结构

PluginGenerateEndParams 结构中包含 start_timeend_time,确保提供全面的统计信息。


395-413: 改进了用户反馈和统计准确性

在成功构建的情况下,打印构建时长并使用更新的参数调用 generate_end,提高了用户反馈和统计信息的准确性。

crates/mako/src/dev/mod.rs (5)

36-36: 简化了 serve 方法

移除回调参数简化了 serve 方法,减少了潜在的错误来源。


46-50: 改进了变更监控流程

watch_for_changes 方法中直接处理重建,简化了变更监控流程。


Line range hint 336-380:
改进了时间跟踪和错误报告

使用 chrono::Local::now().timestamp_millis() 提高了时间跟踪的精度,并改进了错误报告的清晰度。


371-391: 增强了编译统计信息处理

emit_dev_chunks 方法中直接处理编译统计信息,并包含 start_timeend_time,确保统计信息的准确性和全面性。


390-391: 增强了 PluginGenerateEndParams 结构

PluginGenerateEndParams 结构中包含 start_timeend_time,确保提供全面的统计信息。

crates/mako/src/stats.rs (2)

318-319: 结构体修改通过

AssetsInfo 结构体的新字段 path 集成正确。


516-517: 结构体修改通过

StatsJsonMap 结构体的新字段 start_timeend_time 集成正确。

crates/mako/src/generate/mod.rs (3)

52-61: 函数修改通过

generate_bundless 函数的修改正确,返回 Result<StatsJsonMap> 符合预期。


Line range hint 76-217:
函数修改通过

generate 函数的修改正确,返回 Result<StatsJsonMap> 符合预期。


Line range hint 270-374:
函数修改通过

emit_dev_chunks 函数的修改正确,返回 Result<StatsJsonMap> 符合预期。

Comments failed to post (2)
crates/mako/src/stats.rs

212-308: 提高代码的可读性和性能

可以通过合并一些链式调用来提高代码的可读性和性能。

- let abs_path = &self.context.root;
- let output_path = &self.context.config.output.path;
- let dist_path = diff_paths(output_path, abs_path).unwrap_or_else(|| output_path.clone());
- let mut path_str = dist_path.to_str().unwrap().to_string();
+ let mut path_str = diff_paths(&self.context.config.output.path, &self.context.root)
+     .unwrap_or_else(|| self.context.config.output.path.clone())
+     .to_str()
+     .unwrap()
+     .to_string();
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    pub fn print_stats(&self) {
        let mut assets = self.context.stats_info.get_assets();
        // 按照产物名称排序
        assets.sort();
        // 产物路径需要按照 output.path 来
        let mut path_str = diff_paths(&self.context.config.output.path, &self.context.root)
            .unwrap_or_else(|| self.context.config.output.path.clone())
            .to_str()
            .unwrap()
            .to_string();
        if !path_str.ends_with('/') {
            path_str.push('/');
        }
        let dist = path_str.truecolor(128, 128, 128);

        // 最长的文件名字, size长度, map_size长度, 后续保持输出整齐
        let mut max_length_name = String::new();
        let mut max_size = 0;
        let mut max_map_size = 0;
        // 记录 name size map_size 的数组
        let mut assets_vec: Vec<(String, u64, u64)> = vec![];

        // 生成 (name, size, map_size) 的 vec
        for asset in assets {
            let name = asset.hashname.clone();
            let size_length = human_readable_size(asset.size).chars().count();
            // 记录较长的名字
            if name.chars().count() > max_length_name.chars().count() {
                max_length_name = name.clone();
            }

            // 如果是 .map 文件判断是否是上一个的文件的 sourceMap
            // 前面排序过了, sourceMap 一定 js/css 在后面
            if name.ends_with(".map") {
                let len = assets_vec.len();
                if let Some(last) = assets_vec.get_mut(len - 1) {
                    if name == format!("{}.map", last.0) {
                        // 记录较长的 map_size
                        if size_length > max_map_size {
                            max_map_size = size_length;
                        }
                        *last = (last.0.clone(), last.1, asset.size);
                        continue;
                    }
                }
            }
            // 记录较长的 size
            if size_length > max_size {
                max_size = size_length;
            }
            assets_vec.push((asset.hashname.clone(), asset.size, 0));
        }

        // Sort the output stats by their size in desc order
        assets_vec.sort_by_key(|(_, size, _)| std::cmp::Reverse(*size));
        // 输出 stats
        let mut s = String::new();
        for asset in assets_vec {
            let file_name = format!("{}{}", dist, asset.0);
            let length = format!("{}{}", dist, max_length_name).chars().count() + 2;
            let file_name_str: String = pad_string(&file_name, length, false);
            let color_file_name_str = match file_name {
                s if s.ends_with(".js") => file_name_str.cyan(),
                s if s.ends_with(".css") => file_name_str.magenta(),
                _ => file_name_str.green(),
            };
            // 没有 map 的输出
            if asset.2 == 0 {
                let size = human_readable_size(asset.1);
                s.push_str(
                    format!(
                        "{} {}\n",
                        color_file_name_str,
                        pad_string(&size, max_size, true),
                    )
                    .as_str(),
                );
            } else {
                // 有 map 的输出, | map: map_size
                let size = human_readable_size(asset.1);
                let map_size = human_readable_size(asset.2);
                s.push_str(
                    format!(
                        "{} {} {} {}\n",
                        color_file_name_str,
                        pad_string(&size, max_size, true)
                            .truecolor(128, 128, 128)
                            .bold(),
                        "│ map:".truecolor(128, 128, 128),
                        pad_string(&map_size, max_map_size, true).truecolor(128, 128, 128)
                    )
                    .as_str(),
                );
            }
        }

        println!("{}", s.trim_end_matches('\n'));
    }

19-210: 改进文件大小计算的错误处理

当前对 file_size 函数的错误处理过于简单,仅仅返回 0。建议记录错误日志以便于调试。

- let size = file_size(asset.0).unwrap();
+ let size = match file_size(asset.0) {
+     Ok(size) => size,
+     Err(e) => {
+         eprintln!("Failed to get file size for {}: {}", asset.0, e);
+         0
+     }
+ };
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    pub fn create_stats_info(&self) -> StatsJsonMap {
        let mut stats_map = StatsJsonMap::new();
        let context = self.context.clone();

        // 获取 hash
        let hash = self.full_hash();
        // 获取 root_path
        let root_path = context.root.to_string_lossy().to_string();
        // 获取 output_path
        let output_path = context.config.output.path.to_string_lossy().to_string();

        stats_map.built_at = chrono::Local::now().timestamp_millis();
        stats_map.hash = hash;
        stats_map.root_path = root_path;
        stats_map.output_path = output_path;

        let stats_info = &context.stats_info;

        // 把 context 中的静态资源信息加入到 stats_info 中
        self.context
            .assets_info
            .lock()
            .unwrap()
            .iter()
            .for_each(|asset| {
                let size = match file_size(asset.0) {
                    Ok(size) => size,
                    Err(e) => {
                        eprintln!("Failed to get file size for {}: {}", asset.0, e);
                        0
                    }
                };
                stats_info.add_assets(
                    size,
                    asset.1.clone(),
                    "".to_string(),
                    self.context
                        .config
                        .output
                        .path
                        .join(asset.1.clone())
                        .to_string_lossy()
                        .to_string(),
                    asset.1.clone(),
                );
            });

        // 获取 assets
        stats_map.assets = stats_info
            .get_assets()
            .iter()
            .map(|asset| StatsJsonAssetsItem {
                assets_type: StatsJsonType::Asset(asset.assets_type.clone()),
                size: asset.size,
                name: asset.hashname.clone(),
                path: asset.path.clone(),
            })
            .collect();

        let chunk_graph = self.context.chunk_graph.read().unwrap();
        let module_graph = self.context.module_graph.read().unwrap();
        let chunks = chunk_graph.get_chunks();

        // 在 chunks 中获取 modules
        let mut chunk_modules: Vec<StatsJsonChunkModuleItem> = Vec::new();

        // 获取 chunks
        stats_map.chunks = chunks
            .iter()
            .map(|chunk| {
                let modules = chunk.get_modules();
                let entry = matches!(chunk.chunk_type, ChunkType::Entry(_, _, _));
                let id = chunk.id.id.clone();
                let chunk_modules: Vec<StatsJsonChunkModuleItem> = modules
                    .iter()
                    .filter(|module| {
                        // ?modules 是虚拟模块,暂不记录
                        // TODO: 支持虚拟模块属性,同时增加 content 以用于 size 计算等用途
                        !module.id.contains("?modules")
                    })
                    .map(|module| {
                        let id = module.id.clone();
                        // 去拿 module 的文件 size 时,有可能 module 不存在,size 则设为 0
                        // 场景: xlsx 中引入了 fs 模块
                        let size = match file_size(&id) {
                            Ok(size) => size,
                            Err(..) => 0,
                        };
                        let module = StatsJsonChunkModuleItem {
                            module_type: StatsJsonType::Module("module".to_string()),
                            size,
                            id,
                            // TODO: 现在是从每个 chunk 中找到包含的 module, 所以 chunk_id 是单个, 但是一个 module 有可能存在于多个 chunk 中
                            chunks: vec![chunk.id.id.clone()],
                        };
                        chunk_modules.push(module.clone());
                        module
                    })
                    .collect();
                let files: Vec<String> = stats_info
                    .get_assets()
                    .iter()
                    .filter(|asset| asset.chunk_id == id)
                    .map(|asset| asset.hashname.clone())
                    .collect();
                let siblings = chunk_graph
                    .sync_dependencies_chunk(&chunk.id)
                    .iter()
                    .map(|id| id.id.clone())
                    .collect::<Vec<_>>();
                let origin_chunk_modules = match chunk.chunk_type {
                    // sync chunk is the common dependency of async chunk
                    // so the origin chunk module within its dependent async chunk rather than itself
                    ChunkType::Sync => chunk_graph
                        .dependents_chunk(&chunk.id)
                        .iter()
                        .filter_map(|chunk_id| chunk_graph.chunk(chunk_id).unwrap().modules.last())
                        .collect::<Vec<_>>(),
                    _ => vec![chunk.modules.last().unwrap()],
                };
                let mut origins_set = IndexMap::new();
                for origin_chunk_module in origin_chunk_modules {
                    let origin_deps = module_graph.get_dependents(origin_chunk_module);

                    for (id, dep) in origin_deps {
                        let unique_key = format!("{}:{}", id.id, dep.source);

                        if !origins_set.contains_key(&unique_key) {
                            origins_set.insert(
                                unique_key,
                                StatsJsonChunkOriginItem {
                                    module: id.id.clone(),
                                    module_identifier: id.id.clone(),
                                    module_name: module_graph
                                        .get_module(id)
                                        .and_then(|module| {
                                            module.info.as_ref().map(|info| {
                                                info.file.path.to_string_lossy().to_string()
                                            })
                                        })
                                        .unwrap_or("".to_string()),
                                    // -> "lo-hi"
                                    loc: dep
                                        .span
                                        .map(|span| {
                                            format!("{}-{}", span.lo.to_u32(), span.hi.to_u32())
                                        })
                                        .unwrap_or("".to_string()),
                                    request: dep.source.clone(),
                                },
                            );
                        }
                    }
                }
                let origins = origins_set.into_values().collect::<Vec<_>>();

                StatsJsonChunkItem {
                    chunk_type: StatsJsonType::Chunk("chunk".to_string()),
                    id,
                    files,
                    entry,
                    modules: chunk_modules,
                    siblings,
                    origins,
                }
            })
            .collect();
        stats_map.entrypoints = chunks
            .iter()
            .filter_map(|chunk| match &chunk.chunk_type {
                ChunkType::Entry(_, name, _) => {
                    let mut chunks = chunk_graph
                        .entry_dependencies_chunk(&chunk.id)
                        .into_iter()
                        .map(|id| id.id)
                        .collect::<Vec<_>>();

                    chunks.push(chunk.id.id.clone());

                    Some((
                        name.clone(),
                        StatsJsonEntryItem {
                            name: name.clone(),
                            chunks,
                        },
                    ))
                }
                _ => None,
            })
            .collect::<HashMap<_, _>>();
        stats_map.chunk_modules = chunk_modules;

        stats_map.modules = stats_info.get_modules();
        stats_map.rsc_client_components = stats_info.get_rsc_client_components();
        stats_map.rsc_css_modules = stats_info.get_rsc_css_modules();

        stats_map
    }

提高代码的可读性和性能

可以通过合并一些链式调用来提高代码的可读性和性能。

- let root_path = context.root.to_string_lossy().to_string();
- let output_path = context.config.output.path.to_string_lossy().to_string();
+ let root_path = context.root.to_string_lossy().into_owned();
+ let output_path = context.config.output.path.to_string_lossy().into_owned();
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    pub fn create_stats_info(&self) -> StatsJsonMap {
        let mut stats_map = StatsJsonMap::new();
        let context = self.context.clone();

        // 获取 hash
        let hash = self.full_hash();
        // 获取 root_path
        let root_path = context.root.to_string_lossy().into_owned();
        // 获取 output_path
        let output_path = context.config.output.path.to_string_lossy().into_owned();

        stats_map.built_at = chrono::Local::now().timestamp_millis();
        stats_map.hash = hash;
        stats_map.root_path = root_path;
        stats_map.output_path = output_path;

        let stats_info = &context.stats_info;

        // 把 context 中的静态资源信息加入到 stats_info 中
        self.context
            .assets_info
            .lock()
            .unwrap()
            .iter()
            .for_each(|asset| {
                let size = file_size(asset.0).unwrap();
                stats_info.add_assets(
                    size,
                    asset.1.clone(),
                    "".to_string(),
                    self.context
                        .config
                        .output
                        .path
                        .join(asset.1.clone())
                        .to_string_lossy()
                        .to_string(),
                    asset.1.clone(),
                );
            });

        // 获取 assets
        stats_map.assets = stats_info
            .get_assets()
            .iter()
            .map(|asset| StatsJsonAssetsItem {
                assets_type: StatsJsonType::Asset(asset.assets_type.clone()),
                size: asset.size,
                name: asset.hashname.clone(),
                path: asset.path.clone(),
            })
            .collect();

        let chunk_graph = self.context.chunk_graph.read().unwrap();
        let module_graph = self.context.module_graph.read().unwrap();
        let chunks = chunk_graph.get_chunks();

        // 在 chunks 中获取 modules
        let mut chunk_modules: Vec<StatsJsonChunkModuleItem> = Vec::new();

        // 获取 chunks
        stats_map.chunks = chunks
            .iter()
            .map(|chunk| {
                let modules = chunk.get_modules();
                let entry = matches!(chunk.chunk_type, ChunkType::Entry(_, _, _));
                let id = chunk.id.id.clone();
                let chunk_modules: Vec<StatsJsonChunkModuleItem> = modules
                    .iter()
                    .filter(|module| {
                        // ?modules 是虚拟模块,暂不记录
                        // TODO: 支持虚拟模块属性,同时增加 content 以用于 size 计算等用途
                        !module.id.contains("?modules")
                    })
                    .map(|module| {
                        let id = module.id.clone();
                        // 去拿 module 的文件 size 时,有可能 module 不存在,size 则设为 0
                        // 场景: xlsx 中引入了 fs 模块
                        let size = match file_size(&id) {
                            Ok(size) => size,
                            Err(..) => 0,
                        };
                        let module = StatsJsonChunkModuleItem {
                            module_type: StatsJsonType::Module("module".to_string()),
                            size,
                            id,
                            // TODO: 现在是从每个 chunk 中找到包含的 module, 所以 chunk_id 是单个, 但是一个 module 有可能存在于多个 chunk 中
                            chunks: vec![chunk.id.id.clone()],
                        };
                        chunk_modules.push(module.clone());
                        module
                    })
                    .collect();
                let files: Vec<String> = stats_info
                    .get_assets()
                    .iter()
                    .filter(|asset| asset.chunk_id == id)
                    .map(|asset| asset.hashname.clone())
                    .collect();
                let siblings = chunk_graph
                    .sync_dependencies_chunk(&chunk.id)
                    .iter()
                    .map(|id| id.id.clone())
                    .collect::<Vec<_>>();
                let origin_chunk_modules = match chunk.chunk_type {
                    // sync chunk is the common dependency of async chunk
                    // so the origin chunk module within its dependent async chunk rather than itself
                    ChunkType::Sync => chunk_graph
                        .dependents_chunk(&chunk.id)
                        .iter()
                        .filter_map(|chunk_id| chunk_graph.chunk(chunk_id).unwrap().modules.last())
                        .collect::<Vec<_>>(),
                    _ => vec![chunk.modules.last().unwrap()],
                };
                let mut origins_set = IndexMap::new();
                for origin_chunk_module in origin_chunk_modules {
                    let origin_deps = module_graph.get_dependents(origin_chunk_module);

                    for (id, dep) in origin_deps {
                        let unique_key = format!("{}:{}", id.id, dep.source);

                        if !origins_set.contains_key(&unique_key) {
                            origins_set.insert(
                                unique_key,
                                StatsJsonChunkOriginItem {
                                    module: id.id.clone(),
                                    module_identifier: id.id.clone(),
                                    module_name: module_graph
                                        .get_module(id)
                                        .and_then(|module| {
                                            module.info.as_ref().map(|info| {
                                                info.file.path.to_string_lossy().to_string()
                                            })
                                        })
                                        .unwrap_or("".to_string()),
                                    // -> "lo-hi"
                                    loc: dep
                                        .span
                                        .map(|span| {
                                            format!("{}-{}", span.lo.to_u32(), span.hi.to_u32())
                                        })
                                        .unwrap_or("".to_string()),
                                    request: dep.source.clone(),
                                },
                            );
                        }
                    }
                }
                let origins = origins_set.into_values().collect::<Vec<_>>();

                StatsJsonChunkItem {
                    chunk_type: StatsJsonType::Chunk("chunk".to_string()),
                    id,
                    files,
                    entry,
                    modules: chunk_modules,
                    siblings,
                    origins,
                }
            })
            .collect();
        stats_map.entrypoints = chunks
            .iter()
            .filter_map(|chunk| match &chunk.chunk_type {
                ChunkType::Entry(_, name, _) => {
                    let mut chunks = chunk_graph
                        .entry_dependencies_chunk(&chunk.id)
                        .into_iter()
                        .map(|id| id.id)
                        .collect::<Vec<_>>();

                    chunks.push(chunk.id.id.clone());

                    Some((
                        name.clone(),
                        StatsJsonEntryItem {
                            name: name.clone(),
                            chunks,
                        },
                    ))
                }
                _ => None,
            })
            .collect::<HashMap<_, _>>();
        stats_map.chunk_modules = chunk_modules;

        stats_map.modules = stats_info.get_modules();
        stats_map.rsc_client_components = stats_info.get_rsc_client_components();
        stats_map.rsc_css_modules = stats_info.get_rsc_css_modules();

        stats_map
    }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 709d4a7 and 336a26e.

Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing (15)
  • crates/binding/src/lib.rs (1 hunks)
  • crates/binding/src/tsfn.rs (2 hunks)
  • crates/mako/Cargo.toml (1 hunks)
  • crates/mako/src/compiler.rs (4 hunks)
  • crates/mako/src/dev/mod.rs (10 hunks)
  • crates/mako/src/generate/mod.rs (16 hunks)
  • crates/mako/src/lib.rs (1 hunks)
  • crates/mako/src/main.rs (1 hunks)
  • crates/mako/src/plugin.rs (3 hunks)
  • crates/mako/src/stats.rs (10 hunks)
  • crates/mako/src/utils/profile_gui.rs (1 hunks)
  • docs/config.md (2 hunks)
  • docs/config.zh-CN.md (1 hunks)
  • e2e/fixtures/hooks/plugins.config.js (1 hunks)
  • packages/mako/binding.d.ts (2 hunks)
Files skipped from review due to trivial changes (2)
  • crates/mako/src/lib.rs
  • docs/config.md
Files skipped from review as they are similar to previous changes (11)
  • crates/binding/src/lib.rs
  • crates/binding/src/tsfn.rs
  • crates/mako/Cargo.toml
  • crates/mako/src/compiler.rs
  • crates/mako/src/generate/mod.rs
  • crates/mako/src/main.rs
  • crates/mako/src/plugin.rs
  • crates/mako/src/utils/profile_gui.rs
  • docs/config.zh-CN.md
  • e2e/fixtures/hooks/plugins.config.js
  • packages/mako/binding.d.ts
Additional comments not posted (10)
crates/mako/src/dev/mod.rs (5)

371-380: 时间跟踪精度改进

emit_dev_chunks 方法现在使用 chrono::Local::now().timestamp_millis() 记录时间戳,提升了时间跟踪的精度。


Line range hint 78-131:
请求处理改进

handle_requests 方法的更新提升了错误处理和响应生成的健壮性和清晰度。


286-288: 文件监控改进

watch_for_changes 方法的更新提升了文件监控的可靠性,并确保错误得到妥善处理。


Line range hint 336-380:
验证错误处理逻辑

rebuild 方法现在直接在 emit_dev_chunks 方法中处理编译时间统计信息。请确保错误处理逻辑足够健壮,以应对可能的异常情况。

Verification successful

验证错误处理逻辑

rebuild 方法现在直接在 emit_dev_chunks 方法中处理编译时间统计信息。经过检查,错误处理逻辑足够健壮,能够应对可能的异常情况。

  • update_result 处理失败时记录错误并返回 Ok(())
  • next_hash 处理失败时记录错误并返回 Err(e)
  • emit_dev_chunks 方法内使用 map_err 处理错误。
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the error handling logic in the rebuild method.

# Test: Search for error handling in the rebuild method. Expect: Proper error handling for all potential errors.
rg --type rust -A 5 $'fn rebuild'

Length of output: 394


Script:

#!/bin/bash
# Description: Retrieve the full implementation of the rebuild method for error handling verification.

# Search for the complete implementation of the rebuild method.
rg --type rust -A 50 'fn rebuild' crates/mako/src/dev/mod.rs

Length of output: 1807


Script:

#!/bin/bash
# Description: Retrieve the remaining implementation of the rebuild method for error handling verification.

# Search for the complete implementation of the rebuild method, extending the line range.
rg --type rust -A 100 'fn rebuild' crates/mako/src/dev/mod.rs

Length of output: 3475


Line range hint 36-50:
验证移除回调参数的影响

serve 方法移除了回调参数,简化了控制流。请确保代码库中所有依赖此回调的部分已被正确更新。

Verification successful

验证移除回调参数的影响

serve 方法移除了回调参数,简化了控制流。搜索结果表明,代码库中没有任何使用回调参数的 serve 方法调用。这表明代码库已正确更新。

  • crates/binding/src/lib.rs
  • crates/mako/src/main.rs
  • crates/mako/src/utils/profile_gui.rs
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the impact of removing the callback parameter from the serve method.

# Test: Search for the usage of the serve method. Expect: No usage with callback parameter.
rg --type rust -A 5 $'serve('

Length of output: 93


Script:

#!/bin/bash
# Description: Verify the impact of removing the callback parameter from the serve method.

# Test: Search for the usage of the serve method. Expect: No usage with callback parameter.
rg --type rust -A 5 'serve('

Length of output: 93


Script:

#!/bin/bash
# Description: Verify the impact of removing the callback parameter from the serve method.

# Test: Search for the usage of the serve method. Expect: No usage with callback parameter.
rg --type rust -A 5 'serve\('

Length of output: 1985


Script:

#!/bin/bash
# Description: Verify the impact of removing the callback parameter from the serve method.

# Test: Search for the usage of the serve method with a callback parameter in the specified files.
rg --type rust 'serve\([^)]*\)' crates/binding/src/lib.rs crates/mako/src/main.rs crates/mako/src/utils/profile_gui.rs

Length of output: 306

crates/mako/src/stats.rs (5)

19-210: 编译过程统计信息改进

create_stats_info 方法的更新增强了对编译过程的跟踪和报告能力,提升了功能性和可用性。


212-308: 统计信息输出改进

print_stats 方法的更新提升了生成统计信息的可读性和一致性。


Line range hint 501-535:
统计信息结构改进

StatsJsonMap 结构的更新简化了路径处理,并增强了统计信息的全面性。


Line range hint 307-319:
资源信息结构改进

AssetsInfo 结构的更新确保了与 StatsJsonMap 结构的一致性。


Line range hint 364-534:
统计信息管理改进

StatsInfo 类的更新增强了管理和报告资源信息的能力。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 336a26e and 9d8c046.

Files selected for processing (1)
  • crates/mako/src/generate/mod.rs (16 hunks)
Files skipped from review as they are similar to previous changes (1)
  • crates/mako/src/generate/mod.rs

Copy link
Contributor

@PeachScript PeachScript left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants