Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions cli/tsc/dts/node/globals.d.cts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ export {}; // Make this a module

// #region Fetch and friends
// Conditional type aliases, used at the end of this file.
// Will either be empty if lib.dom (or lib.webworker) is included, or the undici version otherwise.
type _Request = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").Request;
type _Response = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").Response;
type _FormData = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").FormData;
type _Headers = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").Headers;
type _MessageEvent = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").MessageEvent;
type _RequestInit = typeof globalThis extends { onmessage: any } ? {}
// Deno's web globals don't expose `onmessage` on `globalThis` outside of
// worker contexts, so detect Deno's web fetch surface via `Blob` instead.
type _HasWebFetchGlobals = typeof globalThis extends { Blob: any } ? true : false;
type _Request = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").Request;
type _Response = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").Response;
type _FormData = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").FormData;
type _Headers = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").Headers;
type _MessageEvent = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").MessageEvent;
type _RequestInit = _HasWebFetchGlobals extends true ? {}
: import("./undici/index.d.ts").RequestInit;
type _ResponseInit = typeof globalThis extends { onmessage: any } ? {}
type _ResponseInit = _HasWebFetchGlobals extends true ? {}
: import("./undici/index.d.ts").ResponseInit;
type _WebSocket = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").WebSocket;
type _EventSource = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").EventSource;
type _CloseEvent = typeof globalThis extends { onmessage: any } ? {} : import("./undici/index.d.ts").CloseEvent;
type _WebSocket = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").WebSocket;
type _EventSource = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").EventSource;
type _CloseEvent = _HasWebFetchGlobals extends true ? {} : import("./undici/index.d.ts").CloseEvent;
// #endregion Fetch and friends

// Conditional type definitions for webstorage interface, which conflicts with lib.dom otherwise.
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/lsp_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17545,6 +17545,30 @@ fn lsp_tsconfig_node_modules_dts_diagnostics() {
client.shutdown();
}

// Regression test for #33012: the @types/node `globals.d.cts` heuristic for
// detecting the web fetch surface used `onmessage`, which isn't on Deno's
// `globalThis` outside of workers, so LSP would fall back to undici types
// for `RequestInit` and reject otherwise-valid `new Request(url, init)`.
#[test(timeout = 300)]
fn lsp_request_init_global_matches_check() {
let context = TestContextBuilder::new().use_temp_cwd().build();
let temp_dir = context.temp_dir();
temp_dir.write("deno.json", json!({}).to_string());
let file = temp_dir.source_file(
"main.ts",
concat!(
"const init: Parameters<typeof fetch>[1] = {};\n",
"const _request = new Request(\"https://deno.com\", init);\n",
"console.log(_request);\n",
),
);
let mut client = context.new_lsp_command().build();
client.initialize_default();
let diagnostics = client.did_open_file(&file);
assert_eq!(diagnostics.all(), vec![]);
client.shutdown();
}

#[test(timeout = 300)]
fn lsp_tsconfig_root_dirs() {
let context = TestContextBuilder::new()
Expand Down
43 changes: 43 additions & 0 deletions tools/update_types_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
Node,
Project,
ScriptTarget,
SourceFile,
SyntaxKind,
VariableDeclaration,
} from "jsr:@ts-morph/ts-morph@27.0.2";
Expand Down Expand Up @@ -260,11 +261,53 @@ function modifySourceFiles() {
),
);
}

// Detect Deno's web fetch surface via `Blob` instead of `onmessage`.
// `onmessage` only exists on `globalThis` in worker contexts, so the
// upstream heuristic falls back to undici's types in regular Deno code
// and breaks LSP type checks against `RequestInit` etc. (denoland/deno#33012).
if (sourceFile.getBaseName() === "globals.d.cts") {
rewriteFetchGlobalsDiscriminator(sourceFile);
}
}

project.saveSync();
}

function rewriteFetchGlobalsDiscriminator(sourceFile: SourceFile) {
const startMarker = "// #region Fetch and friends\n";
const endMarker = "// #endregion Fetch and friends\n";
const text = sourceFile.getFullText();
const start = text.indexOf(startMarker);
const end = text.indexOf(endMarker);
if (start === -1 || end === -1) {
return;
}
const blockStart = start + startMarker.length;
const blockBody = text.slice(blockStart, end);
if (!blockBody.includes("{ onmessage: any }")) {
return;
}
const rewritten = blockBody
.replace(
"// Conditional type aliases, used at the end of this file.\n" +
"// Will either be empty if lib.dom (or lib.webworker) is included, " +
"or the undici version otherwise.\n",
"// Conditional type aliases, used at the end of this file.\n" +
"// Deno's web globals don't expose `onmessage` on `globalThis` " +
"outside of\n" +
"// worker contexts, so detect Deno's web fetch surface via `Blob` " +
"instead.\n" +
"type _HasWebFetchGlobals = typeof globalThis extends { Blob: any } " +
"? true : false;\n",
)
.replaceAll(
"typeof globalThis extends { onmessage: any }",
"_HasWebFetchGlobals extends true",
);
sourceFile.replaceText([blockStart, end], rewritten);
}

function handleInterface(decl: InterfaceDeclaration) {
switch (decl.getName()) {
case "ImportMeta": {
Expand Down
Loading