fix(unstable): support importing files in npm packages as bytes and text (#30065)

This commit is contained in:
David Sherret 2025-07-10 15:06:33 -04:00 committed by GitHub
parent a046f2c1bf
commit cf441584a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 545 additions and 349 deletions

View file

@ -18,7 +18,6 @@ use deno_error::JsErrorBox;
use deno_lib::args::CaData; use deno_lib::args::CaData;
use deno_lib::args::get_root_cert_store; use deno_lib::args::get_root_cert_store;
use deno_lib::args::npm_process_state; use deno_lib::args::npm_process_state;
use deno_lib::loader::NpmModuleLoader;
use deno_lib::npm::NpmRegistryReadPermissionChecker; use deno_lib::npm::NpmRegistryReadPermissionChecker;
use deno_lib::npm::NpmRegistryReadPermissionCheckerMode; use deno_lib::npm::NpmRegistryReadPermissionCheckerMode;
use deno_lib::npm::create_npm_process_state_provider; use deno_lib::npm::create_npm_process_state_provider;
@ -915,7 +914,6 @@ impl CliFactory {
let in_npm_pkg_checker = self.in_npm_pkg_checker()?; let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
let workspace_factory = self.workspace_factory()?; let workspace_factory = self.workspace_factory()?;
let resolver_factory = self.resolver_factory()?; let resolver_factory = self.resolver_factory()?;
let node_code_translator = resolver_factory.node_code_translator()?;
let cjs_tracker = self.cjs_tracker()?.clone(); let cjs_tracker = self.cjs_tracker()?.clone();
let npm_registry_permission_checker = { let npm_registry_permission_checker = {
let mode = if resolver_factory.use_byonm()? { let mode = if resolver_factory.use_byonm()? {
@ -949,11 +947,7 @@ impl CliFactory {
in_npm_pkg_checker.clone(), in_npm_pkg_checker.clone(),
self.main_module_graph_container().await?.clone(), self.main_module_graph_container().await?.clone(),
self.module_load_preparer().await?.clone(), self.module_load_preparer().await?.clone(),
NpmModuleLoader::new( resolver_factory.npm_module_loader()?.clone(),
self.cjs_tracker()?.clone(),
node_code_translator.clone(),
self.sys(),
),
npm_registry_permission_checker, npm_registry_permission_checker,
cli_npm_resolver.clone(), cli_npm_resolver.clone(),
resolver_factory.parsed_source_cache().clone(), resolver_factory.parsed_source_cache().clone(),

View file

@ -1,228 +1,75 @@
// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow; use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use deno_media_type::MediaType; use deno_media_type::MediaType;
use deno_resolver::cjs::CjsTracker; use deno_resolver::loader::LoadedModuleSource;
use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_core::FastString;
use deno_runtime::deno_core::ModuleSourceCode; use deno_runtime::deno_core::ModuleSourceCode;
use deno_runtime::deno_core::ModuleType; use deno_runtime::deno_core::ModuleType;
use node_resolver::InNpmPackageChecker; use deno_runtime::deno_core::RequestedModuleType;
use node_resolver::IsBuiltInNodeModuleChecker;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::analyze::CjsCodeAnalyzer;
use node_resolver::analyze::NodeCodeTranslator;
use thiserror::Error;
use url::Url;
use crate::sys::DenoLibSys; pub fn module_type_from_media_and_requested_type(
use crate::util::text_encoding::from_utf8_lossy_cow; media_type: MediaType,
requested_module_type: &RequestedModuleType,
pub struct ModuleCodeStringSource { ) -> ModuleType {
pub code: ModuleSourceCode, match requested_module_type {
pub found_url: Url, RequestedModuleType::Json => ModuleType::Json,
pub module_type: ModuleType, RequestedModuleType::Text => ModuleType::Text,
} RequestedModuleType::Bytes => ModuleType::Bytes,
RequestedModuleType::None | RequestedModuleType::Other(_) => {
#[derive(Debug, Error, deno_error::JsError)] match media_type {
#[class(type)] MediaType::Json => ModuleType::Json,
#[error("[{}]: Stripping types is currently unsupported for files under node_modules, for \"{}\"", self.code(), specifier)] MediaType::Wasm => ModuleType::Wasm,
pub struct StrippingTypesNodeModulesError { _ => ModuleType::JavaScript,
pub specifier: Url,
}
impl StrippingTypesNodeModulesError {
pub fn code(&self) -> &'static str {
"ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING"
}
}
#[derive(Debug, Error, deno_error::JsError)]
pub enum NpmModuleLoadError {
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
#[class(inherit)]
#[error(transparent)]
StrippingTypesNodeModules(#[from] StrippingTypesNodeModulesError),
#[class(inherit)]
#[error(transparent)]
ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError),
#[class(inherit)]
#[error(transparent)]
TranslateCjsToEsm(#[from] node_resolver::analyze::TranslateCjsToEsmError),
#[class(inherit)]
#[error("Unable to load {}{}", file_path.display(), maybe_referrer.as_ref().map(|r| format!(" imported from {}", r)).unwrap_or_default())]
UnableToLoad {
file_path: PathBuf,
maybe_referrer: Option<Url>,
#[source]
#[inherit]
source: std::io::Error,
},
#[class(inherit)]
#[error(
"{}",
format_dir_import_message(file_path, maybe_referrer, suggestion)
)]
DirImport {
file_path: PathBuf,
maybe_referrer: Option<Url>,
suggestion: Option<&'static str>,
#[source]
#[inherit]
source: std::io::Error,
},
}
fn format_dir_import_message(
file_path: &std::path::Path,
maybe_referrer: &Option<Url>,
suggestion: &Option<&'static str>,
) -> String {
// directory imports are not allowed when importing from an
// ES module, so provide the user with a helpful error message
let dir_path = file_path;
let mut msg = "Directory import ".to_string();
msg.push_str(&dir_path.to_string_lossy());
if let Some(referrer) = maybe_referrer {
msg.push_str(" is not supported resolving import from ");
msg.push_str(referrer.as_str());
if let Some(entrypoint_name) = suggestion {
msg.push_str("\nDid you mean to import ");
msg.push_str(entrypoint_name);
msg.push_str(" within the directory?");
}
}
msg
}
#[derive(Clone)]
pub struct NpmModuleLoader<
TCjsCodeAnalyzer: CjsCodeAnalyzer,
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSys: DenoLibSys,
> {
cjs_tracker: Arc<CjsTracker<DenoInNpmPackageChecker, TSys>>,
sys: TSys,
node_code_translator: Arc<
NodeCodeTranslator<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>,
>,
}
impl<
TCjsCodeAnalyzer: CjsCodeAnalyzer,
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSys: DenoLibSys,
>
NpmModuleLoader<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>
{
pub fn new(
cjs_tracker: Arc<CjsTracker<DenoInNpmPackageChecker, TSys>>,
node_code_translator: Arc<
NodeCodeTranslator<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>,
>,
sys: TSys,
) -> Self {
Self {
cjs_tracker,
node_code_translator,
sys,
}
}
pub async fn load(
&self,
specifier: &Url,
maybe_referrer: Option<&Url>,
) -> Result<ModuleCodeStringSource, NpmModuleLoadError> {
let file_path = deno_path_util::url_to_file_path(specifier)?;
let code = self.sys.fs_read(&file_path).map_err(|source| {
if self.sys.fs_is_dir_no_err(&file_path) {
let suggestion = ["index.mjs", "index.js", "index.cjs"]
.into_iter()
.find(|e| self.sys.fs_is_file_no_err(file_path.join(e)));
NpmModuleLoadError::DirImport {
file_path,
maybe_referrer: maybe_referrer.cloned(),
suggestion,
source,
}
} else {
NpmModuleLoadError::UnableToLoad {
file_path,
maybe_referrer: maybe_referrer.cloned(),
source,
}
} }
})?;
let media_type = MediaType::from_specifier(specifier);
if media_type.is_emittable() {
return Err(NpmModuleLoadError::StrippingTypesNodeModules(
StrippingTypesNodeModulesError {
specifier: specifier.clone(),
},
));
} }
let code = if self.cjs_tracker.is_maybe_cjs(specifier, media_type)? {
// translate cjs to esm if it's cjs and inject node globals
let code = from_utf8_lossy_cow(code);
ModuleSourceCode::String(
self
.node_code_translator
.translate_cjs_to_esm(specifier, Some(code))
.await?
.into_owned()
.into(),
)
} else {
// esm and json code is untouched
ModuleSourceCode::Bytes(match code {
Cow::Owned(bytes) => bytes.into_boxed_slice().into(),
Cow::Borrowed(bytes) => bytes.into(),
})
};
Ok(ModuleCodeStringSource {
code,
found_url: specifier.clone(),
module_type: module_type_from_media_type(MediaType::from_specifier(
specifier,
)),
})
} }
} }
pub fn module_type_from_media_type(media_type: MediaType) -> ModuleType { pub fn loaded_module_source_to_module_source_code(
match media_type { loaded_module_source: LoadedModuleSource,
MediaType::Json => ModuleType::Json, ) -> ModuleSourceCode {
MediaType::Wasm => ModuleType::Wasm, match loaded_module_source {
_ => ModuleType::JavaScript, LoadedModuleSource::ArcStr(text) => ModuleSourceCode::String(text.into()),
LoadedModuleSource::ArcBytes(bytes) => {
ModuleSourceCode::Bytes(bytes.into())
}
LoadedModuleSource::String(text) => match text {
Cow::Borrowed(static_text) => {
ModuleSourceCode::String(FastString::from_static(static_text))
}
Cow::Owned(text) => ModuleSourceCode::String(text.into()),
},
LoadedModuleSource::Bytes(bytes) => match bytes {
Cow::Borrowed(static_bytes) => {
ModuleSourceCode::Bytes(static_bytes.into())
}
Cow::Owned(bytes) => {
ModuleSourceCode::Bytes(bytes.into_boxed_slice().into())
}
},
}
}
pub fn as_deno_resolver_requested_module_type(
value: &RequestedModuleType,
) -> deno_resolver::loader::RequestedModuleType<'_> {
match value {
RequestedModuleType::None => {
deno_resolver::loader::RequestedModuleType::None
}
RequestedModuleType::Json => {
deno_resolver::loader::RequestedModuleType::Json
}
RequestedModuleType::Text => {
deno_resolver::loader::RequestedModuleType::Text
}
RequestedModuleType::Bytes => {
deno_resolver::loader::RequestedModuleType::Bytes
}
RequestedModuleType::Other(text) => {
deno_resolver::loader::RequestedModuleType::Other(text)
}
} }
} }

View file

@ -7,6 +7,7 @@ pub fn is_valid_utf8(bytes: &[u8]) -> bool {
matches!(String::from_utf8_lossy(bytes), Cow::Borrowed(_)) matches!(String::from_utf8_lossy(bytes), Cow::Borrowed(_))
} }
// todo(https://github.com/rust-lang/rust/issues/129436): remove once stabilized
#[inline(always)] #[inline(always)]
pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String { pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
match String::from_utf8_lossy(&bytes) { match String::from_utf8_lossy(&bytes) {

View file

@ -43,10 +43,9 @@ use deno_error::JsErrorBox;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::WalkOptions; use deno_graph::WalkOptions;
use deno_lib::loader::ModuleCodeStringSource; use deno_lib::loader::as_deno_resolver_requested_module_type;
use deno_lib::loader::NpmModuleLoadError; use deno_lib::loader::loaded_module_source_to_module_source_code;
use deno_lib::loader::StrippingTypesNodeModulesError; use deno_lib::loader::module_type_from_media_and_requested_type;
use deno_lib::loader::module_type_from_media_type;
use deno_lib::npm::NpmRegistryReadPermissionChecker; use deno_lib::npm::NpmRegistryReadPermissionChecker;
use deno_lib::util::hash::FastInsecureHasher; use deno_lib::util::hash::FastInsecureHasher;
use deno_lib::worker::CreateModuleLoaderResult; use deno_lib::worker::CreateModuleLoaderResult;
@ -57,8 +56,10 @@ use deno_resolver::file_fetcher::FetchPermissionsOptionRef;
use deno_resolver::graph::ResolveWithGraphErrorKind; use deno_resolver::graph::ResolveWithGraphErrorKind;
use deno_resolver::graph::ResolveWithGraphOptions; use deno_resolver::graph::ResolveWithGraphOptions;
use deno_resolver::loader::LoadPreparedModuleError; use deno_resolver::loader::LoadPreparedModuleError;
use deno_resolver::loader::PreparedModuleOrAsset; use deno_resolver::loader::LoadedModule;
use deno_resolver::loader::PreparedModuleSource; use deno_resolver::loader::LoadedModuleOrAsset;
use deno_resolver::loader::NpmModuleLoadError;
use deno_resolver::loader::StrippingTypesNodeModulesError;
use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::ResolveNpmReqRefError; use deno_resolver::npm::ResolveNpmReqRefError;
use deno_runtime::code_cache; use deno_runtime::code_cache;
@ -69,7 +70,6 @@ use deno_runtime::deno_permissions::CheckSpecifierKind;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use eszip::EszipV2; use eszip::EszipV2;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::InNpmPackageChecker; use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionKind; use node_resolver::NodeResolutionKind;
use node_resolver::ResolutionMode; use node_resolver::ResolutionMode;
@ -92,7 +92,6 @@ use crate::graph_container::ModuleGraphUpdatePermit;
use crate::graph_util::BuildGraphRequest; use crate::graph_util::BuildGraphRequest;
use crate::graph_util::BuildGraphWithNpmOptions; use crate::graph_util::BuildGraphWithNpmOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
use crate::node::CliCjsCodeAnalyzer;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CliCjsTracker; use crate::resolver::CliCjsTracker;
use crate::resolver::CliResolver; use crate::resolver::CliResolver;
@ -104,13 +103,8 @@ use crate::util::progress_bar::ProgressBar;
use crate::util::text_encoding::code_without_source_map; use crate::util::text_encoding::code_without_source_map;
use crate::util::text_encoding::source_map_from_code; use crate::util::text_encoding::source_map_from_code;
pub type CliNpmModuleLoader = deno_lib::loader::NpmModuleLoader< pub type CliNpmModuleLoader =
CliCjsCodeAnalyzer, deno_resolver::loader::DenoNpmModuleLoader<CliSys>;
DenoInNpmPackageChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSys,
>;
pub type CliEmitter = pub type CliEmitter =
deno_resolver::emit::Emitter<DenoInNpmPackageChecker, CliSys>; deno_resolver::emit::Emitter<DenoInNpmPackageChecker, CliSys>;
pub type CliPreparedModuleLoader = pub type CliPreparedModuleLoader =
@ -336,7 +330,7 @@ struct SharedCliModuleLoaderState {
in_npm_pkg_checker: DenoInNpmPackageChecker, in_npm_pkg_checker: DenoInNpmPackageChecker,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
npm_module_loader: CliNpmModuleLoader, npm_module_loader: Arc<CliNpmModuleLoader>,
npm_registry_permission_checker: npm_registry_permission_checker:
Arc<NpmRegistryReadPermissionChecker<CliSys>>, Arc<NpmRegistryReadPermissionChecker<CliSys>>,
npm_resolver: CliNpmResolver, npm_resolver: CliNpmResolver,
@ -398,7 +392,7 @@ impl CliModuleLoaderFactory {
in_npm_pkg_checker: DenoInNpmPackageChecker, in_npm_pkg_checker: DenoInNpmPackageChecker,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
npm_module_loader: CliNpmModuleLoader, npm_module_loader: Arc<CliNpmModuleLoader>,
npm_registry_permission_checker: Arc< npm_registry_permission_checker: Arc<
NpmRegistryReadPermissionChecker<CliSys>, NpmRegistryReadPermissionChecker<CliSys>,
>, >,
@ -530,6 +524,12 @@ impl CliModuleLoaderFactory {
} }
} }
struct ModuleCodeStringSource {
pub code: ModuleSourceCode,
pub found_url: ModuleSpecifier,
pub module_type: ModuleType,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)] #[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(generic)] #[class(generic)]
#[error("Loading unprepared module: {}{}", .specifier, .maybe_referrer.as_ref().map(|r| format!(", imported from: {}", r)).unwrap_or_default())] #[error("Loading unprepared module: {}{}", .specifier, .maybe_referrer.as_ref().map(|r| format!(", imported from: {}", r)).unwrap_or_default())]
@ -712,63 +712,28 @@ impl<TGraphContainer: ModuleGraphContainer>
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
requested_module_type: &RequestedModuleType, requested_module_type: &RequestedModuleType,
) -> Result<ModuleCodeStringSource, LoadCodeSourceError> { ) -> Result<ModuleCodeStringSource, LoadCodeSourceError> {
fn as_deno_resolver_requested_module_type(
value: &RequestedModuleType,
) -> deno_resolver::loader::RequestedModuleType<'_> {
match value {
RequestedModuleType::None => {
deno_resolver::loader::RequestedModuleType::None
}
RequestedModuleType::Json => {
deno_resolver::loader::RequestedModuleType::Json
}
RequestedModuleType::Text => {
deno_resolver::loader::RequestedModuleType::Text
}
RequestedModuleType::Bytes => {
deno_resolver::loader::RequestedModuleType::Bytes
}
RequestedModuleType::Other(text) => {
deno_resolver::loader::RequestedModuleType::Other(text)
}
}
}
let graph = self.graph_container.graph(); let graph = self.graph_container.graph();
let deno_resolver_requested_module_type =
as_deno_resolver_requested_module_type(requested_module_type);
match self match self
.shared .shared
.prepared_module_loader .prepared_module_loader
.load_prepared_module( .load_prepared_module(
&graph, &graph,
specifier, specifier,
&as_deno_resolver_requested_module_type(requested_module_type), &deno_resolver_requested_module_type,
) )
.await .await
.map_err(LoadCodeSourceError::from)? .map_err(LoadCodeSourceError::from)?
{ {
Some(module_or_asset) => match module_or_asset { Some(module_or_asset) => match module_or_asset {
PreparedModuleOrAsset::Module(prepared_module) => { LoadedModuleOrAsset::Module(prepared_module) => {
Ok(ModuleCodeStringSource { Ok(self.loaded_module_to_module_code_string_source(
code: match prepared_module.source { prepared_module,
PreparedModuleSource::ArcStr(text) => { requested_module_type,
ModuleSourceCode::String(text.into()) ))
}
PreparedModuleSource::ArcBytes(bytes) => {
ModuleSourceCode::Bytes(bytes.into())
}
},
found_url: prepared_module.specifier.clone(),
module_type: match requested_module_type {
RequestedModuleType::Json => ModuleType::Json,
RequestedModuleType::Text => ModuleType::Text,
RequestedModuleType::Bytes => ModuleType::Bytes,
RequestedModuleType::None | RequestedModuleType::Other(_) => {
module_type_from_media_type(prepared_module.media_type)
}
},
})
} }
PreparedModuleOrAsset::ExternalAsset { specifier } => { LoadedModuleOrAsset::ExternalAsset { specifier } => {
self.load_asset( self.load_asset(
specifier, specifier,
/* do not use dynamic import permissions because this was statically analyzable */ CheckSpecifierKind::Static, /* do not use dynamic import permissions because this was statically analyzable */ CheckSpecifierKind::Static,
@ -812,13 +777,22 @@ impl<TGraphContainer: ModuleGraphContainer>
} else { } else {
Cow::Borrowed(specifier) Cow::Borrowed(specifier)
}; };
if self.shared.in_npm_pkg_checker.in_npm_package(&specifier) { if self.shared.in_npm_pkg_checker.in_npm_package(&specifier) {
return self let loaded_module = self
.shared .shared
.npm_module_loader .npm_module_loader
.load(&specifier, maybe_referrer) .load(
&specifier,
maybe_referrer,
&deno_resolver_requested_module_type,
)
.await .await
.map_err(LoadCodeSourceError::from); .map_err(LoadCodeSourceError::from)?;
return Ok(self.loaded_module_to_module_code_string_source(
loaded_module,
requested_module_type,
));
} }
match requested_module_type { match requested_module_type {
@ -839,6 +813,21 @@ impl<TGraphContainer: ModuleGraphContainer>
} }
} }
fn loaded_module_to_module_code_string_source(
&self,
loaded_module: LoadedModule,
requested_module_type: &RequestedModuleType,
) -> ModuleCodeStringSource {
ModuleCodeStringSource {
code: loaded_module_source_to_module_source_code(loaded_module.source),
found_url: loaded_module.specifier.clone(),
module_type: module_type_from_media_and_requested_type(
loaded_module.media_type,
requested_module_type,
),
}
}
async fn load_asset( async fn load_asset(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,

View file

@ -5,9 +5,9 @@ use std::sync::Arc;
use deno_core::url::Url; use deno_core::url::Url;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use deno_lib::loader::NpmModuleLoader;
use deno_lib::standalone::binary::CjsExportAnalysisEntry; use deno_lib::standalone::binary::CjsExportAnalysisEntry;
use deno_media_type::MediaType; use deno_media_type::MediaType;
use deno_resolver::loader::NpmModuleLoader;
use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmReqResolver; use deno_resolver::npm::NpmReqResolver;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;

View file

@ -27,7 +27,9 @@ use deno_lib::args::CaData;
use deno_lib::args::RootCertStoreLoadError; use deno_lib::args::RootCertStoreLoadError;
use deno_lib::args::get_root_cert_store; use deno_lib::args::get_root_cert_store;
use deno_lib::args::npm_pkg_req_ref_to_binary_command; use deno_lib::args::npm_pkg_req_ref_to_binary_command;
use deno_lib::loader::NpmModuleLoader; use deno_lib::loader::as_deno_resolver_requested_module_type;
use deno_lib::loader::loaded_module_source_to_module_source_code;
use deno_lib::loader::module_type_from_media_and_requested_type;
use deno_lib::npm::NpmRegistryReadPermissionChecker; use deno_lib::npm::NpmRegistryReadPermissionChecker;
use deno_lib::npm::NpmRegistryReadPermissionCheckerMode; use deno_lib::npm::NpmRegistryReadPermissionCheckerMode;
use deno_lib::npm::create_npm_process_state_provider; use deno_lib::npm::create_npm_process_state_provider;
@ -48,6 +50,7 @@ use deno_package_json::PackageJsonDepValue;
use deno_resolver::DenoResolveErrorKind; use deno_resolver::DenoResolveErrorKind;
use deno_resolver::cjs::CjsTracker; use deno_resolver::cjs::CjsTracker;
use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::loader::NpmModuleLoader;
use deno_resolver::npm::ByonmNpmResolverCreateOptions; use deno_resolver::npm::ByonmNpmResolverCreateOptions;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::DenoInNpmPackageChecker; use deno_resolver::npm::DenoInNpmPackageChecker;
@ -404,18 +407,31 @@ impl ModuleLoader for EmbeddedModuleLoader {
async move { async move {
let code_source = shared let code_source = shared
.npm_module_loader .npm_module_loader
.load(&original_specifier, maybe_referrer.as_ref()) .load(
&original_specifier,
maybe_referrer.as_ref(),
&as_deno_resolver_requested_module_type(&requested_module_type),
)
.await .await
.map_err(JsErrorBox::from_err)?; .map_err(JsErrorBox::from_err)?;
let code_cache_entry = shared.get_code_cache( let code_cache_entry = match requested_module_type {
&code_source.found_url, RequestedModuleType::None => shared.get_code_cache(
code_source.code.as_bytes(), code_source.specifier,
); code_source.source.as_bytes(),
),
RequestedModuleType::Other(_)
| RequestedModuleType::Json
| RequestedModuleType::Text
| RequestedModuleType::Bytes => None,
};
Ok(deno_core::ModuleSource::new_with_redirect( Ok(deno_core::ModuleSource::new_with_redirect(
code_source.module_type, module_type_from_media_and_requested_type(
code_source.code, code_source.media_type,
&requested_module_type,
),
loaded_module_source_to_module_source_code(code_source.source),
&original_specifier, &original_specifier,
&code_source.found_url, code_source.specifier,
code_cache_entry, code_cache_entry,
)) ))
} }

View file

@ -200,7 +200,7 @@ pub enum PackageJsonLoadError {
source: serde_json::Error, source: serde_json::Error,
}, },
#[error( #[error(
"\"exports\" cannot contains some keys starting with '.' and some not.\nThe exports object must either be an object of package subpath keys\nor an object of main entry condition name keys only." "\"exports\" cannot contain some keys starting with '.' and some not.\nThe exports object must either be an object of package subpath keys\nor an object of main entry condition name keys only."
)] )]
#[class(type)] #[class(type)]
InvalidExports, InvalidExports,

View file

@ -61,6 +61,8 @@ use crate::deno_json::CompilerOptionsResolver;
use crate::deno_json::CompilerOptionsResolverRc; use crate::deno_json::CompilerOptionsResolverRc;
use crate::import_map::WorkspaceExternalImportMapLoader; use crate::import_map::WorkspaceExternalImportMapLoader;
use crate::import_map::WorkspaceExternalImportMapLoaderRc; use crate::import_map::WorkspaceExternalImportMapLoaderRc;
use crate::loader::DenoNpmModuleLoaderRc;
use crate::loader::NpmModuleLoader;
use crate::lockfile::LockfileLock; use crate::lockfile::LockfileLock;
use crate::lockfile::LockfileLockRc; use crate::lockfile::LockfileLockRc;
use crate::npm::ByonmNpmResolverCreateOptions; use crate::npm::ByonmNpmResolverCreateOptions;
@ -224,6 +226,7 @@ pub trait WorkspaceFactorySys:
+ crate::npm::NpmResolverSys + crate::npm::NpmResolverSys
+ deno_cache_dir::GlobalHttpCacheSys + deno_cache_dir::GlobalHttpCacheSys
+ deno_cache_dir::LocalHttpCacheSys + deno_cache_dir::LocalHttpCacheSys
+ crate::loader::NpmModuleLoaderSys
{ {
} }
@ -693,6 +696,7 @@ pub struct ResolverFactory<TSys: WorkspaceFactorySys> {
found_package_json_dep_flag: crate::graph::FoundPackageJsonDepFlagRc, found_package_json_dep_flag: crate::graph::FoundPackageJsonDepFlagRc,
in_npm_package_checker: Deferred<DenoInNpmPackageChecker>, in_npm_package_checker: Deferred<DenoInNpmPackageChecker>,
node_code_translator: Deferred<DenoNodeCodeTranslatorRc<TSys>>, node_code_translator: Deferred<DenoNodeCodeTranslatorRc<TSys>>,
npm_module_loader: Deferred<DenoNpmModuleLoaderRc<TSys>>,
node_resolver: Deferred< node_resolver: Deferred<
NodeResolverRc< NodeResolverRc<
DenoInNpmPackageChecker, DenoInNpmPackageChecker,
@ -746,6 +750,7 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
in_npm_package_checker: Default::default(), in_npm_package_checker: Default::default(),
node_code_translator: Default::default(), node_code_translator: Default::default(),
node_resolver: Default::default(), node_resolver: Default::default(),
npm_module_loader: Default::default(),
npm_req_resolver: Default::default(), npm_req_resolver: Default::default(),
npm_resolution: Default::default(), npm_resolution: Default::default(),
npm_resolver: Default::default(), npm_resolver: Default::default(),
@ -949,6 +954,18 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
}) })
} }
pub fn npm_module_loader(
&self,
) -> Result<&DenoNpmModuleLoaderRc<TSys>, anyhow::Error> {
self.npm_module_loader.get_or_try_init(|| {
Ok(new_rc(NpmModuleLoader::new(
self.cjs_tracker()?.clone(),
self.node_code_translator()?.clone(),
self.workspace_factory.sys.clone(),
)))
})
}
pub fn npm_resolution(&self) -> &NpmResolutionCellRc { pub fn npm_resolution(&self) -> &NpmResolutionCellRc {
&self.npm_resolution &self.npm_resolution
} }

