mirror of
https://github.com/denoland/deno.git
synced 2024-12-18 05:14:21 -05:00
fix(compile): output contents of embedded file system (#27302)
This commit is contained in:
parent
9df6be8916
commit
c6fa62896d
10 changed files with 344 additions and 136 deletions
|
@ -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<u8>,
|
||||
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,
|
||||
|
|
|
@ -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<DirEntryOutput<'a>>),
|
||||
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::<Vec<_>>();
|
||||
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
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TreeNode>,
|
||||
}
|
||||
|
||||
impl TreeNode {
|
||||
pub fn from_text(text: String) -> Self {
|
||||
Self {
|
||||
text,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_tree_node<TWrite: Write>(
|
||||
tree_node: &TreeNode,
|
||||
writer: &mut TWrite,
|
||||
) -> fmt::Result {
|
||||
fn print_children<TWrite: Write>(
|
||||
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<TreeNode> {
|
||||
fn build_dep_info(&mut self, dep: &Dependency) -> Vec<DisplayTreeNode> {
|
||||
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<NpmResolutionPackage>),
|
||||
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<TreeNode> {
|
||||
) -> Vec<DisplayTreeNode> {
|
||||
let mut deps = package.dependencies.values().collect::<Vec<_>>();
|
||||
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<TreeNode> {
|
||||
) -> Option<DisplayTreeNode> {
|
||||
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)")
|
||||
|
|
|
@ -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<DisplayTreeNode>,
|
||||
}
|
||||
|
||||
impl DisplayTreeNode {
|
||||
pub fn from_text(text: String) -> Self {
|
||||
Self {
|
||||
text,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print<TWrite: std::fmt::Write>(
|
||||
&self,
|
||||
writer: &mut TWrite,
|
||||
) -> std::fmt::Result {
|
||||
fn print_children<TWrite: std::fmt::Write>(
|
||||
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::*;
|
||||
|
|
|
@ -1119,6 +1119,11 @@ Compile file:///[WILDCARD]/main.ts to [WILDCARD]
|
|||
Warning Failed resolving symlink. Ignoring.
|
||||
Path: [WILDCARD]
|
||||
Message: [WILDCARD])
|
||||
|
||||
Embedded File System
|
||||
|
||||
[WILDCARD]
|
||||
|
||||
"#,
|
||||
);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
Check file:///[WILDLINE]/main.js
|
||||
Compile file:///[WILDLINE]
|
||||
|
||||
Embedded File System
|
||||
|
||||
[WILDCARD]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue