mod error;
pub mod v1;
pub mod v2;
use std::pin::Pin;
use std::sync::Arc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use futures::io::AsyncBufReadExt;
use futures::io::AsyncReadExt;
use futures::Future;
use serde::Deserialize;
use serde::Serialize;
use v2::EszipV2Modules;
pub use crate::error::ParseError;
pub use crate::v1::EszipV1;
pub use crate::v2::EszipV2;
pub use deno_ast;
pub use deno_graph;
pub enum Eszip {
V1(EszipV1),
V2(EszipV2),
}
type EszipParserFuture<R> =
Pin<Box<dyn Future<Output = Result<futures::io::BufReader<R>, ParseError>>>>;
impl Eszip {
pub async fn parse<R: futures::io::AsyncRead + Unpin + 'static>(
reader: R,
) -> Result<(Eszip, EszipParserFuture<R>), ParseError> {
let mut reader = futures::io::BufReader::new(reader);
reader.fill_buf().await?;
let buffer = reader.buffer();
if EszipV2::has_magic(buffer) {
let (eszip, fut) = EszipV2::parse(reader).await?;
Ok((Eszip::V2(eszip), Box::pin(fut)))
} else {
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer).await?;
let eszip = EszipV1::parse(&buffer)?;
let fut = async move { Ok::<_, ParseError>(reader) };
Ok((Eszip::V1(eszip), Box::pin(fut)))
}
}
pub fn get_module(&self, specifier: &str) -> Option<Module> {
match self {
Eszip::V1(eszip) => eszip.get_module(specifier),
Eszip::V2(eszip) => eszip.get_module(specifier),
}
}
pub fn get_import_map(&self, specifier: &str) -> Option<Module> {
match self {
Eszip::V1(eszip) => eszip.get_import_map(specifier),
Eszip::V2(eszip) => eszip.get_import_map(specifier),
}
}
pub fn take_npm_snapshot(
&mut self,
) -> Option<ValidSerializedNpmResolutionSnapshot> {
match self {
Eszip::V1(_) => None,
Eszip::V2(eszip) => eszip.take_npm_snapshot(),
}
}
}
pub struct Module {
pub specifier: String,
pub kind: ModuleKind,
inner: ModuleInner,
}
pub enum ModuleInner {
V1(EszipV1),
V2(EszipV2Modules),
}
impl Module {
pub async fn source(&self) -> Option<Arc<[u8]>> {
match &self.inner {
ModuleInner::V1(eszip_v1) => eszip_v1.get_module_source(&self.specifier),
ModuleInner::V2(eszip_v2) => {
eszip_v2.get_module_source(&self.specifier).await
}
}
}
pub async fn take_source(&self) -> Option<Arc<[u8]>> {
match &self.inner {
ModuleInner::V1(eszip_v1) => eszip_v1.take(&self.specifier),
ModuleInner::V2(eszip_v2) => {
eszip_v2.take_module_source(&self.specifier).await
}
}
}
pub async fn source_map(&self) -> Option<Arc<[u8]>> {
match &self.inner {
ModuleInner::V1(_) => None,
ModuleInner::V2(eszip) => {
eszip.get_module_source_map(&self.specifier).await
}
}
}
pub async fn take_source_map(&self) -> Option<Arc<[u8]>> {
match &self.inner {
ModuleInner::V1(_) => None,
ModuleInner::V2(eszip) => {
eszip.take_module_source_map(&self.specifier).await
}
}
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ModuleKind {
JavaScript = 0,
Json = 1,
Jsonc = 2,
OpaqueData = 3,
}
#[cfg(test)]
mod tests {
use crate::Eszip;
use futures::io::AllowStdIo;
#[tokio::test]
async fn parse_v1() {
let file = std::fs::File::open("./src/testdata/basic.json").unwrap();
let (eszip, fut) = Eszip::parse(AllowStdIo::new(file)).await.unwrap();
fut.await.unwrap();
assert!(matches!(eszip, Eszip::V1(_)));
eszip.get_module("https://gist.githubusercontent.com/lucacasonato/f3e21405322259ca4ed155722390fda2/raw/e25acb49b681e8e1da5a2a33744b7a36d538712d/hello.js").unwrap();
}
#[tokio::test]
async fn parse_v2() {
let file = std::fs::File::open("./src/testdata/redirect.eszip2").unwrap();
let (eszip, fut) = Eszip::parse(AllowStdIo::new(file)).await.unwrap();
fut.await.unwrap();
assert!(matches!(eszip, Eszip::V2(_)));
eszip.get_module("file:///main.ts").unwrap();
}
#[tokio::test]
async fn take_source_v1() {
let file = std::fs::File::open("./src/testdata/basic.json").unwrap();
let (eszip, fut) = Eszip::parse(AllowStdIo::new(file)).await.unwrap();
fut.await.unwrap();
assert!(matches!(eszip, Eszip::V1(_)));
let specifier = "https://gist.githubusercontent.com/lucacasonato/f3e21405322259ca4ed155722390fda2/raw/e25acb49b681e8e1da5a2a33744b7a36d538712d/hello.js";
let module = eszip.get_module(specifier).unwrap();
assert_eq!(module.specifier, specifier);
let source = module.take_source().await.unwrap();
assert!(!source.is_empty());
assert!(module.source_map().await.is_none());
assert!(eszip.get_module(specifier).is_none());
}
#[tokio::test]
async fn take_source_v2() {
let file = std::fs::File::open("./src/testdata/redirect.eszip2").unwrap();
let (eszip, fut) = Eszip::parse(AllowStdIo::new(file)).await.unwrap();
fut.await.unwrap();
assert!(matches!(eszip, Eszip::V2(_)));
let specifier = "file:///main.ts";
let module = eszip.get_module(specifier).unwrap();
let source = module.take_source().await.unwrap();
assert!(!source.is_empty());
let module = eszip.get_module(specifier).unwrap();
assert_eq!(module.specifier, specifier);
assert!(module.source().await.is_none());
assert!(module.source_map().await.is_some());
let source_map = module.take_source_map().await.unwrap();
assert!(!source_map.is_empty());
assert!(module.source_map().await.is_none());
}
}