View file

@ -1,5 +1,18 @@
// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2018-2025 the Deno authors. MIT license.
mod npm;
#[cfg(all(feature = "graph", feature = "deno_ast"))]
mod prepared;
use std::borrow::Cow;
use deno_media_type::MediaType;
pub use npm::*;
#[cfg(all(feature = "graph", feature = "deno_ast"))]
pub use prepared::*;
use url::Url;
pub enum RequestedModuleType<'a> { pub enum RequestedModuleType<'a> {
None, None,
Json, Json,
@ -8,8 +21,31 @@ pub enum RequestedModuleType<'a> {
Other(&'a str), Other(&'a str),
} }
#[cfg(all(feature = "graph", feature = "deno_ast"))] #[allow(clippy::disallowed_types)]
mod prepared; type ArcStr = std::sync::Arc<str>;
#[allow(clippy::disallowed_types)]
type ArcBytes = std::sync::Arc<[u8]>;
#[cfg(all(feature = "graph", feature = "deno_ast"))] pub struct LoadedModule<'a> {
pub use prepared::*; pub specifier: &'a Url,
pub media_type: MediaType,
pub source: LoadedModuleSource,
}
pub enum LoadedModuleSource {
ArcStr(ArcStr),
ArcBytes(ArcBytes),
String(Cow<'static, str>),
Bytes(Cow<'static, [u8]>),
}
impl LoadedModuleSource {
pub fn as_bytes(&self) -> &[u8] {
match self {
LoadedModuleSource::ArcStr(text) => text.as_bytes(),
LoadedModuleSource::ArcBytes(bytes) => bytes,
LoadedModuleSource::String(text) => text.as_bytes(),
LoadedModuleSource::Bytes(bytes) => bytes,
}
}
}

251
libs/resolver/loader/npm.rs Normal file
View file

@ -0,0 +1,251 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::path::PathBuf;
use deno_media_type::MediaType;
use node_resolver::InNpmPackageChecker;
use node_resolver::IsBuiltInNodeModuleChecker;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::analyze::CjsCodeAnalyzer;
use node_resolver::analyze::NodeCodeTranslatorRc;
use node_resolver::analyze::NodeCodeTranslatorSys;
use thiserror::Error;
use url::Url;
use super::LoadedModule;
use super::LoadedModuleSource;
use super::RequestedModuleType;
use crate::cjs::CjsTrackerRc;
#[derive(Debug, Error, deno_error::JsError)]
#[class(type)]
#[error("[{}]: Stripping types is currently unsupported for files under node_modules, for \"{}\"", self.code(), specifier)]
pub struct StrippingTypesNodeModulesError {
pub specifier: Url,
}
impl StrippingTypesNodeModulesError {
pub fn code(&self) -> &'static str {
"ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING"
}
}
#[derive(Debug, Error, deno_error::JsError)]
pub enum NpmModuleLoadError {
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
#[class(inherit)]
#[error(transparent)]
StrippingTypesNodeModules(#[from] StrippingTypesNodeModulesError),
#[class(inherit)]
#[error(transparent)]
ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError),
#[class(inherit)]
#[error(transparent)]
TranslateCjsToEsm(#[from] node_resolver::analyze::TranslateCjsToEsmError),
#[class(inherit)]
#[error("Unable to load {}{}", file_path.display(), maybe_referrer.as_ref().map(|r| format!(" imported from {}", r)).unwrap_or_default())]
UnableToLoad {
file_path: PathBuf,
maybe_referrer: Option<Url>,
#[source]
#[inherit]
source: std::io::Error,
},
#[class(inherit)]
#[error(
"{}",
format_dir_import_message(file_path, maybe_referrer, suggestion)
)]
DirImport {
file_path: PathBuf,
maybe_referrer: Option<Url>,
suggestion: Option<&'static str>,
#[source]
#[inherit]
source: std::io::Error,
},
}
fn format_dir_import_message(
file_path: &std::path::Path,
maybe_referrer: &Option<Url>,
suggestion: &Option<&'static str>,
) -> String {
// directory imports are not allowed when importing from an
// ES module, so provide the user with a helpful error message
let dir_path = file_path;
let mut msg = "Directory import ".to_string();
msg.push_str(&dir_path.to_string_lossy());
if let Some(referrer) = maybe_referrer {
msg.push_str(" is not supported resolving import from ");
msg.push_str(referrer.as_str());
if let Some(entrypoint_name) = suggestion {
msg.push_str("\nDid you mean to import ");
msg.push_str(entrypoint_name);
msg.push_str(" within the directory?");
}
}
msg
}
#[sys_traits::auto_impl]
pub trait NpmModuleLoaderSys: NodeCodeTranslatorSys {}
#[allow(clippy::disallowed_types)]
pub type DenoNpmModuleLoaderRc<TSys> =
crate::sync::MaybeArc<DenoNpmModuleLoader<TSys>>;
pub type DenoNpmModuleLoader<TSys> = NpmModuleLoader<
crate::cjs::analyzer::DenoCjsCodeAnalyzer<TSys>,
crate::npm::DenoInNpmPackageChecker,
node_resolver::DenoIsBuiltInNodeModuleChecker,
crate::npm::NpmResolver<TSys>,
TSys,
>;
#[derive(Clone)]
pub struct NpmModuleLoader<
TCjsCodeAnalyzer: CjsCodeAnalyzer,
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSys: NpmModuleLoaderSys,
> {
cjs_tracker: CjsTrackerRc<TInNpmPackageChecker, TSys>,
sys: TSys,
node_code_translator: NodeCodeTranslatorRc<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>,
}
impl<
TCjsCodeAnalyzer: CjsCodeAnalyzer,
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSys: NpmModuleLoaderSys,
>
NpmModuleLoader<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>
{
pub fn new(
cjs_tracker: CjsTrackerRc<TInNpmPackageChecker, TSys>,
node_code_translator: NodeCodeTranslatorRc<
TCjsCodeAnalyzer,
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>,
sys: TSys,
) -> Self {
Self {
cjs_tracker,
node_code_translator,
sys,
}
}
pub async fn load<'a>(
&self,
specifier: &'a Url,
maybe_referrer: Option<&Url>,
requested_module_type: &RequestedModuleType<'_>,
) -> Result<LoadedModule<'a>, NpmModuleLoadError> {
let file_path = deno_path_util::url_to_file_path(specifier)?;
let code = self.sys.fs_read(&file_path).map_err(|source| {
if self.sys.fs_is_dir_no_err(&file_path) {
let suggestion = ["index.mjs", "index.js", "index.cjs"]
.into_iter()
.find(|e| self.sys.fs_is_file_no_err(file_path.join(e)));
NpmModuleLoadError::DirImport {
file_path,
maybe_referrer: maybe_referrer.cloned(),
suggestion,
source,
}
} else {
NpmModuleLoadError::UnableToLoad {
file_path,
maybe_referrer: maybe_referrer.cloned(),
source,
}
}
})?;
let media_type = MediaType::from_specifier(specifier);
match requested_module_type {
RequestedModuleType::Text | RequestedModuleType::Bytes => {
Ok(LoadedModule {
specifier,
media_type,
source: LoadedModuleSource::Bytes(code),
})
}
RequestedModuleType::None
| RequestedModuleType::Json
| RequestedModuleType::Other(_) => {
if media_type.is_emittable() {
return Err(NpmModuleLoadError::StrippingTypesNodeModules(
StrippingTypesNodeModulesError {
specifier: specifier.clone(),
},
));
}
let source = if self.cjs_tracker.is_maybe_cjs(specifier, media_type)? {
// translate cjs to esm if it's cjs and inject node globals
let code = from_utf8_lossy_cow(code);
LoadedModuleSource::String(
self
.node_code_translator
.translate_cjs_to_esm(specifier, Some(code))
.await?
.into_owned()
.into(),
)
} else {
// esm and json code is untouched
LoadedModuleSource::Bytes(code)
};
Ok(LoadedModule {
source,
specifier,
media_type,
})
}
}
}
}
#[inline(always)]
fn from_utf8_lossy_cow(bytes: Cow<[u8]>) -> Cow<str> {
match bytes {
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
Cow::Owned(bytes) => Cow::Owned(from_utf8_lossy_owned(bytes)),
}
}
// todo(https://github.com/rust-lang/rust/issues/129436): remove once stabilized
#[inline(always)]
fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
match String::from_utf8_lossy(&bytes) {
Cow::Owned(code) => code,
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
// UTF-8 if `Cow::Borrowed` is returned.
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
}
}

