diff options
Diffstat (limited to 'cli/npm/cache.rs')
-rw-r--r-- | cli/npm/cache.rs | 92 |
1 files changed, 73 insertions, 19 deletions
diff --git a/cli/npm/cache.rs b/cli/npm/cache.rs index e5ce3dfdd..b052f89cd 100644 --- a/cli/npm/cache.rs +++ b/cli/npm/cache.rs @@ -1,6 +1,5 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use std::borrow::Cow; use std::fs; use std::path::Path; use std::path::PathBuf; @@ -208,24 +207,18 @@ impl ReadonlyNpmCache { pub fn package_name_folder(&self, name: &str, registry_url: &Url) -> PathBuf { let mut dir = self.registry_folder(registry_url); - let parts = name.split('/').map(Cow::Borrowed).collect::<Vec<_>>(); if name.to_lowercase() != name { - // Lowercase package names introduce complications. - // When implementing this ensure: - // 1. It works on case insensitive filesystems. ex. JSON should not - // conflict with json... yes you read that right, those are separate - // packages. - // 2. We can figure out the package id from the path. This is used - // in resolve_package_id_from_specifier - // Probably use a hash of the package name at `npm/-/<hash>` then create - // a mapping for these package names. - todo!("deno currently doesn't support npm package names that are not all lowercase"); - } - // ensure backslashes are used on windows - for part in parts { - dir = dir.join(&*part); + let encoded_name = mixed_case_package_name_encode(name); + // Using the encoded directory may have a collision with an actual package name + // so prefix it with an underscore since npm packages can't start with that + dir.join(format!("_{}", encoded_name)) + } else { + // ensure backslashes are used on windows + for part in name.split('/') { + dir = dir.join(part); + } + dir } - dir } pub fn registry_folder(&self, registry_url: &Url) -> PathBuf { @@ -262,11 +255,27 @@ impl ReadonlyNpmCache { )) // this not succeeding indicates a fatal issue, so unwrap .unwrap(); - let relative_url = registry_root_dir.make_relative(specifier)?; + let mut relative_url = registry_root_dir.make_relative(specifier)?; if relative_url.starts_with("../") { return None; } + // base32 decode the url if it starts with an underscore + // * Ex. _{base32(package_name)}/ + if let Some(end_url) = relative_url.strip_prefix('_') { + let mut parts = end_url + .split('/') + .map(ToOwned::to_owned) + .collect::<Vec<_>>(); + match mixed_case_package_name_decode(&parts[0]) { + Some(part) => { + parts[0] = part; + } + None => return None, + } + relative_url = parts.join("/"); + } + // examples: // * chalk/5.0.1/ // * @types/chalk/5.0.1/ @@ -473,6 +482,21 @@ impl NpmCache { } } +pub fn mixed_case_package_name_encode(name: &str) -> String { + // use base32 encoding because it's reversable and the character set + // only includes the characters within 0-9 and A-Z so it can be lower cased + base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + name.as_bytes(), + ) + .to_lowercase() +} + +pub fn mixed_case_package_name_decode(name: &str) -> Option<String> { + base32::decode(base32::Alphabet::RFC4648 { padding: false }, name) + .and_then(|b| String::from_utf8(b).ok()) +} + #[cfg(test)] mod test { use deno_core::url::Url; @@ -482,7 +506,7 @@ mod test { use crate::npm::semver::NpmVersion; #[test] - fn should_get_lowercase_package_folder() { + fn should_get_package_folder() { let root_dir = crate::deno_dir::DenoDir::new(None).unwrap().root; let cache = ReadonlyNpmCache::new(root_dir.clone()); let registry_url = Url::parse("https://registry.npmjs.org/").unwrap(); @@ -516,5 +540,35 @@ mod test { .join("json") .join("1.2.5_1"), ); + + assert_eq!( + cache.package_folder_for_id( + &NpmPackageCacheFolderId { + name: "JSON".to_string(), + version: NpmVersion::parse("2.1.5").unwrap(), + copy_index: 0, + }, + ®istry_url, + ), + root_dir + .join("registry.npmjs.org") + .join("_jjju6tq") + .join("2.1.5"), + ); + + assert_eq!( + cache.package_folder_for_id( + &NpmPackageCacheFolderId { + name: "@types/JSON".to_string(), + version: NpmVersion::parse("2.1.5").unwrap(), + copy_index: 0, + }, + ®istry_url, + ), + root_dir + .join("registry.npmjs.org") + .join("_ib2hs4dfomxuuu2pjy") + .join("2.1.5"), + ); } } |