summaryrefslogtreecommitdiff
path: root/cli/tools/vendor/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tools/vendor/mod.rs')
-rw-r--r--cli/tools/vendor/mod.rs578
1 files changed, 0 insertions, 578 deletions
diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs
deleted file mode 100644
index 3de08f1d0..000000000
--- a/cli/tools/vendor/mod.rs
+++ /dev/null
@@ -1,578 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use std::path::Path;
-use std::path::PathBuf;
-use std::sync::Arc;
-
-use deno_ast::ModuleSpecifier;
-use deno_ast::TextChange;
-use deno_core::anyhow::bail;
-use deno_core::anyhow::Context;
-use deno_core::error::AnyError;
-use deno_core::futures::FutureExt;
-use deno_core::resolve_url_or_path;
-use deno_graph::GraphKind;
-use deno_runtime::colors;
-use log::warn;
-
-use crate::args::CliOptions;
-use crate::args::ConfigFile;
-use crate::args::Flags;
-use crate::args::FmtOptionsConfig;
-use crate::args::VendorFlags;
-use crate::factory::CliFactory;
-use crate::tools::fmt::format_json;
-use crate::util::fs::canonicalize_path;
-use crate::util::fs::resolve_from_cwd;
-use crate::util::path::relative_specifier;
-use deno_runtime::fs_util::specifier_to_file_path;
-
-mod analyze;
-mod build;
-mod import_map;
-mod mappings;
-mod specifiers;
-#[cfg(test)]
-mod test;
-
-pub async fn vendor(
- flags: Arc<Flags>,
- vendor_flags: VendorFlags,
-) -> Result<(), AnyError> {
- log::info!(
- "{}",
- colors::yellow("⚠️ Warning: `deno vendor` is deprecated and will be removed in Deno 2.0.\nAdd `\"vendor\": true` to your `deno.json` or use the `--vendor` flag instead."),
- );
- let mut cli_options = CliOptions::from_flags(flags)?;
- let raw_output_dir = match &vendor_flags.output_path {
- Some(output_path) => PathBuf::from(output_path).to_owned(),
- None => PathBuf::from("vendor/"),
- };
- let output_dir = resolve_from_cwd(&raw_output_dir)?;
- validate_output_dir(&output_dir, &vendor_flags)?;
- validate_options(&mut cli_options, &output_dir)?;
- let factory = CliFactory::from_cli_options(Arc::new(cli_options));
- let cli_options = factory.cli_options()?;
- if cli_options.workspace().config_folders().len() > 1 {
- bail!("deno vendor is not supported in a workspace. Set `\"vendor\": true` in the workspace deno.json file instead");
- }
- let entry_points =
- resolve_entry_points(&vendor_flags, cli_options.initial_cwd())?;
- let jsx_import_source = cli_options
- .workspace()
- .to_maybe_jsx_import_source_config()?;
- let module_graph_creator = factory.module_graph_creator().await?.clone();
- let workspace_resolver = factory.workspace_resolver().await?;
- let root_folder = cli_options.workspace().root_folder_configs();
- let maybe_config_file = root_folder.deno_json.as_ref();
- let output = build::build(build::BuildInput {
- entry_points,
- build_graph: move |entry_points| {
- async move {
- module_graph_creator
- .create_graph(GraphKind::All, entry_points)
- .await
- }
- .boxed_local()
- },
- parsed_source_cache: factory.parsed_source_cache(),
- output_dir: &output_dir,
- maybe_original_import_map: workspace_resolver.maybe_import_map(),
- maybe_jsx_import_source: jsx_import_source.as_ref(),
- resolver: factory.resolver().await?.as_graph_resolver(),
- environment: &build::RealVendorEnvironment,
- })
- .await?;
-
- let vendored_count = output.vendored_count;
- let graph = output.graph;
- let npm_package_count = graph.npm_packages.len();
- let try_add_node_modules_dir = npm_package_count > 0
- && cli_options
- .node_modules_dir()?
- .map(|m| m.uses_node_modules_dir())
- .unwrap_or(true);
-
- log::info!(
- concat!("Vendored {} {} into {} directory.",),
- vendored_count,
- if vendored_count == 1 {
- "module"
- } else {
- "modules"
- },
- raw_output_dir.display(),
- );
-
- let try_add_import_map = vendored_count > 0;
- let modified_result = maybe_update_config_file(
- &output_dir,
- maybe_config_file,
- try_add_import_map,
- try_add_node_modules_dir,
- );
-
- // cache the node_modules folder when it's been added to the config file
- if modified_result.added_node_modules_dir {
- let node_modules_path =
- cli_options.node_modules_dir_path().cloned().or_else(|| {
- maybe_config_file
- .as_ref()
- .map(|d| &d.specifier)
- .filter(|c| c.scheme() == "file")
- .and_then(|c| c.to_file_path().ok())
- .map(|config_path| config_path.parent().unwrap().join("node_modules"))
- });
- if let Some(node_modules_path) = node_modules_path {
- let cli_options =
- cli_options.with_node_modules_dir_path(node_modules_path);
- let factory = CliFactory::from_cli_options(Arc::new(cli_options));
- if let Some(managed) = factory.npm_resolver().await?.as_managed() {
- managed.cache_packages().await?;
- }
- }
- log::info!(
- concat!(
- "Vendored {} npm {} into node_modules directory. Set `nodeModulesDir: false` ",
- "in the Deno configuration file to disable vendoring npm packages in the future.",
- ),
- npm_package_count,
- if npm_package_count == 1 {
- "package"
- } else {
- "packages"
- },
- );
- }
-
- if vendored_count > 0 {
- let import_map_path = raw_output_dir.join("import_map.json");
- if modified_result.updated_import_map {
- log::info!(
- concat!(
- "\nUpdated your local Deno configuration file with a reference to the ",
- "new vendored import map at {}. Invoking Deno subcommands will now ",
- "automatically resolve using the vendored modules. You may override ",
- "this by providing the `--import-map <other-import-map>` flag or by ",
- "manually editing your Deno configuration file.",
- ),
- import_map_path.display(),
- );
- } else {
- log::info!(
- concat!(
- "\nTo use vendored modules, specify the `--import-map {}` flag when ",
- r#"invoking Deno subcommands or add an `"importMap": "<path_to_vendored_import_map>"` "#,
- "entry to a deno.json file.",
- ),
- import_map_path.display(),
- );
- }
- }
-
- Ok(())
-}
-
-fn validate_output_dir(
- output_dir: &Path,
- flags: &VendorFlags,
-) -> 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.",
- ));
- }
- Ok(())
-}
-
-fn validate_options(
- options: &mut CliOptions,
- output_dir: &Path,
-) -> Result<(), AnyError> {
- let import_map_specifier = options
- .resolve_specified_import_map_specifier()?
- .or_else(|| {
- let config_file = options.workspace().root_deno_json()?;
- config_file
- .to_import_map_specifier()
- .ok()
- .flatten()
- .or_else(|| {
- if config_file.is_an_import_map() {
- Some(config_file.specifier.clone())
- } else {
- None
- }
- })
- });
- // check the import map
- if let Some(import_map_path) = import_map_specifier
- .and_then(|p| specifier_to_file_path(&p).ok())
- .and_then(|p| 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 = canonicalize_path(output_dir).with_context(|| {
- format!("Failed to canonicalize: {}", output_dir.display())
- })?;
-
- if import_map_path.starts_with(output_dir) {
- // canonicalize to make the test for this pass on the CI
- let cwd = canonicalize_path(&std::env::current_dir()?)?;
- // We don't allow using the output directory to help generate the
- // new state because this may lead to cryptic error messages.
- log::warn!(
- concat!(
- "Ignoring import map. Specifying an import map file ({}) in the ",
- "deno vendor output directory is not supported. If you wish to use ",
- "an import map while vendoring, please specify one located outside ",
- "this directory."
- ),
- import_map_path
- .strip_prefix(&cwd)
- .unwrap_or(&import_map_path)
- .display()
- .to_string(),
- );
-
- // don't use an import map in the config
- options.set_import_map_specifier(None);
- }
- }
-
- Ok(())
-}
-
-fn maybe_update_config_file(
- output_dir: &Path,
- maybe_config_file: Option<&Arc<ConfigFile>>,
- try_add_import_map: bool,
- try_add_node_modules_dir: bool,
-) -> ModifiedResult {
- assert!(output_dir.is_absolute());
- let config_file = match maybe_config_file {
- Some(config_file) => config_file,
- None => return ModifiedResult::default(),
- };
- if config_file.specifier.scheme() != "file" {
- return ModifiedResult::default();
- }
-
- let fmt_config_options = config_file
- .to_fmt_config()
- .ok()
- .map(|config| config.options)
- .unwrap_or_default();
- let result = update_config_file(
- config_file,
- &fmt_config_options,
- if try_add_import_map {
- Some(
- ModuleSpecifier::from_file_path(output_dir.join("import_map.json"))
- .unwrap(),
- )
- } else {
- None
- },
- try_add_node_modules_dir,
- );
- match result {
- Ok(modified_result) => modified_result,
- Err(err) => {
- warn!("Error updating config file. {:#}", err);
- ModifiedResult::default()
- }
- }
-}
-
-fn update_config_file(
- config_file: &ConfigFile,
- fmt_options: &FmtOptionsConfig,
- import_map_specifier: Option<ModuleSpecifier>,
- try_add_node_modules_dir: bool,
-) -> Result<ModifiedResult, AnyError> {
- let config_path = specifier_to_file_path(&config_file.specifier)?;
- let config_text = std::fs::read_to_string(&config_path)?;
- let import_map_specifier =
- import_map_specifier.and_then(|import_map_specifier| {
- relative_specifier(&config_file.specifier, &import_map_specifier)
- });
- let modified_result = update_config_text(
- &config_text,
- fmt_options,
- import_map_specifier.as_deref(),
- try_add_node_modules_dir,
- )?;
- if let Some(new_text) = &modified_result.new_text {
- std::fs::write(config_path, new_text)?;
- }
- Ok(modified_result)
-}
-
-#[derive(Default)]
-struct ModifiedResult {
- updated_import_map: bool,
- added_node_modules_dir: bool,
- new_text: Option<String>,
-}
-
-fn update_config_text(
- text: &str,
- fmt_options: &FmtOptionsConfig,
- import_map_specifier: Option<&str>,
- try_add_node_modules_dir: bool,
-) -> Result<ModifiedResult, AnyError> {
- use jsonc_parser::ast::ObjectProp;
- use jsonc_parser::ast::Value;
- let text = if text.trim().is_empty() { "{}\n" } else { text };
- let ast =
- jsonc_parser::parse_to_ast(text, &Default::default(), &Default::default())?;
- let obj = match ast.value {
- Some(Value::Object(obj)) => obj,
- _ => bail!("Failed updating config file due to no object."),
- };
- let mut modified_result = ModifiedResult::default();
- let mut text_changes = Vec::new();
- let mut should_format = false;
-
- if try_add_node_modules_dir {
- // Only modify the nodeModulesDir property if it's not set
- // as this allows people to opt-out of this when vendoring
- // by specifying `nodeModulesDir: false`
- if obj.get("nodeModulesDir").is_none() {
- let insert_position = obj.range.end - 1;
- text_changes.push(TextChange {
- range: insert_position..insert_position,
- new_text: r#""nodeModulesDir": "auto""#.to_string(),
- });
- should_format = true;
- modified_result.added_node_modules_dir = true;
- }
- }
-
- if let Some(import_map_specifier) = import_map_specifier {
- let import_map_specifier = import_map_specifier.replace('\"', "\\\"");
- match obj.get("importMap") {
- Some(ObjectProp {
- value: Value::StringLit(lit),
- ..
- }) => {
- text_changes.push(TextChange {
- range: lit.range.start..lit.range.end,
- new_text: format!("\"{}\"", import_map_specifier),
- });
- modified_result.updated_import_map = true;
- }
- None => {
- // insert it crudely at a position that won't cause any issues
- // with comments and format after to make it look nice
- let insert_position = obj.range.end - 1;
- text_changes.push(TextChange {
- range: insert_position..insert_position,
- new_text: format!(r#""importMap": "{}""#, import_map_specifier),
- });
- should_format = true;
- modified_result.updated_import_map = true;
- }
- // shouldn't happen
- Some(_) => {
- bail!("Failed updating importMap in config file due to invalid type.")
- }
- }
- }
-
- if text_changes.is_empty() {
- return Ok(modified_result);
- }
-
- let new_text = deno_ast::apply_text_changes(text, text_changes);
- modified_result.new_text = if should_format {
- format_json(&PathBuf::from("deno.json"), &new_text, fmt_options)
- .ok()
- .map(|formatted_text| formatted_text.unwrap_or(new_text))
- } else {
- Some(new_text)
- };
- Ok(modified_result)
-}
-
-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)
- }
- }
-}
-
-fn resolve_entry_points(
- flags: &VendorFlags,
- initial_cwd: &Path,
-) -> Result<Vec<ModuleSpecifier>, AnyError> {
- flags
- .specifiers
- .iter()
- .map(|p| resolve_url_or_path(p, initial_cwd).map_err(|e| e.into()))
- .collect::<Result<Vec<_>, _>>()
-}
-
-#[cfg(test)]
-mod internal_test {
- use super::*;
- use pretty_assertions::assert_eq;
-
- #[test]
- fn update_config_text_no_existing_props_add_prop() {
- let result = update_config_text(
- "{\n}",
- &Default::default(),
- Some("./vendor/import_map.json"),
- false,
- )
- .unwrap();
- assert!(result.updated_import_map);
- assert!(!result.added_node_modules_dir);
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "importMap": "./vendor/import_map.json"
-}
-"#
- );
-
- let result = update_config_text(
- "{\n}",
- &Default::default(),
- Some("./vendor/import_map.json"),
- true,
- )
- .unwrap();
- assert!(result.updated_import_map);
- assert!(result.added_node_modules_dir);
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "nodeModulesDir": "auto",
- "importMap": "./vendor/import_map.json"
-}
-"#
- );
-
- let result =
- update_config_text("{\n}", &Default::default(), None, true).unwrap();
- assert!(!result.updated_import_map);
- assert!(result.added_node_modules_dir);
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "nodeModulesDir": "auto"
-}
-"#
- );
- }
-
- #[test]
- fn update_config_text_existing_props_add_prop() {
- let result = update_config_text(
- r#"{
- "tasks": {
- "task1": "other"
- }
-}
-"#,
- &Default::default(),
- Some("./vendor/import_map.json"),
- false,
- )
- .unwrap();
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "tasks": {
- "task1": "other"
- },
- "importMap": "./vendor/import_map.json"
-}
-"#
- );
-
- // trailing comma
- let result = update_config_text(
- r#"{
- "tasks": {
- "task1": "other"
- },
-}
-"#,
- &Default::default(),
- Some("./vendor/import_map.json"),
- false,
- )
- .unwrap();
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "tasks": {
- "task1": "other"
- },
- "importMap": "./vendor/import_map.json"
-}
-"#
- );
- }
-
- #[test]
- fn update_config_text_update_prop() {
- let result = update_config_text(
- r#"{
- "importMap": "./local.json"
-}
-"#,
- &Default::default(),
- Some("./vendor/import_map.json"),
- false,
- )
- .unwrap();
- assert_eq!(
- result.new_text.unwrap(),
- r#"{
- "importMap": "./vendor/import_map.json"
-}
-"#
- );
- }
-
- #[test]
- fn no_update_node_modules_dir() {
- // will not update if this is already set (even if it's "none")
- let result = update_config_text(
- r#"{
- "nodeModulesDir": "none"
-}
-"#,
- &Default::default(),
- None,
- true,
- )
- .unwrap();
- assert!(!result.added_node_modules_dir);
- assert!(!result.updated_import_map);
- assert_eq!(result.new_text, None);
-
- let result = update_config_text(
- r#"{
- "nodeModulesDir": "auto"
-}
-"#,
- &Default::default(),
- None,
- true,
- )
- .unwrap();
- assert!(!result.added_node_modules_dir);
- assert!(!result.updated_import_map);
- assert_eq!(result.new_text, None);
- }
-}