fix(compile): Support import maps (#13756)

This commit is contained in:
Divy Srivastava 2022-02-24 18:58:00 +05:30 committed by GitHub
parent 3e8180c793
commit 03c55b4970
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 17 deletions

View file

@ -396,19 +396,21 @@ async fn compile_command(
) -> Result<i32, AnyError> {
let debug = flags.log_level == Some(log::Level::Debug);
let run_flags =
tools::standalone::compile_to_runtime_flags(&flags, compile_flags.args)?;
let run_flags = tools::standalone::compile_to_runtime_flags(
&flags,
compile_flags.args.clone(),
)?;
let module_specifier = resolve_url_or_path(&compile_flags.source_file)?;
let ps = ProcState::build(Arc::new(flags)).await?;
let deno_dir = &ps.dir;
let output = compile_flags.output.and_then(|output| {
if fs_util::path_has_trailing_slash(&output) {
let output = compile_flags.output.as_ref().and_then(|output| {
if fs_util::path_has_trailing_slash(output) {
let infer_file_name = infer_name_from_url(&module_specifier).map(PathBuf::from)?;
Some(output.join(infer_file_name))
} else {
Some(output)
Some(output.to_path_buf())
}
}).or_else(|| {
infer_name_from_url(&module_specifier).map(PathBuf::from)
@ -423,6 +425,8 @@ async fn compile_command(
generic_error("There should only be one reference to ModuleGraph")
})?;
graph.valid().unwrap();
let eszip = eszip::EszipV2::from_graph(graph, Default::default())?;
info!(
@ -441,7 +445,9 @@ async fn compile_command(
eszip,
module_specifier.clone(),
run_flags,
)?;
ps,
)
.await?;
info!("{} {}", colors::green("Emit"), output.display());

View file

@ -6,6 +6,7 @@ use crate::flags::Flags;
use crate::ops;
use crate::proc_state::ProcState;
use crate::version;
use crate::ImportMapResolver;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::type_error;
@ -19,6 +20,7 @@ use deno_core::url::Url;
use deno_core::v8_set_flags;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
use deno_graph::source::Resolver;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_tls::create_default_root_cert_store;
use deno_runtime::deno_tls::rustls_pemfile;
@ -28,6 +30,7 @@ use deno_runtime::permissions::PermissionsOptions;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;
use deno_runtime::BootstrapOptions;
use import_map::parse_from_json;
use log::Level;
use std::env::current_exe;
use std::io::BufReader;
@ -51,6 +54,7 @@ pub struct Metadata {
pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<Vec<u8>>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub maybe_import_map: Option<(Url, String)>,
pub entrypoint: ModuleSpecifier,
}
@ -119,17 +123,26 @@ fn u64_from_bytes(arr: &[u8]) -> Result<u64, AnyError> {
Ok(u64::from_be_bytes(*fixed_arr))
}
struct EmbeddedModuleLoader(eszip::EszipV2);
struct EmbeddedModuleLoader {
eszip: eszip::EszipV2,
maybe_import_map_resolver: Option<ImportMapResolver>,
}
impl ModuleLoader for EmbeddedModuleLoader {
fn resolve(
&self,
specifier: &str,
base: &str,
referrer: &str,
_is_main: bool,
) -> Result<ModuleSpecifier, AnyError> {
let resolve = deno_core::resolve_import(specifier, base)?;
Ok(resolve)
let referrer = deno_core::resolve_url_or_path(referrer).unwrap();
self.maybe_import_map_resolver.as_ref().map_or_else(
|| {
deno_core::resolve_import(specifier, referrer.as_str())
.map_err(|err| err.into())
},
|r| r.resolve(specifier, &referrer).to_result(),
)
}
fn load(
@ -143,7 +156,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
let is_data_uri = get_source_from_data_url(&module_specifier).ok();
let module = self
.0
.eszip
.get_module(module_specifier.as_str())
.ok_or_else(|| type_error("Module not found"));
@ -208,7 +221,16 @@ pub async fn run(
let permissions = Permissions::from_options(&metadata.permissions);
let blob_store = BlobStore::default();
let broadcast_channel = InMemoryBroadcastChannel::default();
let module_loader = Rc::new(EmbeddedModuleLoader(eszip));
let module_loader = Rc::new(EmbeddedModuleLoader {
eszip,
maybe_import_map_resolver: metadata.maybe_import_map.map(
|(base, source)| {
ImportMapResolver::new(Arc::new(
parse_from_json(&base, &source).unwrap().import_map,
))
},
),
});
let create_web_worker_cb = Arc::new(|_| {
todo!("Worker are currently not supported in standalone binaries");
});

View file

@ -450,6 +450,40 @@ fn standalone_runtime_flags() {
.contains("PermissionDenied: Requires write access"));
}
#[test]
fn standalone_import_map() {
let dir = TempDir::new().expect("tempdir fail");
let exe = if cfg!(windows) {
dir.path().join("import_map.exe")
} else {
dir.path().join("import_map")
};
let output = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("compile")
.arg("--unstable")
.arg("--allow-read")
.arg("--import-map")
.arg("standalone_import_map.json")
.arg("--output")
.arg(&exe)
.arg("./standalone_import_map.ts")
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
let output = Command::new(exe)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());
}
#[test]
// https://github.com/denoland/deno/issues/12670
fn skip_rebundle() {

View file

@ -0,0 +1,5 @@
{
"imports": {
"hello": "./001_hello.js"
}
}

View file

@ -0,0 +1 @@
import "hello";

View file

@ -5,11 +5,17 @@ use crate::flags::CheckFlag;
use crate::flags::DenoSubcommand;
use crate::flags::Flags;
use crate::flags::RunFlags;
use crate::standalone::Metadata;
use crate::standalone::MAGIC_TRAILER;
use crate::ProcState;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_graph::ModuleSpecifier;
use deno_runtime::deno_fetch::reqwest::Client;
use deno_runtime::permissions::Permissions;
use std::env;
use std::fs::read;
use std::fs::File;
@ -20,9 +26,6 @@ use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use crate::standalone::Metadata;
use crate::standalone::MAGIC_TRAILER;
pub async fn get_base_binary(
deno_dir: &DenoDir,
target: Option<String>,
@ -85,11 +88,12 @@ async fn download_base_binary(
/// This functions creates a standalone deno binary by appending a bundle
/// and magic trailer to the currently executing binary.
pub fn create_standalone_binary(
pub async fn create_standalone_binary(
mut original_bin: Vec<u8>,
eszip: eszip::EszipV2,
entrypoint: ModuleSpecifier,
flags: Flags,
ps: ProcState,
) -> Result<Vec<u8>, AnyError> {
let mut eszip_archive = eszip.into_bytes();
@ -97,6 +101,26 @@ pub fn create_standalone_binary(
Some(ca_file) => Some(read(ca_file)?),
None => None,
};
let maybe_import_map: Option<(Url, String)> = match flags
.import_map_path
.as_ref()
{
None => None,
Some(import_map_url) => {
let import_map_specifier = deno_core::resolve_url_or_path(import_map_url)
.context(format!("Bad URL (\"{}\") for import map.", import_map_url))?;
let file = ps
.file_fetcher
.fetch(&import_map_specifier, &mut Permissions::allow_all())
.await
.context(format!(
"Unable to load '{}' import map",
import_map_specifier
))?;
Some((import_map_specifier, file.source.to_string()))
}
};
let metadata = Metadata {
argv: flags.argv.clone(),
unstable: flags.unstable,
@ -111,6 +135,7 @@ pub fn create_standalone_binary(
ca_stores: flags.ca_stores,
ca_data,
entrypoint,
maybe_import_map,
};
let mut metadata = serde_json::to_string(&metadata)?.as_bytes().to_vec();
@ -233,7 +258,7 @@ pub fn compile_to_runtime_flags(
coverage_dir: flags.coverage_dir.clone(),
enable_testing_features: false,
ignore: vec![],
import_map_path: None,
import_map_path: flags.import_map_path.clone(),
inspect_brk: None,
inspect: None,
location: flags.location.clone(),