mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 13:00:36 -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::DeserializedDataSection;
|
||||||
use super::serialization::RemoteModulesStore;
|
use super::serialization::RemoteModulesStore;
|
||||||
use super::serialization::RemoteModulesStoreBuilder;
|
use super::serialization::RemoteModulesStoreBuilder;
|
||||||
|
use super::virtual_fs::output_vfs;
|
||||||
use super::virtual_fs::FileBackedVfs;
|
use super::virtual_fs::FileBackedVfs;
|
||||||
use super::virtual_fs::VfsBuilder;
|
use super::virtual_fs::VfsBuilder;
|
||||||
use super::virtual_fs::VfsFileSubDataKind;
|
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> {
|
pub struct DenoCompileBinaryWriter<'a> {
|
||||||
cjs_tracker: &'a CjsTracker,
|
cjs_tracker: &'a CjsTracker,
|
||||||
cli_options: &'a CliOptions,
|
cli_options: &'a CliOptions,
|
||||||
|
@ -407,18 +418,14 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
|
|
||||||
pub async fn write_bin(
|
pub async fn write_bin(
|
||||||
&self,
|
&self,
|
||||||
writer: File,
|
options: WriteBinOptions<'_>,
|
||||||
graph: &ModuleGraph,
|
|
||||||
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
|
|
||||||
entrypoint: &ModuleSpecifier,
|
|
||||||
include_files: &[ModuleSpecifier],
|
|
||||||
compile_flags: &CompileFlags,
|
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
// Select base binary based on target
|
// 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 {
|
if options.compile_flags.no_terminal {
|
||||||
let target = compile_flags.resolve_target();
|
let target = options.compile_flags.resolve_target();
|
||||||
if !target.contains("windows") {
|
if !target.contains("windows") {
|
||||||
bail!(
|
bail!(
|
||||||
"The `--no-terminal` flag is only available when targeting Windows (current: {})",
|
"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)
|
set_windows_binary_to_gui(&mut original_binary)
|
||||||
.context("Setting windows binary to GUI.")?;
|
.context("Setting windows binary to GUI.")?;
|
||||||
}
|
}
|
||||||
if compile_flags.icon.is_some() {
|
if options.compile_flags.icon.is_some() {
|
||||||
let target = compile_flags.resolve_target();
|
let target = options.compile_flags.resolve_target();
|
||||||
if !target.contains("windows") {
|
if !target.contains("windows") {
|
||||||
bail!(
|
bail!(
|
||||||
"The `--icon` flag is only available when targeting Windows (current: {})",
|
"The `--icon` flag is only available when targeting Windows (current: {})",
|
||||||
|
@ -437,17 +444,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self.write_standalone_binary(options, original_binary).await
|
||||||
.write_standalone_binary(
|
|
||||||
writer,
|
|
||||||
original_binary,
|
|
||||||
graph,
|
|
||||||
root_dir_url,
|
|
||||||
entrypoint,
|
|
||||||
include_files,
|
|
||||||
compile_flags,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_base_binary(
|
async fn get_base_binary(
|
||||||
|
@ -552,14 +549,18 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn write_standalone_binary(
|
async fn write_standalone_binary(
|
||||||
&self,
|
&self,
|
||||||
writer: File,
|
options: WriteBinOptions<'_>,
|
||||||
original_bin: Vec<u8>,
|
original_bin: Vec<u8>,
|
||||||
graph: &ModuleGraph,
|
|
||||||
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
|
|
||||||
entrypoint: &ModuleSpecifier,
|
|
||||||
include_files: &[ModuleSpecifier],
|
|
||||||
compile_flags: &CompileFlags,
|
|
||||||
) -> Result<(), AnyError> {
|
) -> 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() {
|
let ca_data = match self.cli_options.ca_data() {
|
||||||
Some(CaData::File(ca_file)) => Some(
|
Some(CaData::File(ca_file)) => Some(
|
||||||
std::fs::read(ca_file).with_context(|| format!("Reading {ca_file}"))?,
|
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(),
|
otel_config: self.cli_options.otel_config(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
output_vfs(&vfs, display_output_filename);
|
||||||
|
|
||||||
write_binary_bytes(
|
write_binary_bytes(
|
||||||
writer,
|
writer,
|
||||||
original_bin,
|
original_bin,
|
||||||
|
|
|
@ -31,6 +31,7 @@ use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
use crate::util::display::DisplayTreeNode;
|
||||||
use crate::util::fs::canonicalize_path;
|
use crate::util::fs::canonicalize_path;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -51,6 +52,7 @@ pub struct StripRootError {
|
||||||
target: PathBuf,
|
target: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct VfsBuilder {
|
pub struct VfsBuilder {
|
||||||
root_path: PathBuf,
|
root_path: PathBuf,
|
||||||
root_dir: VirtualDirectory,
|
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)]
|
#[derive(Debug)]
|
||||||
enum VfsEntryRef<'a> {
|
enum VfsEntryRef<'a> {
|
||||||
Dir(&'a VirtualDirectory),
|
Dir(&'a VirtualDirectory),
|
||||||
|
@ -1013,6 +1134,7 @@ impl FileBackedVfs {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use console_static_text::ansi::strip_ansi_codes;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use test_util::TempDir;
|
use test_util::TempDir;
|
||||||
|
|
||||||
|
@ -1299,4 +1421,50 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(all_buf.to_vec(), b"123456789");
|
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::factory::CliFactory;
|
||||||
use crate::http_util::HttpClientProvider;
|
use crate::http_util::HttpClientProvider;
|
||||||
use crate::standalone::binary::StandaloneRelativeFileBaseUrl;
|
use crate::standalone::binary::StandaloneRelativeFileBaseUrl;
|
||||||
|
use crate::standalone::binary::WriteBinOptions;
|
||||||
use crate::standalone::is_standalone_binary;
|
use crate::standalone::is_standalone_binary;
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
use deno_ast::ModuleSpecifier;
|
use deno_ast::ModuleSpecifier;
|
||||||
|
@ -15,6 +16,7 @@ use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::resolve_url_or_path;
|
use deno_core::resolve_url_or_path;
|
||||||
use deno_graph::GraphKind;
|
use deno_graph::GraphKind;
|
||||||
|
use deno_path_util::url_from_file_path;
|
||||||
use deno_terminal::colors;
|
use deno_terminal::colors;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -93,7 +95,16 @@ pub async fn compile(
|
||||||
.and_then(|p| ModuleSpecifier::from_directory_path(p).ok())
|
.and_then(|p| ModuleSpecifier::from_directory_path(p).ok())
|
||||||
.iter(),
|
.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::debug!("Binary root dir: {}", root_dir_url);
|
||||||
log::info!(
|
log::info!(
|
||||||
|
@ -120,14 +131,18 @@ pub async fn compile(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let write_result = binary_writer
|
let write_result = binary_writer
|
||||||
.write_bin(
|
.write_bin(WriteBinOptions {
|
||||||
file,
|
writer: file,
|
||||||
&graph,
|
display_output_filename: &output_path
|
||||||
StandaloneRelativeFileBaseUrl::from(&root_dir_url),
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy(),
|
||||||
|
graph: &graph,
|
||||||
|
root_dir_url: StandaloneRelativeFileBaseUrl::from(&root_dir_url),
|
||||||
entrypoint,
|
entrypoint,
|
||||||
&include_files,
|
include_files: &include_files,
|
||||||
&compile_flags,
|
compile_flags: &compile_flags,
|
||||||
)
|
})
|
||||||
.await
|
.await
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!(
|
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()
|
ModuleSpecifier::parse(found_dir).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,14 +477,17 @@ mod test {
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(resolve("file:///a/b/c", &["file:///a/b/c/d"]), "file:///a/");
|
|
||||||
assert_eq!(
|
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/"
|
"file:///a/b/"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve("file:///a/b/c/", &["file:///a/b/c/d"]),
|
||||||
|
"file:///a/b/c/"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve("file:///a/b/c/", &["file:///a/b/c/d", "file:///a/b/c/e"]),
|
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:///");
|
assert_eq!(resolve("file:///", &["file:///a/b/c/d"]), "file:///");
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
|
@ -488,7 +495,7 @@ mod test {
|
||||||
// this will ignore the other one because it's on a separate drive
|
// this will ignore the other one because it's on a separate drive
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve("file:///c:/a/b/c/", &["file:///v:/a/b/c/d"]),
|
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::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -35,6 +34,7 @@ use crate::graph_util::graph_exit_integrity_errors;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::npm::ManagedCliNpmResolver;
|
use crate::npm::ManagedCliNpmResolver;
|
||||||
use crate::util::checksum;
|
use crate::util::checksum;
|
||||||
|
use crate::util::display::DisplayTreeNode;
|
||||||
|
|
||||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||||
|
|
||||||
|
@ -342,76 +342,6 @@ fn add_npm_packages_to_json(
|
||||||
json.insert("npmPackages".to_string(), json_packages.into());
|
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.
|
/// Precached information about npm packages that are used in deno info.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct NpmInfo {
|
struct NpmInfo {
|
||||||
|
@ -568,7 +498,7 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
)?;
|
)?;
|
||||||
writeln!(writer)?;
|
writeln!(writer)?;
|
||||||
let root_node = self.build_module_info(root, false);
|
let root_node = self.build_module_info(root, false);
|
||||||
print_tree_node(&root_node, writer)?;
|
root_node.print(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(err) => {
|
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);
|
let mut children = Vec::with_capacity(2);
|
||||||
if !dep.maybe_code.is_none() {
|
if !dep.maybe_code.is_none() {
|
||||||
if let Some(child) = self.build_resolved_info(&dep.maybe_code, false) {
|
if let Some(child) = self.build_resolved_info(&dep.maybe_code, false) {
|
||||||
|
@ -599,7 +529,11 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
children
|
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 {
|
enum PackageOrSpecifier {
|
||||||
Package(Box<NpmResolutionPackage>),
|
Package(Box<NpmResolutionPackage>),
|
||||||
Specifier(ModuleSpecifier),
|
Specifier(ModuleSpecifier),
|
||||||
|
@ -645,7 +579,7 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
format!("{} {}", header_text, maybe_size_to_text(maybe_size))
|
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 {
|
if !was_seen {
|
||||||
match &package_or_specifier {
|
match &package_or_specifier {
|
||||||
|
@ -683,14 +617,14 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
fn build_npm_deps(
|
fn build_npm_deps(
|
||||||
&mut self,
|
&mut self,
|
||||||
package: &NpmResolutionPackage,
|
package: &NpmResolutionPackage,
|
||||||
) -> Vec<TreeNode> {
|
) -> Vec<DisplayTreeNode> {
|
||||||
let mut deps = package.dependencies.values().collect::<Vec<_>>();
|
let mut deps = package.dependencies.values().collect::<Vec<_>>();
|
||||||
deps.sort();
|
deps.sort();
|
||||||
let mut children = Vec::with_capacity(deps.len());
|
let mut children = Vec::with_capacity(deps.len());
|
||||||
for dep_id in deps.into_iter() {
|
for dep_id in deps.into_iter() {
|
||||||
let maybe_size = self.npm_info.package_sizes.get(dep_id).cloned();
|
let maybe_size = self.npm_info.package_sizes.get(dep_id).cloned();
|
||||||
let size_str = maybe_size_to_text(maybe_size);
|
let size_str = maybe_size_to_text(maybe_size);
|
||||||
let mut child = TreeNode::from_text(format!(
|
let mut child = DisplayTreeNode::from_text(format!(
|
||||||
"npm:/{} {}",
|
"npm:/{} {}",
|
||||||
dep_id.as_serialized(),
|
dep_id.as_serialized(),
|
||||||
size_str
|
size_str
|
||||||
|
@ -715,7 +649,7 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
err: &ModuleError,
|
err: &ModuleError,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
) -> TreeNode {
|
) -> DisplayTreeNode {
|
||||||
self.seen.insert(specifier.to_string());
|
self.seen.insert(specifier.to_string());
|
||||||
match err {
|
match err {
|
||||||
ModuleError::InvalidTypeAssertion { .. } => {
|
ModuleError::InvalidTypeAssertion { .. } => {
|
||||||
|
@ -758,8 +692,8 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
&self,
|
&self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
error_msg: &str,
|
error_msg: &str,
|
||||||
) -> TreeNode {
|
) -> DisplayTreeNode {
|
||||||
TreeNode::from_text(format!(
|
DisplayTreeNode::from_text(format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
colors::red(specifier),
|
colors::red(specifier),
|
||||||
colors::red_bold(error_msg)
|
colors::red_bold(error_msg)
|
||||||
|
@ -770,7 +704,7 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
resolution: &Resolution,
|
resolution: &Resolution,
|
||||||
type_dep: bool,
|
type_dep: bool,
|
||||||
) -> Option<TreeNode> {
|
) -> Option<DisplayTreeNode> {
|
||||||
match resolution {
|
match resolution {
|
||||||
Resolution::Ok(resolved) => {
|
Resolution::Ok(resolved) => {
|
||||||
let specifier = &resolved.specifier;
|
let specifier = &resolved.specifier;
|
||||||
|
@ -778,14 +712,14 @@ impl<'a> GraphDisplayContext<'a> {
|
||||||
Some(match self.graph.try_get(resolved_specifier) {
|
Some(match self.graph.try_get(resolved_specifier) {
|
||||||
Ok(Some(module)) => self.build_module_info(module, type_dep),
|
Ok(Some(module)) => self.build_module_info(module, type_dep),
|
||||||
Err(err) => self.build_error_info(err, resolved_specifier),
|
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(specifier),
|
||||||
colors::red_bold("(missing)")
|
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::italic(err.to_string()),
|
||||||
colors::red_bold("(resolve error)")
|
colors::red_bold("(resolve error)")
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use deno_runtime::colors;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
/// A function that converts a float to a string the represents a human
|
/// A function that converts a float to a string the represents a human
|
||||||
|
@ -85,6 +86,78 @@ where
|
||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1119,6 +1119,11 @@ Compile file:///[WILDCARD]/main.ts to [WILDCARD]
|
||||||
Warning Failed resolving symlink. Ignoring.
|
Warning Failed resolving symlink. Ignoring.
|
||||||
Path: [WILDCARD]
|
Path: [WILDCARD]
|
||||||
Message: [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
|
Check [WILDCARD]main.ts
|
||||||
Compile [WILDCARD]main.ts to out[WILDCARD]
|
Compile [WILDCARD]main.ts to out[WILDCARD]
|
||||||
Warning Environment variables from the file "environment.env" were embedded in the generated executable file
|
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
|
Check file:///[WILDLINE]/main.js
|
||||||
Compile file:///[WILDLINE]
|
Compile file:///[WILDLINE]
|
||||||
|
|
||||||
|
Embedded File System
|
||||||
|
|
||||||
|
[WILDCARD]
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDCARD]
|
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
|
Initialize @denotest/esm-basic@1.0.0
|
||||||
Check file:///[WILDCARD]/node_modules_symlink_outside/main.ts
|
Check file:///[WILDCARD]/node_modules_symlink_outside/main.ts
|
||||||
Compile file:///[WILDCARD]/node_modules_symlink_outside/main.ts to [WILDLINE]
|
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…
Add table
Reference in a new issue