feat: load multiple eszips (#1)

This commit is contained in:
Heyang Zhou 2024-07-27 00:01:31 +08:00 committed by GitHub
parent deb67c9102
commit 2f179e4dfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 37 deletions

View File

@ -178,7 +178,7 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
if run_flags.is_stdin() {
tools::run::run_from_stdin(flags).await
} else if flags.eszip {
tools::run::run_eszip(flags).await
tools::run::run_eszip(flags, run_flags).await
} else {
tools::run::run_script(WorkerExecutionMode::Run, flags, run_flags.watch).await
}

View File

@ -96,7 +96,7 @@ fn main() {
let image_name =
current_exe_path.file_name().unwrap().to_string_lossy();
let exit_code = standalone::run(
eszip,
vec![eszip],
metadata,
current_exe_path.as_os_str().as_encoded_bytes(),
&image_name,

View File

@ -95,7 +95,7 @@ struct WorkspaceEszipModule {
}
struct WorkspaceEszip {
eszip: eszip::EszipV2,
eszips: Vec<eszip::EszipV2>,
root_dir_url: Arc<ModuleSpecifier>,
}
@ -104,17 +104,28 @@ impl WorkspaceEszip {
&self,
specifier: &ModuleSpecifier,
) -> Option<WorkspaceEszipModule> {
let lookup = |key: &str| {
for (i, x) in self.eszips.iter().enumerate().rev() {
println!("specifiers[{}]: {:?}", i, x.specifiers());
if let Some(x) = x.get_module(key) {
return Some(x);
}
}
None
};
if specifier.scheme() == "file" {
let specifier_key = EszipRelativeFileBaseUrl::new(&self.root_dir_url)
.specifier_key(specifier);
let module = self.eszip.get_module(&specifier_key)?;
println!("Trying file specifier key: {}", specifier_key);
let module = lookup(&specifier_key)?;
let specifier = self.root_dir_url.join(&module.specifier).unwrap();
Some(WorkspaceEszipModule {
specifier,
inner: module,
})
} else {
let module = self.eszip.get_module(specifier.as_str())?;
let module = lookup(specifier.as_str())?;
Some(WorkspaceEszipModule {
specifier: ModuleSpecifier::parse(&module.specifier).unwrap(),
inner: module,
@ -329,7 +340,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
let Some(module) = self.shared.eszip.get_module(original_specifier) else {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(format!(
"Module not found: {}",
"Module specifier not found: {}",
original_specifier
))));
};
@ -338,7 +349,10 @@ impl ModuleLoader for EmbeddedModuleLoader {
deno_core::ModuleLoadResponse::Async(
async move {
let code = module.inner.source().await.ok_or_else(|| {
type_error(format!("Module not found: {}", original_specifier))
type_error(format!(
"Module source is not available: {}",
original_specifier
))
})?;
let code = arc_u8_to_arc_str(code)
.map_err(|_| type_error("Module source is not utf-8"))?;
@ -349,9 +363,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
eszip::ModuleKind::Jsonc => {
return Err(type_error("jsonc modules not supported"))
}
eszip::ModuleKind::OpaqueData => {
unreachable!();
}
eszip::ModuleKind::OpaqueData => ModuleType::JavaScript,
},
ModuleSourceCode::String(code.into()),
&original_specifier,
@ -429,11 +441,13 @@ impl RootCertStoreProvider for StandaloneRootCertStoreProvider {
}
pub async fn run(
mut eszip: eszip::EszipV2,
mut eszips: Vec<eszip::EszipV2>,
metadata: Metadata,
image_path: &[u8],
image_name: &str,
) -> Result<i32, AnyError> {
assert!(!eszips.is_empty());
let image_path_hash = Sha256::digest(image_path);
let mut image_path_hash_buf = [0u8; 40];
let image_path_hash = &*faster_hex::hex_encode(
@ -470,7 +484,8 @@ pub async fn run(
let (fs, npm_resolver, maybe_vfs_root) = match metadata.node_modules {
Some(binary::NodeModules::Managed { node_modules_dir }) => {
// this will always have a snapshot
let snapshot = eszip.take_npm_snapshot().unwrap();
// TODO: support npm modules in eszips other than the last one
let snapshot = eszips.last_mut().unwrap().take_npm_snapshot().unwrap();
let vfs_root_dir_path = if node_modules_dir.is_some() {
root_path.clone()
} else {
@ -625,7 +640,7 @@ pub async fn run(
let module_loader_factory = StandaloneModuleLoaderFactory {
shared: Arc::new(SharedModuleLoaderState {
eszip: WorkspaceEszip {
eszip,
eszips,
root_dir_url,
},
workspace_resolver,

View File

@ -3,20 +3,22 @@
use std::io::Read;
use deno_config::workspace::PackageJsonDepResolution;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::io::BufReader;
use deno_core::futures::io::Cursor;
use deno_core::futures::stream::FuturesOrdered;
use deno_core::futures::StreamExt;
use deno_core::unsync::spawn;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::WorkerExecutionMode;
use eszip::EszipV2;
use tokio_util::compat::TokioAsyncReadCompatExt;
use crate::args::CaData;
use crate::args::EvalFlags;
use crate::args::Flags;
use crate::args::RunFlags;
use crate::args::WatchFlagsWithPaths;
use crate::factory::CliFactory;
use crate::factory::CliFactoryBuilder;
@ -212,27 +214,50 @@ async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
Ok(())
}
pub async fn run_eszip(flags: Flags) -> Result<i32, AnyError> {
pub async fn run_eszip(
flags: Flags,
run_flags: RunFlags,
) -> Result<i32, AnyError> {
// TODO(bartlomieju): actually I think it will also fail if there's an import
// map specified and bare specifier is used on the command line
let factory = CliFactory::from_flags(flags.clone())?;
let cli_options = factory.cli_options();
let file_fetcher = factory.file_fetcher()?;
let permissions = PermissionsContainer::new(Permissions::from_options(
&cli_options.permissions_options()?,
)?);
let main_module = cli_options.resolve_main_module()?;
// TODO: streaming load
let eszip = file_fetcher.fetch(&main_module, &permissions).await?;
let eszip = BufReader::new(Cursor::new(eszip.source));
let (eszip, loader) = EszipV2::parse(eszip).await?;
spawn(async move {
if let Err(e) = loader.await {
log::error!("Error loading ESZip: {}", e);
std::process::exit(1);
}
});
// entrypoint#path1,path2,...
let (entrypoint, files) = run_flags
.script
.split_once("#")
.with_context(|| "eszip: invalid script string")?;
println!("running eszip: entrypoint={} files={}", entrypoint, files);
// TODO: handle paths that contain ','
let files = files.split(",").collect::<Vec<_>>();
let mut headers = FuturesOrdered::new();
for path in files {
let file = tokio::fs::File::open(path).await?;
let eszip = BufReader::new(file.compat());
let path = path.to_string();
headers.push_back(async move {
let (eszip, loader) = match EszipV2::parse(eszip).await {
Ok(x) => x,
Err(e) => {
log::error!("Error parsing eszip header at {}: {}", path, e);
std::process::exit(1);
}
};
spawn(async move {
if let Err(e) = loader.await {
log::error!("Error loading eszip at {}: {}", path, e);
std::process::exit(1);
}
});
eszip
});
}
let headers = headers.collect::<Vec<_>>().await;
let ca_data = match cli_options.ca_data() {
Some(CaData::File(ca_file)) => Some(
std::fs::read(ca_file).with_context(|| format!("Reading: {ca_file}"))?,
@ -240,12 +265,9 @@ pub async fn run_eszip(flags: Flags) -> Result<i32, AnyError> {
Some(CaData::Bytes(bytes)) => Some(bytes.clone()),
None => None,
};
let Some(entrypoint_key) = eszip.specifiers().into_iter().next() else {
bail!("No modules found in eszip");
};
crate::standalone::run(
eszip,
headers,
Metadata {
argv: flags.argv,
seed: flags.seed,
@ -257,7 +279,7 @@ pub async fn run_eszip(flags: Flags) -> Result<i32, AnyError> {
ca_data,
unsafely_ignore_certificate_errors: flags
.unsafely_ignore_certificate_errors,
entrypoint_key,
entrypoint_key: entrypoint.to_string(),
node_modules: None,
disable_deprecated_api_warning: false,
unstable_config: flags.unstable_config,
@ -268,7 +290,7 @@ pub async fn run_eszip(flags: Flags) -> Result<i32, AnyError> {
pkg_json_resolution: PackageJsonDepResolution::Disabled,
},
},
main_module.to_string().as_bytes(),
run_flags.script.as_bytes(),
"run-eszip",
)
.await