diff options
Diffstat (limited to 'cli/tools/vendor/mod.rs')
-rw-r--r-- | cli/tools/vendor/mod.rs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs new file mode 100644 index 000000000..eb9c91071 --- /dev/null +++ b/cli/tools/vendor/mod.rs @@ -0,0 +1,172 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::path::Path; +use std::path::PathBuf; + +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_runtime::permissions::Permissions; + +use crate::flags::VendorFlags; +use crate::fs_util; +use crate::lockfile; +use crate::proc_state::ProcState; +use crate::resolver::ImportMapResolver; +use crate::resolver::JsxResolver; +use crate::tools::vendor::specifiers::is_remote_specifier_text; + +mod analyze; +mod build; +mod import_map; +mod mappings; +mod specifiers; +#[cfg(test)] +mod test; + +pub async fn vendor(ps: ProcState, flags: VendorFlags) -> Result<(), AnyError> { + let raw_output_dir = match &flags.output_path { + Some(output_path) => output_path.to_owned(), + None => PathBuf::from("vendor/"), + }; + let output_dir = fs_util::resolve_from_cwd(&raw_output_dir)?; + validate_output_dir(&output_dir, &flags, &ps)?; + let graph = create_graph(&ps, &flags).await?; + let vendored_count = + build::build(&graph, &output_dir, &build::RealVendorEnvironment)?; + + eprintln!( + r#"Vendored {} {} into {} directory. + +To use vendored modules, specify the `--import-map` flag when invoking deno subcommands: + deno run -A --import-map {} {}"#, + vendored_count, + if vendored_count == 1 { + "module" + } else { + "modules" + }, + raw_output_dir.display(), + raw_output_dir.join("import_map.json").display(), + flags + .specifiers + .iter() + .map(|s| s.as_str()) + .find(|s| !is_remote_specifier_text(s)) + .unwrap_or("main.ts"), + ); + + Ok(()) +} + +fn validate_output_dir( + output_dir: &Path, + flags: &VendorFlags, + ps: &ProcState, +) -> Result<(), AnyError> { + if !flags.force && !is_dir_empty(output_dir)? { + bail!(concat!( + "Output directory was not empty. Please specify an empty directory or use ", + "--force to ignore this error and potentially overwrite its contents.", + )); + } + + // check the import map + if let Some(import_map_path) = ps + .maybe_import_map + .as_ref() + .and_then(|m| m.base_url().to_file_path().ok()) + .and_then(|p| fs_util::canonicalize_path(&p).ok()) + { + // make the output directory in order to canonicalize it for the check below + std::fs::create_dir_all(&output_dir)?; + let output_dir = + fs_util::canonicalize_path(output_dir).with_context(|| { + format!("Failed to canonicalize: {}", output_dir.display()) + })?; + + if import_map_path.starts_with(&output_dir) { + // We don't allow using the output directory to help generate the new state + // of itself because supporting this scenario adds a lot of complexity. + bail!( + "Using an import map found in the output directory is not supported." + ); + } + } + + Ok(()) +} + +fn is_dir_empty(dir_path: &Path) -> Result<bool, AnyError> { + match std::fs::read_dir(&dir_path) { + Ok(mut dir) => Ok(dir.next().is_none()), + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(true), + Err(err) => { + bail!("Error reading directory {}: {}", dir_path.display(), err) + } + } +} + +async fn create_graph( + ps: &ProcState, + flags: &VendorFlags, +) -> Result<deno_graph::ModuleGraph, AnyError> { + let entry_points = flags + .specifiers + .iter() + .map(|p| { + let url = resolve_url_or_path(p)?; + Ok((url, deno_graph::ModuleKind::Esm)) + }) + .collect::<Result<Vec<_>, AnyError>>()?; + + // todo(dsherret): there is a lot of copy and paste here from + // other parts of the codebase. We should consolidate this. + let mut cache = crate::cache::FetchCacher::new( + ps.dir.gen_cache.clone(), + ps.file_fetcher.clone(), + Permissions::allow_all(), + Permissions::allow_all(), + ); + let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); + let maybe_imports = if let Some(config_file) = &ps.maybe_config_file { + config_file.to_maybe_imports()? + } else { + None + }; + let maybe_import_map_resolver = + ps.maybe_import_map.clone().map(ImportMapResolver::new); + let maybe_jsx_resolver = ps + .maybe_config_file + .as_ref() + .map(|cf| { + cf.to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) + }) + .flatten(); + let maybe_resolver = if maybe_jsx_resolver.is_some() { + maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) + } else { + maybe_import_map_resolver + .as_ref() + .map(|im| im.as_resolver()) + }; + + let graph = deno_graph::create_graph( + entry_points, + false, + maybe_imports, + &mut cache, + maybe_resolver, + maybe_locker, + None, + None, + ) + .await; + + graph.lock()?; + graph.valid()?; + + Ok(graph) +} |