2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2020-09-05 20:34:02 -04:00
|
|
|
|
2023-04-14 16:22:33 -04:00
|
|
|
use crate::args::CliOptions;
|
|
|
|
use crate::args::DenoSubcommand;
|
2022-11-25 18:29:48 -05:00
|
|
|
use crate::args::TsTypeLib;
|
2023-04-14 16:22:33 -04:00
|
|
|
use crate::args::TypeCheckMode;
|
|
|
|
use crate::cache::ParsedSourceCache;
|
|
|
|
use crate::emit::Emitter;
|
|
|
|
use crate::graph_util::graph_lock_or_exit;
|
|
|
|
use crate::graph_util::graph_valid_with_cli_options;
|
|
|
|
use crate::graph_util::ModuleGraphBuilder;
|
|
|
|
use crate::graph_util::ModuleGraphContainer;
|
2022-08-20 11:31:33 -04:00
|
|
|
use crate::node;
|
2023-04-21 21:02:46 -04:00
|
|
|
use crate::node::CliNodeCodeTranslator;
|
2023-04-14 16:22:33 -04:00
|
|
|
use crate::proc_state::CjsResolutionStore;
|
|
|
|
use crate::proc_state::FileWatcherReporter;
|
|
|
|
use crate::resolver::CliGraphResolver;
|
|
|
|
use crate::tools::check;
|
2023-04-14 18:05:46 -04:00
|
|
|
use crate::tools::check::TypeChecker;
|
2023-04-14 16:22:33 -04:00
|
|
|
use crate::util::progress_bar::ProgressBar;
|
2022-11-28 17:28:54 -05:00
|
|
|
use crate::util::text_encoding::code_without_source_map;
|
|
|
|
use crate::util::text_encoding::source_map_from_code;
|
2021-10-10 17:26:22 -04:00
|
|
|
|
2022-07-19 11:58:18 -04:00
|
|
|
use deno_ast::MediaType;
|
|
|
|
use deno_core::anyhow::anyhow;
|
2022-09-03 09:43:35 -04:00
|
|
|
use deno_core::anyhow::Context;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_core::error::custom_error;
|
|
|
|
use deno_core::error::generic_error;
|
2020-09-14 12:48:57 -04:00
|
|
|
use deno_core::error::AnyError;
|
2020-09-21 12:36:37 -04:00
|
|
|
use deno_core::futures::future::FutureExt;
|
|
|
|
use deno_core::futures::Future;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_core::parking_lot::Mutex;
|
2022-07-19 11:58:18 -04:00
|
|
|
use deno_core::resolve_url;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_core::resolve_url_or_path;
|
2023-03-21 18:33:12 -04:00
|
|
|
use deno_core::ModuleCode;
|
2020-03-02 13:12:49 -05:00
|
|
|
use deno_core::ModuleLoader;
|
2022-07-19 11:58:18 -04:00
|
|
|
use deno_core::ModuleSource;
|
2020-01-05 11:56:18 -05:00
|
|
|
use deno_core::ModuleSpecifier;
|
2022-07-19 11:58:18 -04:00
|
|
|
use deno_core::ModuleType;
|
2020-09-19 19:17:35 -04:00
|
|
|
use deno_core::OpState;
|
2023-01-10 08:35:44 -05:00
|
|
|
use deno_core::ResolutionKind;
|
2022-07-19 11:58:18 -04:00
|
|
|
use deno_core::SourceMapGetter;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_graph::source::Resolver;
|
2023-02-22 14:15:25 -05:00
|
|
|
use deno_graph::EsmModule;
|
|
|
|
use deno_graph::JsonModule;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_graph::Module;
|
|
|
|
use deno_graph::Resolution;
|
|
|
|
use deno_lockfile::Lockfile;
|
2023-04-21 21:02:46 -04:00
|
|
|
use deno_runtime::deno_node;
|
|
|
|
use deno_runtime::deno_node::NodeResolution;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_runtime::deno_node::NodeResolutionMode;
|
2023-04-24 19:44:35 -04:00
|
|
|
use deno_runtime::deno_node::NodeResolver;
|
2023-01-07 11:25:34 -05:00
|
|
|
use deno_runtime::permissions::PermissionsContainer;
|
2023-04-14 16:22:33 -04:00
|
|
|
use deno_semver::npm::NpmPackageReqReference;
|
|
|
|
use std::borrow::Cow;
|
2020-02-08 14:34:31 -05:00
|
|
|
use std::cell::RefCell;
|
2023-04-14 16:22:33 -04:00
|
|
|
use std::collections::HashSet;
|
2019-11-16 19:17:47 -05:00
|
|
|
use std::pin::Pin;
|
2020-02-08 14:34:31 -05:00
|
|
|
use std::rc::Rc;
|
2019-07-31 13:16:03 -04:00
|
|
|
use std::str;
|
2023-04-14 16:22:33 -04:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
pub struct ModuleLoadPreparer {
|
|
|
|
options: Arc<CliOptions>,
|
|
|
|
graph_container: Arc<ModuleGraphContainer>,
|
|
|
|
lockfile: Option<Arc<Mutex<Lockfile>>>,
|
|
|
|
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
|
|
|
module_graph_builder: Arc<ModuleGraphBuilder>,
|
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
|
|
|
progress_bar: ProgressBar,
|
|
|
|
resolver: Arc<CliGraphResolver>,
|
2023-04-14 18:05:46 -04:00
|
|
|
type_checker: Arc<TypeChecker>,
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ModuleLoadPreparer {
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub fn new(
|
|
|
|
options: Arc<CliOptions>,
|
|
|
|
graph_container: Arc<ModuleGraphContainer>,
|
|
|
|
lockfile: Option<Arc<Mutex<Lockfile>>>,
|
|
|
|
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
|
|
|
module_graph_builder: Arc<ModuleGraphBuilder>,
|
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
|
|
|
progress_bar: ProgressBar,
|
|
|
|
resolver: Arc<CliGraphResolver>,
|
2023-04-14 18:05:46 -04:00
|
|
|
type_checker: Arc<TypeChecker>,
|
2023-04-14 16:22:33 -04:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
options,
|
|
|
|
graph_container,
|
|
|
|
lockfile,
|
|
|
|
maybe_file_watcher_reporter,
|
|
|
|
module_graph_builder,
|
|
|
|
parsed_source_cache,
|
|
|
|
progress_bar,
|
|
|
|
resolver,
|
2023-04-14 18:05:46 -04:00
|
|
|
type_checker,
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method must be called for a module or a static importer of that
|
|
|
|
/// module before attempting to `load()` it from a `JsRuntime`. It will
|
|
|
|
/// populate the graph data in memory with the necessary source code, write
|
|
|
|
/// emits where necessary or report any module graph / type checking errors.
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub async fn prepare_module_load(
|
|
|
|
&self,
|
|
|
|
roots: Vec<ModuleSpecifier>,
|
|
|
|
is_dynamic: bool,
|
|
|
|
lib: TsTypeLib,
|
2023-04-26 16:23:28 -04:00
|
|
|
permissions: PermissionsContainer,
|
2023-04-14 16:22:33 -04:00
|
|
|
) -> Result<(), AnyError> {
|
|
|
|
log::debug!("Preparing module load.");
|
|
|
|
let _pb_clear_guard = self.progress_bar.clear_guard();
|
|
|
|
|
2023-04-26 16:23:28 -04:00
|
|
|
let mut cache = self.module_graph_builder.create_fetch_cacher(permissions);
|
2023-04-14 16:22:33 -04:00
|
|
|
let maybe_imports = self.options.to_maybe_imports()?;
|
|
|
|
let graph_resolver = self.resolver.as_graph_resolver();
|
|
|
|
let graph_npm_resolver = self.resolver.as_graph_npm_resolver();
|
|
|
|
let maybe_file_watcher_reporter: Option<&dyn deno_graph::source::Reporter> =
|
|
|
|
if let Some(reporter) = &self.maybe_file_watcher_reporter {
|
|
|
|
Some(reporter)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
let analyzer = self.parsed_source_cache.as_analyzer();
|
|
|
|
|
|
|
|
log::debug!("Creating module graph.");
|
|
|
|
let mut graph_update_permit =
|
|
|
|
self.graph_container.acquire_update_permit().await;
|
|
|
|
let graph = graph_update_permit.graph_mut();
|
|
|
|
|
|
|
|
// Determine any modules that have already been emitted this session and
|
|
|
|
// should be skipped.
|
|
|
|
let reload_exclusions: HashSet<ModuleSpecifier> =
|
|
|
|
graph.specifiers().map(|(s, _)| s.clone()).collect();
|
|
|
|
|
|
|
|
self
|
|
|
|
.module_graph_builder
|
|
|
|
.build_graph_with_npm_resolution(
|
|
|
|
graph,
|
|
|
|
roots.clone(),
|
|
|
|
&mut cache,
|
|
|
|
deno_graph::BuildOptions {
|
|
|
|
is_dynamic,
|
|
|
|
imports: maybe_imports,
|
|
|
|
resolver: Some(graph_resolver),
|
|
|
|
npm_resolver: Some(graph_npm_resolver),
|
|
|
|
module_analyzer: Some(&*analyzer),
|
|
|
|
reporter: maybe_file_watcher_reporter,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
2023-04-14 18:05:46 -04:00
|
|
|
graph_valid_with_cli_options(graph, &roots, &self.options)?;
|
|
|
|
|
|
|
|
// If there is a lockfile...
|
2023-04-14 16:22:33 -04:00
|
|
|
if let Some(lockfile) = &self.lockfile {
|
2023-04-14 18:05:46 -04:00
|
|
|
let mut lockfile = lockfile.lock();
|
|
|
|
// validate the integrity of all the modules
|
|
|
|
graph_lock_or_exit(graph, &mut lockfile);
|
|
|
|
// update it with anything new
|
|
|
|
lockfile.write()?;
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// save the graph and get a reference to the new graph
|
|
|
|
let graph = graph_update_permit.commit();
|
|
|
|
|
|
|
|
drop(_pb_clear_guard);
|
|
|
|
|
|
|
|
// type check if necessary
|
|
|
|
if self.options.type_check_mode() != TypeCheckMode::None
|
|
|
|
&& !self.graph_container.is_type_checked(&roots, lib)
|
|
|
|
{
|
|
|
|
let graph = Arc::new(graph.segment(&roots));
|
2023-04-14 18:05:46 -04:00
|
|
|
self
|
|
|
|
.type_checker
|
|
|
|
.check(
|
|
|
|
graph,
|
|
|
|
check::CheckOptions {
|
|
|
|
lib,
|
|
|
|
log_ignored_options: false,
|
|
|
|
reload: self.options.reload_flag()
|
|
|
|
&& !roots.iter().all(|r| reload_exclusions.contains(r)),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await?;
|
2023-04-14 16:22:33 -04:00
|
|
|
self.graph_container.set_type_checked(&roots, lib);
|
|
|
|
}
|
|
|
|
|
|
|
|
log::debug!("Prepared module load.");
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper around prepare_module_load that loads and type checks
|
|
|
|
/// the provided files.
|
|
|
|
pub async fn load_and_type_check_files(
|
|
|
|
&self,
|
|
|
|
files: &[String],
|
|
|
|
) -> Result<(), AnyError> {
|
|
|
|
let lib = self.options.ts_type_lib_window();
|
|
|
|
|
|
|
|
let specifiers = files
|
|
|
|
.iter()
|
|
|
|
.map(|file| resolve_url_or_path(file, self.options.initial_cwd()))
|
|
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
self
|
|
|
|
.prepare_module_load(
|
|
|
|
specifiers,
|
|
|
|
false,
|
|
|
|
lib,
|
|
|
|
PermissionsContainer::allow_all(),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|
2020-04-03 13:40:11 -04:00
|
|
|
|
2023-04-25 18:36:31 -04:00
|
|
|
pub struct ModuleCodeSource {
|
2023-03-21 18:33:12 -04:00
|
|
|
pub code: ModuleCode,
|
2022-07-19 11:58:18 -04:00
|
|
|
pub found_url: ModuleSpecifier,
|
|
|
|
pub media_type: MediaType,
|
|
|
|
}
|
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
struct SharedCliModuleLoaderState {
|
|
|
|
lib_window: TsTypeLib,
|
|
|
|
lib_worker: TsTypeLib,
|
|
|
|
is_inspecting: bool,
|
|
|
|
is_repl: bool,
|
2023-04-14 16:22:33 -04:00
|
|
|
emitter: Arc<Emitter>,
|
|
|
|
graph_container: Arc<ModuleGraphContainer>,
|
|
|
|
module_load_preparer: Arc<ModuleLoadPreparer>,
|
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
|
|
|
resolver: Arc<CliGraphResolver>,
|
2023-04-25 18:36:31 -04:00
|
|
|
npm_module_loader: NpmModuleLoader,
|
2019-03-14 19:17:52 -04:00
|
|
|
}
|
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
pub struct CliModuleLoaderFactory {
|
|
|
|
state: Arc<SharedCliModuleLoaderState>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CliModuleLoaderFactory {
|
2023-01-10 08:35:44 -05:00
|
|
|
pub fn new(
|
2023-04-27 10:05:20 -04:00
|
|
|
options: &CliOptions,
|
|
|
|
emitter: Arc<Emitter>,
|
|
|
|
graph_container: Arc<ModuleGraphContainer>,
|
|
|
|
module_load_preparer: Arc<ModuleLoadPreparer>,
|
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
|
|
|
resolver: Arc<CliGraphResolver>,
|
|
|
|
npm_module_loader: NpmModuleLoader,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
state: Arc::new(SharedCliModuleLoaderState {
|
|
|
|
lib_window: options.ts_type_lib_window(),
|
|
|
|
lib_worker: options.ts_type_lib_worker(),
|
|
|
|
is_inspecting: options.is_inspecting(),
|
|
|
|
is_repl: matches!(options.sub_command(), DenoSubcommand::Repl(_)),
|
|
|
|
emitter,
|
|
|
|
graph_container,
|
|
|
|
module_load_preparer,
|
|
|
|
parsed_source_cache,
|
|
|
|
resolver,
|
|
|
|
npm_module_loader,
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_for_main(
|
|
|
|
&self,
|
2023-01-10 08:35:44 -05:00
|
|
|
root_permissions: PermissionsContainer,
|
|
|
|
dynamic_permissions: PermissionsContainer,
|
2023-04-27 10:05:20 -04:00
|
|
|
) -> CliModuleLoader {
|
|
|
|
self.create_with_lib(
|
|
|
|
self.state.lib_window,
|
2023-01-10 08:35:44 -05:00
|
|
|
root_permissions,
|
|
|
|
dynamic_permissions,
|
2023-04-27 10:05:20 -04:00
|
|
|
)
|
2020-09-19 19:17:35 -04:00
|
|
|
}
|
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
pub fn create_for_worker(
|
|
|
|
&self,
|
2023-01-10 08:35:44 -05:00
|
|
|
root_permissions: PermissionsContainer,
|
|
|
|
dynamic_permissions: PermissionsContainer,
|
2023-04-27 10:05:20 -04:00
|
|
|
) -> CliModuleLoader {
|
|
|
|
self.create_with_lib(
|
|
|
|
self.state.lib_worker,
|
2023-01-10 08:35:44 -05:00
|
|
|
root_permissions,
|
|
|
|
dynamic_permissions,
|
2023-04-27 10:05:20 -04:00
|
|
|
)
|
2020-09-19 19:17:35 -04:00
|
|
|
}
|
2022-07-19 11:58:18 -04:00
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
fn create_with_lib(
|
|
|
|
&self,
|
|
|
|
lib: TsTypeLib,
|
|
|
|
root_permissions: PermissionsContainer,
|
|
|
|
dynamic_permissions: PermissionsContainer,
|
|
|
|
) -> CliModuleLoader {
|
|
|
|
CliModuleLoader {
|
|
|
|
lib,
|
|
|
|
root_permissions,
|
|
|
|
dynamic_permissions,
|
|
|
|
shared: self.state.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CliModuleLoader {
|
|
|
|
lib: TsTypeLib,
|
|
|
|
/// The initial set of permissions used to resolve the static imports in the
|
|
|
|
/// worker. These are "allow all" for main worker, and parent thread
|
|
|
|
/// permissions for Web Worker.
|
|
|
|
root_permissions: PermissionsContainer,
|
|
|
|
/// Permissions used to resolve dynamic imports, these get passed as
|
|
|
|
/// "root permissions" for Web Worker.
|
|
|
|
dynamic_permissions: PermissionsContainer,
|
|
|
|
shared: Arc<SharedCliModuleLoaderState>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CliModuleLoader {
|
2022-07-19 11:58:18 -04:00
|
|
|
fn load_prepared_module(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
2023-04-04 08:46:31 -04:00
|
|
|
maybe_referrer: Option<&ModuleSpecifier>,
|
2022-07-19 11:58:18 -04:00
|
|
|
) -> Result<ModuleCodeSource, AnyError> {
|
2023-02-15 13:44:52 -05:00
|
|
|
if specifier.scheme() == "node" {
|
2023-02-22 14:15:25 -05:00
|
|
|
unreachable!(); // Node built-in modules should be handled internally.
|
2023-02-15 13:44:52 -05:00
|
|
|
}
|
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
let graph = self.shared.graph_container.graph();
|
2023-02-09 22:00:23 -05:00
|
|
|
match graph.get(specifier) {
|
2023-02-22 14:15:25 -05:00
|
|
|
Some(deno_graph::Module::Json(JsonModule {
|
|
|
|
source,
|
2023-02-09 22:00:23 -05:00
|
|
|
media_type,
|
|
|
|
specifier,
|
|
|
|
..
|
2023-02-22 14:15:25 -05:00
|
|
|
})) => Ok(ModuleCodeSource {
|
2023-04-04 08:46:31 -04:00
|
|
|
code: source.clone().into(),
|
2023-02-22 14:15:25 -05:00
|
|
|
found_url: specifier.clone(),
|
|
|
|
media_type: *media_type,
|
|
|
|
}),
|
|
|
|
Some(deno_graph::Module::Esm(EsmModule {
|
|
|
|
source,
|
|
|
|
media_type,
|
|
|
|
specifier,
|
|
|
|
..
|
|
|
|
})) => {
|
2023-03-21 18:33:12 -04:00
|
|
|
let code: ModuleCode = match media_type {
|
2022-07-19 11:58:18 -04:00
|
|
|
MediaType::JavaScript
|
|
|
|
| MediaType::Unknown
|
|
|
|
| MediaType::Cjs
|
|
|
|
| MediaType::Mjs
|
2023-04-04 08:46:31 -04:00
|
|
|
| MediaType::Json => source.clone().into(),
|
2023-03-21 18:33:12 -04:00
|
|
|
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
|
|
|
|
Default::default()
|
|
|
|
}
|
2022-07-19 11:58:18 -04:00
|
|
|
MediaType::TypeScript
|
|
|
|
| MediaType::Mts
|
|
|
|
| MediaType::Cts
|
|
|
|
| MediaType::Jsx
|
|
|
|
| MediaType::Tsx => {
|
|
|
|
// get emit text
|
2023-04-27 10:05:20 -04:00
|
|
|
self.shared.emitter.emit_parsed_source(
|
|
|
|
specifier,
|
|
|
|
*media_type,
|
|
|
|
source,
|
|
|
|
)?
|
2022-07-19 11:58:18 -04:00
|
|
|
}
|
|
|
|
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {
|
2023-02-09 22:00:23 -05:00
|
|
|
panic!("Unexpected media type {media_type} for {specifier}")
|
2022-07-19 11:58:18 -04:00
|
|
|
}
|
|
|
|
};
|
2022-08-22 12:14:59 -04:00
|
|
|
|
|
|
|
// at this point, we no longer need the parsed source in memory, so free it
|
2023-04-27 10:05:20 -04:00
|
|
|
self.shared.parsed_source_cache.free(specifier);
|
2022-08-22 12:14:59 -04:00
|
|
|
|
2022-07-19 11:58:18 -04:00
|
|
|
Ok(ModuleCodeSource {
|
|
|
|
code,
|
2023-02-09 22:00:23 -05:00
|
|
|
found_url: specifier.clone(),
|
2022-07-19 11:58:18 -04:00
|
|
|
media_type: *media_type,
|
|
|
|
})
|
|
|
|
}
|
2022-09-12 18:11:32 -04:00
|
|
|
_ => {
|
2023-01-27 10:43:16 -05:00
|
|
|
let mut msg = format!("Loading unprepared module: {specifier}");
|
2022-09-12 18:11:32 -04:00
|
|
|
if let Some(referrer) = maybe_referrer {
|
|
|
|
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
|
|
|
}
|
|
|
|
Err(anyhow!(msg))
|
|
|
|
}
|
2022-07-19 11:58:18 -04:00
|
|
|
}
|
|
|
|
}
|
2022-08-20 11:31:33 -04:00
|
|
|
|
|
|
|
fn load_sync(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
2023-04-04 08:46:31 -04:00
|
|
|
maybe_referrer: Option<&ModuleSpecifier>,
|
2023-01-10 08:35:44 -05:00
|
|
|
is_dynamic: bool,
|
2022-08-20 11:31:33 -04:00
|
|
|
) -> Result<ModuleSource, AnyError> {
|
2023-04-25 18:36:31 -04:00
|
|
|
let permissions = if is_dynamic {
|
|
|
|
&self.dynamic_permissions
|
|
|
|
} else {
|
|
|
|
&self.root_permissions
|
|
|
|
};
|
2023-04-27 10:05:20 -04:00
|
|
|
let code_source = if let Some(code_source) =
|
|
|
|
self.shared.npm_module_loader.load_sync(
|
|
|
|
specifier,
|
|
|
|
maybe_referrer,
|
|
|
|
permissions,
|
|
|
|
)? {
|
2023-04-25 18:36:31 -04:00
|
|
|
code_source
|
2022-08-20 11:31:33 -04:00
|
|
|
} else {
|
2022-09-12 18:11:32 -04:00
|
|
|
self.load_prepared_module(specifier, maybe_referrer)?
|
2022-08-20 11:31:33 -04:00
|
|
|
};
|
2023-04-27 10:05:20 -04:00
|
|
|
let code = if self.shared.is_inspecting {
|
2022-08-20 11:31:33 -04:00
|
|
|
// we need the code with the source map in order for
|
|
|
|
// it to work with --inspect or --inspect-brk
|
|
|
|
code_source.code
|
|
|
|
} else {
|
|
|
|
// reduce memory and throw away the source map
|
|
|
|
// because we don't need it
|
|
|
|
code_without_source_map(code_source.code)
|
|
|
|
};
|
2023-04-04 08:46:31 -04:00
|
|
|
Ok(ModuleSource::new_with_redirect(
|
|
|
|
match code_source.media_type {
|
2022-08-20 11:31:33 -04:00
|
|
|
MediaType::Json => ModuleType::Json,
|
|
|
|
_ => ModuleType::JavaScript,
|
|
|
|
},
|
2023-04-04 08:46:31 -04:00
|
|
|
code,
|
|
|
|
specifier,
|
|
|
|
&code_source.found_url,
|
|
|
|
))
|
2022-08-20 11:31:33 -04:00
|
|
|
}
|
2020-04-27 19:12:38 -04:00
|
|
|
}
|
|
|
|
|
2020-09-19 19:17:35 -04:00
|
|
|
impl ModuleLoader for CliModuleLoader {
|
2019-06-09 09:08:20 -04:00
|
|
|
fn resolve(
|
|
|
|
&self,
|
|
|
|
specifier: &str,
|
|
|
|
referrer: &str,
|
2023-01-10 08:35:44 -05:00
|
|
|
kind: ResolutionKind,
|
2020-09-14 12:48:57 -04:00
|
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
2023-04-24 21:07:48 -04:00
|
|
|
let permissions = if matches!(kind, ResolutionKind::DynamicImport) {
|
|
|
|
&self.dynamic_permissions
|
2023-01-10 08:35:44 -05:00
|
|
|
} else {
|
2023-04-24 21:07:48 -04:00
|
|
|
&self.root_permissions
|
2023-01-10 08:35:44 -05:00
|
|
|
};
|
2023-04-14 16:22:33 -04:00
|
|
|
|
|
|
|
// TODO(bartlomieju): ideally we shouldn't need to call `current_dir()` on each
|
|
|
|
// call - maybe it should be caller's responsibility to pass it as an arg?
|
|
|
|
let cwd = std::env::current_dir().context("Unable to get CWD")?;
|
|
|
|
let referrer_result = deno_core::resolve_url_or_path(referrer, &cwd);
|
|
|
|
|
|
|
|
if let Ok(referrer) = referrer_result.as_ref() {
|
2023-04-27 10:05:20 -04:00
|
|
|
if let Some(result) = self
|
|
|
|
.shared
|
|
|
|
.npm_module_loader
|
|
|
|
.resolve_if_in_npm_package(specifier, referrer, permissions)
|
|
|
|
{
|
2023-04-25 18:36:31 -04:00
|
|
|
return result;
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
let graph = self.shared.graph_container.graph();
|
2023-04-14 16:22:33 -04:00
|
|
|
let maybe_resolved = match graph.get(referrer) {
|
|
|
|
Some(Module::Esm(module)) => {
|
|
|
|
module.dependencies.get(specifier).map(|d| &d.maybe_code)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
match maybe_resolved {
|
|
|
|
Some(Resolution::Ok(resolved)) => {
|
|
|
|
let specifier = &resolved.specifier;
|
|
|
|
|
|
|
|
return match graph.get(specifier) {
|
|
|
|
Some(Module::Npm(module)) => self
|
2023-04-27 10:05:20 -04:00
|
|
|
.shared
|
2023-04-25 18:36:31 -04:00
|
|
|
.npm_module_loader
|
|
|
|
.resolve_npm_module(module, permissions),
|
2023-04-14 16:22:33 -04:00
|
|
|
Some(Module::Node(module)) => {
|
2023-04-21 21:02:46 -04:00
|
|
|
deno_node::resolve_builtin_node_module(&module.module_name)
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
Some(Module::Esm(module)) => Ok(module.specifier.clone()),
|
|
|
|
Some(Module::Json(module)) => Ok(module.specifier.clone()),
|
|
|
|
Some(Module::External(module)) => {
|
|
|
|
Ok(node::resolve_specifier_into_node_modules(&module.specifier))
|
|
|
|
}
|
|
|
|
None => Ok(specifier.clone()),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Some(Resolution::Err(err)) => {
|
|
|
|
return Err(custom_error(
|
|
|
|
"TypeError",
|
|
|
|
format!("{}\n", err.to_string_with_range()),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
Some(Resolution::None) | None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Built-in Node modules
|
|
|
|
if let Some(module_name) = specifier.strip_prefix("node:") {
|
2023-04-21 21:02:46 -04:00
|
|
|
return deno_node::resolve_builtin_node_module(module_name);
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL
|
|
|
|
// and `Deno.core.evalContext` API. Ideally we should always have a referrer filled
|
|
|
|
// but sadly that's not the case due to missing APIs in V8.
|
2023-04-27 10:05:20 -04:00
|
|
|
let referrer = if referrer.is_empty() && self.shared.is_repl {
|
2023-04-14 16:22:33 -04:00
|
|
|
deno_core::resolve_path("./$deno$repl.ts", &cwd)?
|
|
|
|
} else {
|
|
|
|
referrer_result?
|
|
|
|
};
|
|
|
|
|
|
|
|
// FIXME(bartlomieju): this is another hack way to provide NPM specifier
|
|
|
|
// support in REPL. This should be fixed.
|
2023-04-27 10:05:20 -04:00
|
|
|
let resolution = self.shared.resolver.resolve(specifier, &referrer);
|
2023-04-14 16:22:33 -04:00
|
|
|
|
2023-04-27 10:05:20 -04:00
|
|
|
if self.shared.is_repl {
|
2023-04-14 16:22:33 -04:00
|
|
|
let specifier = resolution
|
|
|
|
.as_ref()
|
|
|
|
.ok()
|
|
|
|
.map(Cow::Borrowed)
|
|
|
|
.or_else(|| ModuleSpecifier::parse(specifier).ok().map(Cow::Owned));
|
|
|
|
if let Some(specifier) = specifier {
|
|
|
|
if let Ok(reference) =
|
|
|
|
NpmPackageReqReference::from_specifier(&specifier)
|
|
|
|
{
|
|
|
|
return self
|
2023-04-27 10:05:20 -04:00
|
|
|
.shared
|
2023-04-25 18:36:31 -04:00
|
|
|
.npm_module_loader
|
|
|
|
.resolve_for_repl(&reference, permissions);
|
2023-04-14 16:22:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resolution
|
2019-06-05 16:35:38 -04:00
|
|
|
}
|
|
|
|
|
2019-06-12 19:55:59 -04:00
|
|
|
fn load(
|
|
|
|
&self,
|
2022-07-19 11:58:18 -04:00
|
|
|
specifier: &ModuleSpecifier,
|
2023-04-04 08:46:31 -04:00
|
|
|
maybe_referrer: Option<&ModuleSpecifier>,
|
2023-01-10 08:35:44 -05:00
|
|
|
is_dynamic: bool,
|
2020-03-02 13:12:49 -05:00
|
|
|
) -> Pin<Box<deno_core::ModuleSourceFuture>> {
|
2021-10-10 17:26:22 -04:00
|
|
|
// NOTE: this block is async only because of `deno_core` interface
|
|
|
|
// requirements; module was already loaded when constructing module graph
|
2022-07-19 11:58:18 -04:00
|
|
|
// during call to `prepare_load` so we can load it synchronously.
|
2023-01-10 08:35:44 -05:00
|
|
|
Box::pin(deno_core::futures::future::ready(self.load_sync(
|
|
|
|
specifier,
|
|
|
|
maybe_referrer,
|
|
|
|
is_dynamic,
|
|
|
|
)))
|
2019-06-05 16:35:38 -04:00
|
|
|
}
|
2020-04-30 08:37:06 -04:00
|
|
|
|
|
|
|
fn prepare_load(
|
|
|
|
&self,
|
2023-01-10 08:35:44 -05:00
|
|
|
_op_state: Rc<RefCell<OpState>>,
|
2020-10-22 20:50:15 -04:00
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
_maybe_referrer: Option<String>,
|
|
|
|
is_dynamic: bool,
|
2020-09-14 12:48:57 -04:00
|
|
|
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
|
2023-04-27 10:05:20 -04:00
|
|
|
if let Some(result) =
|
|
|
|
self.shared.npm_module_loader.maybe_prepare_load(specifier)
|
|
|
|
{
|
2023-04-25 18:36:31 -04:00
|
|
|
return Box::pin(deno_core::futures::future::ready(result));
|
2022-08-20 11:31:33 -04:00
|
|
|
}
|
|
|
|
|
2020-10-22 20:50:15 -04:00
|
|
|
let specifier = specifier.clone();
|
2023-04-27 10:05:20 -04:00
|
|
|
let module_load_preparer = self.shared.module_load_preparer.clone();
|
2020-09-19 19:17:35 -04:00
|
|
|
|
2021-10-10 17:26:22 -04:00
|
|
|
let root_permissions = if is_dynamic {
|
2023-01-10 08:35:44 -05:00
|
|
|
self.dynamic_permissions.clone()
|
2021-10-10 17:26:22 -04:00
|
|
|
} else {
|
|
|
|
self.root_permissions.clone()
|
|
|
|
};
|
2022-06-28 16:45:55 -04:00
|
|
|
let lib = self.lib;
|
2021-01-06 15:31:16 -05:00
|
|
|
|
2020-05-29 10:32:15 -04:00
|
|
|
async move {
|
2023-04-14 16:22:33 -04:00
|
|
|
module_load_preparer
|
2023-04-26 16:23:28 -04:00
|
|
|
.prepare_module_load(vec![specifier], is_dynamic, lib, root_permissions)
|
2023-04-14 16:22:33 -04:00
|
|
|
.await
|
2020-05-29 10:32:15 -04:00
|
|
|
}
|
|
|
|
.boxed_local()
|
2020-04-30 08:37:06 -04:00
|
|
|
}
|
2019-06-05 16:35:38 -04:00
|
|
|
}
|
2022-07-19 11:58:18 -04:00
|
|
|
|
|
|
|
impl SourceMapGetter for CliModuleLoader {
|
|
|
|
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
|
2022-08-20 11:31:33 -04:00
|
|
|
let specifier = resolve_url(file_name).ok()?;
|
|
|
|
match specifier.scheme() {
|
|
|
|
// we should only be looking for emits for schemes that denote external
|
|
|
|
// modules, which the disk_cache supports
|
|
|
|
"wasm" | "file" | "http" | "https" | "data" | "blob" => (),
|
|
|
|
_ => return None,
|
2022-07-19 11:58:18 -04:00
|
|
|
}
|
2022-09-12 18:11:32 -04:00
|
|
|
let source = self.load_prepared_module(&specifier, None).ok()?;
|
2022-08-20 11:31:33 -04:00
|
|
|
source_map_from_code(&source.code)
|
2022-07-19 11:58:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_source_line(
|
|
|
|
&self,
|
|
|
|
file_name: &str,
|
|
|
|
line_number: usize,
|
|
|
|
) -> Option<String> {
|
2023-04-27 10:05:20 -04:00
|
|
|
let graph = self.shared.graph_container.graph();
|
2023-02-09 22:00:23 -05:00
|
|
|
let code = match graph.get(&resolve_url(file_name).ok()?) {
|
2023-02-22 14:15:25 -05:00
|
|
|
Some(deno_graph::Module::Esm(module)) => &module.source,
|
|
|
|
Some(deno_graph::Module::Json(module)) => &module.source,
|
2022-07-19 11:58:18 -04:00
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
// Do NOT use .lines(): it skips the terminating empty line.
|
|
|
|
// (due to internally using_terminator() instead of .split())
|
|
|
|
let lines: Vec<&str> = code.split('\n').collect();
|
|
|
|
if line_number >= lines.len() {
|
|
|
|
Some(format!(
|
|
|
|
"{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)",
|
|
|
|
crate::colors::yellow("Warning"), line_number + 1,
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
Some(lines[line_number].to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-25 18:36:31 -04:00
|
|
|
|
|
|
|
pub struct NpmModuleLoader {
|
|
|
|
cjs_resolutions: Arc<CjsResolutionStore>,
|
|
|
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
|
|
|
node_resolver: Arc<NodeResolver>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NpmModuleLoader {
|
|
|
|
pub fn new(
|
|
|
|
cjs_resolutions: Arc<CjsResolutionStore>,
|
|
|
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
|
|
|
node_resolver: Arc<NodeResolver>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
cjs_resolutions,
|
|
|
|
node_code_translator,
|
|
|
|
node_resolver,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_if_in_npm_package(
|
|
|
|
&self,
|
|
|
|
specifier: &str,
|
|
|
|
referrer: &ModuleSpecifier,
|
|
|
|
permissions: &PermissionsContainer,
|
|
|
|
) -> Option<Result<ModuleSpecifier, AnyError>> {
|
|
|
|
if self.node_resolver.in_npm_package(referrer) {
|
|
|
|
// we're in an npm package, so use node resolution
|
|
|
|
Some(
|
|
|
|
self
|
|
|
|
.handle_node_resolve_result(self.node_resolver.resolve(
|
|
|
|
specifier,
|
|
|
|
referrer,
|
|
|
|
NodeResolutionMode::Execution,
|
|
|
|
permissions,
|
|
|
|
))
|
|
|
|
.with_context(|| {
|
|
|
|
format!("Could not resolve '{specifier}' from '{referrer}'.")
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_npm_module(
|
|
|
|
&self,
|
|
|
|
module: &deno_graph::NpmModule,
|
|
|
|
permissions: &PermissionsContainer,
|
|
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
|
|
|
self
|
|
|
|
.handle_node_resolve_result(self.node_resolver.resolve_npm_reference(
|
|
|
|
&module.nv_reference,
|
|
|
|
NodeResolutionMode::Execution,
|
|
|
|
permissions,
|
|
|
|
))
|
|
|
|
.with_context(|| format!("Could not resolve '{}'.", module.nv_reference))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_for_repl(
|
|
|
|
&self,
|
|
|
|
reference: &NpmPackageReqReference,
|
|
|
|
permissions: &PermissionsContainer,
|
|
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
|
|
|
self
|
|
|
|
.handle_node_resolve_result(self.node_resolver.resolve_npm_req_reference(
|
|
|
|
reference,
|
|
|
|
NodeResolutionMode::Execution,
|
|
|
|
permissions,
|
|
|
|
))
|
|
|
|
.with_context(|| format!("Could not resolve '{reference}'."))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_prepare_load(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Option<Result<(), AnyError>> {
|
|
|
|
if self.node_resolver.in_npm_package(specifier) {
|
|
|
|
// nothing to prepare
|
|
|
|
Some(Ok(()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_sync(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
maybe_referrer: Option<&ModuleSpecifier>,
|
|
|
|
permissions: &PermissionsContainer,
|
|
|
|
) -> Result<Option<ModuleCodeSource>, AnyError> {
|
|
|
|
if !self.node_resolver.in_npm_package(specifier) {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
let file_path = specifier.to_file_path().unwrap();
|
|
|
|
let code = std::fs::read_to_string(&file_path).with_context(|| {
|
|
|
|
let mut msg = "Unable to load ".to_string();
|
|
|
|
msg.push_str(&file_path.to_string_lossy());
|
|
|
|
if let Some(referrer) = &maybe_referrer {
|
|
|
|
msg.push_str(" imported from ");
|
|
|
|
msg.push_str(referrer.as_str());
|
|
|
|
}
|
|
|
|
msg
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let code = if self.cjs_resolutions.contains(specifier) {
|
|
|
|
// translate cjs to esm if it's cjs and inject node globals
|
|
|
|
self.node_code_translator.translate_cjs_to_esm(
|
|
|
|
specifier,
|
|
|
|
&code,
|
|
|
|
permissions,
|
|
|
|
)?
|
|
|
|
} else {
|
|
|
|
// only inject node globals for esm
|
|
|
|
self
|
|
|
|
.node_code_translator
|
|
|
|
.esm_code_with_node_globals(specifier, &code)?
|
|
|
|
};
|
|
|
|
Ok(Some(ModuleCodeSource {
|
|
|
|
code: code.into(),
|
|
|
|
found_url: specifier.clone(),
|
|
|
|
media_type: MediaType::from_specifier(specifier),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_node_resolve_result(
|
|
|
|
&self,
|
|
|
|
result: Result<Option<NodeResolution>, AnyError>,
|
|
|
|
) -> Result<ModuleSpecifier, AnyError> {
|
|
|
|
let response = match result? {
|
|
|
|
Some(response) => response,
|
|
|
|
None => return Err(generic_error("not found")),
|
|
|
|
};
|
|
|
|
if let NodeResolution::CommonJs(specifier) = &response {
|
|
|
|
// remember that this was a common js resolution
|
|
|
|
self.cjs_resolutions.insert(specifier.clone());
|
|
|
|
} else if let NodeResolution::BuiltIn(specifier) = &response {
|
|
|
|
return deno_node::resolve_builtin_node_module(specifier);
|
|
|
|
}
|
|
|
|
Ok(response.into_url())
|
|
|
|
}
|
|
|
|
}
|