Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 79 additions & 22 deletions rs-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
use indexmap::IndexMap;
use serde_json::Map;
use serde_json::Value;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Debug;
use thiserror::Error;
use url::Url;

use self::merge::code_unit_compare;
use self::merge::MergeDiagnostic;

#[cfg(feature = "ext")]
pub mod ext;
pub mod merge;
pub mod specifier;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -22,6 +25,7 @@ pub enum ImportMapDiagnostic {
InvalidAddress(String, String),
InvalidAddressNotString(String, String),
InvalidTopLevelKey(String),
Merge(Box<MergeDiagnostic>),
}

impl fmt::Display for ImportMapDiagnostic {
Expand Down Expand Up @@ -58,6 +62,9 @@ impl fmt::Display for ImportMapDiagnostic {
ImportMapDiagnostic::InvalidTopLevelKey(key) => {
write!(f, "Invalid top-level key \"{}\". Only \"imports\" and \"scopes\" can be present.", key)
}
ImportMapDiagnostic::Merge(merge) => {
write!(f, "Due to merge: {merge}")
}
}
}
}
Expand Down Expand Up @@ -130,6 +137,7 @@ struct SpecifierMapValue {
raw_key: Option<String>,
/// The raw value if it differs from the actual value.
raw_value: Option<String>,
/// None in case of block by null entry
maybe_address: Option<Url>,
}

Expand Down Expand Up @@ -199,6 +207,7 @@ type SpecifierMapInner = IndexMap<String, SpecifierMapValue>;

#[derive(Debug, Clone)]
pub struct SpecifierMap {
#[deprecated = "import map base_url doesn't work with merged import maps"]
base_url: Url,
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that base_url be better stored by the side of ImportMaps that are not merged on the application side.

inner: SpecifierMapInner,
}
Expand All @@ -221,6 +230,8 @@ impl SpecifierMap {
})
}