View file

@ -15,6 +15,8 @@ use node_resolver::errors::ClosestPkgJsonError;
use thiserror::Error; use thiserror::Error;
use url::Url; use url::Url;
use super::LoadedModule;
use super::LoadedModuleSource;
use super::RequestedModuleType; use super::RequestedModuleType;
use crate::cache::ParsedSourceCacheRc; use crate::cache::ParsedSourceCacheRc;
use crate::cjs::CjsTrackerRc; use crate::cjs::CjsTrackerRc;
@ -29,22 +31,6 @@ use crate::npm::NpmResolverSys;
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
type ArcStr = std::sync::Arc<str>; type ArcStr = std::sync::Arc<str>;
#[allow(clippy::disallowed_types)]
type ArcBytes = std::sync::Arc<[u8]>;
pub enum PreparedModuleSource {
ArcStr(ArcStr),
ArcBytes(ArcBytes),
}
impl PreparedModuleSource {
pub fn as_bytes(&self) -> &[u8] {
match self {
PreparedModuleSource::ArcStr(text) => text.as_bytes(),
PreparedModuleSource::ArcBytes(bytes) => bytes,
}
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)] #[derive(Debug, thiserror::Error, deno_error::JsError)]
#[error("{message}")] #[error("{message}")]
@ -91,14 +77,8 @@ pub trait PreparedModuleLoaderSys:
{ {
} }
pub struct PreparedModule<'graph> { pub enum LoadedModuleOrAsset<'graph> {
pub specifier: &'graph Url, Module(LoadedModule<'graph>),
pub media_type: MediaType,
pub source: PreparedModuleSource,
}
pub enum PreparedModuleOrAsset<'graph> {
Module(PreparedModule<'graph>),
/// A module that the graph knows about, but the data /// A module that the graph knows about, but the data
/// is not stored in the graph itself. It's up to the caller /// is not stored in the graph itself. It's up to the caller
/// to fetch this data. /// to fetch this data.
@ -108,7 +88,7 @@ pub enum PreparedModuleOrAsset<'graph> {
} }
enum CodeOrDeferredEmit<'a> { enum CodeOrDeferredEmit<'a> {
Source(PreparedModule<'a>), Source(LoadedModule<'a>),
DeferredEmit { DeferredEmit {
specifier: &'a Url, specifier: &'a Url,
media_type: MediaType, media_type: MediaType,
@ -159,8 +139,7 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
graph: &'graph ModuleGraph, graph: &'graph ModuleGraph,
specifier: &Url, specifier: &Url,
requested_module_type: &RequestedModuleType<'_>, requested_module_type: &RequestedModuleType<'_>,
) -> Result<Option<PreparedModuleOrAsset<'graph>>, LoadPreparedModuleError> ) -> Result<Option<LoadedModuleOrAsset<'graph>>, LoadPreparedModuleError> {
{
// Note: keep this in sync with the sync version below // Note: keep this in sync with the sync version below
match self.load_prepared_module_or_defer_emit( match self.load_prepared_module_or_defer_emit(
graph, graph,
@ -168,7 +147,7 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
requested_module_type, requested_module_type,
)? { )? {
Some(CodeOrDeferredEmit::Source(source)) => { Some(CodeOrDeferredEmit::Source(source)) => {
Ok(Some(PreparedModuleOrAsset::Module(source))) Ok(Some(LoadedModuleOrAsset::Module(source)))
} }
Some(CodeOrDeferredEmit::DeferredEmit { Some(CodeOrDeferredEmit::DeferredEmit {
specifier, specifier,
@ -183,9 +162,9 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(Some(PreparedModuleOrAsset::Module(PreparedModule { Ok(Some(LoadedModuleOrAsset::Module(LoadedModule {
// note: it's faster to provide a string to v8 if we know it's a string // note: it's faster to provide a string to v8 if we know it's a string
source: PreparedModuleSource::ArcStr(transpile_result), source: LoadedModuleSource::ArcStr(transpile_result),
specifier, specifier,
media_type, media_type,
}))) })))
@ -198,15 +177,15 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
.load_maybe_cjs(specifier, media_type, source) .load_maybe_cjs(specifier, media_type, source)
.await .await
.map(|text| { .map(|text| {
Some(PreparedModuleOrAsset::Module(PreparedModule { Some(LoadedModuleOrAsset::Module(LoadedModule {
specifier, specifier,
media_type, media_type,
source: PreparedModuleSource::ArcStr(text), source: LoadedModuleSource::ArcStr(text),
})) }))
}) })
.map_err(LoadPreparedModuleError::LoadMaybeCjs), .map_err(LoadPreparedModuleError::LoadMaybeCjs),
Some(CodeOrDeferredEmit::ExternalAsset { specifier }) => { Some(CodeOrDeferredEmit::ExternalAsset { specifier }) => {
Ok(Some(PreparedModuleOrAsset::ExternalAsset { specifier })) Ok(Some(LoadedModuleOrAsset::ExternalAsset { specifier }))
} }
None => Ok(None), None => Ok(None),
} }
@ -216,7 +195,7 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
&self, &self,
graph: &'graph ModuleGraph, graph: &'graph ModuleGraph,
specifier: &Url, specifier: &Url,
) -> Result<Option<PreparedModule<'graph>>, anyhow::Error> { ) -> Result<Option<LoadedModule<'graph>>, anyhow::Error> {
// Note: keep this in sync with the async version above // Note: keep this in sync with the async version above
match self.load_prepared_module_or_defer_emit( match self.load_prepared_module_or_defer_emit(
graph, graph,
@ -239,9 +218,9 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(Some(PreparedModule { Ok(Some(LoadedModule {
// note: it's faster to provide a string if we know it's a string // note: it's faster to provide a string if we know it's a string
source: PreparedModuleSource::ArcStr(transpile_result), source: LoadedModuleSource::ArcStr(transpile_result),
specifier, specifier,
media_type, media_type,
})) }))
@ -282,22 +261,22 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
.. ..
})) => match requested_module_type { })) => match requested_module_type {
RequestedModuleType::Bytes => match source.try_get_original_bytes() { RequestedModuleType::Bytes => match source.try_get_original_bytes() {
Some(bytes) => Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { Some(bytes) => Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcBytes(bytes), source: LoadedModuleSource::ArcBytes(bytes),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))), }))),
None => Ok(Some(CodeOrDeferredEmit::ExternalAsset { specifier })), None => Ok(Some(CodeOrDeferredEmit::ExternalAsset { specifier })),
}, },
RequestedModuleType::Text => { RequestedModuleType::Text => {
Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcStr(source.text.clone()), source: LoadedModuleSource::ArcStr(source.text.clone()),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))) })))
} }
_ => Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { _ => Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcStr(source.text.clone()), source: LoadedModuleSource::ArcStr(source.text.clone()),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))), }))),
@ -310,16 +289,16 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
.. ..
})) => match requested_module_type { })) => match requested_module_type {
RequestedModuleType::Bytes => match source.try_get_original_bytes() { RequestedModuleType::Bytes => match source.try_get_original_bytes() {
Some(bytes) => Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { Some(bytes) => Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcBytes(bytes), source: LoadedModuleSource::ArcBytes(bytes),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))), }))),
None => Ok(Some(CodeOrDeferredEmit::ExternalAsset { specifier })), None => Ok(Some(CodeOrDeferredEmit::ExternalAsset { specifier })),
}, },
RequestedModuleType::Text => { RequestedModuleType::Text => {
Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcStr(source.text.clone()), source: LoadedModuleSource::ArcStr(source.text.clone()),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))) })))
@ -373,8 +352,8 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcStr(code), source: LoadedModuleSource::ArcStr(code),
specifier, specifier,
media_type: *media_type, media_type: *media_type,
}))) })))
@ -382,8 +361,8 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: PreparedModuleLoaderSys>
}, },
Some(deno_graph::Module::Wasm(WasmModule { Some(deno_graph::Module::Wasm(WasmModule {
source, specifier, .. source, specifier, ..
})) => Ok(Some(CodeOrDeferredEmit::Source(PreparedModule { })) => Ok(Some(CodeOrDeferredEmit::Source(LoadedModule {
source: PreparedModuleSource::ArcBytes(source.clone()), source: LoadedModuleSource::ArcBytes(source.clone()),
specifier, specifier,
media_type: MediaType::Wasm, media_type: MediaType::Wasm,
}))), }))),

