summaryrefslogtreecommitdiff
path: root/cli/tsc.rs
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2021-01-06 13:22:38 +1100
committerGitHub <noreply@github.com>2021-01-06 13:22:38 +1100
commit54240c22af6233d1d977d469868b0d9050cad6da (patch)
tree81af9892f8728852387fad9c2d243ab933de927f /cli/tsc.rs
parent60c9c857584bf5180dd0f7b937683dd9691aef84 (diff)
feat(cli): support data urls (#8866)
Closes: #5059 Co-authored-by: Valentin Anger <syrupthinker@gryphno.de>
Diffstat (limited to 'cli/tsc.rs')
-rw-r--r--cli/tsc.rs95
1 files changed, 75 insertions, 20 deletions
diff --git a/cli/tsc.rs b/cli/tsc.rs
index d63667876..29ea45140 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -82,6 +82,19 @@ fn get_maybe_hash(
}
}
+fn hash_data_url(
+ specifier: &ModuleSpecifier,
+ media_type: &MediaType,
+) -> String {
+ assert_eq!(
+ specifier.as_url().scheme(),
+ "data",
+ "Specifier must be a data: specifier."
+ );
+ let hash = crate::checksum::gen(&[specifier.as_url().path().as_bytes()]);
+ format!("data:///{}{}", hash, media_type.as_ts_extension())
+}
+
/// tsc only supports `.ts`, `.tsx`, `.d.ts`, `.js`, or `.jsx` as root modules
/// and so we have to detect the apparent media type based on extensions it
/// supports.
@@ -152,7 +165,9 @@ pub struct Response {
pub stats: Stats,
}
+#[derive(Debug)]
struct State {
+ data_url_map: HashMap<String, ModuleSpecifier>,
hash_data: Vec<Vec<u8>>,
emitted_files: Vec<EmittedFile>,
graph: Arc<Mutex<Graph>>,
@@ -167,10 +182,12 @@ impl State {
hash_data: Vec<Vec<u8>>,
maybe_tsbuildinfo: Option<String>,
root_map: HashMap<String, ModuleSpecifier>,
+ data_url_map: HashMap<String, ModuleSpecifier>,
) -> Self {
State {
+ data_url_map,
hash_data,
- emitted_files: Vec::new(),
+ emitted_files: Default::default(),
graph,
maybe_tsbuildinfo,
maybe_response: None,
@@ -231,7 +248,9 @@ fn emit(state: &mut State, args: Value) -> Result<Value, AnyError> {
let specifiers = specifiers
.iter()
.map(|s| {
- if let Some(remapped_specifier) = state.root_map.get(s) {
+ if let Some(data_specifier) = state.data_url_map.get(s) {
+ data_specifier.clone()
+ } else if let Some(remapped_specifier) = state.root_map.get(s) {
remapped_specifier.clone()
} else {
ModuleSpecifier::resolve_url_or_path(s).unwrap()
@@ -278,12 +297,15 @@ fn load(state: &mut State, args: Value) -> Result<Value, AnyError> {
maybe_source
} else {
let graph = state.graph.lock().unwrap();
- let specifier =
- if let Some(remapped_specifier) = state.root_map.get(&v.specifier) {
- remapped_specifier.clone()
- } else {
- specifier
- };
+ let specifier = if let Some(data_specifier) =
+ state.data_url_map.get(&v.specifier)
+ {
+ data_specifier.clone()
+ } else if let Some(remapped_specifier) = state.root_map.get(&v.specifier) {
+ remapped_specifier.clone()
+ } else {
+ specifier
+ };
let maybe_source = graph.get_source(&specifier);
media_type = if let Some(media_type) = graph.get_media_type(&specifier) {
media_type
@@ -313,7 +335,9 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
let v: ResolveArgs = serde_json::from_value(args)
.context("Invalid request from JavaScript for \"op_resolve\".")?;
let mut resolved: Vec<(String, String)> = Vec::new();
- let referrer = if let Some(remapped_base) = state.root_map.get(&v.base) {
+ let referrer = if let Some(data_specifier) = state.data_url_map.get(&v.base) {
+ data_specifier.clone()
+ } else if let Some(remapped_base) = state.root_map.get(&v.base) {
remapped_base.clone()
} else {
ModuleSpecifier::resolve_url_or_path(&v.base).context(
@@ -340,10 +364,18 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
resolved_specifier
)
};
- resolved.push((
- resolved_specifier.to_string(),
- media_type.as_ts_extension(),
- ));
+ let resolved_specifier_str = if resolved_specifier.as_url().scheme()
+ == "data"
+ {
+ let specifier_str = hash_data_url(&resolved_specifier, &media_type);
+ state
+ .data_url_map
+ .insert(specifier_str.clone(), resolved_specifier);
+ specifier_str
+ } else {
+ resolved_specifier.to_string()
+ };
+ resolved.push((resolved_specifier_str, media_type.as_ts_extension()));
}
// in certain situations, like certain dynamic imports, we won't have
// the source file in the graph, so we will return a fake module to
@@ -384,17 +416,24 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
// extensions and remap any that are unacceptable to tsc and add them to the
// op state so when requested, we can remap to the original specifier.
let mut root_map = HashMap::new();
+ let mut data_url_map = HashMap::new();
let root_names: Vec<String> = request
.root_names
.iter()
.map(|(s, mt)| {
- let ext_media_type = get_tsc_media_type(s);
- if mt != &ext_media_type {
- let new_specifier = format!("{}{}", s, mt.as_ts_extension());
- root_map.insert(new_specifier.clone(), s.clone());
- new_specifier
+ if s.as_url().scheme() == "data" {
+ let specifier_str = hash_data_url(s, mt);
+ data_url_map.insert(specifier_str.clone(), s.clone());
+ specifier_str
} else {
- s.as_str().to_owned()
+ let ext_media_type = get_tsc_media_type(s);
+ if mt != &ext_media_type {
+ let new_specifier = format!("{}{}", s, mt.as_ts_extension());
+ root_map.insert(new_specifier.clone(), s.clone());
+ new_specifier
+ } else {
+ s.as_str().to_owned()
+ }
}
})
.collect();
@@ -407,6 +446,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
request.hash_data.clone(),
request.maybe_tsbuildinfo.clone(),
root_map,
+ data_url_map,
));
}
@@ -484,7 +524,13 @@ mod tests {
.await
.expect("module not inserted");
let graph = Arc::new(Mutex::new(builder.get_graph()));
- State::new(graph, hash_data, maybe_tsbuildinfo, HashMap::new())
+ State::new(
+ graph,
+ hash_data,
+ maybe_tsbuildinfo,
+ HashMap::new(),
+ HashMap::new(),
+ )
}
async fn test_exec(
@@ -560,6 +606,15 @@ mod tests {
}
#[test]
+ fn test_hash_data_url() {
+ let specifier = ModuleSpecifier::resolve_url(
+ "data:application/javascript,console.log(\"Hello%20Deno\");",
+ )
+ .unwrap();
+ assert_eq!(hash_data_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
+ }
+
+ #[test]
fn test_get_tsc_media_type() {
let fixtures = vec![
("file:///a.ts", MediaType::TypeScript),