#[deprecated = "specifier map base_url doesn't work with merged import maps"]
#[allow(deprecated)]
pub fn contains(&self, key: &str) -> bool {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yet I have no idea what's the good place for this nethod, but it doesn't seem to be used anywhere

if let Ok(key) = normalize_specifier_key(key, &self.base_url) {
self.inner.contains_key(&key)
Expand All @@ -229,6 +240,8 @@ impl SpecifierMap {
}
}

#[deprecated = "specifier map base_url doesn't work with merged import maps"]
#[allow(deprecated)]
pub fn append(&mut self, key: String, value: String) -> Result<(), String> {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...In that case, methods like this be better implemented on SpecifierMapBuilder struct

let start_index = self
.inner
Expand Down Expand Up @@ -280,12 +293,9 @@ impl SpecifierMap {

fn sort(&mut self) {
// Sort in longest and alphabetical order.
self.inner.sort_by(|k1, _v1, k2, _v2| match k1.cmp(k2) {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
// index map guarantees that there can't be duplicate keys
Ordering::Equal => unreachable!(),
});
self
.inner
.sort_by(|k1, _v1, k2, _v2| code_unit_compare(k1, k2).reverse());
}
}

Expand Down Expand Up @@ -318,11 +328,27 @@ pub struct ImportMapWithDiagnostics {
pub diagnostics: Vec<ImportMapDiagnostic>,
}

impl ImportMapWithDiagnostics {
pub fn merge(&mut self, new: ImportMapWithDiagnostics) {
let mut merge_diagnostics = vec![];
merge::merge(&mut self.import_map, new.import_map, &mut merge_diagnostics);

self.diagnostics.extend(
merge_diagnostics
.into_iter()
.map(Box::new)
.map(ImportMapDiagnostic::Merge),
);
// Should diagnostics ignored due to merge be dropped?
self.diagnostics.extend(new.diagnostics);
}
}

#[derive(Default)]
pub struct ImportMapOptions {
/// `(parsed_address, key, maybe_scope) -> new_address`
#[allow(clippy::type_complexity)]
pub address_hook: Option<Box<dyn (Fn(&str, &str, Option<&str>) -> String)>>,
pub address_hook: Option<Box<dyn Fn(&str, &str, Option<&str>) -> String>>,
/// Whether to expand imports in the import map.
///
/// This functionality can be used to modify the import map
Expand All @@ -346,8 +372,23 @@ impl Debug for ImportMapOptions {
}
}

#[derive(Default)]
#[non_exhaustive]
pub struct ResolveOptions {
only_remapped: bool,
}
impl ResolveOptions {
/// If set - `ImportMap::resolve_with` should not return any result
/// if the specifier was not remapped.
pub fn only_remapped(mut self) -> Self {
self.only_remapped = true;
self
}
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ImportMap {
#[deprecated = "specifier map base_url doesn't work with merged import maps"]
#[serde(skip)]
base_url: Url,

Expand All @@ -356,6 +397,7 @@ pub struct ImportMap {
}

impl ImportMap {
#[allow(deprecated)]
pub fn new(base_url: Url) -> Self {
Self {
base_url: base_url.clone(),
Expand All @@ -367,6 +409,8 @@ impl ImportMap {
}
}

#[deprecated = "import map base_url doesn't work with merged import maps"]
#[allow(deprecated)]
pub fn base_url(&self) -> &Url {
&self.base_url
}
Expand Down Expand Up @@ -421,6 +465,15 @@ impl ImportMap {
&self,
specifier: &str,
referrer: &Url,
) -> Result<Url, ImportMapError> {
self.resolve_with(specifier, referrer, &ResolveOptions::default())
}

pub fn resolve_with(
&self,
specifier: &str,
referrer: &Url,
opts: &ResolveOptions,
) -> Result<Url, ImportMapError> {
let as_url: Option<Url> = try_url_like_specifier(specifier, referrer);
let normalized_specifier = if let Some(url) = as_url.as_ref() {
Expand Down Expand Up @@ -453,8 +506,10 @@ impl ImportMap {
}

// The specifier was able to be turned into a URL, but wasn't remapped into anything.
if let Some(as_url) = as_url {
return Ok(as_url);
if !opts.only_remapped {
if let Some(as_url) = as_url {
return Ok(as_url);
}
}

Err(
Expand Down Expand Up @@ -482,6 +537,8 @@ impl ImportMap {
})
}

#[deprecated = "import map base_url doesn't work with merged import maps"]
#[allow(deprecated)]
pub fn get_or_append_scope_mut(
&mut self,
key: &str,
Expand Down Expand Up @@ -514,6 +571,7 @@ impl ImportMap {
Some(key.to_string())
},
imports: SpecifierMap {
#[allow(deprecated)]
base_url,
inner: Default::default(),
},
Expand Down Expand Up @@ -617,6 +675,7 @@ pub fn parse_from_json_with_options(
parse_specifier_map(unresolved_imports, &base_url, &mut diagnostics);
let scopes = parse_scope_map(unresolved_scopes, &base_url, &mut diagnostics)?;

#[allow(deprecated)]
Ok(ImportMapWithDiagnostics {
diagnostics,
import_map: ImportMap {
Expand Down Expand Up @@ -646,6 +705,7 @@ pub fn parse_from_value_with_options(
parse_specifier_map(unresolved_imports, &base_url, &mut diagnostics);
let scopes = parse_scope_map(unresolved_scopes, &base_url, &mut diagnostics)?;

#[allow(deprecated)]
Ok(ImportMapWithDiagnostics {
diagnostics,
import_map: ImportMap {
Expand Down Expand Up @@ -866,13 +926,10 @@ fn parse_specifier_map(
}

// Sort in longest and alphabetical order.
normalized_map.sort_by(|k1, _v1, k2, _v2| match k1.cmp(k2) {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
// JSON guarantees that there can't be duplicate keys
Ordering::Equal => unreachable!(),
});
normalized_map
.sort_by(|k1, _v1, k2, _v2| code_unit_compare(k1, k2).reverse());

#[allow(deprecated)]
SpecifierMap {
inner: normalized_map,
base_url: base_url.clone(),
Expand Down Expand Up @@ -923,12 +980,8 @@ fn parse_scope_map(
}

// Sort in longest and alphabetical order.
normalized_map.sort_by(|k1, _v1, k2, _v2| match k1.cmp(k2) {
Ordering::Greater => Ordering::Less,
Ordering::Less => Ordering::Greater,
// JSON guarantees that there can't be duplicate keys
Ordering::Equal => unreachable!(),
});
normalized_map
.sort_by(|k1, _v1, k2, _v2| code_unit_compare(k1, k2).reverse());

Ok(normalized_map)
}
Expand Down Expand Up @@ -1137,6 +1190,7 @@ fn resolve_imports_match(
}

#[cfg(test)]
#[allow(deprecated)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
Expand All @@ -1154,6 +1208,7 @@ mod test {
},
);
let specifiers = SpecifierMap {
#[allow(deprecated)]
base_url: Url::parse("file:///").unwrap(),
inner: specifiers,
};
Expand All @@ -1173,6 +1228,7 @@ mod test {
},
);
let specifiers = SpecifierMap {
#[allow(deprecated)]
base_url: Url::parse("file:///").unwrap(),
inner: specifiers,
};
Expand All @@ -1199,6 +1255,7 @@ mod test {
},
);
let specifiers = SpecifierMap {
#[allow(deprecated)]
base_url: Url::parse("file:///").unwrap(),
inner: specifiers,
};
Expand Down
Loading
Loading