View file

@ -0,0 +1,19 @@
{
"tempDir": true,
"tests": {
"run": {
"args": "run main.ts",
"output": "run.out"
},
"compile": {
"steps": [{
"args": "compile --unstable-raw-imports --output bin main.ts",
"output": "[WILDCARD]"
}, {
"commandName": "./bin",
"args": [],
"output": "run.out"
}]
}
}
}

View file

@ -0,0 +1,5 @@
{
"unstable": [
"raw-imports"
]
}

View file

@ -0,0 +1,9 @@
import bytes from "package/style.css" with { type: "bytes" };
import text from "package/style.css" with { type: "text" };
console.log(bytes);
console.log(text);
import bytesUtf8Bom from "package/style_utf8_bom.css" with { type: "bytes" };
import textUtf8Bom from "package/style_utf8_bom.css" with { type: "text" };
console.log(bytesUtf8Bom);
console.log(textUtf8Bom);

View file

@ -0,0 +1,3 @@
{
"name": "package"
}

View file

@ -0,0 +1,3 @@
div {
border-color: green;
}

View file

@ -0,0 +1,3 @@
div {
border-color: green;
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,22 @@
Uint8Array(31) [
100, 105, 118, 32, 123, 10, 32,
32, 98, 111, 114, 100, 101, 114,
45, 99, 111, 108, 111, 114, 58,
32, 103, 114, 101, 101, 110, 59,
10, 125, 10
]
div {
border-color: green;
}
Uint8Array(34) [
239, 187, 191, 100, 105, 118, 32, 123,
10, 32, 32, 98, 111, 114, 100, 101,
114, 45, 99, 111, 108, 111, 114, 58,
32, 103, 114, 101, 101, 110, 59, 10,
125, 10
]
div {
border-color: green;
}