mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
fix: ensure concurrent non-statically analyzable dynamic imports do not sometimes fail (#17923)
Closes #17918
This commit is contained in:
parent
a27d0885f4
commit
9aebc8bc19
8 changed files with 273 additions and 83 deletions
|
@ -3,6 +3,7 @@
|
||||||
use crate::args::CliOptions;
|
use crate::args::CliOptions;
|
||||||
use crate::args::Lockfile;
|
use crate::args::Lockfile;
|
||||||
use crate::args::TsConfigType;
|
use crate::args::TsConfigType;
|
||||||
|
use crate::args::TsTypeLib;
|
||||||
use crate::args::TypeCheckMode;
|
use crate::args::TypeCheckMode;
|
||||||
use crate::cache;
|
use crate::cache;
|
||||||
use crate::cache::TypeCheckCache;
|
use crate::cache::TypeCheckCache;
|
||||||
|
@ -16,6 +17,7 @@ use crate::tools::check;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::error::custom_error;
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::parking_lot::RwLock;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_graph::Module;
|
use deno_graph::Module;
|
||||||
use deno_graph::ModuleGraph;
|
use deno_graph::ModuleGraph;
|
||||||
|
@ -24,7 +26,11 @@ use deno_graph::ResolutionError;
|
||||||
use deno_graph::SpecifierError;
|
use deno_graph::SpecifierError;
|
||||||
use deno_runtime::permissions::PermissionsContainer;
|
use deno_runtime::permissions::PermissionsContainer;
|
||||||
use import_map::ImportMapError;
|
use import_map::ImportMapError;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Semaphore;
|
||||||
|
use tokio::sync::SemaphorePermit;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct GraphValidOptions {
|
pub struct GraphValidOptions {
|
||||||
|
@ -309,6 +315,111 @@ fn get_resolution_error_bare_specifier(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
struct GraphData {
|
||||||
|
graph: Arc<ModuleGraph>,
|
||||||
|
checked_libs: HashMap<TsTypeLib, HashSet<ModuleSpecifier>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holds the `ModuleGraph` and what parts of it are type checked.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModuleGraphContainer {
|
||||||
|
update_semaphore: Arc<Semaphore>,
|
||||||
|
graph_data: Arc<RwLock<GraphData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ModuleGraphContainer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
update_semaphore: Arc::new(Semaphore::new(1)),
|
||||||
|
graph_data: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleGraphContainer {
|
||||||
|
/// Acquires a permit to modify the module graph without other code
|
||||||
|
/// having the chance to modify it. In the meantime, other code may
|
||||||
|
/// still read from the existing module graph.
|
||||||
|
pub async fn acquire_update_permit(&self) -> ModuleGraphUpdatePermit {
|
||||||
|
let permit = self.update_semaphore.acquire().await.unwrap();
|
||||||
|
ModuleGraphUpdatePermit {
|
||||||
|
permit,
|
||||||
|
graph_data: self.graph_data.clone(),
|
||||||
|
graph: (*self.graph_data.read().graph).clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn graph(&self) -> Arc<ModuleGraph> {
|
||||||
|
self.graph_data.read().graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark `roots` and all of their dependencies as type checked under `lib`.
|
||||||
|
/// Assumes that all of those modules are known.
|
||||||
|
pub fn set_type_checked(&self, roots: &[ModuleSpecifier], lib: TsTypeLib) {
|
||||||
|
// It's ok to analyze and update this while the module graph itself is
|
||||||
|
// being updated in a permit because the module graph update is always
|
||||||
|
// additive and this will be a subset of the original graph
|
||||||
|
let graph = self.graph();
|
||||||
|
let entries = graph.walk(
|
||||||
|
roots,
|
||||||
|
deno_graph::WalkOptions {
|
||||||
|
check_js: true,
|
||||||
|
follow_dynamic: true,
|
||||||
|
follow_type_only: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// now update
|
||||||
|
let mut data = self.graph_data.write();
|
||||||
|
let checked_lib_set = data.checked_libs.entry(lib).or_default();
|
||||||
|
for (specifier, _) in entries {
|
||||||
|
checked_lib_set.insert(specifier.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if `roots` are all marked as type checked under `lib`.
|
||||||
|
pub fn is_type_checked(
|
||||||
|
&self,
|
||||||
|
roots: &[ModuleSpecifier],
|
||||||
|
lib: TsTypeLib,
|
||||||
|
) -> bool {
|
||||||
|
let data = self.graph_data.read();
|
||||||
|
match data.checked_libs.get(&lib) {
|
||||||
|
Some(checked_lib_set) => roots.iter().all(|r| {
|
||||||
|
let found = data.graph.resolve(r);
|
||||||
|
checked_lib_set.contains(&found)
|
||||||
|
}),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A permit for updating the module graph. When complete and
|
||||||
|
/// everything looks fine, calling `.commit()` will store the
|
||||||
|
/// new graph in the ModuleGraphContainer.
|
||||||
|
pub struct ModuleGraphUpdatePermit<'a> {
|
||||||
|
permit: SemaphorePermit<'a>,
|
||||||
|
graph_data: Arc<RwLock<GraphData>>,
|
||||||
|
graph: ModuleGraph,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ModuleGraphUpdatePermit<'a> {
|
||||||
|
/// Gets the module graph for mutation.
|
||||||
|
pub fn graph_mut(&mut self) -> &mut ModuleGraph {
|
||||||
|
&mut self.graph
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the mutated module graph in the container
|
||||||
|
/// and returns an Arc to the new module graph.
|
||||||
|
pub fn commit(self) -> Arc<ModuleGraph> {
|
||||||
|
let graph = Arc::new(self.graph);
|
||||||
|
self.graph_data.write().graph = graph.clone();
|
||||||
|
drop(self.permit); // explicit drop for clarity
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
@ -20,6 +20,7 @@ use crate::file_fetcher::FileFetcher;
|
||||||
use crate::graph_util::build_graph_with_npm_resolution;
|
use crate::graph_util::build_graph_with_npm_resolution;
|
||||||
use crate::graph_util::graph_lock_or_exit;
|
use crate::graph_util::graph_lock_or_exit;
|
||||||
use crate::graph_util::graph_valid_with_cli_options;
|
use crate::graph_util::graph_valid_with_cli_options;
|
||||||
|
use crate::graph_util::ModuleGraphContainer;
|
||||||
use crate::http_util::HttpClient;
|
use crate::http_util::HttpClient;
|
||||||
use crate::node;
|
use crate::node;
|
||||||
use crate::node::NodeResolution;
|
use crate::node::NodeResolution;
|
||||||
|
@ -38,7 +39,6 @@ use deno_core::error::custom_error;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::parking_lot::Mutex;
|
use deno_core::parking_lot::Mutex;
|
||||||
use deno_core::parking_lot::RwLock;
|
|
||||||
use deno_core::resolve_url_or_path;
|
use deno_core::resolve_url_or_path;
|
||||||
use deno_core::CompiledWasmModuleStore;
|
use deno_core::CompiledWasmModuleStore;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
@ -58,7 +58,6 @@ use deno_runtime::permissions::PermissionsContainer;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -78,7 +77,7 @@ pub struct Inner {
|
||||||
pub emit_cache: EmitCache,
|
pub emit_cache: EmitCache,
|
||||||
pub emit_options: deno_ast::EmitOptions,
|
pub emit_options: deno_ast::EmitOptions,
|
||||||
pub emit_options_hash: u64,
|
pub emit_options_hash: u64,
|
||||||
graph_data: Arc<RwLock<GraphData>>,
|
graph_container: ModuleGraphContainer,
|
||||||
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||||
pub maybe_import_map: Option<Arc<ImportMap>>,
|
pub maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||||
|
@ -139,7 +138,7 @@ impl ProcState {
|
||||||
emit_options: self.emit_options.clone(),
|
emit_options: self.emit_options.clone(),
|
||||||
file_fetcher: self.file_fetcher.clone(),
|
file_fetcher: self.file_fetcher.clone(),
|
||||||
http_client: self.http_client.clone(),
|
http_client: self.http_client.clone(),
|
||||||
graph_data: Default::default(),
|
graph_container: Default::default(),
|
||||||
lockfile: self.lockfile.clone(),
|
lockfile: self.lockfile.clone(),
|
||||||
maybe_import_map: self.maybe_import_map.clone(),
|
maybe_import_map: self.maybe_import_map.clone(),
|
||||||
maybe_inspector_server: self.maybe_inspector_server.clone(),
|
maybe_inspector_server: self.maybe_inspector_server.clone(),
|
||||||
|
@ -284,7 +283,7 @@ impl ProcState {
|
||||||
emit_options,
|
emit_options,
|
||||||
file_fetcher: Arc::new(file_fetcher),
|
file_fetcher: Arc::new(file_fetcher),
|
||||||
http_client,
|
http_client,
|
||||||
graph_data: Default::default(),
|
graph_container: Default::default(),
|
||||||
lockfile,
|
lockfile,
|
||||||
maybe_import_map,
|
maybe_import_map,
|
||||||
maybe_inspector_server,
|
maybe_inspector_server,
|
||||||
|
@ -340,7 +339,9 @@ impl ProcState {
|
||||||
let analyzer = self.parsed_source_cache.as_analyzer();
|
let analyzer = self.parsed_source_cache.as_analyzer();
|
||||||
|
|
||||||
log::debug!("Creating module graph.");
|
log::debug!("Creating module graph.");
|
||||||
let mut graph = self.graph_data.read().graph_inner_clone();
|
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
|
// Determine any modules that have already been emitted this session and
|
||||||
// should be skipped.
|
// should be skipped.
|
||||||
|
@ -348,7 +349,7 @@ impl ProcState {
|
||||||
graph.specifiers().map(|(s, _)| s.clone()).collect();
|
graph.specifiers().map(|(s, _)| s.clone()).collect();
|
||||||
|
|
||||||
build_graph_with_npm_resolution(
|
build_graph_with_npm_resolution(
|
||||||
&mut graph,
|
graph,
|
||||||
&self.npm_resolver,
|
&self.npm_resolver,
|
||||||
roots.clone(),
|
roots.clone(),
|
||||||
&mut cache,
|
&mut cache,
|
||||||
|
@ -365,15 +366,12 @@ impl ProcState {
|
||||||
|
|
||||||
// If there is a lockfile, validate the integrity of all the modules.
|
// If there is a lockfile, validate the integrity of all the modules.
|
||||||
if let Some(lockfile) = &self.lockfile {
|
if let Some(lockfile) = &self.lockfile {
|
||||||
graph_lock_or_exit(&graph, &mut lockfile.lock());
|
graph_lock_or_exit(graph, &mut lockfile.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph = {
|
graph_valid_with_cli_options(graph, &roots, &self.options)?;
|
||||||
graph_valid_with_cli_options(&graph, &roots, &self.options)?;
|
// save the graph and get a reference to the new graph
|
||||||
let mut graph_data = self.graph_data.write();
|
let graph = graph_update_permit.commit();
|
||||||
graph_data.update_graph(Arc::new(graph));
|
|
||||||
graph_data.graph.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
if graph.has_node_specifier
|
if graph.has_node_specifier
|
||||||
&& self.options.type_check_mode() != TypeCheckMode::None
|
&& self.options.type_check_mode() != TypeCheckMode::None
|
||||||
|
@ -388,14 +386,11 @@ impl ProcState {
|
||||||
|
|
||||||
// type check if necessary
|
// type check if necessary
|
||||||
if self.options.type_check_mode() != TypeCheckMode::None
|
if self.options.type_check_mode() != TypeCheckMode::None
|
||||||
&& !self.graph_data.read().is_type_checked(&roots, lib)
|
&& !self.graph_container.is_type_checked(&roots, lib)
|
||||||
{
|
{
|
||||||
log::debug!("Type checking.");
|
log::debug!("Type checking.");
|
||||||
let maybe_config_specifier = self.options.maybe_config_file_specifier();
|
let maybe_config_specifier = self.options.maybe_config_file_specifier();
|
||||||
let graph = {
|
let graph = Arc::new(graph.segment(&roots));
|
||||||
let graph_data = self.graph_data.read();
|
|
||||||
Arc::new(graph_data.graph.segment(&roots))
|
|
||||||
};
|
|
||||||
let options = check::CheckOptions {
|
let options = check::CheckOptions {
|
||||||
type_check_mode: self.options.type_check_mode(),
|
type_check_mode: self.options.type_check_mode(),
|
||||||
debug: self.options.log_level() == Some(log::Level::Debug),
|
debug: self.options.log_level() == Some(log::Level::Debug),
|
||||||
|
@ -412,7 +407,7 @@ impl ProcState {
|
||||||
TypeCheckCache::new(&self.dir.type_checking_cache_db_file_path());
|
TypeCheckCache::new(&self.dir.type_checking_cache_db_file_path());
|
||||||
let check_result =
|
let check_result =
|
||||||
check::check(graph, &check_cache, &self.npm_resolver, options)?;
|
check::check(graph, &check_cache, &self.npm_resolver, options)?;
|
||||||
self.graph_data.write().set_type_checked(&roots, lib);
|
self.graph_container.set_type_checked(&roots, lib);
|
||||||
if !check_result.diagnostics.is_empty() {
|
if !check_result.diagnostics.is_empty() {
|
||||||
return Err(anyhow!(check_result.diagnostics));
|
return Err(anyhow!(check_result.diagnostics));
|
||||||
}
|
}
|
||||||
|
@ -492,8 +487,7 @@ impl ProcState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph_data = self.graph_data.read();
|
let graph = self.graph_container.graph();
|
||||||
let graph = &graph_data.graph;
|
|
||||||
let maybe_resolved = match graph.get(&referrer) {
|
let maybe_resolved = match graph.get(&referrer) {
|
||||||
Some(Module::Esm(module)) => {
|
Some(Module::Esm(module)) => {
|
||||||
module.dependencies.get(specifier).map(|d| &d.maybe_code)
|
module.dependencies.get(specifier).map(|d| &d.maybe_code)
|
||||||
|
@ -684,11 +678,7 @@ impl ProcState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn graph(&self) -> Arc<ModuleGraph> {
|
pub fn graph(&self) -> Arc<ModuleGraph> {
|
||||||
self.graph_data.read().graph.clone()
|
self.graph_container.graph()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_node_builtin_specifier(&self) -> bool {
|
|
||||||
self.graph_data.read().graph.has_node_specifier
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,57 +705,3 @@ impl deno_graph::source::Reporter for FileWatcherReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct GraphData {
|
|
||||||
graph: Arc<ModuleGraph>,
|
|
||||||
checked_libs: HashMap<TsTypeLib, HashSet<ModuleSpecifier>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GraphData {
|
|
||||||
/// Store data from `graph` into `self`.
|
|
||||||
pub fn update_graph(&mut self, graph: Arc<ModuleGraph>) {
|
|
||||||
self.graph = graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo(dsherret): remove the need for cloning this (maybe if we used an AsyncRefCell)
|
|
||||||
pub fn graph_inner_clone(&self) -> ModuleGraph {
|
|
||||||
(*self.graph).clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark `roots` and all of their dependencies as type checked under `lib`.
|
|
||||||
/// Assumes that all of those modules are known.
|
|
||||||
pub fn set_type_checked(
|
|
||||||
&mut self,
|
|
||||||
roots: &[ModuleSpecifier],
|
|
||||||
lib: TsTypeLib,
|
|
||||||
) {
|
|
||||||
let entries = self.graph.walk(
|
|
||||||
roots,
|
|
||||||
deno_graph::WalkOptions {
|
|
||||||
check_js: true,
|
|
||||||
follow_dynamic: true,
|
|
||||||
follow_type_only: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let checked_lib_set = self.checked_libs.entry(lib).or_default();
|
|
||||||
for (specifier, _) in entries {
|
|
||||||
checked_lib_set.insert(specifier.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if `roots` are all marked as type checked under `lib`.
|
|
||||||
pub fn is_type_checked(
|
|
||||||
&self,
|
|
||||||
roots: &[ModuleSpecifier],
|
|
||||||
lib: TsTypeLib,
|
|
||||||
) -> bool {
|
|
||||||
match self.checked_libs.get(&lib) {
|
|
||||||
Some(checked_lib_set) => roots.iter().all(|r| {
|
|
||||||
let found = self.graph.resolve(r);
|
|
||||||
checked_lib_set.contains(&found)
|
|
||||||
}),
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -545,6 +545,12 @@ itest!(dynamic_import_already_rejected {
|
||||||
output: "run/dynamic_import_already_rejected/main.out",
|
output: "run/dynamic_import_already_rejected/main.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(dynamic_import_concurrent_non_statically_analyzable {
|
||||||
|
args: "run --allow-read --allow-net --quiet run/dynamic_import_concurrent_non_statically_analyzable/main.ts",
|
||||||
|
output: "run/dynamic_import_concurrent_non_statically_analyzable/main.out",
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
||||||
itest!(no_check_imports_not_used_as_values {
|
itest!(no_check_imports_not_used_as_values {
|
||||||
args: "run --config run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json --no-check run/no_check_imports_not_used_as_values/main.ts",
|
args: "run --config run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json --no-check run/no_check_imports_not_used_as_values/main.ts",
|
||||||
output: "run/no_check_imports_not_used_as_values/main.out",
|
output: "run/no_check_imports_not_used_as_values/main.out",
|
||||||
|
|
100
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/main.out
vendored
Normal file
100
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/main.out
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
20
|
||||||
|
21
|
||||||
|
22
|
||||||
|
23
|
||||||
|
24
|
||||||
|
25
|
||||||
|
26
|
||||||
|
27
|
||||||
|
28
|
||||||
|
29
|
||||||
|
30
|
||||||
|
31
|
||||||
|
32
|
||||||
|
33
|
||||||
|
34
|
||||||
|
35
|
||||||
|
36
|
||||||
|
37
|
||||||
|
38
|
||||||
|
39
|
||||||
|
40
|
||||||
|
41
|
||||||
|
42
|
||||||
|
43
|
||||||
|
44
|
||||||
|
45
|
||||||
|
46
|
||||||
|
47
|
||||||
|
48
|
||||||
|
49
|
||||||
|
50
|
||||||
|
51
|
||||||
|
52
|
||||||
|
53
|
||||||
|
54
|
||||||
|
55
|
||||||
|
56
|
||||||
|
57
|
||||||
|
58
|
||||||
|
59
|
||||||
|
60
|
||||||
|
61
|
||||||
|
62
|
||||||
|
63
|
||||||
|
64
|
||||||
|
65
|
||||||
|
66
|
||||||
|
67
|
||||||
|
68
|
||||||
|
69
|
||||||
|
70
|
||||||
|
71
|
||||||
|
72
|
||||||
|
73
|
||||||
|
74
|
||||||
|
75
|
||||||
|
76
|
||||||
|
77
|
||||||
|
78
|
||||||
|
79
|
||||||
|
80
|
||||||
|
81
|
||||||
|
82
|
||||||
|
83
|
||||||
|
84
|
||||||
|
85
|
||||||
|
86
|
||||||
|
87
|
||||||
|
88
|
||||||
|
89
|
||||||
|
90
|
||||||
|
91
|
||||||
|
92
|
||||||
|
93
|
||||||
|
94
|
||||||
|
95
|
||||||
|
96
|
||||||
|
97
|
||||||
|
98
|
||||||
|
99
|
16
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/main.ts
vendored
Normal file
16
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/main.ts
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import * as path from "http://localhost:4545/deno_std/path/mod.ts";
|
||||||
|
|
||||||
|
const currentDir = path.dirname(path.fromFileUrl(import.meta.url));
|
||||||
|
const url = path.toFileUrl(path.join(currentDir, "./mod.ts"));
|
||||||
|
const urls = [];
|
||||||
|
|
||||||
|
// this is hard to reproduce, but doing this will help
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
urls.push(url.toString() + "#" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = await Promise.all(urls.map((url) => import(url)));
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
result.outputValue();
|
||||||
|
}
|
7
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/mod.ts
vendored
Normal file
7
cli/tests/testdata/run/dynamic_import_concurrent_non_statically_analyzable/mod.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// sleep a bit so many concurrent tasks end up
|
||||||
|
// attempting to build the graph at the same time
|
||||||
|
import "http://localhost:4545/sleep/10";
|
||||||
|
|
||||||
|
export function outputValue() {
|
||||||
|
console.log(parseInt(new URL(import.meta.url).hash.slice(1), 10));
|
||||||
|
}
|
|
@ -293,8 +293,7 @@ impl CliMainWorker {
|
||||||
&mut self,
|
&mut self,
|
||||||
id: ModuleId,
|
id: ModuleId,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
if self.ps.npm_resolver.has_packages()
|
if self.ps.npm_resolver.has_packages() || self.ps.graph().has_node_specifier
|
||||||
|| self.ps.has_node_builtin_specifier()
|
|
||||||
{
|
{
|
||||||
self.initialize_main_module_for_node().await?;
|
self.initialize_main_module_for_node().await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ use std::sync::Mutex;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
use std::task::Context;
|
use std::task::Context;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
|
use std::time::Duration;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
@ -1058,6 +1059,20 @@ async fn main_server(
|
||||||
return Ok(file_resp);
|
return Ok(file_resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(suffix) = req.uri().path().strip_prefix("/deno_std/") {
|
||||||
|
let mut file_path = std_path();
|
||||||
|
file_path.push(suffix);
|
||||||
|
if let Ok(file) = tokio::fs::read(&file_path).await {
|
||||||
|
let file_resp = custom_headers(req.uri().path(), file);
|
||||||
|
return Ok(file_resp);
|
||||||
|
}
|
||||||
|
} else if let Some(suffix) = req.uri().path().strip_prefix("/sleep/") {
|
||||||
|
let duration = suffix.parse::<u64>().unwrap();
|
||||||
|
tokio::time::sleep(Duration::from_millis(duration)).await;
|
||||||
|
return Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", "application/typescript")
|
||||||
|
.body(Body::empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::builder()
|
Response::builder()
|
||||||
|
|
Loading…
Reference in a new issue