From c6fa62896d2b1f6b4661659fe977ef4e2feffd9a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 11 Dec 2024 09:40:50 -0500 Subject: [PATCH] fix(compile): output contents of embedded file system (#27302) --- cli/standalone/binary.rs | 59 +++--- cli/standalone/virtual_fs.rs | 168 ++++++++++++++++++ cli/tools/compile.rs | 53 +++--- cli/tools/info.rs | 100 ++--------- cli/util/display.rs | 73 ++++++++ tests/integration/compile_tests.rs | 5 + .../compile/env_vars_support/compile.out | 6 + .../compile/package_json_type/compile.out | 4 + .../main_compile_file.out | 6 +- .../main_compile_folder.out | 6 +- 10 files changed, 344 insertions(+), 136 deletions(-) diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index dec1d89110..7728728a26 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -87,6 +87,7 @@ use super::serialization::DenoCompileModuleData; use super::serialization::DeserializedDataSection; use super::serialization::RemoteModulesStore; use super::serialization::RemoteModulesStoreBuilder; +use super::virtual_fs::output_vfs; use super::virtual_fs::FileBackedVfs; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VfsFileSubDataKind; @@ -367,6 +368,16 @@ pub fn extract_standalone( })) } +pub struct WriteBinOptions<'a> { + pub writer: File, + pub display_output_filename: &'a str, + pub graph: &'a ModuleGraph, + pub root_dir_url: StandaloneRelativeFileBaseUrl<'a>, + pub entrypoint: &'a ModuleSpecifier, + pub include_files: &'a [ModuleSpecifier], + pub compile_flags: &'a CompileFlags, +} + pub struct DenoCompileBinaryWriter<'a> { cjs_tracker: &'a CjsTracker, cli_options: &'a CliOptions, @@ -407,18 +418,14 @@ impl<'a> DenoCompileBinaryWriter<'a> { pub async fn write_bin( &self, - writer: File, - graph: &ModuleGraph, - root_dir_url: StandaloneRelativeFileBaseUrl<'_>, - entrypoint: &ModuleSpecifier, - include_files: &[ModuleSpecifier], - compile_flags: &CompileFlags, + options: WriteBinOptions<'_>, ) -> Result<(), AnyError> { // Select base binary based on target - let mut original_binary = self.get_base_binary(compile_flags).await?; + let mut original_binary = + self.get_base_binary(options.compile_flags).await?; - if compile_flags.no_terminal { - let target = compile_flags.resolve_target(); + if options.compile_flags.no_terminal { + let target = options.compile_flags.resolve_target(); if !target.contains("windows") { bail!( "The `--no-terminal` flag is only available when targeting Windows (current: {})", @@ -428,8 +435,8 @@ impl<'a> DenoCompileBinaryWriter<'a> { set_windows_binary_to_gui(&mut original_binary) .context("Setting windows binary to GUI.")?; } - if compile_flags.icon.is_some() { - let target = compile_flags.resolve_target(); + if options.compile_flags.icon.is_some() { + let target = options.compile_flags.resolve_target(); if !target.contains("windows") { bail!( "The `--icon` flag is only available when targeting Windows (current: {})", @@ -437,17 +444,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { ) } } - self - .write_standalone_binary( - writer, - original_binary, - graph, - root_dir_url, - entrypoint, - include_files, - compile_flags, - ) - .await + self.write_standalone_binary(options, original_binary).await } async fn get_base_binary( @@ -552,14 +549,18 @@ impl<'a> DenoCompileBinaryWriter<'a> { #[allow(clippy::too_many_arguments)] async fn write_standalone_binary( &self, - writer: File, + options: WriteBinOptions<'_>, original_bin: Vec, - graph: &ModuleGraph, - root_dir_url: StandaloneRelativeFileBaseUrl<'_>, - entrypoint: &ModuleSpecifier, - include_files: &[ModuleSpecifier], - compile_flags: &CompileFlags, ) -> Result<(), AnyError> { + let WriteBinOptions { + writer, + display_output_filename, + graph, + root_dir_url, + entrypoint, + include_files, + compile_flags, + } = options; let ca_data = match self.cli_options.ca_data() { Some(CaData::File(ca_file)) => Some( std::fs::read(ca_file).with_context(|| format!("Reading {ca_file}"))?, @@ -784,6 +785,8 @@ impl<'a> DenoCompileBinaryWriter<'a> { otel_config: self.cli_options.otel_config(), }; + output_vfs(&vfs, display_output_filename); + write_binary_bytes( writer, original_bin, diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index 814774b71b..ce7c0bb625 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -31,6 +31,7 @@ use serde::Serialize; use thiserror::Error; use crate::util; +use crate::util::display::DisplayTreeNode; use crate::util::fs::canonicalize_path; #[derive(Debug, Copy, Clone)] @@ -51,6 +52,7 @@ pub struct StripRootError { target: PathBuf, } +#[derive(Debug)] pub struct VfsBuilder { root_path: PathBuf, root_dir: VirtualDirectory, @@ -364,6 +366,125 @@ impl VfsBuilder { } } +pub fn output_vfs(builder: &VfsBuilder, executable_name: &str) { + if !log::log_enabled!(log::Level::Info) { + return; // no need to compute if won't output + } + + if builder.root_dir.entries.is_empty() { + return; // nothing to output + } + + let mut text = String::new(); + let display_tree = vfs_as_display_tree(builder, executable_name); + display_tree.print(&mut text).unwrap(); // unwrap ok because it's writing to a string + log::info!( + "\n{}\n", + deno_terminal::colors::bold("Embedded File System") + ); + log::info!("{}\n", text.trim()); +} + +fn vfs_as_display_tree( + builder: &VfsBuilder, + executable_name: &str, +) -> DisplayTreeNode { + enum EntryOutput<'a> { + All, + Subset(Vec>), + File, + Symlink(&'a [String]), + } + + impl<'a> EntryOutput<'a> { + pub fn as_display_tree(&self, name: String) -> DisplayTreeNode { + DisplayTreeNode { + text: match self { + EntryOutput::All | EntryOutput::Subset(_) | EntryOutput::File => name, + EntryOutput::Symlink(parts) => { + format!("{} --> {}", name, parts.join("/")) + } + }, + children: match self { + EntryOutput::All => vec![DisplayTreeNode::from_text("*".to_string())], + EntryOutput::Subset(vec) => vec + .iter() + .map(|e| e.output.as_display_tree(e.name.to_string())) + .collect(), + EntryOutput::File | EntryOutput::Symlink(_) => vec![], + }, + } + } + } + + pub struct DirEntryOutput<'a> { + name: &'a str, + output: EntryOutput<'a>, + } + + fn include_all_entries<'a>( + dir: &Path, + vfs_dir: &'a VirtualDirectory, + ) -> EntryOutput<'a> { + EntryOutput::Subset( + vfs_dir + .entries + .iter() + .map(|entry| DirEntryOutput { + name: entry.name(), + output: analyze_entry(&dir.join(entry.name()), entry), + }) + .collect(), + ) + } + + fn analyze_entry<'a>(path: &Path, entry: &'a VfsEntry) -> EntryOutput<'a> { + match entry { + VfsEntry::Dir(virtual_directory) => analyze_dir(path, virtual_directory), + VfsEntry::File(_) => EntryOutput::File, + VfsEntry::Symlink(virtual_symlink) => { + EntryOutput::Symlink(&virtual_symlink.dest_parts) + } + } + } + + fn analyze_dir<'a>( + dir: &Path, + vfs_dir: &'a VirtualDirectory, + ) -> EntryOutput<'a> { + let real_entry_count = std::fs::read_dir(dir) + .ok() + .map(|entries| entries.flat_map(|e| e.ok()).count()) + .unwrap_or(0); + if real_entry_count == vfs_dir.entries.len() { + let children = vfs_dir + .entries + .iter() + .map(|entry| DirEntryOutput { + name: entry.name(), + output: analyze_entry(&dir.join(entry.name()), entry), + }) + .collect::>(); + if children + .iter() + .all(|c| !matches!(c.output, EntryOutput::Subset(_))) + { + EntryOutput::All + } else { + EntryOutput::Subset(children) + } + } else { + include_all_entries(dir, vfs_dir) + } + } + + // always include all the entries for the root directory, otherwise the + // user might not have context about what's being shown + let output = include_all_entries(&builder.root_path, &builder.root_dir); + output + .as_display_tree(deno_terminal::colors::italic(executable_name).to_string()) +} + #[derive(Debug)] enum VfsEntryRef<'a> { Dir(&'a VirtualDirectory), @@ -1013,6 +1134,7 @@ impl FileBackedVfs { #[cfg(test)] mod test { + use console_static_text::ansi::strip_ansi_codes; use std::io::Write; use test_util::TempDir; @@ -1299,4 +1421,50 @@ mod test { .unwrap(); assert_eq!(all_buf.to_vec(), b"123456789"); } + + #[test] + fn test_vfs_as_display_tree() { + let temp_dir = TempDir::new(); + temp_dir.write("root.txt", ""); + temp_dir.create_dir_all("a"); + temp_dir.write("a/a.txt", ""); + temp_dir.write("a/b.txt", ""); + temp_dir.create_dir_all("b"); + temp_dir.write("b/a.txt", ""); + temp_dir.write("b/b.txt", ""); + temp_dir.create_dir_all("c"); + temp_dir.write("c/a.txt", "contents"); + temp_dir.symlink_file("c/a.txt", "c/b.txt"); + assert_eq!(temp_dir.read_to_string("c/b.txt"), "contents"); // ensure the symlink works + let mut vfs_builder = + VfsBuilder::new(temp_dir.path().to_path_buf()).unwrap(); + // full dir + vfs_builder + .add_dir_recursive(temp_dir.path().join("a").as_path()) + .unwrap(); + // part of the dir + vfs_builder + .add_file_at_path(temp_dir.path().join("b/a.txt").as_path()) + .unwrap(); + // symlink + vfs_builder + .add_dir_recursive(temp_dir.path().join("c").as_path()) + .unwrap(); + temp_dir.write("c/c.txt", ""); // write an extra file so it shows the whole directory + let node = vfs_as_display_tree(&vfs_builder, "executable"); + let mut text = String::new(); + node.print(&mut text).unwrap(); + assert_eq!( + strip_ansi_codes(&text), + r#"executable +├─┬ a +│ └── * +├─┬ b +│ └── a.txt +└─┬ c + ├── a.txt + └── b.txt --> c/a.txt +"# + ); + } } diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 5cd19ecf71..4d0607ba71 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -6,6 +6,7 @@ use crate::args::Flags; use crate::factory::CliFactory; use crate::http_util::HttpClientProvider; use crate::standalone::binary::StandaloneRelativeFileBaseUrl; +use crate::standalone::binary::WriteBinOptions; use crate::standalone::is_standalone_binary; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; @@ -15,6 +16,7 @@ use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_graph::GraphKind; +use deno_path_util::url_from_file_path; use deno_terminal::colors; use rand::Rng; use std::path::Path; @@ -93,7 +95,16 @@ pub async fn compile( .and_then(|p| ModuleSpecifier::from_directory_path(p).ok()) .iter(), ) - .chain(include_files.iter()), + .chain(include_files.iter()) + .chain( + // sometimes the import map path is outside the root dir + cli_options + .workspace() + .to_import_map_path() + .ok() + .and_then(|p| p.and_then(|p| url_from_file_path(&p).ok())) + .iter(), + ), ); log::debug!("Binary root dir: {}", root_dir_url); log::info!( @@ -120,14 +131,18 @@ pub async fn compile( })?; let write_result = binary_writer - .write_bin( - file, - &graph, - StandaloneRelativeFileBaseUrl::from(&root_dir_url), + .write_bin(WriteBinOptions { + writer: file, + display_output_filename: &output_path + .file_name() + .unwrap() + .to_string_lossy(), + graph: &graph, + root_dir_url: StandaloneRelativeFileBaseUrl::from(&root_dir_url), entrypoint, - &include_files, - &compile_flags, - ) + include_files: &include_files, + compile_flags: &compile_flags, + }) .await .with_context(|| { format!( @@ -368,17 +383,6 @@ fn resolve_root_dir_from_specifiers<'a>( } } } - let found_dir = if is_file_system_root(found_dir) { - found_dir - } else { - // include the parent dir name because it helps create some context - found_dir - .strip_suffix('/') - .unwrap_or(found_dir) - .rfind('/') - .map(|i| &found_dir[..i + 1]) - .unwrap_or(found_dir) - }; ModuleSpecifier::parse(found_dir).unwrap() } @@ -473,14 +477,17 @@ mod test { .to_string() } - assert_eq!(resolve("file:///a/b/c", &["file:///a/b/c/d"]), "file:///a/"); assert_eq!( - resolve("file:///a/b/c/", &["file:///a/b/c/d"]), + resolve("file:///a/b/e", &["file:///a/b/c/d"]), "file:///a/b/" ); + assert_eq!( + resolve("file:///a/b/c/", &["file:///a/b/c/d"]), + "file:///a/b/c/" + ); assert_eq!( resolve("file:///a/b/c/", &["file:///a/b/c/d", "file:///a/b/c/e"]), - "file:///a/b/" + "file:///a/b/c/" ); assert_eq!(resolve("file:///", &["file:///a/b/c/d"]), "file:///"); if cfg!(windows) { @@ -488,7 +495,7 @@ mod test { // this will ignore the other one because it's on a separate drive assert_eq!( resolve("file:///c:/a/b/c/", &["file:///v:/a/b/c/d"]), - "file:///c:/a/b/" + "file:///c:/a/b/c/" ); } } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index f58732a904..7a35f597c3 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::collections::HashSet; -use std::fmt; use std::fmt::Write; use std::sync::Arc; @@ -35,6 +34,7 @@ use crate::graph_util::graph_exit_integrity_errors; use crate::npm::CliNpmResolver; use crate::npm::ManagedCliNpmResolver; use crate::util::checksum; +use crate::util::display::DisplayTreeNode; const JSON_SCHEMA_VERSION: u8 = 1; @@ -342,76 +342,6 @@ fn add_npm_packages_to_json( json.insert("npmPackages".to_string(), json_packages.into()); } -struct TreeNode { - text: String, - children: Vec, -} - -impl TreeNode { - pub fn from_text(text: String) -> Self { - Self { - text, - children: Default::default(), - } - } -} - -fn print_tree_node( - tree_node: &TreeNode, - writer: &mut TWrite, -) -> fmt::Result { - fn print_children( - writer: &mut TWrite, - prefix: &str, - children: &[TreeNode], - ) -> fmt::Result { - const SIBLING_CONNECTOR: char = '├'; - const LAST_SIBLING_CONNECTOR: char = '└'; - const CHILD_DEPS_CONNECTOR: char = '┬'; - const CHILD_NO_DEPS_CONNECTOR: char = '─'; - const VERTICAL_CONNECTOR: char = '│'; - const EMPTY_CONNECTOR: char = ' '; - - let child_len = children.len(); - for (index, child) in children.iter().enumerate() { - let is_last = index + 1 == child_len; - let sibling_connector = if is_last { - LAST_SIBLING_CONNECTOR - } else { - SIBLING_CONNECTOR - }; - let child_connector = if child.children.is_empty() { - CHILD_NO_DEPS_CONNECTOR - } else { - CHILD_DEPS_CONNECTOR - }; - writeln!( - writer, - "{} {}", - colors::gray(format!("{prefix}{sibling_connector}─{child_connector}")), - child.text - )?; - let child_prefix = format!( - "{}{}{}", - prefix, - if is_last { - EMPTY_CONNECTOR - } else { - VERTICAL_CONNECTOR - }, - EMPTY_CONNECTOR - ); - print_children(writer, &child_prefix, &child.children)?; - } - - Ok(()) - } - - writeln!(writer, "{}", tree_node.text)?; - print_children(writer, "", &tree_node.children)?; - Ok(()) -} - /// Precached information about npm packages that are used in deno info. #[derive(Default)] struct NpmInfo { @@ -568,7 +498,7 @@ impl<'a> GraphDisplayContext<'a> { )?; writeln!(writer)?; let root_node = self.build_module_info(root, false); - print_tree_node(&root_node, writer)?; + root_node.print(writer)?; Ok(()) } Err(err) => { @@ -584,7 +514,7 @@ impl<'a> GraphDisplayContext<'a> { } } - fn build_dep_info(&mut self, dep: &Dependency) -> Vec { + fn build_dep_info(&mut self, dep: &Dependency) -> Vec { let mut children = Vec::with_capacity(2); if !dep.maybe_code.is_none() { if let Some(child) = self.build_resolved_info(&dep.maybe_code, false) { @@ -599,7 +529,11 @@ impl<'a> GraphDisplayContext<'a> { children } - fn build_module_info(&mut self, module: &Module, type_dep: bool) -> TreeNode { + fn build_module_info( + &mut self, + module: &Module, + type_dep: bool, + ) -> DisplayTreeNode { enum PackageOrSpecifier { Package(Box), Specifier(ModuleSpecifier), @@ -645,7 +579,7 @@ impl<'a> GraphDisplayContext<'a> { format!("{} {}", header_text, maybe_size_to_text(maybe_size)) }; - let mut tree_node = TreeNode::from_text(header_text); + let mut tree_node = DisplayTreeNode::from_text(header_text); if !was_seen { match &package_or_specifier { @@ -683,14 +617,14 @@ impl<'a> GraphDisplayContext<'a> { fn build_npm_deps( &mut self, package: &NpmResolutionPackage, - ) -> Vec { + ) -> Vec { let mut deps = package.dependencies.values().collect::>(); deps.sort(); let mut children = Vec::with_capacity(deps.len()); for dep_id in deps.into_iter() { let maybe_size = self.npm_info.package_sizes.get(dep_id).cloned(); let size_str = maybe_size_to_text(maybe_size); - let mut child = TreeNode::from_text(format!( + let mut child = DisplayTreeNode::from_text(format!( "npm:/{} {}", dep_id.as_serialized(), size_str @@ -715,7 +649,7 @@ impl<'a> GraphDisplayContext<'a> { &mut self, err: &ModuleError, specifier: &ModuleSpecifier, - ) -> TreeNode { + ) -> DisplayTreeNode { self.seen.insert(specifier.to_string()); match err { ModuleError::InvalidTypeAssertion { .. } => { @@ -758,8 +692,8 @@ impl<'a> GraphDisplayContext<'a> { &self, specifier: &ModuleSpecifier, error_msg: &str, - ) -> TreeNode { - TreeNode::from_text(format!( + ) -> DisplayTreeNode { + DisplayTreeNode::from_text(format!( "{} {}", colors::red(specifier), colors::red_bold(error_msg) @@ -770,7 +704,7 @@ impl<'a> GraphDisplayContext<'a> { &mut self, resolution: &Resolution, type_dep: bool, - ) -> Option { + ) -> Option { match resolution { Resolution::Ok(resolved) => { let specifier = &resolved.specifier; @@ -778,14 +712,14 @@ impl<'a> GraphDisplayContext<'a> { Some(match self.graph.try_get(resolved_specifier) { Ok(Some(module)) => self.build_module_info(module, type_dep), Err(err) => self.build_error_info(err, resolved_specifier), - Ok(None) => TreeNode::from_text(format!( + Ok(None) => DisplayTreeNode::from_text(format!( "{} {}", colors::red(specifier), colors::red_bold("(missing)") )), }) } - Resolution::Err(err) => Some(TreeNode::from_text(format!( + Resolution::Err(err) => Some(DisplayTreeNode::from_text(format!( "{} {}", colors::italic(err.to_string()), colors::red_bold("(resolve error)") diff --git a/cli/util/display.rs b/cli/util/display.rs index d18b045d8d..8795d3db68 100644 --- a/cli/util/display.rs +++ b/cli/util/display.rs @@ -2,6 +2,7 @@ use deno_core::error::AnyError; use deno_core::serde_json; +use deno_runtime::colors; use std::io::Write; /// A function that converts a float to a string the represents a human @@ -85,6 +86,78 @@ where Ok(()) } +pub struct DisplayTreeNode { + pub text: String, + pub children: Vec, +} + +impl DisplayTreeNode { + pub fn from_text(text: String) -> Self { + Self { + text, + children: Default::default(), + } + } + + pub fn print( + &self, + writer: &mut TWrite, + ) -> std::fmt::Result { + fn print_children( + writer: &mut TWrite, + prefix: &str, + children: &[DisplayTreeNode], + ) -> std::fmt::Result { + const SIBLING_CONNECTOR: char = '├'; + const LAST_SIBLING_CONNECTOR: char = '└'; + const CHILD_DEPS_CONNECTOR: char = '┬'; + const CHILD_NO_DEPS_CONNECTOR: char = '─'; + const VERTICAL_CONNECTOR: char = '│'; + const EMPTY_CONNECTOR: char = ' '; + + let child_len = children.len(); + for (index, child) in children.iter().enumerate() { + let is_last = index + 1 == child_len; + let sibling_connector = if is_last { + LAST_SIBLING_CONNECTOR + } else { + SIBLING_CONNECTOR + }; + let child_connector = if child.children.is_empty() { + CHILD_NO_DEPS_CONNECTOR + } else { + CHILD_DEPS_CONNECTOR + }; + writeln!( + writer, + "{} {}", + colors::gray(format!( + "{prefix}{sibling_connector}─{child_connector}" + )), + child.text + )?; + let child_prefix = format!( + "{}{}{}", + prefix, + if is_last { + EMPTY_CONNECTOR + } else { + VERTICAL_CONNECTOR + }, + EMPTY_CONNECTOR + ); + print_children(writer, &child_prefix, &child.children)?; + } + + Ok(()) + } + + writeln!(writer, "{}", self.text)?; + print_children(writer, "", &self.children)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/integration/compile_tests.rs b/tests/integration/compile_tests.rs index fa6364a136..62c5cf8fab 100644 --- a/tests/integration/compile_tests.rs +++ b/tests/integration/compile_tests.rs @@ -1119,6 +1119,11 @@ Compile file:///[WILDCARD]/main.ts to [WILDCARD] Warning Failed resolving symlink. Ignoring. Path: [WILDCARD] Message: [WILDCARD]) + +Embedded File System + +[WILDCARD] + "#, ); diff --git a/tests/specs/compile/env_vars_support/compile.out b/tests/specs/compile/env_vars_support/compile.out index 2d004e7cb1..02332ea0eb 100644 --- a/tests/specs/compile/env_vars_support/compile.out +++ b/tests/specs/compile/env_vars_support/compile.out @@ -2,3 +2,9 @@ Warning Parsing failed within the specified environment file: environment.env at Check [WILDCARD]main.ts Compile [WILDCARD]main.ts to out[WILDCARD] Warning Environment variables from the file "environment.env" were embedded in the generated executable file + +Embedded File System + +out[WILDLINE] +└── main.ts + diff --git a/tests/specs/compile/package_json_type/compile.out b/tests/specs/compile/package_json_type/compile.out index 913e363c3e..0370192809 100644 --- a/tests/specs/compile/package_json_type/compile.out +++ b/tests/specs/compile/package_json_type/compile.out @@ -1,2 +1,6 @@ Check file:///[WILDLINE]/main.js Compile file:///[WILDLINE] + +Embedded File System + +[WILDCARD] diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out index e5b39a7527..70de321361 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_file.out @@ -1,2 +1,6 @@ Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDCARD] -Warning Symlink target is outside '[WILDCARD]compile'. Inlining symlink at '[WILDCARD]node_modules_symlink_outside[WILDCARD]node_modules[WILDCARD]test.txt' to '[WILDCARD]target.txt' as file. +Warning Symlink target is outside '[WILDCARD]node_modules_symlink_outside'. Inlining symlink at '[WILDCARD]node_modules_symlink_outside[WILDCARD]node_modules[WILDCARD]test.txt' to '[WILDCARD]target.txt' as file. + +Embedded File System + +[WILDCARD] diff --git a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out index 1a7eb0a4f2..205c6a9281 100644 --- a/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out +++ b/tests/testdata/compile/node_modules_symlink_outside/main_compile_folder.out @@ -3,4 +3,8 @@ Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz Initialize @denotest/esm-basic@1.0.0 Check file:///[WILDCARD]/node_modules_symlink_outside/main.ts Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDLINE] -Warning Symlink target is outside '[WILDLINE]compile'. Excluding symlink at '[WILDLINE]node_modules_symlink_outside[WILDLINE]node_modules[WILDLINE]symlink_dir' with target '[WILDLINE]some_folder'. +Warning Symlink target is outside '[WILDLINE]node_modules_symlink_outside'. Excluding symlink at '[WILDLINE]node_modules_symlink_outside[WILDLINE]node_modules[WILDLINE]symlink_dir' with target '[WILDLINE]some_folder'. + +Embedded File System + +[WILDCARD]