0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

fix(cli): properly handle roots with extensions that don't match media type (#8114)

This commit is contained in:
Kitson Kelly 2020-10-26 07:17:58 +11:00 committed by GitHub
parent 95854b88ad
commit 3d19fb493b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 19 deletions

View file

@ -758,8 +758,11 @@ impl Graph2 {
info!("{} {}", colors::green("Check"), specifier);
}
let root_names: Vec<String> =
self.roots.iter().map(|ms| ms.to_string()).collect();
let root_names: Vec<(ModuleSpecifier, MediaType)> = self
.roots
.iter()
.map(|ms| (ms.clone(), self.get_media_type(ms).unwrap()))
.collect();
let maybe_tsbuildinfo = self.maybe_tsbuildinfo.clone();
let hash_data =
vec![config.as_bytes(), version::DENO.as_bytes().to_owned()];

View file

@ -0,0 +1,2 @@
[WILDCARD]
Check http://localhost:4545/cli/tests/subdir/no_js_ext

View file

@ -0,0 +1,2 @@
[WILDCARD]
Check http://localhost:4545/cli/tests/subdir/no_js_ext@1.0.0

View file

@ -2722,6 +2722,18 @@ itest!(_053_import_compression {
http_server: true,
});
itest!(cache_extensionless {
args: "cache --reload http://localhost:4545/cli/tests/subdir/no_js_ext",
output: "cache_extensionless.out",
http_server: true,
});
itest!(cache_random_extension {
args: "cache --reload http://localhost:4545/cli/tests/subdir/no_js_ext@1.0.0",
output: "cache_random_extension.out",
http_server: true,
});
itest!(cafile_url_imports {
args: "run --quiet --reload --cert tls/RootCA.pem cafile_url_imports.ts",
output: "cafile_url_imports.ts.out",

View file

@ -0,0 +1,3 @@
// @ts-check
import { printHello } from "./mod2.ts";
printHello();

View file

@ -20,8 +20,8 @@ use deno_core::OpFn;
use deno_core::RuntimeOptions;
use deno_core::Snapshot;
use serde::Deserialize;
use serde::Serialize;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug, Clone, Default, Eq, PartialEq)]
@ -32,23 +32,19 @@ pub struct EmittedFile {
}
/// A structure representing a request to be sent to the tsc runtime.
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
pub struct Request {
/// The TypeScript compiler options which will be serialized and sent to
/// tsc.
pub config: TsConfig,
/// Indicates to the tsc runtime if debug logging should occur.
pub debug: bool,
#[serde(skip_serializing)]
pub graph: Rc<RefCell<Graph2>>,
#[serde(skip_serializing)]
pub hash_data: Vec<Vec<u8>>,
#[serde(skip_serializing)]
pub maybe_tsbuildinfo: Option<String>,
/// A vector of strings that represent the root/entry point modules for the
/// program.
pub root_names: Vec<String>,
pub root_names: Vec<(ModuleSpecifier, MediaType)>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@ -69,6 +65,7 @@ struct State {
graph: Rc<RefCell<Graph2>>,
maybe_tsbuildinfo: Option<String>,
maybe_response: Option<RespondArgs>,
root_map: HashMap<String, ModuleSpecifier>,
}
impl State {
@ -76,6 +73,7 @@ impl State {
graph: Rc<RefCell<Graph2>>,
hash_data: Vec<Vec<u8>>,
maybe_tsbuildinfo: Option<String>,
root_map: HashMap<String, ModuleSpecifier>,
) -> Self {
State {
hash_data,
@ -83,6 +81,7 @@ impl State {
graph,
maybe_tsbuildinfo,
maybe_response: None,
root_map,
}
}
}
@ -138,7 +137,13 @@ fn emit(state: &mut State, args: Value) -> Result<Value, AnyError> {
maybe_specifiers: if let Some(specifiers) = &v.maybe_specifiers {
let specifiers = specifiers
.iter()
.map(|s| ModuleSpecifier::resolve_url_or_path(s).unwrap())
.map(|s| {
if let Some(remapped_specifier) = state.root_map.get(s) {
remapped_specifier.clone()
} else {
ModuleSpecifier::resolve_url_or_path(s).unwrap()
}
})
.collect();
Some(specifiers)
} else {
@ -174,6 +179,12 @@ fn load(state: &mut State, args: Value) -> Result<Value, AnyError> {
Some("declare var a: any;\nexport = a;\n".to_string())
} else {
let graph = state.graph.borrow();
let specifier =
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
@ -207,9 +218,13 @@ 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 = ModuleSpecifier::resolve_url_or_path(&v.base).context(
"Error converting a string module specifier for \"op_resolve\".",
)?;
let referrer = if let Some(remapped_base) = state.root_map.get(&v.base) {
remapped_base.clone()
} else {
ModuleSpecifier::resolve_url_or_path(&v.base).context(
"Error converting a string module specifier for \"op_resolve\".",
)?
};
for specifier in &v.specifiers {
if specifier.starts_with("asset:///") {
resolved.push((
@ -272,6 +287,25 @@ pub fn exec(
startup_snapshot: Some(snapshot),
..Default::default()
});
// tsc cannot handle root specifiers that don't have one of the "acceptable"
// extensions. Therefore, we have to check the root modules against their
// 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 root_names: Vec<String> = request
.root_names
.iter()
.map(|(s, mt)| {
let ext_media_type = MediaType::from(&s.as_str().to_owned());
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();
{
let op_state = runtime.op_state();
@ -280,6 +314,7 @@ pub fn exec(
request.graph.clone(),
request.hash_data.clone(),
request.maybe_tsbuildinfo.clone(),
root_map,
));
}
@ -290,8 +325,12 @@ pub fn exec(
runtime.register_op("op_respond", op(respond));
let startup_source = "globalThis.startup({ legacyFlag: false })";
let request_str =
serde_json::to_string(&request).context("Could not serialize request.")?;
let request_value = json!({
"config": request.config,
"debug": request.debug,
"rootNames": root_names,
});
let request_str = request_value.to_string();
let exec_source = format!("globalThis.exec({})", request_str);
runtime
@ -354,7 +393,7 @@ mod tests {
.await
.expect("module not inserted");
let graph = Rc::new(RefCell::new(builder.get_graph()));
State::new(graph, hash_data, maybe_tsbuildinfo)
State::new(graph, hash_data, maybe_tsbuildinfo, HashMap::new())
}
#[tokio::test]
@ -601,7 +640,7 @@ mod tests {
graph,
hash_data,
maybe_tsbuildinfo: None,
root_names: vec!["https://deno.land/x/a.ts".to_string()],
root_names: vec![(specifier, MediaType::TypeScript)],
};
let actual = exec(js::compiler_isolate_init(), request)
.expect("exec should have not errored");
@ -651,7 +690,7 @@ mod tests {
graph,
hash_data,
maybe_tsbuildinfo: None,
root_names: vec!["file:///reexports.ts".to_string()],
root_names: vec![(specifier, MediaType::TypeScript)],
};
let actual = exec(js::compiler_isolate_init(), request)
.expect("exec should have not errored");

View file

@ -420,6 +420,19 @@ pub async fn run_all_servers() {
HeaderValue::from_static("application/typescript"),
);
res
}))
.or(warp::path!("cli"/"tests"/"subdir"/"no_js_ext@1.0.0").map(|| {
let mut res = Response::new(Body::from(
r#"import { printHello } from "./mod2.ts";
printHello();
"#,
));
let h = res.headers_mut();
h.insert(
"Content-type",
HeaderValue::from_static("application/javascript"),
);
res
}));
let content_type_handler = warp::any()
@ -522,7 +535,7 @@ fn custom_headers(path: warp::path::Peek, f: warp::fs::File) -> Box<dyn Reply> {
Some("application/x-www-form-urlencoded")
} else if p.contains("unknown_ext") || p.contains("no_ext") {
Some("text/typescript")
} else if p.contains("mismatch_ext") {
} else if p.contains("mismatch_ext") || p.contains("no_js_ext") {
Some("text/javascript")
} else if p.ends_with(".ts") || p.ends_with(".tsx") {
Some("application/typescript")