mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat: support npm specifiers in deno info
for display text output only (#16470)
This commit is contained in:
parent
2c674dcd20
commit
edaceecec7
24 changed files with 591 additions and 556 deletions
|
@ -1212,6 +1212,7 @@ TypeScript compiler cache: Subdirectory containing TS compiler output.",
|
|||
.arg(no_config_arg())
|
||||
.arg(config_arg())
|
||||
.arg(import_map_arg())
|
||||
.arg(local_npm_arg())
|
||||
.arg(
|
||||
Arg::new("json")
|
||||
.long("json")
|
||||
|
@ -2512,6 +2513,7 @@ fn info_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
|||
import_map_arg_parse(flags, matches);
|
||||
location_arg_parse(flags, matches);
|
||||
ca_file_arg_parse(flags, matches);
|
||||
local_npm_args_parse(flags, matches);
|
||||
let json = matches.is_present("json");
|
||||
flags.subcommand = DenoSubcommand::Info(InfoFlags {
|
||||
file: matches.value_of("file").map(|f| f.to_string()),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::{uri_error, AnyError};
|
||||
use deno_core::error::uri_error;
|
||||
use deno_core::error::AnyError;
|
||||
pub use deno_core::normalize_path;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::deno_crypto::rand;
|
||||
|
@ -9,8 +10,11 @@ use deno_runtime::deno_node::PathClean;
|
|||
use std::borrow::Cow;
|
||||
use std::env::current_dir;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Error, ErrorKind, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::io::Error;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub fn atomic_write_file<T: AsRef<[u8]>>(
|
||||
|
@ -573,6 +577,20 @@ pub fn root_url_to_safe_local_dirname(root: &ModuleSpecifier) -> PathBuf {
|
|||
result
|
||||
}
|
||||
|
||||
/// Gets the total size (in bytes) of a directory.
|
||||
pub fn dir_size(path: &Path) -> std::io::Result<u64> {
|
||||
let entries = std::fs::read_dir(path)?;
|
||||
let mut total = 0;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
total += match entry.metadata()? {
|
||||
data if data.is_dir() => dir_size(&entry.path())?,
|
||||
data => data.len(),
|
||||
};
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -697,6 +697,13 @@ impl NpmResolution {
|
|||
Ok(snapshot)
|
||||
}
|
||||
|
||||
pub fn resolve_package_from_id(
|
||||
&self,
|
||||
id: &NpmPackageId,
|
||||
) -> Option<NpmResolutionPackage> {
|
||||
self.snapshot.read().package_from_id(id).cloned()
|
||||
}
|
||||
|
||||
pub fn resolve_package_from_package(
|
||||
&self,
|
||||
name: &str,
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::lockfile::Lockfile;
|
|||
use crate::npm::cache::should_sync_download;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmPackageId;
|
||||
use crate::npm::NpmPackageReq;
|
||||
use crate::npm::NpmResolutionPackage;
|
||||
|
||||
|
@ -36,6 +37,8 @@ pub trait InnerNpmPackageResolver: Send + Sync {
|
|||
specifier: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, AnyError>;
|
||||
|
||||
fn package_size(&self, package_id: &NpmPackageId) -> Result<u64, AnyError>;
|
||||
|
||||
fn has_packages(&self) -> bool;
|
||||
|
||||
fn add_package_reqs(
|
||||
|
|
|
@ -15,6 +15,7 @@ use deno_core::url::Url;
|
|||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_node::TYPES_CONDITIONS;
|
||||
|
||||
use crate::fs_util;
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::npm::resolution::NpmResolution;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
|
@ -110,6 +111,11 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
|
|||
Ok(self.package_folder(&pkg_id))
|
||||
}
|
||||
|
||||
fn package_size(&self, package_id: &NpmPackageId) -> Result<u64, AnyError> {
|
||||
let package_folder = self.package_folder(package_id);
|
||||
Ok(fs_util::dir_size(&package_folder)?)
|
||||
}
|
||||
|
||||
fn has_packages(&self) -> bool {
|
||||
self.resolution.has_packages()
|
||||
}
|
||||
|
|
|
@ -177,6 +177,22 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
Ok(package_root_path)
|
||||
}
|
||||
|
||||
fn package_size(&self, package_id: &NpmPackageId) -> Result<u64, AnyError> {
|
||||
match self.resolution.resolve_package_from_id(package_id) {
|
||||
Some(package) => Ok(fs_util::dir_size(
|
||||
// package is stored at:
|
||||
// node_modules/.deno/<package_id>/node_modules/<package_name>
|
||||
&self
|
||||
.root_node_modules_path
|
||||
.join(".deno")
|
||||
.join(package.id.to_string())
|
||||
.join("node_modules")
|
||||
.join(package.id.name),
|
||||
)?),
|
||||
None => bail!("Could not find package folder for '{}'", package_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_packages(&self) -> bool {
|
||||
self.resolution.has_packages()
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use crate::lockfile::Lockfile;
|
|||
use self::common::InnerNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
use super::NpmCache;
|
||||
use super::NpmPackageId;
|
||||
use super::NpmPackageReq;
|
||||
use super::NpmRegistryApi;
|
||||
use super::NpmResolutionSnapshot;
|
||||
|
@ -212,6 +213,14 @@ impl NpmPackageResolver {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
/// Attempts to get the package size in bytes.
|
||||
pub fn package_size(
|
||||
&self,
|
||||
package_id: &NpmPackageId,
|
||||
) -> Result<u64, AnyError> {
|
||||
self.inner.package_size(package_id)
|
||||
}
|
||||
|
||||
/// Gets if the provided specifier is in an npm package.
|
||||
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self
|
||||
|
@ -301,10 +310,14 @@ impl NpmPackageResolver {
|
|||
self.unstable,
|
||||
self.no_npm,
|
||||
self.local_node_modules_path.clone(),
|
||||
Some(self.inner.snapshot()),
|
||||
Some(self.snapshot()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.inner.snapshot()
|
||||
}
|
||||
|
||||
pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
self.inner.lock(lockfile)
|
||||
}
|
||||
|
|
|
@ -733,6 +733,31 @@ itest!(compile_errors {
|
|||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(info_chalk {
|
||||
args: "info --quiet --unstable npm/cjs_with_deps/main.js",
|
||||
output: "npm/cjs_with_deps/main.info.out",
|
||||
exit_code: 0,
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(info_chalk_node_modules_dir {
|
||||
args: "info --quiet --unstable --node-modules-dir $TESTDATA/npm/cjs_with_deps/main.js",
|
||||
output: "npm/cjs_with_deps/main.info.out",
|
||||
exit_code: 0,
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
temp_cwd: true,
|
||||
});
|
||||
|
||||
itest!(info_cli_chalk {
|
||||
args: "info --quiet --unstable npm:chalk@4",
|
||||
output: "npm/deno_info_chalk.out",
|
||||
exit_code: 0,
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
fn env_vars_no_sync_download() -> Vec<(String, String)> {
|
||||
vec![
|
||||
("DENO_NODE_COMPAT_URL".to_string(), util::std_file_url()),
|
||||
|
|
3
cli/tests/testdata/cert/cafile_info.ts.out
vendored
3
cli/tests/testdata/cert/cafile_info.ts.out
vendored
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]https[WILDCARD]localhost_PORT5545[WILDCARD]
|
||||
type: TypeScript
|
||||
dependencies: 8 unique (total [WILDCARD])
|
||||
dependencies: 8 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
https://localhost:5545/cert/cafile_info.ts ([WILDCARD])
|
||||
├── https://localhost:5545/subdir/mt_application_ecmascript.j2.js ([WILDCARD])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]031_info_ts_error.ts
|
||||
type: TypeScript
|
||||
dependencies: 0 unique (total [WILDCARD])
|
||||
dependencies: 0 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
[WILDCARD]031_info_ts_error.ts ([WILDCARD])
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD]
|
||||
type: TypeScript
|
||||
dependencies: 8 unique (total [WILDCARD])
|
||||
dependencies: 8 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
http://127.0.0.1:4545/run/048_media_types_jsx.ts ([WILDCARD])
|
||||
├── http://localhost:4545/subdir/mt_application_ecmascript_jsx.j2.jsx ([WILDCARD])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]005_more_imports.ts
|
||||
type: TypeScript
|
||||
dependencies: 3 unique (total [WILDCARD])
|
||||
dependencies: 3 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
file://[WILDCARD]/005_more_imports.ts ([WILDCARD])
|
||||
└─┬ file://[WILDCARD]/subdir/mod1.ts ([WILDCARD])
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]test.ts
|
||||
type: TypeScript
|
||||
dependencies: 7 unique (total [WILDCARD])
|
||||
dependencies: 7 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
[WILDCARD]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]mod.ts
|
||||
type: TypeScript
|
||||
dependencies: 1 unique (total [WILDCARD])
|
||||
dependencies: 1 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
file://[WILDCARD]/mod.ts ([WILDCARD])
|
||||
└── file://[WILDCARD]/types.d.ts ([WILDCARD])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]error_009_missing_js_module.js
|
||||
type: JavaScript
|
||||
dependencies: 0 unique (total 26B)
|
||||
dependencies: 0 unique
|
||||
size: 26B
|
||||
|
||||
file://[WILDCARD]/error_009_missing_js_module.js (26B)
|
||||
└── file://[WILDCARD]/bad-module.js (missing)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]info_recursive_imports_test.ts
|
||||
type: TypeScript
|
||||
dependencies: 4 unique (total [WILDCARD])
|
||||
dependencies: 4 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
file://[WILDCARD]/info_recursive_imports_test.ts ([WILDCARD])
|
||||
└─┬ file://[WILDCARD]/recursive_imports/A.ts ([WILDCARD])
|
||||
|
|
4
cli/tests/testdata/info/info_type_import.out
vendored
4
cli/tests/testdata/info/info_type_import.out
vendored
|
@ -1,5 +1,7 @@
|
|||
local: [WILDCARD]info_type_import.ts
|
||||
type: TypeScript
|
||||
dependencies: 1 unique (total [WILDCARD])
|
||||
dependencies: 1 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
[WILDCARD]info_type_import.ts ([WILDCARD])
|
||||
└── [WILDCARD]type_and_code.ts ([WILDCARD])
|
||||
|
|
3
cli/tests/testdata/info/multiple_imports.out
vendored
3
cli/tests/testdata/info/multiple_imports.out
vendored
|
@ -1,7 +1,8 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]http[WILDCARD]127.0.0.1_PORT4545[WILDCARD]
|
||||
type: TypeScript
|
||||
dependencies: 8 unique (total [WILDCARD])
|
||||
dependencies: 8 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
http://127.0.0.1:4545/run/019_media_types.ts ([WILDCARD])
|
||||
├── http://localhost:4545/subdir/mt_application_ecmascript.j2.js ([WILDCARD])
|
||||
|
|
3
cli/tests/testdata/info/types_header.out
vendored
3
cli/tests/testdata/info/types_header.out
vendored
|
@ -1,7 +1,8 @@
|
|||
[WILDCARD]
|
||||
local: [WILDCARD]type_directives_01.ts
|
||||
type: TypeScript
|
||||
dependencies: 2 unique (total [WILDCARD])
|
||||
dependencies: 2 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
[WILDCARD]/type_directives_01.ts ([WILDCARD])
|
||||
└─┬ http://127.0.0.1:4545/xTypeScriptTypes.js ([WILDCARD])
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Warning the configuration file "[WILDCARD]/deno-override.json" contains an entry for "importMap" that is being ignored.
|
||||
local: [WILDCARD]test.ts
|
||||
type: TypeScript
|
||||
dependencies: 0 unique (total [WILDCARD])
|
||||
dependencies: 0 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
file:///[WILDCARD]/test.ts ([WILDCARD])
|
||||
|
|
22
cli/tests/testdata/npm/cjs_with_deps/main.info.out
vendored
Normal file
22
cli/tests/testdata/npm/cjs_with_deps/main.info.out
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
local: [WILDCARD]main.js
|
||||
type: JavaScript
|
||||
dependencies: 14 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
file:///[WILDCARD]/npm/cjs_with_deps/main.js ([WILDCARD])
|
||||
├─┬ npm:chai@4.3 - 4.3.6 ([WILDCARD])
|
||||
│ ├── npm:assertion-error@1.1.0 ([WILDCARD])
|
||||
│ ├── npm:check-error@1.0.2 ([WILDCARD])
|
||||
│ ├─┬ npm:deep-eql@3.0.1 ([WILDCARD])
|
||||
│ │ └── npm:type-detect@4.0.8 ([WILDCARD])
|
||||
│ ├── npm:get-func-name@2.0.0 ([WILDCARD])
|
||||
│ ├─┬ npm:loupe@2.3.4 ([WILDCARD])
|
||||
│ │ └── npm:get-func-name@2.0.0 ([WILDCARD])
|
||||
│ ├── npm:pathval@1.1.1 ([WILDCARD])
|
||||
│ └── npm:type-detect@4.0.8 ([WILDCARD])
|
||||
└─┬ npm:chalk@4 - 4.1.2 ([WILDCARD])
|
||||
├─┬ npm:ansi-styles@4.3.0 ([WILDCARD])
|
||||
│ └─┬ npm:color-convert@2.0.1 ([WILDCARD])
|
||||
│ └── npm:color-name@1.1.4 ([WILDCARD])
|
||||
└─┬ npm:supports-color@7.2.0 ([WILDCARD])
|
||||
└── npm:has-flag@4.0.0 ([WILDCARD])
|
10
cli/tests/testdata/npm/deno_info_chalk.out
vendored
Normal file
10
cli/tests/testdata/npm/deno_info_chalk.out
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
type: Unknown
|
||||
dependencies: 5 unique
|
||||
size: [WILDCARD]
|
||||
|
||||
npm:chalk@4 - 4.1.2 ([WILDCARD])
|
||||
├─┬ npm:ansi-styles@4.3.0 ([WILDCARD])
|
||||
│ └─┬ npm:color-convert@2.0.1 ([WILDCARD])
|
||||
│ └── npm:color-name@1.1.4 ([WILDCARD])
|
||||
└─┬ npm:supports-color@7.2.0 ([WILDCARD])
|
||||
└── npm:has-flag@4.0.0 ([WILDCARD])
|
|
@ -1,6 +1,7 @@
|
|||
local: [WILDCARD]017_import_redirect.ts
|
||||
type: TypeScript
|
||||
dependencies: 1 unique (total 278B)
|
||||
dependencies: 1 unique
|
||||
size: 278B
|
||||
|
||||
file:///[WILDCARD]/017_import_redirect.ts ([WILDCARD])
|
||||
└── https://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts ([WILDCARD])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
|
@ -22,6 +23,12 @@ use crate::args::InfoFlags;
|
|||
use crate::checksum;
|
||||
use crate::display;
|
||||
use crate::lsp;
|
||||
use crate::npm::NpmPackageId;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::npm::NpmPackageReq;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
use crate::npm::NpmResolutionPackage;
|
||||
use crate::npm::NpmResolutionSnapshot;
|
||||
use crate::proc_state::ProcState;
|
||||
|
||||
pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> {
|
||||
|
@ -34,7 +41,7 @@ pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> {
|
|||
display::write_json_to_stdout(&json!(graph))?;
|
||||
} else {
|
||||
let mut output = String::new();
|
||||
fmt_module_graph(&graph, &mut output)?;
|
||||
GraphDisplayContext::write(&graph, &ps.npm_resolver, &mut output)?;
|
||||
display::write_to_stdout_ignore_sigpipe(output.as_bytes())?;
|
||||
}
|
||||
} else {
|
||||
|
@ -121,564 +128,454 @@ fn print_cache_info(
|
|||
}
|
||||
}
|
||||
|
||||
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 = ' ';
|
||||
struct TreeNode {
|
||||
text: String,
|
||||
children: Vec<TreeNode>,
|
||||
}
|
||||
|
||||
fn fmt_module_graph(graph: &ModuleGraph, f: &mut impl Write) -> fmt::Result {
|
||||
if graph.roots.is_empty() || graph.roots.len() > 1 {
|
||||
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: &Vec<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 {
|
||||
package_sizes: HashMap<NpmPackageId, u64>,
|
||||
resolved_reqs: HashMap<NpmPackageReq, NpmPackageId>,
|
||||
packages: HashMap<NpmPackageId, NpmResolutionPackage>,
|
||||
specifiers: HashMap<ModuleSpecifier, NpmPackageReq>,
|
||||
}
|
||||
|
||||
impl NpmInfo {
|
||||
pub fn build<'a>(
|
||||
graph: &'a ModuleGraph,
|
||||
npm_resolver: &'a NpmPackageResolver,
|
||||
npm_snapshot: &'a NpmResolutionSnapshot,
|
||||
) -> Self {
|
||||
let mut info = NpmInfo::default();
|
||||
if !npm_resolver.has_packages() {
|
||||
return info; // skip going over the specifiers if there's no npm packages
|
||||
}
|
||||
|
||||
for (specifier, _) in graph.specifiers() {
|
||||
if let Ok(reference) = NpmPackageReference::from_specifier(&specifier) {
|
||||
info
|
||||
.specifiers
|
||||
.insert(specifier.clone(), reference.req.clone());
|
||||
if let Ok(package) =
|
||||
npm_snapshot.resolve_package_from_deno_module(&reference.req)
|
||||
{
|
||||
info.resolved_reqs.insert(reference.req, package.id.clone());
|
||||
if !info.packages.contains_key(&package.id) {
|
||||
info.fill_package_info(package, npm_resolver, npm_snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
fn fill_package_info<'a>(
|
||||
&mut self,
|
||||
package: &NpmResolutionPackage,
|
||||
npm_resolver: &'a NpmPackageResolver,
|
||||
npm_snapshot: &'a NpmResolutionSnapshot,
|
||||
) {
|
||||
self.packages.insert(package.id.clone(), package.clone());
|
||||
if let Ok(size) = npm_resolver.package_size(&package.id) {
|
||||
self.package_sizes.insert(package.id.clone(), size);
|
||||
}
|
||||
for id in package.dependencies.values() {
|
||||
if !self.packages.contains_key(id) {
|
||||
if let Some(package) = npm_snapshot.package_from_id(id) {
|
||||
self.fill_package_info(package, npm_resolver, npm_snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<&NpmResolutionPackage> {
|
||||
self
|
||||
.specifiers
|
||||
.get(specifier)
|
||||
.and_then(|package_req| self.resolved_reqs.get(package_req))
|
||||
.and_then(|id| self.packages.get(id))
|
||||
}
|
||||
}
|
||||
|
||||
struct GraphDisplayContext<'a> {
|
||||
graph: &'a ModuleGraph,
|
||||
npm_info: NpmInfo,
|
||||
seen: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'a> GraphDisplayContext<'a> {
|
||||
pub fn write<TWrite: Write>(
|
||||
graph: &'a ModuleGraph,
|
||||
npm_resolver: &'a NpmPackageResolver,
|
||||
writer: &mut TWrite,
|
||||
) -> fmt::Result {
|
||||
let npm_snapshot = npm_resolver.snapshot();
|
||||
let npm_info = NpmInfo::build(graph, npm_resolver, &npm_snapshot);
|
||||
Self {
|
||||
graph,
|
||||
npm_info,
|
||||
seen: Default::default(),
|
||||
}
|
||||
.into_writer(writer)
|
||||
}
|
||||
|
||||
fn into_writer<TWrite: Write>(mut self, writer: &mut TWrite) -> fmt::Result {
|
||||
if self.graph.roots.is_empty() || self.graph.roots.len() > 1 {
|
||||
return writeln!(
|
||||
f,
|
||||
writer,
|
||||
"{} displaying graphs that have multiple roots is not supported.",
|
||||
colors::red("error:")
|
||||
);
|
||||
}
|
||||
let root_specifier = graph.resolve(&graph.roots[0].0);
|
||||
match graph.try_get(&root_specifier) {
|
||||
|
||||
let root_specifier = self.graph.resolve(&self.graph.roots[0].0);
|
||||
match self.graph.try_get(&root_specifier) {
|
||||
Ok(Some(root)) => {
|
||||
if let Some(cache_info) = root.maybe_cache_info.as_ref() {
|
||||
if let Some(local) = &cache_info.local {
|
||||
writeln!(
|
||||
f,
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("local:"),
|
||||
local.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(emit) = &cache_info.emit {
|
||||
writeln!(f, "{} {}", colors::bold("emit:"), emit.to_string_lossy())?;
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("emit:"),
|
||||
emit.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(map) = &cache_info.map {
|
||||
writeln!(f, "{} {}", colors::bold("map:"), map.to_string_lossy())?;
|
||||
}
|
||||
}
|
||||
writeln!(f, "{} {}", colors::bold("type:"), root.media_type)?;
|
||||
let modules = graph.modules();
|
||||
let total_size: f64 = modules.iter().map(|m| m.size() as f64).sum();
|
||||
let dep_count = modules.len() - 1;
|
||||
writeln!(
|
||||
f,
|
||||
"{} {} unique {}",
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("map:"),
|
||||
map.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
writeln!(writer, "{} {}", colors::bold("type:"), root.media_type)?;
|
||||
let modules = self.graph.modules();
|
||||
let total_modules_size =
|
||||
modules.iter().map(|m| m.size() as f64).sum::<f64>();
|
||||
let total_npm_package_size = self
|
||||
.npm_info
|
||||
.package_sizes
|
||||
.values()
|
||||
.map(|s| *s as f64)
|
||||
.sum::<f64>();
|
||||
let total_size = total_modules_size + total_npm_package_size;
|
||||
let dep_count = modules.len() - 1 + self.npm_info.packages.len()
|
||||
- self.npm_info.resolved_reqs.len();
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {} unique",
|
||||
colors::bold("dependencies:"),
|
||||
dep_count,
|
||||
colors::gray(format!("(total {})", display::human_size(total_size)))
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"\n{} {}",
|
||||
root_specifier,
|
||||
colors::gray(format!("({})", display::human_size(root.size() as f64)))
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("size:"),
|
||||
display::human_size(total_size),
|
||||
)?;
|
||||
let mut seen = HashSet::new();
|
||||
let dep_len = root.dependencies.len();
|
||||
for (idx, (_, dep)) in root.dependencies.iter().enumerate() {
|
||||
fmt_dep_info(
|
||||
dep,
|
||||
f,
|
||||
"",
|
||||
idx == dep_len - 1 && root.maybe_types_dependency.is_none(),
|
||||
graph,
|
||||
&mut seen,
|
||||
)?;
|
||||
}
|
||||
writeln!(writer)?;
|
||||
let root_node = self.build_module_info(root, false);
|
||||
print_tree_node(&root_node, writer)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(ModuleGraphError::Missing(_)) => {
|
||||
writeln!(f, "{} module could not be found", colors::red("error:"))
|
||||
writeln!(
|
||||
writer,
|
||||
"{} module could not be found",
|
||||
colors::red("error:")
|
||||
)
|
||||
}
|
||||
Err(err) => {
|
||||
writeln!(f, "{} {}", colors::red("error:"), err)
|
||||
writeln!(writer, "{} {}", colors::red("error:"), err)
|
||||
}
|
||||
Ok(None) => {
|
||||
writeln!(f, "{} an internal error occurred", colors::red("error:"))
|
||||
writeln!(
|
||||
writer,
|
||||
"{} an internal error occurred",
|
||||
colors::red("error:")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_dep_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||
dep: &Dependency,
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
graph: &ModuleGraph,
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
fn build_dep_info(&mut self, dep: &Dependency) -> Vec<TreeNode> {
|
||||
let mut children = Vec::with_capacity(2);
|
||||
if !dep.maybe_code.is_none() {
|
||||
fmt_resolved_info(
|
||||
&dep.maybe_code,
|
||||
f,
|
||||
prefix.clone(),
|
||||
dep.maybe_type.is_none() && last,
|
||||
graph,
|
||||
false,
|
||||
seen,
|
||||
)?;
|
||||
if let Some(child) = self.build_resolved_info(&dep.maybe_code, false) {
|
||||
children.push(child);
|
||||
}
|
||||
}
|
||||
if !dep.maybe_type.is_none() {
|
||||
fmt_resolved_info(&dep.maybe_type, f, prefix, last, graph, true, seen)?;
|
||||
if let Some(child) = self.build_resolved_info(&dep.maybe_type, true) {
|
||||
children.push(child);
|
||||
}
|
||||
}
|
||||
children
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_module_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||
module: &Module,
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
graph: &ModuleGraph,
|
||||
type_dep: bool,
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
let was_seen = seen.contains(&module.specifier);
|
||||
let children = !((module.dependencies.is_empty()
|
||||
&& module.maybe_types_dependency.is_none())
|
||||
|| was_seen);
|
||||
let (specifier_str, size_str) = if was_seen {
|
||||
fn build_module_info(&mut self, module: &Module, type_dep: bool) -> TreeNode {
|
||||
enum PackageOrSpecifier {
|
||||
Package(NpmResolutionPackage),
|
||||
Specifier(ModuleSpecifier),
|
||||
}
|
||||
|
||||
use PackageOrSpecifier::*;
|
||||
|
||||
let package_or_specifier =
|
||||
match self.npm_info.package_from_specifier(&module.specifier) {
|
||||
Some(package) => Package(package.clone()),
|
||||
None => Specifier(module.specifier.clone()),
|
||||
};
|
||||
let was_seen = !self.seen.insert(match &package_or_specifier {
|
||||
Package(package) => package.id.to_string(),
|
||||
Specifier(specifier) => specifier.to_string(),
|
||||
});
|
||||
let header_text = if was_seen {
|
||||
let specifier_str = if type_dep {
|
||||
colors::italic_gray(&module.specifier).to_string()
|
||||
} else {
|
||||
colors::gray(&module.specifier).to_string()
|
||||
};
|
||||
(specifier_str, colors::gray(" *").to_string())
|
||||
format!("{} {}", specifier_str, colors::gray("*"))
|
||||
} else {
|
||||
let specifier_str = if type_dep {
|
||||
colors::italic(&module.specifier).to_string()
|
||||
} else {
|
||||
module.specifier.to_string()
|
||||
};
|
||||
let size_str =
|
||||
colors::gray(format!(" ({})", display::human_size(module.size() as f64)))
|
||||
.to_string();
|
||||
(specifier_str, size_str)
|
||||
let header_text = match &package_or_specifier {
|
||||
Package(package) => {
|
||||
format!("{} - {}", specifier_str, package.id.version)
|
||||
}
|
||||
Specifier(_) => specifier_str,
|
||||
};
|
||||
let maybe_size = match &package_or_specifier {
|
||||
Package(package) => self
|
||||
.npm_info
|
||||
.package_sizes
|
||||
.get(&package.id)
|
||||
.map(|s| *s as u64),
|
||||
Specifier(_) => module
|
||||
.maybe_source
|
||||
.as_ref()
|
||||
.map(|s| s.as_bytes().len() as u64),
|
||||
};
|
||||
format!("{} {}", header_text, maybe_size_to_text(maybe_size))
|
||||
};
|
||||
|
||||
seen.insert(module.specifier.clone());
|
||||
|
||||
fmt_info_msg(
|
||||
f,
|
||||
prefix.clone(),
|
||||
last,
|
||||
children,
|
||||
format!("{}{}", specifier_str, size_str),
|
||||
)?;
|
||||
let mut tree_node = TreeNode::from_text(header_text);
|
||||
|
||||
if !was_seen {
|
||||
let mut prefix = prefix.to_string();
|
||||
if last {
|
||||
prefix.push(EMPTY_CONNECTOR);
|
||||
} else {
|
||||
prefix.push(VERTICAL_CONNECTOR);
|
||||
}
|
||||
prefix.push(EMPTY_CONNECTOR);
|
||||
let dep_len = module.dependencies.len();
|
||||
if let Some((_, type_dep)) = &module.maybe_types_dependency {
|
||||
fmt_resolved_info(type_dep, f, &prefix, dep_len == 0, graph, true, seen)?;
|
||||
}
|
||||
for (idx, (_, dep)) in module.dependencies.iter().enumerate() {
|
||||
fmt_dep_info(
|
||||
dep,
|
||||
f,
|
||||
&prefix,
|
||||
idx == dep_len - 1 && module.maybe_types_dependency.is_none(),
|
||||
graph,
|
||||
seen,
|
||||
)?;
|
||||
if let Some(child) = self.build_resolved_info(type_dep, true) {
|
||||
tree_node.children.push(child);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
match &package_or_specifier {
|
||||
Package(package) => {
|
||||
tree_node.children.extend(self.build_npm_deps(package));
|
||||
}
|
||||
Specifier(_) => {
|
||||
for dep in module.dependencies.values() {
|
||||
tree_node.children.extend(self.build_dep_info(dep));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tree_node
|
||||
}
|
||||
|
||||
fn fmt_error_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||
fn build_npm_deps(
|
||||
&mut self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> Vec<TreeNode> {
|
||||
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!("npm:{} {}", dep_id, size_str));
|
||||
if let Some(package) = self.npm_info.packages.get(dep_id) {
|
||||
if !package.dependencies.is_empty() {
|
||||
if self.seen.contains(&package.id.to_string()) {
|
||||
child.text = format!("{} {}", child.text, colors::gray("*"));
|
||||
} else {
|
||||
let package = package.clone();
|
||||
child.children.extend(self.build_npm_deps(&package));
|
||||
}
|
||||
}
|
||||
}
|
||||
children.push(child);
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
fn build_error_info(
|
||||
&mut self,
|
||||
err: &ModuleGraphError,
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
specifier: &ModuleSpecifier,
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
seen.insert(specifier.clone());
|
||||
) -> TreeNode {
|
||||
self.seen.insert(specifier.to_string());
|
||||
match err {
|
||||
ModuleGraphError::InvalidSource(_, _) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(invalid source)")
|
||||
self.build_error_msg(specifier, "(invalid source)")
|
||||
}
|
||||
ModuleGraphError::InvalidTypeAssertion { .. } => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(invalid import assertion)")
|
||||
self.build_error_msg(specifier, "(invalid import assertion)")
|
||||
}
|
||||
ModuleGraphError::LoadingErr(_, _) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(loading error)")
|
||||
self.build_error_msg(specifier, "(loading error)")
|
||||
}
|
||||
ModuleGraphError::ParseErr(_, _) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(parsing error)")
|
||||
self.build_error_msg(specifier, "(parsing error)")
|
||||
}
|
||||
ModuleGraphError::ResolutionError(_) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(resolution error)")
|
||||
self.build_error_msg(specifier, "(resolution error)")
|
||||
}
|
||||
ModuleGraphError::UnsupportedImportAssertionType(_, _) => {
|
||||
self.build_error_msg(specifier, "(unsupported import assertion)")
|
||||
}
|
||||
ModuleGraphError::UnsupportedImportAssertionType(_, _) => fmt_error_msg(
|
||||
f,
|
||||
prefix,
|
||||
last,
|
||||
specifier,
|
||||
"(unsupported import assertion)",
|
||||
),
|
||||
ModuleGraphError::UnsupportedMediaType(_, _) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(unsupported)")
|
||||
self.build_error_msg(specifier, "(unsupported)")
|
||||
}
|
||||
ModuleGraphError::Missing(_) => {
|
||||
fmt_error_msg(f, prefix, last, specifier, "(missing)")
|
||||
self.build_error_msg(specifier, "(missing)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_info_msg<S, M>(
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
children: bool,
|
||||
msg: M,
|
||||
) -> fmt::Result
|
||||
where
|
||||
S: AsRef<str> + fmt::Display + Clone,
|
||||
M: AsRef<str> + fmt::Display,
|
||||
{
|
||||
let sibling_connector = if last {
|
||||
LAST_SIBLING_CONNECTOR
|
||||
} else {
|
||||
SIBLING_CONNECTOR
|
||||
};
|
||||
let child_connector = if children {
|
||||
CHILD_DEPS_CONNECTOR
|
||||
} else {
|
||||
CHILD_NO_DEPS_CONNECTOR
|
||||
};
|
||||
writeln!(
|
||||
f,
|
||||
"{} {}",
|
||||
colors::gray(format!(
|
||||
"{}{}─{}",
|
||||
prefix, sibling_connector, child_connector
|
||||
)),
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
fn fmt_error_msg<S, M>(
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
fn build_error_msg(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
error_msg: M,
|
||||
) -> fmt::Result
|
||||
where
|
||||
S: AsRef<str> + fmt::Display + Clone,
|
||||
M: AsRef<str> + fmt::Display,
|
||||
{
|
||||
fmt_info_msg(
|
||||
f,
|
||||
prefix,
|
||||
last,
|
||||
false,
|
||||
format!("{} {}", colors::red(specifier), colors::red_bold(error_msg)),
|
||||
)
|
||||
}
|
||||
error_msg: &str,
|
||||
) -> TreeNode {
|
||||
TreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::red(specifier),
|
||||
colors::red_bold(error_msg)
|
||||
))
|
||||
}
|
||||
|
||||
fn fmt_resolved_info<S: AsRef<str> + fmt::Display + Clone>(
|
||||
fn build_resolved_info(
|
||||
&mut self,
|
||||
resolved: &Resolved,
|
||||
f: &mut impl Write,
|
||||
prefix: S,
|
||||
last: bool,
|
||||
graph: &ModuleGraph,
|
||||
type_dep: bool,
|
||||
seen: &mut HashSet<ModuleSpecifier>,
|
||||
) -> fmt::Result {
|
||||
) -> Option<TreeNode> {
|
||||
match resolved {
|
||||
Resolved::Ok { specifier, .. } => {
|
||||
let resolved_specifier = graph.resolve(specifier);
|
||||
match graph.try_get(&resolved_specifier) {
|
||||
Ok(Some(module)) => {
|
||||
fmt_module_info(module, f, prefix, last, graph, type_dep, seen)
|
||||
}
|
||||
Err(err) => {
|
||||
fmt_error_info(&err, f, prefix, last, &resolved_specifier, seen)
|
||||
}
|
||||
Ok(None) => fmt_info_msg(
|
||||
f,
|
||||
prefix,
|
||||
last,
|
||||
false,
|
||||
format!(
|
||||
let resolved_specifier = self.graph.resolve(specifier);
|
||||
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!(
|
||||
"{} {}",
|
||||
colors::red(specifier),
|
||||
colors::red_bold("(missing)")
|
||||
),
|
||||
),
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
Resolved::Err(err) => fmt_info_msg(
|
||||
f,
|
||||
prefix,
|
||||
last,
|
||||
false,
|
||||
format!(
|
||||
Resolved::Err(err) => Some(TreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::italic(err.to_string()),
|
||||
colors::red_bold("(resolve error)")
|
||||
),
|
||||
),
|
||||
_ => Ok(()),
|
||||
))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deno_graph::source::CacheInfo;
|
||||
use deno_graph::source::MemoryLoader;
|
||||
use deno_graph::source::Source;
|
||||
use test_util::strip_ansi_codes;
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_info_graph() {
|
||||
let mut loader = MemoryLoader::new(
|
||||
vec![
|
||||
(
|
||||
"https://deno.land/x/example/a.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/a.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"import * as b from "./b.ts";
|
||||
import type { F } from "./f.d.ts";
|
||||
import * as g from "./g.js";
|
||||
"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/b.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/b.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"
|
||||
// @deno-types="./c.d.ts"
|
||||
import * as c from "./c.js";
|
||||
import * as d from "./d.ts";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/c.js",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/c.js",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/javascript",
|
||||
)]),
|
||||
content: r#"export const c = "c";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/c.d.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/c.d.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"export const c: "c";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/d.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/d.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"import * as e from "./e.ts";
|
||||
export const d = "d";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/e.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/e.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"import * as b from "./b.ts";
|
||||
export const e = "e";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/f.d.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/f.d.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"export interface F { }"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/g.js",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/g.js",
|
||||
maybe_headers: Some(vec![
|
||||
("content-type", "application/javascript"),
|
||||
("x-typescript-types", "./g.d.ts"),
|
||||
]),
|
||||
content: r#"export const g = "g";"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/g.d.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/g.d.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"export const g: "g";"#,
|
||||
},
|
||||
),
|
||||
],
|
||||
vec![(
|
||||
"https://deno.land/x/example/a.ts",
|
||||
CacheInfo {
|
||||
local: Some(PathBuf::from(
|
||||
"/cache/deps/https/deno.land/x/example/a.ts",
|
||||
)),
|
||||
emit: Some(PathBuf::from(
|
||||
"/cache/deps/https/deno.land/x/example/a.js",
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
)],
|
||||
);
|
||||
let root_specifier =
|
||||
ModuleSpecifier::parse("https://deno.land/x/example/a.ts").unwrap();
|
||||
let graph = deno_graph::create_graph(
|
||||
vec![(root_specifier, ModuleKind::Esm)],
|
||||
&mut loader,
|
||||
deno_graph::GraphOptions {
|
||||
is_dynamic: false,
|
||||
imports: None,
|
||||
resolver: None,
|
||||
locker: None,
|
||||
module_analyzer: None,
|
||||
reporter: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let mut output = String::new();
|
||||
fmt_module_graph(&graph, &mut output).unwrap();
|
||||
assert_eq!(
|
||||
strip_ansi_codes(&output),
|
||||
r#"local: /cache/deps/https/deno.land/x/example/a.ts
|
||||
emit: /cache/deps/https/deno.land/x/example/a.js
|
||||
type: TypeScript
|
||||
dependencies: 8 unique (total 477B)
|
||||
|
||||
https://deno.land/x/example/a.ts (129B)
|
||||
├─┬ https://deno.land/x/example/b.ts (120B)
|
||||
│ ├── https://deno.land/x/example/c.js (21B)
|
||||
│ ├── https://deno.land/x/example/c.d.ts (20B)
|
||||
│ └─┬ https://deno.land/x/example/d.ts (62B)
|
||||
│ └─┬ https://deno.land/x/example/e.ts (62B)
|
||||
│ └── https://deno.land/x/example/b.ts *
|
||||
├── https://deno.land/x/example/f.d.ts (22B)
|
||||
└─┬ https://deno.land/x/example/g.js (21B)
|
||||
└── https://deno.land/x/example/g.d.ts (20B)
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_info_graph_import_assertion() {
|
||||
let mut loader = MemoryLoader::new(
|
||||
vec![
|
||||
(
|
||||
"https://deno.land/x/example/a.ts",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/a.ts",
|
||||
maybe_headers: Some(vec![(
|
||||
"content-type",
|
||||
"application/typescript",
|
||||
)]),
|
||||
content: r#"import b from "./b.json" assert { type: "json" };
|
||||
const c = await import("./c.json", { assert: { type: "json" } });
|
||||
"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/b.json",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/b.json",
|
||||
maybe_headers: Some(vec![("content-type", "application/json")]),
|
||||
content: r#"{"b":"c"}"#,
|
||||
},
|
||||
),
|
||||
(
|
||||
"https://deno.land/x/example/c.json",
|
||||
Source::Module {
|
||||
specifier: "https://deno.land/x/example/c.json",
|
||||
maybe_headers: Some(vec![("content-type", "application/json")]),
|
||||
content: r#"{"c":1}"#,
|
||||
},
|
||||
),
|
||||
],
|
||||
vec![(
|
||||
"https://deno.land/x/example/a.ts",
|
||||
CacheInfo {
|
||||
local: Some(PathBuf::from(
|
||||
"/cache/deps/https/deno.land/x/example/a.ts",
|
||||
)),
|
||||
emit: Some(PathBuf::from(
|
||||
"/cache/deps/https/deno.land/x/example/a.js",
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
)],
|
||||
);
|
||||
let root_specifier =
|
||||
ModuleSpecifier::parse("https://deno.land/x/example/a.ts").unwrap();
|
||||
let graph = deno_graph::create_graph(
|
||||
vec![(root_specifier, ModuleKind::Esm)],
|
||||
&mut loader,
|
||||
deno_graph::GraphOptions {
|
||||
is_dynamic: false,
|
||||
imports: None,
|
||||
resolver: None,
|
||||
locker: None,
|
||||
module_analyzer: None,
|
||||
reporter: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let mut output = String::new();
|
||||
fmt_module_graph(&graph, &mut output).unwrap();
|
||||
assert_eq!(
|
||||
strip_ansi_codes(&output),
|
||||
r#"local: /cache/deps/https/deno.land/x/example/a.ts
|
||||
emit: /cache/deps/https/deno.land/x/example/a.js
|
||||
type: TypeScript
|
||||
dependencies: 2 unique (total 156B)
|
||||
|
||||
https://deno.land/x/example/a.ts (140B)
|
||||
├── https://deno.land/x/example/b.json (9B)
|
||||
└── https://deno.land/x/example/c.json (7B)
|
||||
"#
|
||||
);
|
||||
fn maybe_size_to_text(maybe_size: Option<u64>) -> String {
|
||||
colors::gray(format!(
|
||||
"({})",
|
||||
match maybe_size {
|
||||
Some(size) => display::human_size(size as f64),
|
||||
None => "unknown".to_string(),
|
||||
}
|
||||
))
|
||||
.to_string()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue