mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 21:50:00 -05:00
perf(lsp): use a stub module in tsc for failed resolutions (#23313)
This commit is contained in:
parent
5758470ee4
commit
f1ea8ca358
4 changed files with 91 additions and 41 deletions
|
@ -1112,19 +1112,29 @@ impl Default for LspTsConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LspTsConfig {
|
impl LspTsConfig {
|
||||||
pub fn new(config_file: Option<&ConfigFile>) -> Self {
|
pub fn new(
|
||||||
|
config_file: Option<&ConfigFile>,
|
||||||
|
import_map: Option<&ImportMap>,
|
||||||
|
) -> Self {
|
||||||
let mut ts_config = Self::default();
|
let mut ts_config = Self::default();
|
||||||
if let Some(config_file) = config_file {
|
match ts_config.inner.merge_tsconfig_from_config_file(config_file) {
|
||||||
match config_file.to_compiler_options() {
|
Ok(Some(ignored_options)) => lsp_warn!("{}", ignored_options),
|
||||||
Ok((value, maybe_ignored_options)) => {
|
Err(err) => lsp_warn!("{}", err),
|
||||||
ts_config.inner.merge(&value);
|
_ => {}
|
||||||
if let Some(ignored_options) = maybe_ignored_options {
|
|
||||||
lsp_warn!("{}", ignored_options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => lsp_warn!("{}", err),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let mut maybe_map_jsx_import_source = || {
|
||||||
|
let import_map = import_map?;
|
||||||
|
let referrer = &config_file?.specifier;
|
||||||
|
let compiler_options = ts_config.inner.0.as_object_mut()?;
|
||||||
|
let jsx_import_source =
|
||||||
|
compiler_options.get("jsxImportSource")?.as_str()?;
|
||||||
|
let jsx_import_source =
|
||||||
|
import_map.resolve(jsx_import_source, referrer).ok()?;
|
||||||
|
compiler_options
|
||||||
|
.insert("jsxImportSource".to_string(), json!(jsx_import_source));
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
maybe_map_jsx_import_source();
|
||||||
ts_config
|
ts_config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1267,7 +1277,6 @@ impl ConfigData {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let lint_rules =
|
let lint_rules =
|
||||||
get_configured_rules(lint_options.rules.clone(), config_file.as_ref());
|
get_configured_rules(lint_options.rules.clone(), config_file.as_ref());
|
||||||
let ts_config = LspTsConfig::new(config_file.as_ref());
|
|
||||||
let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path());
|
let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path());
|
||||||
|
|
||||||
// Load lockfile
|
// Load lockfile
|
||||||
|
@ -1436,6 +1445,7 @@ impl ConfigData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let ts_config = LspTsConfig::new(config_file.as_ref(), import_map.as_ref());
|
||||||
|
|
||||||
ConfigData {
|
ConfigData {
|
||||||
config_file: config_file.map(Arc::new),
|
config_file: config_file.map(Arc::new),
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::lsp::documents::Documents;
|
||||||
use crate::lsp::logging::lsp_warn;
|
use crate::lsp::logging::lsp_warn;
|
||||||
use crate::tsc;
|
use crate::tsc;
|
||||||
use crate::tsc::ResolveArgs;
|
use crate::tsc::ResolveArgs;
|
||||||
|
use crate::tsc::MISSING_DEPENDENCY_SPECIFIER;
|
||||||
use crate::util::path::relative_specifier;
|
use crate::util::path::relative_specifier;
|
||||||
use crate::util::path::specifier_to_file_path;
|
use crate::util::path::specifier_to_file_path;
|
||||||
use crate::util::path::to_percent_decoded_str;
|
use crate::util::path::to_percent_decoded_str;
|
||||||
|
@ -4008,7 +4009,7 @@ fn op_load<'s>(
|
||||||
.mark_with_args("tsc.op.op_load", specifier);
|
.mark_with_args("tsc.op.op_load", specifier);
|
||||||
let specifier = state.specifier_map.normalize(specifier)?;
|
let specifier = state.specifier_map.normalize(specifier)?;
|
||||||
let maybe_load_response =
|
let maybe_load_response =
|
||||||
if specifier.as_str() == "internal:///missing_dependency.d.ts" {
|
if specifier.as_str() == MISSING_DEPENDENCY_SPECIFIER {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let asset_or_document = state.get_asset_or_document(&specifier);
|
let asset_or_document = state.get_asset_or_document(&specifier);
|
||||||
|
@ -4026,11 +4027,11 @@ fn op_load<'s>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
fn op_resolve<'s>(
|
#[serde]
|
||||||
scope: &'s mut v8::HandleScope,
|
fn op_resolve(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
#[serde] args: ResolveArgs,
|
#[serde] args: ResolveArgs,
|
||||||
) -> Result<v8::Local<'s, v8::Value>, AnyError> {
|
) -> Result<Vec<Option<(String, String)>>, AnyError> {
|
||||||
let state = state.borrow_mut::<State>();
|
let state = state.borrow_mut::<State>();
|
||||||
let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args);
|
let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args);
|
||||||
let referrer = state.specifier_map.normalize(&args.base)?;
|
let referrer = state.specifier_map.normalize(&args.base)?;
|
||||||
|
@ -4043,13 +4044,17 @@ fn op_resolve<'s>(
|
||||||
);
|
);
|
||||||
resolved
|
resolved
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|o| {
|
// Resolved `node:` specifier means the user doesn't have @types/node,
|
||||||
o.map(|(s, mt)| {
|
// resolve to stub.
|
||||||
(
|
.map(|o| match o.filter(|(s, _)| s.scheme() != "node") {
|
||||||
state.specifier_map.denormalize(&s),
|
Some((s, mt)) => Some((
|
||||||
mt.as_ts_extension().to_string(),
|
state.specifier_map.denormalize(&s),
|
||||||
)
|
mt.as_ts_extension().to_string(),
|
||||||
})
|
)),
|
||||||
|
None => Some((
|
||||||
|
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||||
|
MediaType::Dts.as_ts_extension().to_string(),
|
||||||
|
)),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -4062,9 +4067,8 @@ fn op_resolve<'s>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = serde_v8::to_v8(scope, specifiers)?;
|
|
||||||
state.performance.measure(mark);
|
state.performance.measure(mark);
|
||||||
Ok(response)
|
Ok(specifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
|
@ -4750,6 +4754,14 @@ mod tests {
|
||||||
(ts_server, snapshot, cache)
|
(ts_server, snapshot, cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_op_state(state_snapshot: Arc<StateSnapshot>) -> OpState {
|
||||||
|
let state =
|
||||||
|
State::new(state_snapshot, Default::default(), Default::default());
|
||||||
|
let mut op_state = OpState::new(None);
|
||||||
|
op_state.put(state);
|
||||||
|
op_state
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_replace_links() {
|
fn test_replace_links() {
|
||||||
let actual = replace_links(r"test {@link http://deno.land/x/mod.ts} test");
|
let actual = replace_links(r"test {@link http://deno.land/x/mod.ts} test");
|
||||||
|
@ -5552,4 +5564,36 @@ mod tests {
|
||||||
Some(false)
|
Some(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolve_unknown_dependency_to_stub_module() {
|
||||||
|
let temp_dir = TempDir::new();
|
||||||
|
let (_, snapshot, _) = setup(
|
||||||
|
&temp_dir,
|
||||||
|
json!({
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"lib": ["deno.ns", "deno.window"],
|
||||||
|
"noEmit": true,
|
||||||
|
}),
|
||||||
|
&[("file:///a.ts", "", 1, LanguageId::TypeScript)],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut state = setup_op_state(snapshot);
|
||||||
|
let resolved = op_resolve::call(
|
||||||
|
&mut state,
|
||||||
|
ResolveArgs {
|
||||||
|
base: "file:///a.ts".to_string(),
|
||||||
|
specifiers: vec!["./b.ts".to_string()],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
vec![Some((
|
||||||
|
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||||
|
MediaType::Dts.as_ts_extension().to_string()
|
||||||
|
))]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -725,10 +725,6 @@ delete Object.prototype.__proto__;
|
||||||
if (item) {
|
if (item) {
|
||||||
isCjsCache.add(item);
|
isCjsCache.add(item);
|
||||||
const [resolvedFileName, extension] = item;
|
const [resolvedFileName, extension] = item;
|
||||||
if (resolvedFileName.startsWith("node:")) {
|
|
||||||
// probably means the user doesn't have @types/node, so resolve to undefined
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
resolvedFileName,
|
resolvedFileName,
|
||||||
extension,
|
extension,
|
||||||
|
|
|
@ -30,7 +30,6 @@ use deno_graph::GraphKind;
|
||||||
use deno_graph::Module;
|
use deno_graph::Module;
|
||||||
use deno_graph::ModuleGraph;
|
use deno_graph::ModuleGraph;
|
||||||
use deno_graph::ResolutionResolved;
|
use deno_graph::ResolutionResolved;
|
||||||
use deno_runtime::deno_node;
|
|
||||||
use deno_runtime::deno_node::NodeResolution;
|
use deno_runtime::deno_node::NodeResolution;
|
||||||
use deno_runtime::deno_node::NodeResolutionMode;
|
use deno_runtime::deno_node::NodeResolutionMode;
|
||||||
use deno_runtime::deno_node::NodeResolver;
|
use deno_runtime::deno_node::NodeResolver;
|
||||||
|
@ -444,6 +443,9 @@ pub fn as_ts_script_kind(media_type: MediaType) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const MISSING_DEPENDENCY_SPECIFIER: &str =
|
||||||
|
"internal:///missing_dependency.d.ts";
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct LoadResponse {
|
struct LoadResponse {
|
||||||
|
@ -471,7 +473,7 @@ fn op_load(
|
||||||
state.maybe_tsbuildinfo.as_deref().map(Cow::Borrowed)
|
state.maybe_tsbuildinfo.as_deref().map(Cow::Borrowed)
|
||||||
// in certain situations we return a "blank" module to tsc and we need to
|
// in certain situations we return a "blank" module to tsc and we need to
|
||||||
// handle the request for that module here.
|
// handle the request for that module here.
|
||||||
} else if load_specifier == "internal:///missing_dependency.d.ts" {
|
} else if load_specifier == MISSING_DEPENDENCY_SPECIFIER {
|
||||||
None
|
None
|
||||||
} else if let Some(name) = load_specifier.strip_prefix("asset:///") {
|
} else if let Some(name) = load_specifier.strip_prefix("asset:///") {
|
||||||
let maybe_source = get_lazily_loaded_asset(name);
|
let maybe_source = get_lazily_loaded_asset(name);
|
||||||
|
@ -575,14 +577,12 @@ fn op_resolve(
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
for specifier in args.specifiers {
|
for specifier in args.specifiers {
|
||||||
if let Some(module_name) = specifier.strip_prefix("node:") {
|
if specifier.starts_with("node:") {
|
||||||
if deno_node::is_builtin_node_module(module_name) {
|
resolved.push((
|
||||||
// return itself for node: specifiers because during type checking
|
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||||
// we resolve to the ambient modules in the @types/node package
|
MediaType::Dts.to_string(),
|
||||||
// rather than deno_std/node
|
));
|
||||||
resolved.push((specifier, MediaType::Dts.to_string()));
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if specifier.starts_with("asset:///") {
|
if specifier.starts_with("asset:///") {
|
||||||
|
@ -632,7 +632,7 @@ fn op_resolve(
|
||||||
(specifier_str, media_type.as_ts_extension().into())
|
(specifier_str, media_type.as_ts_extension().into())
|
||||||
}
|
}
|
||||||
None => (
|
None => (
|
||||||
"internal:///missing_dependency.d.ts".to_string(),
|
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||||
".d.ts".to_string(),
|
".d.ts".to_string(),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -1159,7 +1159,7 @@ mod tests {
|
||||||
.expect("should have not errored");
|
.expect("should have not errored");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual,
|
actual,
|
||||||
vec![("internal:///missing_dependency.d.ts".into(), ".d.ts".into())]
|
vec![(MISSING_DEPENDENCY_SPECIFIER.into(), ".d.ts".into())]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue