From bcdebd0beafb9c2c233434fa77fc98a5a781d8bc Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:18:01 +0200 Subject: [PATCH] Turbopack: trace fs-extra calls --- .../turbopack-ecmascript/src/analyzer/mod.rs | 6 ++ .../src/analyzer/well_known.rs | 42 ++++++++++ .../crates/turbopack-ecmascript/src/utils.rs | 1 + .../test/unit/asset-fs-extra/output.js | 78 +++++++++---------- .../crates/turbopack-tracing/tests/unit.rs | 12 ++- 5 files changed, 96 insertions(+), 43 deletions(-) diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs index c0678e32941231..2468b3cb71f1fc 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs @@ -1772,6 +1772,10 @@ impl JsValue { "fs", "The Node.js fs module: https://nodejs.org/api/fs.html", ), + WellKnownObjectKind::FsExtraModule | WellKnownObjectKind::FsExtraModuleDefault => ( + "fs-extra", + "The Node.js fs-extra module: https://github.com/jprichardson/node-fs-extra", + ), WellKnownObjectKind::FsModulePromises => ( "fs/promises", "The Node.js fs module: https://nodejs.org/api/fs.html#promises-api", @@ -3450,6 +3454,8 @@ pub enum WellKnownObjectKind { FsModule, FsModuleDefault, FsModulePromises, + FsExtraModule, + FsExtraModuleDefault, UrlModule, UrlModuleDefault, ChildProcess, diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs index 8a429c017268a9..039b82a25f13c6 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/well_known.rs @@ -600,6 +600,9 @@ async fn well_known_object_member( WellKnownObjectKind::FsModule | WellKnownObjectKind::FsModuleDefault | WellKnownObjectKind::FsModulePromises => fs_module_member(kind, prop), + WellKnownObjectKind::FsExtraModule | WellKnownObjectKind::FsExtraModuleDefault => { + fs_extra_module_member(kind, prop) + } WellKnownObjectKind::UrlModule | WellKnownObjectKind::UrlModuleDefault => { url_module_member(kind, prop) } @@ -693,6 +696,45 @@ fn fs_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue { ) } +fn fs_extra_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue { + if let Some(word) = prop.as_str() { + match (kind, word) { + // regular fs methods + ( + .., + "realpath" | "realpathSync" | "stat" | "statSync" | "existsSync" + | "createReadStream" | "exists" | "open" | "openSync" | "readFile" | "readFileSync", + ) => { + return JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod( + word.into(), + )); + } + // fs-extra specific + ( + .., + "pathExists" | "pathExistsSync" | "readJson" | "readJSON" | "readJsonSync" + | "readJSONSync", + ) => { + return JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod( + word.into(), + )); + } + (WellKnownObjectKind::FsExtraModule, "default") => { + return JsValue::WellKnownObject(WellKnownObjectKind::FsExtraModuleDefault); + } + _ => {} + } + } + JsValue::unknown( + JsValue::member( + Box::new(JsValue::WellKnownObject(WellKnownObjectKind::FsExtraModule)), + Box::new(prop), + ), + true, + "unsupported property on fs-extra module", + ) +} + fn url_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue { match (kind, prop.as_str()) { (.., Some("pathToFileURL")) => { diff --git a/turbopack/crates/turbopack-ecmascript/src/utils.rs b/turbopack/crates/turbopack-ecmascript/src/utils.rs index 38bd217058c4c3..a541b260f55a4f 100644 --- a/turbopack/crates/turbopack-ecmascript/src/utils.rs +++ b/turbopack/crates/turbopack-ecmascript/src/utils.rs @@ -214,6 +214,7 @@ pub fn module_value_to_well_known_object(module_value: &ModuleValue) -> Option JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom), "@grpc/proto-loader" => JsValue::WellKnownObject(WellKnownObjectKind::NodeProtobufLoader), + "fs-extra" => JsValue::WellKnownObject(WellKnownObjectKind::FsExtraModule), _ => return None, }) } diff --git a/turbopack/crates/turbopack-tracing/tests/node-file-trace/test/unit/asset-fs-extra/output.js b/turbopack/crates/turbopack-tracing/tests/node-file-trace/test/unit/asset-fs-extra/output.js index 11ecba72e66a66..a9acfbc9a379c0 100644 --- a/turbopack/crates/turbopack-tracing/tests/node-file-trace/test/unit/asset-fs-extra/output.js +++ b/turbopack/crates/turbopack-tracing/tests/node-file-trace/test/unit/asset-fs-extra/output.js @@ -1,43 +1,43 @@ ;[ - 'node_modules/fs-extra/lib/copy/copy-sync.js', - 'node_modules/fs-extra/lib/copy/copy.js', - 'node_modules/fs-extra/lib/copy/index.js', - 'node_modules/fs-extra/lib/empty/index.js', - 'node_modules/fs-extra/lib/ensure/file.js', - 'node_modules/fs-extra/lib/ensure/index.js', - 'node_modules/fs-extra/lib/ensure/link.js', - 'node_modules/fs-extra/lib/ensure/symlink-paths.js', - 'node_modules/fs-extra/lib/ensure/symlink-type.js', - 'node_modules/fs-extra/lib/ensure/symlink.js', - 'node_modules/fs-extra/lib/fs/index.js', - 'node_modules/fs-extra/lib/index.js', - 'node_modules/fs-extra/lib/json/index.js', - 'node_modules/fs-extra/lib/json/jsonfile.js', - 'node_modules/fs-extra/lib/json/output-json-sync.js', - 'node_modules/fs-extra/lib/json/output-json.js', - 'node_modules/fs-extra/lib/mkdirs/index.js', - 'node_modules/fs-extra/lib/mkdirs/make-dir.js', - 'node_modules/fs-extra/lib/mkdirs/utils.js', - 'node_modules/fs-extra/lib/move/index.js', - 'node_modules/fs-extra/lib/move/move-sync.js', - 'node_modules/fs-extra/lib/move/move.js', - 'node_modules/fs-extra/lib/output-file/index.js', - 'node_modules/fs-extra/lib/path-exists/index.js', - 'node_modules/fs-extra/lib/remove/index.js', - 'node_modules/fs-extra/lib/remove/rimraf.js', - 'node_modules/fs-extra/lib/util/stat.js', - 'node_modules/fs-extra/lib/util/utimes.js', - 'node_modules/fs-extra/package.json', - 'node_modules/graceful-fs/clone.js', - 'node_modules/graceful-fs/graceful-fs.js', - 'node_modules/graceful-fs/legacy-streams.js', - 'node_modules/graceful-fs/package.json', - 'node_modules/graceful-fs/polyfills.js', - 'node_modules/jsonfile/index.js', - 'node_modules/jsonfile/package.json', - 'node_modules/jsonfile/utils.js', - 'node_modules/universalify/index.js', - 'node_modules/universalify/package.json', + // 'node_modules/fs-extra/lib/copy/copy-sync.js', + // 'node_modules/fs-extra/lib/copy/copy.js', + // 'node_modules/fs-extra/lib/copy/index.js', + // 'node_modules/fs-extra/lib/empty/index.js', + // 'node_modules/fs-extra/lib/ensure/file.js', + // 'node_modules/fs-extra/lib/ensure/index.js', + // 'node_modules/fs-extra/lib/ensure/link.js', + // 'node_modules/fs-extra/lib/ensure/symlink-paths.js', + // 'node_modules/fs-extra/lib/ensure/symlink-type.js', + // 'node_modules/fs-extra/lib/ensure/symlink.js', + // 'node_modules/fs-extra/lib/fs/index.js', + // 'node_modules/fs-extra/lib/index.js', + // 'node_modules/fs-extra/lib/json/index.js', + // 'node_modules/fs-extra/lib/json/jsonfile.js', + // 'node_modules/fs-extra/lib/json/output-json-sync.js', + // 'node_modules/fs-extra/lib/json/output-json.js', + // 'node_modules/fs-extra/lib/mkdirs/index.js', + // 'node_modules/fs-extra/lib/mkdirs/make-dir.js', + // 'node_modules/fs-extra/lib/mkdirs/utils.js', + // 'node_modules/fs-extra/lib/move/index.js', + // 'node_modules/fs-extra/lib/move/move-sync.js', + // 'node_modules/fs-extra/lib/move/move.js', + // 'node_modules/fs-extra/lib/output-file/index.js', + // 'node_modules/fs-extra/lib/path-exists/index.js', + // 'node_modules/fs-extra/lib/remove/index.js', + // 'node_modules/fs-extra/lib/remove/rimraf.js', + // 'node_modules/fs-extra/lib/util/stat.js', + // 'node_modules/fs-extra/lib/util/utimes.js', + // 'node_modules/fs-extra/package.json', + // 'node_modules/graceful-fs/clone.js', + // 'node_modules/graceful-fs/graceful-fs.js', + // 'node_modules/graceful-fs/legacy-streams.js', + // 'node_modules/graceful-fs/package.json', + // 'node_modules/graceful-fs/polyfills.js', + // 'node_modules/jsonfile/index.js', + // 'node_modules/jsonfile/package.json', + // 'node_modules/jsonfile/utils.js', + // 'node_modules/universalify/index.js', + // 'node_modules/universalify/package.json', 'package.json', 'test/unit/asset-fs-extra/asset1.txt', 'test/unit/asset-fs-extra/asset2.json', diff --git a/turbopack/crates/turbopack-tracing/tests/unit.rs b/turbopack/crates/turbopack-tracing/tests/unit.rs index 9744aa725252a3..455fe9038f19b6 100644 --- a/turbopack/crates/turbopack-tracing/tests/unit.rs +++ b/turbopack/crates/turbopack-tracing/tests/unit.rs @@ -44,12 +44,13 @@ static ALLOC: turbo_tasks_malloc::TurboMalloc = turbo_tasks_malloc::TurboMalloc; // TODO fix failures #[rstest] #[case::amd_disable("amd-disable")] -// #[case::array_emission("array-emission")] +#[case::array_emission("array-emission")] #[case::array_holes("array-holes")] +// Ternary currently becomes Unknown as opposed to Alternatives when the condition isn't static // #[case::asset_conditional("asset-conditional")] #[case::asset_fs_array_expr("asset-fs-array-expr")] #[case::asset_fs_array_expr_node_prefix("asset-fs-array-expr-node-prefix")] -// #[case::asset_fs_extra("asset-fs-extra")] +#[case::asset_fs_extra("asset-fs-extra")] #[case::asset_fs_inline_path_babel("asset-fs-inline-path-babel")] #[case::asset_fs_inline_path_enc_es("asset-fs-inline-path-enc-es")] #[case::asset_fs_inline_path_enc_es_2("asset-fs-inline-path-enc-es-2")] @@ -273,6 +274,8 @@ async fn to_list(assets: Vec>>) -> Result = LazyLock::new(|| Regex::new(r",[\s\n]*\]").unwrap()); +static LINE_COMMENTS_COMMA: LazyLock = + LazyLock::new(|| Regex::new(r"(?m)^\s*//.*$").unwrap()); fn node_file_trace(input_path: &str) -> Result<()> { let r = &mut { @@ -305,8 +308,9 @@ fn node_file_trace(input_path: &str) -> Result<()> { let reference = std::fs::read_to_string(reference)?; // crude JS -> JSON conversion - let reference = TRAILING_COMMA - .replace(&reference, "]") + let reference = TRAILING_COMMA.replace(&reference, "]"); + let reference = LINE_COMMENTS_COMMA + .replace_all(&reference, "") .replace(";", "") .replace('\'', "\""); let reference = serde_json::from_str::>(&reference)?