feat(unstable): typescript-go integration for deno check (#30920)

Integrated only with deno check (and test, `run --check`, etc)
currently. All spec tests for deno check pass except for 3, which i've
disabled

---------

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
Co-authored-by: Divy Srivastava <me@littledivy.com>
This commit is contained in:
Nathan Whitaker 2025-10-20 09:59:54 -07:00 committed by GitHub
parent 6e4f7677ea
commit b252cc78c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
133 changed files with 4727 additions and 1451 deletions

View file

@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify";
// Bump this number when you want to purge the cache. // Bump this number when you want to purge the cache.
// Note: the tools/release/01_bump_crate_versions.ts script will update this version // Note: the tools/release/01_bump_crate_versions.ts script will update this version
// automatically via regex, so ensure that this line maintains this format. // automatically via regex, so ensure that this line maintains this format.
const cacheVersion = 76; const cacheVersion = 77;
const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86Runner = "ubuntu-24.04";
const ubuntuX86XlRunner = "ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04"; const ubuntuX86XlRunner = "ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04";

View file

@ -189,8 +189,8 @@ jobs:
~/.cargo/registry/index ~/.cargo/registry/index
~/.cargo/registry/cache ~/.cargo/registry/cache
~/.cargo/git/db ~/.cargo/git/db
key: '76-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' key: '77-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '76-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' restore-keys: '77-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-'
if: '!(matrix.skip)' if: '!(matrix.skip)'
- uses: dsherret/rust-toolchain-file@v1 - uses: dsherret/rust-toolchain-file@v1
if: '!(matrix.skip)' if: '!(matrix.skip)'
@ -392,7 +392,7 @@ jobs:
!./target/*/*.zip !./target/*/*.zip
!./target/*/*.tar.gz !./target/*/*.tar.gz
key: never_saved key: never_saved
restore-keys: '76-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' restore-keys: '77-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
- name: Apply and update mtime cache - name: Apply and update mtime cache
if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))'
uses: ./.github/mtime_cache uses: ./.github/mtime_cache
@ -773,7 +773,7 @@ jobs:
!./target/*/gn_root !./target/*/gn_root
!./target/*/*.zip !./target/*/*.zip
!./target/*/*.tar.gz !./target/*/*.tar.gz
key: '76-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' key: '77-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
libs: libs:
name: build libs name: build libs
needs: needs:

30
Cargo.lock generated
View file

@ -1647,6 +1647,7 @@ dependencies = [
"deno_telemetry", "deno_telemetry",
"deno_terminal 0.2.2", "deno_terminal 0.2.2",
"deno_tower_lsp", "deno_tower_lsp",
"deno_typescript_go_client_rust",
"dhat", "dhat",
"dissimilar", "dissimilar",
"dotenvy", "dotenvy",
@ -3073,6 +3074,19 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "deno_typescript_go_client_rust"
version = "0.1.0"
dependencies = [
"indexmap 2.9.0",
"log",
"rmp",
"serde",
"serde_json",
"serde_repr",
"thiserror 2.0.12",
]
[[package]] [[package]]
name = "deno_unsync" name = "deno_unsync"
version = "0.4.4" version = "0.4.4"
@ -5903,9 +5917,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -7714,6 +7728,17 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "rmp"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]] [[package]]
name = "ron" name = "ron"
version = "0.8.1" version = "0.8.1"
@ -9369,6 +9394,7 @@ dependencies = [
"url", "url",
"win32job", "win32job",
"winapi", "winapi",
"zip",
] ]
[[package]] [[package]]

View file

@ -42,6 +42,7 @@ members = [
"libs/npm_installer", "libs/npm_installer",
"libs/package_json", "libs/package_json",
"libs/resolver", "libs/resolver",
"libs/typescript_go_client",
"runtime", "runtime",
"runtime/features", "runtime/features",
"runtime/permissions", "runtime/permissions",
@ -131,6 +132,7 @@ deno_resolver = { version = "0.50.0", path = "./libs/resolver" }
deno_runtime = { version = "0.227.0", path = "./runtime" } deno_runtime = { version = "0.227.0", path = "./runtime" }
deno_snapshots = { version = "0.34.0", path = "./cli/snapshot" } deno_snapshots = { version = "0.34.0", path = "./cli/snapshot" }
deno_subprocess_windows = { path = "./runtime/subprocess_windows", version = "0.14.0" } deno_subprocess_windows = { path = "./runtime/subprocess_windows", version = "0.14.0" }
deno_typescript_go_client_rust = { version = "0.1.0", path = "./libs/typescript_go_client" }
napi_sym = { version = "0.149.0", path = "./ext/napi/sym" } napi_sym = { version = "0.149.0", path = "./ext/napi/sym" }
node_resolver = { version = "0.57.0", path = "./libs/node_resolver" } node_resolver = { version = "0.57.0", path = "./libs/node_resolver" }
test_util = { package = "test_server", path = "./tests/util/server" } test_util = { package = "test_server", path = "./tests/util/server" }

View file

@ -92,6 +92,7 @@ deno_snapshots.workspace = true
deno_task_shell.workspace = true deno_task_shell.workspace = true
deno_telemetry.workspace = true deno_telemetry.workspace = true
deno_terminal.workspace = true deno_terminal.workspace = true
deno_typescript_go_client_rust.workspace = true
eszip.workspace = true eszip.workspace = true
libsui.workspace = true libsui.workspace = true
node_resolver = { workspace = true, features = ["graph", "sync"] } node_resolver = { workspace = true, features = ["graph", "sync"] }

View file

@ -6628,6 +6628,7 @@ fn unstable_args_parse(
matches.get_flag("unstable-sloppy-imports"); matches.get_flag("unstable-sloppy-imports");
flags.unstable_config.npm_lazy_caching = flags.unstable_config.npm_lazy_caching =
matches.get_flag("unstable-npm-lazy-caching"); matches.get_flag("unstable-npm-lazy-caching");
flags.unstable_config.tsgo = matches.get_flag("unstable-tsgo");
if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) { if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) {
for feature in deno_runtime::UNSTABLE_FEATURES { for feature in deno_runtime::UNSTABLE_FEATURES {

View file

@ -1210,6 +1210,10 @@ impl CliOptions {
self.flags.type_check_mode self.flags.type_check_mode
} }
pub fn unstable_tsgo(&self) -> bool {
self.flags.unstable_config.tsgo || self.workspace().has_unstable("tsgo")
}
pub fn unsafely_ignore_certificate_errors(&self) -> &Option<Vec<String>> { pub fn unsafely_ignore_certificate_errors(&self) -> &Option<Vec<String>> {
&self.flags.unsafely_ignore_certificate_errors &self.flags.unsafely_ignore_certificate_errors
} }

View file

@ -524,6 +524,20 @@ impl CliFactory {
self.resolver_factory()?.in_npm_package_checker() self.resolver_factory()?.in_npm_package_checker()
} }
pub async fn tsgo_path(&self) -> Result<Option<&PathBuf>, AnyError> {
if self.cli_options()?.unstable_tsgo() {
Ok(Some(
crate::tsc::ensure_tsgo(
self.deno_dir()?,
self.http_client_provider().clone(),
)
.await?,
))
} else {
Ok(None)
}
}
pub fn jsr_version_resolver( pub fn jsr_version_resolver(
&self, &self,
) -> Result<&Arc<JsrVersionResolver>, AnyError> { ) -> Result<&Arc<JsrVersionResolver>, AnyError> {
@ -757,6 +771,7 @@ impl CliFactory {
self.module_graph_builder().await?.clone(), self.module_graph_builder().await?.clone(),
self.node_resolver().await?.clone(), self.node_resolver().await?.clone(),
self.npm_resolver().await?.clone(), self.npm_resolver().await?.clone(),
self.resolver_factory()?.pkg_json_resolver().clone(),
self.sys(), self.sys(),
self.compiler_options_resolver()?.clone(), self.compiler_options_resolver()?.clone(),
if cli_options.code_cache_enabled() { if cli_options.code_cache_enabled() {
@ -764,6 +779,7 @@ impl CliFactory {
} else { } else {
None None
}, },
self.tsgo_path().await?.cloned(),
))) )))
} }
.boxed_local(), .boxed_local(),

View file

@ -211,6 +211,7 @@ pub struct UnstableConfig {
pub raw_imports: bool, pub raw_imports: bool,
pub sloppy_imports: bool, pub sloppy_imports: bool,
pub npm_lazy_caching: bool, pub npm_lazy_caching: bool,
pub tsgo: bool,
pub features: Vec<String>, // --unstabe-kv --unstable-cron pub features: Vec<String>, // --unstabe-kv --unstable-cron
} }
@ -234,6 +235,7 @@ impl UnstableConfig {
&mut self.npm_lazy_caching, &mut self.npm_lazy_caching,
UNSTABLE_ENV_VAR_NAMES.npm_lazy_caching, UNSTABLE_ENV_VAR_NAMES.npm_lazy_caching,
); );
maybe_set(&mut self.tsgo, UNSTABLE_ENV_VAR_NAMES.tsgo);
maybe_set(&mut self.raw_imports, UNSTABLE_ENV_VAR_NAMES.raw_imports); maybe_set(&mut self.raw_imports, UNSTABLE_ENV_VAR_NAMES.raw_imports);
maybe_set( maybe_set(
&mut self.sloppy_imports, &mut self.sloppy_imports,

View file

@ -5269,6 +5269,12 @@ fn op_project_version(state: &mut OpState) -> usize {
r r
} }
#[op2]
#[serde]
fn op_tsc_constants() -> crate::tsc::TscConstants {
crate::tsc::TscConstants::new()
}
struct TscRuntime { struct TscRuntime {
js_runtime: JsRuntime, js_runtime: JsRuntime,
server_main_loop_fn_global: v8::Global<v8::Function>, server_main_loop_fn_global: v8::Global<v8::Function>,
@ -5393,6 +5399,7 @@ deno_core::extension!(deno_tsc,
op_is_cancelled, op_is_cancelled,
op_is_node_file, op_is_node_file,
op_load, op_load,
op_tsc_constants,
op_release, op_release,
op_resolve, op_resolve,
op_respond, op_respond,

View file

@ -788,6 +788,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(), npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(),
raw_imports: self.cli_options.unstable_raw_imports(), raw_imports: self.cli_options.unstable_raw_imports(),
sloppy_imports: self.cli_options.unstable_sloppy_imports(), sloppy_imports: self.cli_options.unstable_sloppy_imports(),
tsgo: self.cli_options.unstable_tsgo(),
}, },
otel_config: self.cli_options.otel_config(), otel_config: self.cli_options.otel_config(),
vfs_case_sensitivity: vfs.case_sensitivity, vfs_case_sensitivity: vfs.case_sensitivity,

View file

@ -303,51 +303,12 @@ const CACHE_URL_PREFIX = "cache:///";
/** Diagnostics that are intentionally ignored when compiling TypeScript in /** Diagnostics that are intentionally ignored when compiling TypeScript in
* Deno, as they provide misleading or incorrect information. */ * Deno, as they provide misleading or incorrect information. */
const IGNORED_DIAGNOSTICS = [ const TSC_CONSTANTS = ops.op_tsc_constants();
// TS1452: 'resolution-mode' assertions are only supported when `moduleResolution` is `node16` or `nodenext`. const IGNORED_DIAGNOSTICS = TSC_CONSTANTS.ignoredDiagnosticCodes;
// We specify the resolution mode to be CommonJS for some npm files and this const TYPES_NODE_IGNORABLE_NAMES = new Set(
// diagnostic gets generated even though we're using custom module resolution. TSC_CONSTANTS.typesNodeIgnorableNames,
1452, );
// Module '...' cannot be imported using this construct. The specifier only resolves to an const NODE_ONLY_GLOBALS = new Set(TSC_CONSTANTS.nodeOnlyGlobals);
// ES module, which cannot be imported with 'require'.
1471,
// TS1479: The current file is a CommonJS module whose imports will produce 'require' calls;
// however, the referenced file is an ECMAScript module and cannot be imported with 'require'.
1479,
// TS1543: Importing a JSON file into an ECMAScript module requires a 'type: \"json\"' import
// attribute when 'module' is set to 'NodeNext'.
1543,
// TS2306: File '.../index.d.ts' is not a module.
// We get this for `x-typescript-types` declaration files which don't export
// anything. We prefer to treat these as modules with no exports.
2306,
// TS2688: Cannot find type definition file for '...'.
// We ignore because type definition files can end with '.ts'.
2688,
// TS2792: Cannot find module. Did you mean to set the 'moduleResolution'
// option to 'node', or to add aliases to the 'paths' option?
2792,
// TS2307: Cannot find module '{0}' or its corresponding type declarations.
2307,
// Relative import errors to add an extension
2834,
2835,
// TS5009: Cannot find the common subdirectory path for the input files.
5009,
// TS5055: Cannot write file
// 'http://localhost:4545/subdir/mt_application_x_javascript.j4.js'
// because it would overwrite input file.
5055,
// TypeScript is overly opinionated that only CommonJS modules kinds can
// support JSON imports. Allegedly this was fixed in
// Microsoft/TypeScript#26825 but that doesn't seem to be working here,
// so we will ignore complaints about this compiler setting.
5070,
// TS7016: Could not find a declaration file for module '...'. '...'
// implicitly has an 'any' type. This is due to `allowJs` being off by
// default but importing of a JavaScript module.
7016,
];
// todo(dsherret): can we remove this and just use ts.OperationCanceledException? // todo(dsherret): can we remove this and just use ts.OperationCanceledException?
/** Error thrown on cancellation. */ /** Error thrown on cancellation. */
@ -851,94 +812,14 @@ export function filterMapDiagnostic(diagnostic) {
// list of globals that should be kept in Node's globalThis // list of globals that should be kept in Node's globalThis
ts.deno.setNodeOnlyGlobalNames( ts.deno.setNodeOnlyGlobalNames(
new Set([ NODE_ONLY_GLOBALS,
"__dirname",
"__filename",
'"buffer"',
"Buffer",
"BufferConstructor",
"BufferEncoding",
"clearImmediate",
"clearInterval",
"clearTimeout",
"console",
"Console",
"crypto",
"ErrorConstructor",
"gc",
"Global",
"localStorage",
"queueMicrotask",
"RequestInit",
"ResponseInit",
"sessionStorage",
"setImmediate",
"setInterval",
"setTimeout",
]),
); );
// List of globals in @types/node that collide with Deno's types. // List of globals in @types/node that collide with Deno's types.
// When the `@types/node` package attempts to assign to these types // When the `@types/node` package attempts to assign to these types
// if the type is already in the global symbol table, then assignment // if the type is already in the global symbol table, then assignment
// will be a no-op, but if the global type does not exist then the package can // will be a no-op, but if the global type does not exist then the package can
// create the global. // create the global.
const setTypesNodeIgnorableNames = new Set([ const setTypesNodeIgnorableNames = TYPES_NODE_IGNORABLE_NAMES;
"AbortController",
"AbortSignal",
"AsyncIteratorObject",
"atob",
"Blob",
"BroadcastChannel",
"btoa",
"ByteLengthQueuingStrategy",
"CloseEvent",
"CompressionStream",
"CountQueuingStrategy",
"CustomEvent",
"DecompressionStream",
"Disposable",
"DOMException",
"Event",
"EventSource",
"EventTarget",
"fetch",
"File",
"Float32Array",
"Float64Array",
"FormData",
"Headers",
"ImportMeta",
"MessageChannel",
"MessageEvent",
"MessagePort",
"performance",
"PerformanceEntry",
"PerformanceMark",
"PerformanceMeasure",
"ReadableByteStreamController",
"ReadableStream",
"ReadableStreamBYOBReader",
"ReadableStreamBYOBRequest",
"ReadableStreamDefaultController",
"ReadableStreamDefaultReader",
"ReadonlyArray",
"Request",
"Response",
"Storage",
"TextDecoder",
"TextDecoderStream",
"TextEncoder",
"TextEncoderStream",
"TransformStream",
"TransformStreamDefaultController",
"URL",
"URLPattern",
"URLSearchParams",
"WebSocket",
"WritableStream",
"WritableStreamDefaultController",
"WritableStreamDefaultWriter",
]);
ts.deno.setTypesNodeIgnorableNames(setTypesNodeIgnorableNames); ts.deno.setTypesNodeIgnorableNames(setTypesNodeIgnorableNames);
/** /**

View file

@ -81,10 +81,10 @@ impl From<i64> for DiagnosticCategory {
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DiagnosticMessageChain { pub struct DiagnosticMessageChain {
message_text: String, pub message_text: String,
category: DiagnosticCategory, pub category: DiagnosticCategory,
code: i64, pub code: i64,
next: Option<Vec<DiagnosticMessageChain>>, pub next: Option<Vec<DiagnosticMessageChain>>,
} }
impl DiagnosticMessageChain { impl DiagnosticMessageChain {
@ -358,6 +358,12 @@ impl fmt::Display for Diagnostic {
#[class(generic)] #[class(generic)]
pub struct Diagnostics(Vec<Diagnostic>); pub struct Diagnostics(Vec<Diagnostic>);
impl From<Vec<Diagnostic>> for Diagnostics {
fn from(diagnostics: Vec<Diagnostic>) -> Self {
Diagnostics(diagnostics)
}
}
impl Diagnostics { impl Diagnostics {
#[cfg(test)] #[cfg(test)]
pub fn new(diagnostics: Vec<Diagnostic>) -> Self { pub fn new(diagnostics: Vec<Diagnostic>) -> Self {

671
cli/tsc/go.rs Normal file
View file

@ -0,0 +1,671 @@
// Copyright 2018-2025 the Deno authors. MIT license.
mod setup;
mod tsgo_version;
use std::cell::RefCell;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_config::deno_json::CompilerOptions;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_graph::ModuleGraph;
use deno_typescript_go_client_rust::CallbackHandler;
use deno_typescript_go_client_rust::SyncRpcChannel;
use deno_typescript_go_client_rust::types::GetImpliedNodeFormatForFilePayload;
use deno_typescript_go_client_rust::types::Project;
use deno_typescript_go_client_rust::types::ResolveModuleNamePayload;
use deno_typescript_go_client_rust::types::ResolveTypeReferenceDirectivePayload;
pub use setup::DownloadError;
pub use setup::ensure_tsgo;
use super::Request;
use super::Response;
use crate::args::TypeCheckMode;
macro_rules! jsons {
($($arg:tt)*) => {
serde_json::to_string(&json!($($arg)*))
};
}
fn deser<T: serde::de::DeserializeOwned>(
payload: impl AsRef<str>,
) -> Result<T, serde_json::Error> {
serde_json::from_str::<T>(payload.as_ref())
}
// the way tsgo currently works, it really wants an actual tsconfig.json file.
// it also doesn't let you just pass in root file names. instead of making more changes in tsgo,
// work around both by making a fake tsconfig.json file with the `"files"` field set to the root file names.
// it's "synthetic" because it's not actually on disk, we pass a fake path to load it from memory.
fn synthetic_config(
config: &CompilerOptions,
root_names: &[String],
type_check_mode: TypeCheckMode,
) -> Result<String, serde_json::Error> {
let mut config = serde_json::to_value(config)?;
let obj = config.as_object_mut().unwrap();
obj.insert("allowImportingTsExtensions".to_string(), json!(true));
if type_check_mode != TypeCheckMode::All {
obj.insert("skipDefaultLibCheck".to_string(), json!(true));
}
if let Some(jsx) = obj.get("jsx")
&& jsx.as_str() == Some("precompile")
{
obj.insert("jsx".to_string(), json!("react-jsx"));
}
let config = serde_json::to_string(&json!({
"compilerOptions": config,
"files": root_names,
}))?;
log::debug!("synthetic config: {}", config);
Ok(config)
}
pub fn exec_request(
request: Request,
root_names: Vec<String>,
root_map: HashMap<String, ModuleSpecifier>,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
tsgo_path: &Path,
) -> Result<Response, super::ExecError> {
exec_request_inner(
request,
root_names,
root_map,
remapped_specifiers,
tsgo_path,
)
.map_err(super::ExecError::Go)
}
fn exec_request_inner(
request: Request,
root_names: Vec<String>,
root_map: HashMap<String, ModuleSpecifier>,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
tsgo_path: &Path,
) -> Result<Response, ExecError> {
let handler = Handler::new(
"/virtual/tsconfig.json".to_string(),
synthetic_config(request.config.as_ref(), &root_names, request.check_mode)?,
remapped_specifiers,
root_map,
request.initial_cwd,
request.graph.clone(),
request.maybe_npm,
);
let callbacks = handler.supported_callbacks();
let bin_path = tsgo_path;
let mut channel = SyncRpcChannel::new(bin_path, vec!["--api"], handler)?;
channel.request_sync(
"configure",
jsons!({
"callbacks": callbacks.iter().collect::<Vec<_>>(),
"logFile": "",
"forkContextInfo": {
"typesNodeIgnorableNames": super::TYPES_NODE_IGNORABLE_NAMES,
"nodeOnlyGlobalNames": super::NODE_ONLY_GLOBALS,
},
})?,
)?;
let project = channel.request_sync(
"loadProject",
jsons!({
"configFileName": "/virtual/tsconfig.json",
})?,
)?;
let project = deser::<Project>(project)?;
let file_names = if request.check_mode != TypeCheckMode::All {
root_names
} else {
Vec::new()
};
let diagnostics = channel.request_sync(
"getDiagnostics",
jsons!({
"project": &project.id,
"fileNames": file_names,
})?,
)?;
let diagnostics = deser::<
Vec<deno_typescript_go_client_rust::types::Diagnostic>,
>(diagnostics)?;
Ok(Response {
diagnostics: convert_diagnostics(diagnostics),
maybe_tsbuildinfo: None,
ambient_modules: vec![],
stats: super::Stats::default(),
})
}
fn diagnostic_category(category: &str) -> super::DiagnosticCategory {
match category {
"error" => super::DiagnosticCategory::Error,
"warning" => super::DiagnosticCategory::Warning,
"message" => super::DiagnosticCategory::Message,
"suggestion" => super::DiagnosticCategory::Suggestion,
_ => unreachable!("unexpected diagnostic category: {category}"),
}
}
fn maybe_rewrite_message(message: String, code: u64) -> String {
if code == 2304 && message.starts_with("Cannot find name 'Deno'") {
r#"Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />"#.to_string()
} else if code == 2581 {
r#"Cannot find name '$'. Did you mean to import jQuery? Try adding `import $ from "npm:jquery";`."#.to_string()
} else if code == 2580 {
let regex = lazy_regex::regex!(r#"Cannot find name '([^']+)'"#);
let captures = regex.captures(&message).unwrap();
let name = captures.get(1).unwrap().as_str();
format!("Cannot find name '{}'.", name)
} else if code == 1203 {
"Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead. This will start erroring in a future version of Deno 2 in order to align with TypeScript.".to_string()
} else if code == 2339 && message.contains("on type 'typeof Deno'") {
let regex = lazy_regex::regex!(
r#"Property '([^']+)' does not exist on type 'typeof Deno'"#
);
let captures = regex.captures(&message).unwrap();
let name = captures.get(1).unwrap().as_str();
format!(
"Property '{name}' does not exist on type 'typeof Deno'. 'Deno.{name}' is an unstable API. If not, try changing the 'lib' compiler option to include 'deno.unstable' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib=\"deno.unstable\" />",
)
} else {
message
}
}
fn maybe_remap_category(
code: u64,
category: super::DiagnosticCategory,
) -> super::DiagnosticCategory {
if code == 1203 {
super::DiagnosticCategory::Warning
} else {
category
}
}
fn convert_diagnostic(
diagnostic: deno_typescript_go_client_rust::types::Diagnostic,
_diagnostics: &[deno_typescript_go_client_rust::types::Diagnostic],
) -> super::Diagnostic {
let (start, end) = if diagnostic.start.line == 0
&& diagnostic.start.character == 0
&& diagnostic.end.line == 0
&& diagnostic.end.character == 0
{
(None, None)
} else {
(Some(diagnostic.start), Some(diagnostic.end))
};
super::Diagnostic {
category: maybe_remap_category(
diagnostic.code as u64,
diagnostic_category(diagnostic.category.as_str()),
),
code: diagnostic.code as u64,
start: start.map(|s| super::Position {
line: s.line,
character: s.character,
}),
end: end.map(|e| super::Position {
line: e.line,
character: e.character,
}),
original_source_start: None,
message_chain: None,
message_text: Some(maybe_rewrite_message(
diagnostic.message,
diagnostic.code as u64,
)),
file_name: Some(diagnostic.file_name),
missing_specifier: None,
other: Default::default(),
related_information: if diagnostic.related_information.is_empty() {
None
} else {
Some(
diagnostic
.related_information
.into_iter()
.map(|d| convert_diagnostic(d, _diagnostics))
.collect::<Vec<_>>(),
)
},
reports_deprecated: Some(diagnostic.reports_deprecated),
reports_unnecessary: Some(diagnostic.reports_unnecessary),
source: None,
source_line: Some(diagnostic.source_line),
}
}
fn should_ignore_diagnostic(diagnostic: &super::Diagnostic) -> bool {
super::IGNORED_DIAGNOSTIC_CODES.contains(&diagnostic.code)
}
fn convert_diagnostics(
diagnostics: Vec<deno_typescript_go_client_rust::types::Diagnostic>,
) -> super::Diagnostics {
super::diagnostics::Diagnostics::from(
diagnostics
.iter()
.map(|diagnostic| convert_diagnostic(diagnostic.clone(), &diagnostics))
.filter(|diagnostic| !should_ignore_diagnostic(diagnostic))
.collect::<Vec<_>>(),
)
}
struct Handler {
state: RefCell<HandlerState>,
}
impl Handler {
fn new(
config_path: String,
synthetic_config: String,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
root_map: HashMap<String, ModuleSpecifier>,
current_dir: PathBuf,
graph: Arc<ModuleGraph>,
maybe_npm: Option<super::RequestNpmState>,
) -> Self {
Self {
state: RefCell::new(HandlerState {
config_path,
synthetic_config,
remapped_specifiers,
root_map,
current_dir,
graph,
maybe_npm,
module_kind_map: HashMap::new(),
load_result_pending: HashMap::new(),
}),
}
}
}
fn get_package_json_scope_if_applicable(
state: &mut HandlerState,
payload: String,
) -> Result<String, deno_typescript_go_client_rust::Error> {
log::debug!("get_package_json_scope_if_applicable: {}", payload);
if let Some(maybe_npm) = state.maybe_npm.as_ref() {
let file_path = deser::<String>(&payload)?;
let file_path = if let Ok(specifier) = ModuleSpecifier::parse(&file_path) {
deno_path_util::url_to_file_path(&specifier).ok()
} else {
Some(PathBuf::from(file_path))
};
let Some(file_path) = file_path else {
return Ok(jsons!(None::<String>)?);
};
if let Some(package_json) = maybe_npm
.package_json_resolver
.get_closest_package_jsons(&file_path)
.next()
.and_then(|r| r.ok())
{
let package_directory = package_json.path.parent();
let contents = serde_json::to_string(&package_json).ok();
if let Some(contents) = contents {
return Ok(jsons!({
"packageDirectory": package_directory,
"directoryExists": true,
"contents": contents,
})?);
}
}
}
Ok(jsons!(None::<String>)?)
}
struct HandlerState {
config_path: String,
synthetic_config: String,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
root_map: HashMap<String, ModuleSpecifier>,
current_dir: PathBuf,
graph: Arc<ModuleGraph>,
maybe_npm: Option<super::RequestNpmState>,
module_kind_map:
HashMap<String, deno_typescript_go_client_rust::types::ResolutionMode>,
load_result_pending: HashMap<String, LoadResult>,
}
impl deno_typescript_go_client_rust::CallbackHandler for Handler {
fn supported_callbacks(&self) -> &'static [&'static str] {
&[
"readFile",
"resolveModuleName",
"getPackageJsonScopeIfApplicable",
"getPackageScopeForPath",
"resolveTypeReferenceDirective",
"getImpliedNodeFormatForFile",
"isNodeSourceFile",
]
}
fn handle_callback(
&self,
name: &str,
payload: String,
) -> Result<String, deno_typescript_go_client_rust::Error> {
let mut state = self.state.borrow_mut();
match name {
"readFile" => {
log::debug!("readFile: {}", payload);
let payload = deser::<String>(payload)?;
if payload == state.config_path {
Ok(jsons!(&state.synthetic_config)?)
} else {
if let Some(load_result) = state.load_result_pending.remove(&payload)
{
return Ok(jsons!(load_result.contents)?);
}
let result = load_inner(&mut state, &payload).map_err(adhoc)?;
if let Some(result) = result {
let contents = result.contents;
Ok(jsons!(&contents)?)
} else {
let path = Path::new(&payload);
if let Ok(contents) = std::fs::read_to_string(path) {
Ok(jsons!(&contents)?)
} else {
Ok(jsons!(None::<String>)?)
}
}
}
}
"loadSourceFile" => {
let payload = deser::<String>(payload)?;
log::debug!("loadSourceFile: {}", payload);
if let Some(load_result) = state.load_result_pending.remove(&payload) {
Ok(jsons!(&load_result)?)
} else {
let result = load_inner(&mut state, &payload).map_err(adhoc)?;
Ok(jsons!(&result)?)
}
}
"resolveModuleName" => {
let payload = deser::<ResolveModuleNamePayload>(payload)?;
let (out_name, extension) = resolve_name(&mut state, payload)?;
Ok(jsons!({
"resolvedFileName": out_name,
"extension": extension,
})?)
}
"getPackageJsonScopeIfApplicable" => {
log::debug!("getPackageJsonScopeIfApplicable: {}", payload);
get_package_json_scope_if_applicable(&mut state, payload).inspect(
|res| log::debug!("getPackageJsonScopeIfApplicable -> {}", res),
)
}
"getPackageScopeForPath" => {
log::debug!("getPackageScopeForPath: {}", payload);
get_package_json_scope_if_applicable(&mut state, payload)
.inspect(|res| log::debug!("getPackageScopeForPath -> {}", res))
}
"resolveTypeReferenceDirective" => {
log::debug!("resolveTypeReferenceDirective: {}", payload);
let payload = deser::<ResolveTypeReferenceDirectivePayload>(payload)?;
let payload = ResolveModuleNamePayload {
module_name: payload.type_reference_directive_name,
containing_file: payload.containing_file,
resolution_mode: payload.resolution_mode,
};
let (out_name, extension) = resolve_name(&mut state, payload)?;
log::debug!(
"resolveTypeReferenceDirective: {:?}",
(&out_name, &extension)
);
Ok(jsons!({
"resolvedFileName": out_name,
"extension": extension,
"primary": true,
})?)
}
"getImpliedNodeFormatForFile" => {
let payload = deser::<GetImpliedNodeFormatForFilePayload>(payload)?;
log::debug!("getImpliedNodeFormatForFile: {:?}", payload);
// check if we already determined the module kind from a previous load
if let Some(module_kind) = state.module_kind_map.get(&payload.file_name)
{
log::debug!("getImpliedNodeFormatForFile -> {:?}", module_kind);
Ok(jsons!(&module_kind)?)
} else {
// if not, load the file and determine the module kind
let load_result =
load_inner(&mut state, &payload.file_name).map_err(adhoc)?;
if let Some(load_result) = load_result {
// store the load result in the pending map to avoid loading the file again
state
.load_result_pending
.insert(payload.file_name.clone(), load_result);
let module_kind = state
.module_kind_map
.get(&payload.file_name)
.copied()
.unwrap_or(
deno_typescript_go_client_rust::types::ResolutionMode::ESM,
);
Ok(jsons!(&module_kind)?)
} else {
Ok(jsons!(
&deno_typescript_go_client_rust::types::ResolutionMode::ESM
)?)
}
}
}
"isNodeSourceFile" => {
let path = deser::<String>(payload)?;
let state = &*state;
let result = ModuleSpecifier::parse(&path)
.ok()
.or_else(|| {
deno_path_util::resolve_url_or_path(&path, &state.current_dir).ok()
})
.and_then(|specifier| {
state
.maybe_npm
.as_ref()
.map(|n| n.node_resolver.in_npm_package(&specifier))
})
.unwrap_or(false);
Ok(jsons!(result)?)
}
_ => unreachable!("unknown callback: {name}"),
}
}
}
fn adhoc(err: impl std::error::Error) -> deno_typescript_go_client_rust::Error {
deno_typescript_go_client_rust::Error::AdHoc(err.to_string())
}
fn resolve_name(
handler: &mut HandlerState,
payload: ResolveModuleNamePayload,
) -> Result<(String, Option<&'static str>), deno_typescript_go_client_rust::Error>
{
let graph = &handler.graph;
let maybe_npm = handler.maybe_npm.as_ref();
let referrer = if let Some(remapped_specifier) =
handler.maybe_remapped_specifier(&payload.containing_file)
{
remapped_specifier.clone()
} else {
deno_path_util::resolve_url_or_path(
&payload.containing_file,
&handler.current_dir,
)
.map_err(adhoc)?
};
let referrer_module = graph.get(&referrer);
let specifier = payload.module_name;
let result = super::resolve_specifier_for_tsc(
specifier,
&referrer,
graph,
match payload.resolution_mode {
deno_typescript_go_client_rust::types::ResolutionMode::None => {
super::ResolutionMode::Import
}
deno_typescript_go_client_rust::types::ResolutionMode::CommonJS => {
super::ResolutionMode::Require
}
deno_typescript_go_client_rust::types::ResolutionMode::ESM => {
super::ResolutionMode::Import
}
},
maybe_npm,
referrer_module,
&mut handler.remapped_specifiers,
)
.map_err(adhoc)?;
Ok(result)
}
impl HandlerState {
pub fn maybe_remapped_specifier(
&self,
specifier: &str,
) -> Option<&ModuleSpecifier> {
self
.remapped_specifiers
.get(specifier)
.or_else(|| self.root_map.get(specifier))
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ExecError {
#[class(generic)]
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),
#[class(generic)]
#[error(transparent)]
TsgoClient(#[from] deno_typescript_go_client_rust::Error),
#[class(generic)]
#[error(transparent)]
PackageJsonLoad(#[from] deno_package_json::PackageJsonLoadError),
#[class(generic)]
#[error(transparent)]
PackageJsonLoadError(#[from] node_resolver::errors::PackageJsonLoadError),
#[class(generic)]
#[error(transparent)]
DownloadError(#[from] DownloadError),
#[class(generic)]
#[error(transparent)]
LoadError(#[from] super::LoadError),
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct LoadResult {
contents: String,
script_kind: i32,
}
impl super::LoadContent for String {
fn from_static(source: &'static str) -> Self {
source.to_string()
}
fn from_string(source: String) -> Self {
source
}
fn from_arc_str(source: Arc<str>) -> Self {
source.to_string()
}
}
impl super::Mapper for HandlerState {
fn maybe_remapped_specifier(
&self,
specifier: &str,
) -> Option<&ModuleSpecifier> {
self.maybe_remapped_specifier(specifier)
}
}
fn load_inner(
state: &mut HandlerState,
load_specifier: &str,
) -> Result<Option<LoadResult>, ExecError> {
log::debug!("load_inner: {}", load_specifier);
let result = super::load_for_tsc(
load_specifier,
state.maybe_npm.as_ref(),
&state.current_dir,
&state.graph,
None,
0,
state,
)?;
let Some(result) = result else {
return Ok(None);
};
let is_cjs = result.is_cjs;
let media_type = result.media_type;
let module_kind = get_resolution_mode(is_cjs, media_type);
let script_kind = super::as_ts_script_kind(media_type);
log::debug!("load_inner {load_specifier} -> {:?}", module_kind);
state
.module_kind_map
.insert(load_specifier.to_string(), module_kind);
Ok(Some(LoadResult {
contents: result.data,
script_kind,
}))
}
fn get_resolution_mode(
is_cjs: bool,
media_type: MediaType,
) -> deno_typescript_go_client_rust::types::ResolutionMode {
if is_cjs {
deno_typescript_go_client_rust::types::ResolutionMode::CommonJS
} else {
match media_type {
MediaType::Cjs | MediaType::Dcts | MediaType::Cts => {
deno_typescript_go_client_rust::types::ResolutionMode::CommonJS
}
MediaType::Css
| MediaType::Json
| MediaType::Html
| MediaType::Sql
| MediaType::Wasm
| MediaType::SourceMap
| MediaType::Unknown => {
deno_typescript_go_client_rust::types::ResolutionMode::None
}
_ => deno_typescript_go_client_rust::types::ResolutionMode::ESM,
}
}
}

169
cli/tsc/go/setup.rs Normal file
View file

@ -0,0 +1,169 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::OnceLock;
use deno_core::error::AnyError;
use deno_error::JsErrorBox;
use sha2::Digest;
use super::tsgo_version;
use crate::cache::DenoDir;
use crate::http_util::HttpClientProvider;
fn get_download_url(platform: &str) -> String {
format!(
"{}/typescript-go-{}-{}.zip",
tsgo_version::DOWNLOAD_BASE_URL,
tsgo_version::VERSION,
platform
)
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(generic)]
pub enum DownloadError {
#[error("unsupported platform for typescript-go: {0}")]
UnsupportedPlatform(String),
#[error("invalid download url: {0}")]
InvalidDownloadUrl(String, #[source] deno_core::url::ParseError),
#[error("failed to unpack typescript-go: {0}")]
UnpackFailed(#[source] AnyError),
#[error("failed to rename typescript-go: {0}")]
RenameFailed(#[source] std::io::Error),
#[error("failed to write zip file to {0}: {1}")]
WriteZipFailed(String, #[source] std::io::Error),
#[error("failed to download typescript-go: {0}")]
DownloadFailed(#[source] crate::http_util::DownloadError),
#[error("{0}")]
HttpClient(#[source] JsErrorBox),
#[error("failed to create temp directory: {0}")]
CreateTempDirFailed(#[source] std::io::Error),
#[error("hash mismatch: expected {0}, got {1}")]
HashMismatch(String, String),
#[error("binary not found: {0}")]
BinaryNotFound(String),
}
fn verify_hash(platform: &str, data: &[u8]) -> Result<(), DownloadError> {
let expected_hash = match platform {
"windows-x64" => tsgo_version::HASHES.windows_x64,
"macos-x64" => tsgo_version::HASHES.macos_x64,
"macos-arm64" => tsgo_version::HASHES.macos_arm64,
"linux-x64" => tsgo_version::HASHES.linux_x64,
"linux-arm64" => tsgo_version::HASHES.linux_arm64,
_ => unreachable!(),
};
let (algorithm, expected_hash) = expected_hash.split_once(':').unwrap();
if algorithm != "sha256" {
panic!("Hash algorithm is not sha256");
}
let mut hash = sha2::Sha256::new();
hash.update(data);
let hash = hash.finalize();
let hash = faster_hex::hex_string(&hash);
if hash != expected_hash {
return Err(DownloadError::HashMismatch(expected_hash.to_string(), hash));
}
Ok(())
}
pub async fn ensure_tsgo(
deno_dir: &DenoDir,
http_client_provider: Arc<HttpClientProvider>,
) -> Result<&'static PathBuf, DownloadError> {
static TSGO_PATH: OnceLock<PathBuf> = OnceLock::new();
if let Some(bin_path) = TSGO_PATH.get() {
return Ok(bin_path);
}
if let Ok(tsgo_path) = std::env::var("DENO_TSGO_PATH") {
let tsgo_path = Path::new(&tsgo_path);
if tsgo_path.exists() {
return Ok(TSGO_PATH.get_or_init(|| PathBuf::from(tsgo_path)));
} else {
return Err(DownloadError::BinaryNotFound(
tsgo_path.to_string_lossy().into_owned(),
));
}
}
let platform = match (std::env::consts::OS, std::env::consts::ARCH) {
("windows", "x86_64") => "windows-x64",
("macos", "x86_64") => "macos-x64",
("macos", "aarch64") => "macos-arm64",
("linux", "x86_64") => "linux-x64",
("linux", "aarch64") => "linux-arm64",
_ => {
return Err(DownloadError::UnsupportedPlatform(format!(
"{} {}",
std::env::consts::OS,
std::env::consts::ARCH
)));
}
};
let folder_path = deno_dir
.dl_folder_path()
.join(format!("tsgo-{}", tsgo_version::VERSION));
let bin_path = folder_path.join(format!(
"tsgo-{}{}",
platform,
if cfg!(windows) { ".exe" } else { "" }
));
if bin_path.exists() {
return Ok(TSGO_PATH.get_or_init(|| bin_path));
}
std::fs::create_dir_all(&folder_path)
.map_err(DownloadError::CreateTempDirFailed)?;
let client = http_client_provider
.get_or_create()
.map_err(DownloadError::HttpClient)?;
let download_url = get_download_url(platform);
log::debug!("Downloading tsgo from {}", download_url);
let temp = tempfile::tempdir().map_err(DownloadError::CreateTempDirFailed)?;
let path = temp.path().join("tsgo.zip");
log::debug!("Downloading tsgo to {}", path.display());
let data = client
.download(
deno_core::url::Url::parse(&download_url)
.map_err(|e| DownloadError::InvalidDownloadUrl(download_url, e))?,
)
.await
.map_err(DownloadError::DownloadFailed)?;
verify_hash(platform, &data)?;
std::fs::write(&path, &data).map_err(|e| {
DownloadError::WriteZipFailed(path.display().to_string(), e)
})?;
log::debug!(
"Unpacking tsgo from {} to {}",
path.display(),
temp.path().display()
);
let unpacked_path =
crate::util::archive::unpack_into_dir(crate::util::archive::UnpackArgs {
exe_name: "tsgo",
archive_name: "tsgo.zip",
archive_data: &data,
is_windows: cfg!(windows),
dest_path: temp.path(),
})
.map_err(DownloadError::UnpackFailed)?;
std::fs::rename(unpacked_path, &bin_path)
.map_err(DownloadError::RenameFailed)?;
Ok(TSGO_PATH.get_or_init(|| bin_path))
}

View file

@ -0,0 +1,55 @@
// Copyright 2018-2025 the Deno authors. MIT license.
// This file is auto-generated by tools/update_tsgo.ts
// DO NOT EDIT THIS FILE MANUALLY
pub struct Hashes {
pub windows_x64: &'static str,
pub macos_x64: &'static str,
pub macos_arm64: &'static str,
pub linux_x64: &'static str,
pub linux_arm64: &'static str,
}
impl Hashes {
pub const fn all(&self) -> [&'static str; 5] {
[
self.windows_x64,
self.macos_x64,
self.macos_arm64,
self.linux_x64,
self.linux_arm64,
]
}
}
pub const VERSION: &str = "0.1.6";
pub const DOWNLOAD_BASE_URL: &str =
"https://github.com/denoland/typescript-go/releases/download/v0.1.6";
pub const HASHES: Hashes = Hashes {
windows_x64: "sha256:04f85a9a64807437471cd45ed569b1ee9910dbe9751c1a5085028ae5eb09db56",
macos_x64: "sha256:84619ab4a6ac3dc1c78a62c209ea853cf077871fe086657bc861e268b9c0412c",
macos_arm64: "sha256:cef3d3f60abe9f2947f2e30f8075d860f9bf176a5545c651eabfcaa9e791e0f9",
linux_x64: "sha256:ea0ae7f3782a8372a03ec2c1f1bbb415e0c10a43ce917b0564084f896e0df127",
linux_arm64: "sha256:67b4f4d9982ff5c5c14105b37c48b582e3dc806c8a504f1a1b5416e29de68198",
};
const _: () = {
let sha256 = "sha256".as_bytes();
let mut i = 0;
let hashes = HASHES.all();
while i < hashes.len() {
let hash = hashes[i].as_bytes();
let mut j = 0;
while j < 6 {
if hash[j] != sha256[j] {
panic!("Hash algorithm is not sha256");
}
j += 1;
}
i += 1;
}
};

985
cli/tsc/js.rs Normal file
View file

@ -0,0 +1,985 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use deno_core::FastString;
use deno_core::JsRuntime;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_core::RuntimeOptions;
use deno_core::anyhow::Context;
use deno_core::located_script_name;
use deno_core::op2;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde_json::json;
use deno_core::url::Url;
use deno_graph::GraphKind;
use deno_graph::ModuleGraph;
use deno_lib::util::hash::FastInsecureHasher;
use deno_lib::worker::create_isolate_create_params;
use deno_path_util::resolve_url_or_path;
use node_resolver::ResolutionMode;
use super::LAZILY_LOADED_STATIC_ASSETS;
use super::ResolveArgs;
use super::ResolveError;
use crate::args::TypeCheckMode;
use crate::tsc::Diagnostics;
use crate::tsc::ExecError;
use crate::tsc::LoadError;
use crate::tsc::Request;
use crate::tsc::RequestNpmState;
use crate::tsc::Response;
use crate::tsc::Stats;
use crate::tsc::get_hash;
#[op2]
#[string]
fn op_remap_specifier(
state: &mut OpState,
#[string] specifier: &str,
) -> Option<String> {
let state = state.borrow::<State>();
state
.maybe_remapped_specifier(specifier)
.map(|url| url.to_string())
}
#[op2]
#[serde]
fn op_libs() -> Vec<String> {
let mut out = Vec::with_capacity(LAZILY_LOADED_STATIC_ASSETS.len());
for (key, value) in LAZILY_LOADED_STATIC_ASSETS.iter() {
if !value.is_lib {
continue;
}
let lib = key
.replace("lib.", "")
.replace(".d.ts", "")
.replace("deno_", "deno.");
out.push(lib);
}
out
}
#[op2]
#[serde]
fn op_resolve(
state: &mut OpState,
#[string] base: String,
#[serde] specifiers: Vec<(bool, String)>,
) -> Result<Vec<(String, Option<&'static str>)>, ResolveError> {
op_resolve_inner(state, ResolveArgs { base, specifiers })
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TscConstants {
types_node_ignorable_names: Vec<&'static str>,
node_only_globals: Vec<&'static str>,
ignored_diagnostic_codes: Vec<u64>,
}
impl TscConstants {
pub fn new() -> Self {
Self {
types_node_ignorable_names: super::TYPES_NODE_IGNORABLE_NAMES.to_vec(),
node_only_globals: super::NODE_ONLY_GLOBALS.to_vec(),
ignored_diagnostic_codes: super::IGNORED_DIAGNOSTIC_CODES
.iter()
.copied()
.collect(),
}
}
}
#[op2]
#[serde]
fn op_tsc_constants() -> TscConstants {
TscConstants::new()
}
#[inline]
fn op_resolve_inner(
state: &mut OpState,
args: ResolveArgs,
) -> Result<Vec<(String, Option<&'static str>)>, ResolveError> {
let state = state.borrow_mut::<State>();
let mut resolved: Vec<(String, Option<&'static str>)> =
Vec::with_capacity(args.specifiers.len());
let referrer = if let Some(remapped_specifier) =
state.maybe_remapped_specifier(&args.base)
{
remapped_specifier.clone()
} else {
resolve_url_or_path(&args.base, &state.current_dir)?
};
let referrer_module = state.graph.get(&referrer);
for (is_cjs, specifier) in args.specifiers {
let result = super::resolve_specifier_for_tsc(
specifier,
&referrer,
&state.graph,
if is_cjs {
ResolutionMode::Require
} else {
ResolutionMode::Import
},
state.maybe_npm.as_ref(),
referrer_module,
&mut state.remapped_specifiers,
)?;
resolved.push(result);
}
Ok(resolved)
}
deno_core::extension!(deno_cli_tsc,
ops = [
op_create_hash,
op_emit,
op_is_node_file,
op_load,
op_remap_specifier,
op_resolve,
op_tsc_constants,
op_respond,
op_libs,
],
options = {
request: Request,
root_map: HashMap<String, Url>,
remapped_specifiers: HashMap<String, Url>,
},
state = |state, options| {
state.put(State::new(
options.request.graph,
options.request.hash_data,
options.request.maybe_npm,
options.request.maybe_tsbuildinfo,
options.root_map,
options.remapped_specifiers,
std::env::current_dir()
.context("Unable to get CWD")
.unwrap(),
));
},
customizer = |ext: &mut deno_core::Extension| {
use deno_core::ExtensionFileSource;
ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/99_main_compiler.js", crate::tsc::MAIN_COMPILER_SOURCE.as_str().into()));
ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/97_ts_host.js", crate::tsc::TS_HOST_SOURCE.as_str().into()));
ext.esm_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/98_lsp.js", crate::tsc::LSP_SOURCE.as_str().into()));
ext.js_files.to_mut().push(ExtensionFileSource::new_computed("ext:deno_cli_tsc/00_typescript.js", crate::tsc::TYPESCRIPT_SOURCE.as_str().into()));
ext.esm_entry_point = Some("ext:deno_cli_tsc/99_main_compiler.js");
}
);
// TODO(bartlomieju): this mechanism is questionable.
// Can't we use something more efficient here?
#[op2]
fn op_respond(state: &mut OpState, #[serde] args: RespondArgs) {
op_respond_inner(state, args)
}
#[inline]
fn op_respond_inner(state: &mut OpState, args: RespondArgs) {
let state = state.borrow_mut::<State>();
state.maybe_response = Some(args);
}
#[op2]
#[string]
fn op_create_hash(s: &mut OpState, #[string] text: &str) -> String {
op_create_hash_inner(s, text)
}
#[inline]
fn op_create_hash_inner(s: &mut OpState, text: &str) -> String {
let state = s.borrow_mut::<State>();
get_hash(text, state.hash_data)
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct EmitArgs {
/// The text data/contents of the file.
data: String,
/// The _internal_ filename for the file. This will be used to determine how
/// the file is cached and stored.
file_name: String,
}
#[op2(fast)]
fn op_emit(
state: &mut OpState,
#[string] data: String,
#[string] file_name: String,
) -> bool {
op_emit_inner(state, EmitArgs { data, file_name })
}
#[inline]
fn op_emit_inner(state: &mut OpState, args: EmitArgs) -> bool {
let state = state.borrow_mut::<State>();
match args.file_name.as_ref() {
"internal:///.tsbuildinfo" => state.maybe_tsbuildinfo = Some(args.data),
_ => {
if cfg!(debug_assertions) {
panic!("Unhandled emit write: {}", args.file_name);
}
}
}
true
}
#[op2(fast)]
fn op_is_node_file(state: &mut OpState, #[string] path: &str) -> bool {
let state = state.borrow::<State>();
ModuleSpecifier::parse(path)
.ok()
.and_then(|specifier| {
state
.maybe_npm
.as_ref()
.map(|n| n.node_resolver.in_npm_package(&specifier))
})
.unwrap_or(false)
}
#[derive(Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
struct RespondArgs {
pub diagnostics: Diagnostics,
pub ambient_modules: Vec<String>,
pub stats: Stats,
}
impl super::LoadContent for FastString {
fn from_static(source: &'static str) -> Self {
FastString::from_static(source)
}
fn from_string(source: String) -> Self {
FastString::from(source)
}
fn from_arc_str(source: Arc<str>) -> Self {
FastString::from(source)
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct LoadResponse {
data: FastString,
version: Option<String>,
script_kind: i32,
is_cjs: bool,
}
#[op2]
#[serde]
fn op_load(
state: &mut OpState,
#[string] load_specifier: &str,
) -> Result<Option<LoadResponse>, LoadError> {
op_load_inner(state, load_specifier)
}
impl super::Mapper for State {
fn maybe_remapped_specifier(
&self,
specifier: &str,
) -> Option<&ModuleSpecifier> {
self.maybe_remapped_specifier(specifier)
}
}
fn op_load_inner(
state: &mut OpState,
load_specifier: &str,
) -> Result<Option<LoadResponse>, LoadError> {
let state = state.borrow::<State>();
Ok(
super::load_for_tsc::<FastString, _>(
load_specifier,
state.maybe_npm.as_ref(),
&state.current_dir,
&state.graph,
state.maybe_tsbuildinfo.as_deref(),
state.hash_data,
state,
)?
.map(|res| LoadResponse {
data: res.data,
version: res.version,
is_cjs: res.is_cjs,
script_kind: super::as_ts_script_kind(res.media_type),
}),
)
}
pub fn exec_request(
request: Request,
root_names: Vec<String>,
root_map: HashMap<String, ModuleSpecifier>,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
code_cache: Option<Arc<dyn deno_runtime::code_cache::CodeCache>>,
) -> Result<Response, ExecError> {
let request_value = json!({
"config": request.config,
"debug": request.debug,
"rootNames": root_names,
"localOnly": request.check_mode == TypeCheckMode::Local,
});
let exec_source = format!("globalThis.exec({request_value})");
let mut extensions =
deno_runtime::snapshot_info::get_extensions_in_snapshot();
extensions.push(deno_cli_tsc::init(request, root_map, remapped_specifiers));
let extension_code_cache = code_cache.map(|cache| {
Rc::new(TscExtCodeCache::new(cache)) as Rc<dyn deno_core::ExtCodeCache>
});
let mut runtime = JsRuntime::new(RuntimeOptions {
extensions,
create_params: create_isolate_create_params(&crate::sys::CliSys::default()),
startup_snapshot: deno_snapshots::CLI_SNAPSHOT,
extension_code_cache,
..Default::default()
});
runtime
.execute_script(located_script_name!(), exec_source)
.map_err(ExecError::Js)?;
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();
let state = op_state.take::<State>();
if let Some(response) = state.maybe_response {
let diagnostics = response.diagnostics;
let ambient_modules = response.ambient_modules;
let maybe_tsbuildinfo = state.maybe_tsbuildinfo;
let stats = response.stats;
Ok(Response {
diagnostics,
ambient_modules,
maybe_tsbuildinfo,
stats,
})
} else {
Err(ExecError::ResponseNotSet)
}
}
pub struct TscExtCodeCache {
cache: Arc<dyn deno_runtime::code_cache::CodeCache>,
}
impl TscExtCodeCache {
pub fn new(cache: Arc<dyn deno_runtime::code_cache::CodeCache>) -> Self {
Self { cache }
}
}
impl deno_core::ExtCodeCache for TscExtCodeCache {
fn get_code_cache_info(
&self,
specifier: &ModuleSpecifier,
code: &deno_core::ModuleSourceCode,
esm: bool,
) -> deno_core::SourceCodeCacheInfo {
use deno_runtime::code_cache::CodeCacheType;
let code_hash = FastInsecureHasher::new_deno_versioned()
.write_hashable(code)
.finish();
let data = self
.cache
.get_sync(
specifier,
if esm {
CodeCacheType::EsModule
} else {
CodeCacheType::Script
},
code_hash,
)
.map(Cow::from)
.inspect(|_| {
log::debug!(
"V8 code cache hit for Extension module: {specifier}, [{code_hash:?}]"
);
});
deno_core::SourceCodeCacheInfo {
hash: code_hash,
data,
}
}
fn code_cache_ready(
&self,
specifier: ModuleSpecifier,
source_hash: u64,
code_cache: &[u8],
esm: bool,
) {
use deno_runtime::code_cache::CodeCacheType;
log::debug!(
"Updating V8 code cache for Extension module: {specifier}, [{source_hash:?}]"
);
self.cache.set_sync(
specifier,
if esm {
CodeCacheType::EsModule
} else {
CodeCacheType::Script
},
source_hash,
code_cache,
);
}
}
// TODO(bartlomieju): we have similar struct in `tsc.rs` - maybe at least change
// the name of the struct to avoid confusion?
#[derive(Debug)]
struct State {
hash_data: u64,
graph: Arc<ModuleGraph>,
maybe_tsbuildinfo: Option<String>,
maybe_response: Option<RespondArgs>,
maybe_npm: Option<RequestNpmState>,
// todo(dsherret): it looks like the remapped_specifiers and
// root_map could be combined... what is the point of the separation?
remapped_specifiers: HashMap<String, ModuleSpecifier>,
root_map: HashMap<String, ModuleSpecifier>,
current_dir: PathBuf,
}
impl Default for State {
fn default() -> Self {
Self {
hash_data: Default::default(),
graph: Arc::new(ModuleGraph::new(GraphKind::All)),
maybe_tsbuildinfo: Default::default(),
maybe_response: Default::default(),
maybe_npm: Default::default(),
remapped_specifiers: Default::default(),
root_map: Default::default(),
current_dir: Default::default(),
}
}
}
impl State {
pub fn new(
graph: Arc<ModuleGraph>,
hash_data: u64,
maybe_npm: Option<RequestNpmState>,
maybe_tsbuildinfo: Option<String>,
root_map: HashMap<String, ModuleSpecifier>,
remapped_specifiers: HashMap<String, ModuleSpecifier>,
current_dir: PathBuf,
) -> Self {
State {
hash_data,
graph,
maybe_npm,
maybe_tsbuildinfo,
maybe_response: None,
remapped_specifiers,
root_map,
current_dir,
}
}
pub fn maybe_remapped_specifier(
&self,
specifier: &str,
) -> Option<&ModuleSpecifier> {
self
.remapped_specifiers
.get(specifier)
.or_else(|| self.root_map.get(specifier))
}
}
#[cfg(test)]
mod tests {
use deno_ast::MediaType;
use deno_core::OpState;
use deno_core::futures::future;
use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_error::JsErrorBox;
use deno_graph::GraphKind;
use deno_graph::ModuleGraph;
use deno_runtime::code_cache::CodeCacheType;
use test_util::PathRef;
use super::super::Diagnostic;
use super::super::DiagnosticCategory;
use super::*;
use crate::args::CompilerOptions;
use crate::tsc::MISSING_DEPENDENCY_SPECIFIER;
use crate::tsc::get_lazily_loaded_asset;
#[derive(Debug, Default)]
pub struct MockLoader {
pub fixtures: PathRef,
}
impl deno_graph::source::Loader for MockLoader {
fn load(
&self,
specifier: &ModuleSpecifier,
_options: deno_graph::source::LoadOptions,
) -> deno_graph::source::LoadFuture {
let specifier_text = specifier
.to_string()
.replace(":///", "_")
.replace("://", "_")
.replace('/', "-");
let source_path = self.fixtures.join(specifier_text);
let response = source_path
.read_to_bytes_if_exists()
.map(|c| {
Some(deno_graph::source::LoadResponse::Module {
specifier: specifier.clone(),
mtime: None,
maybe_headers: None,
content: c.into(),
})
})
.map_err(|e| {
deno_graph::source::LoadError::Other(Arc::new(JsErrorBox::generic(
e.to_string(),
)))
});
Box::pin(future::ready(response))
}
}
async fn setup(
maybe_specifier: Option<ModuleSpecifier>,
maybe_hash_data: Option<u64>,
maybe_tsbuildinfo: Option<String>,
) -> OpState {
let specifier = maybe_specifier
.unwrap_or_else(|| ModuleSpecifier::parse("file:///main.ts").unwrap());
let hash_data = maybe_hash_data.unwrap_or(0);
let fixtures = test_util::testdata_path().join("tsc2");
let loader = MockLoader { fixtures };
let mut graph = ModuleGraph::new(GraphKind::TypesOnly);
graph
.build(vec![specifier], Vec::new(), &loader, Default::default())
.await;
let state = State::new(
Arc::new(graph),
hash_data,
None,
maybe_tsbuildinfo,
HashMap::new(),
HashMap::new(),
std::env::current_dir()
.context("Unable to get CWD")
.unwrap(),
);
let mut op_state = OpState::new(None);
op_state.put(state);
op_state
}
async fn test_exec(
specifier: &ModuleSpecifier,
) -> Result<Response, ExecError> {
test_exec_with_cache(specifier, None).await
}
async fn test_exec_with_cache(
specifier: &ModuleSpecifier,
code_cache: Option<Arc<dyn deno_runtime::code_cache::CodeCache>>,
) -> Result<Response, ExecError> {
let hash_data = 123; // something random
let fixtures = test_util::testdata_path().join("tsc2");
let loader = MockLoader { fixtures };
let mut graph = ModuleGraph::new(GraphKind::TypesOnly);
graph
.build(
vec![specifier.clone()],
Vec::new(),
&loader,
Default::default(),
)
.await;
let config = Arc::new(CompilerOptions::new(json!({
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"emitDecoratorMetadata": false,
"incremental": true,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"lib": ["deno.window"],
"noEmit": true,
"outDir": "internal:///",
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "internal:///.tsbuildinfo",
})));
let request = Request {
config,
debug: false,
graph: Arc::new(graph),
hash_data,
maybe_npm: None,
maybe_tsbuildinfo: None,
root_names: vec![(specifier.clone(), MediaType::TypeScript)],
check_mode: TypeCheckMode::All,
initial_cwd: std::env::current_dir().unwrap(),
};
crate::tsc::exec(request, code_cache, None)
}
#[tokio::test]
async fn test_create_hash() {
let mut state = setup(None, Some(123), None).await;
let actual = op_create_hash_inner(&mut state, "some sort of content");
assert_eq!(actual, "11905938177474799758");
}
#[tokio::test]
async fn test_hash_url() {
let specifier = deno_core::resolve_url(
"data:application/javascript,console.log(\"Hello%20Deno\");",
)
.unwrap();
assert_eq!(
crate::tsc::hash_url(&specifier, MediaType::JavaScript),
"data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js"
);
}
#[tokio::test]
async fn test_emit_tsbuildinfo() {
let mut state = setup(None, None, None).await;
let actual = op_emit_inner(
&mut state,
EmitArgs {
data: "some file content".to_string(),
file_name: "internal:///.tsbuildinfo".to_string(),
},
);
assert!(actual);
let state = state.borrow::<State>();
assert_eq!(
state.maybe_tsbuildinfo,
Some("some file content".to_string())
);
}
#[tokio::test]
async fn test_load() {
let mut state = setup(
Some(ModuleSpecifier::parse("https://deno.land/x/mod.ts").unwrap()),
None,
Some("some content".to_string()),
)
.await;
let actual =
op_load_inner(&mut state, "https://deno.land/x/mod.ts").unwrap();
assert_eq!(
serde_json::to_value(actual).unwrap(),
json!({
"data": "console.log(\"hello deno\");\n",
"version": "7821807483407828376",
"scriptKind": 3,
"isCjs": false,
})
);
}
#[tokio::test]
async fn test_load_asset() {
let mut state = setup(
Some(ModuleSpecifier::parse("https://deno.land/x/mod.ts").unwrap()),
None,
Some("some content".to_string()),
)
.await;
let actual = op_load_inner(&mut state, "asset:///lib.dom.d.ts")
.expect("should have invoked op")
.expect("load should have succeeded");
let expected = get_lazily_loaded_asset("lib.dom.d.ts").unwrap();
assert_eq!(actual.data.to_string(), expected.to_string());
assert!(actual.version.is_some());
assert_eq!(actual.script_kind, 3);
}
#[tokio::test]
async fn test_load_tsbuildinfo() {
let mut state = setup(
Some(ModuleSpecifier::parse("https://deno.land/x/mod.ts").unwrap()),
None,
Some("some content".to_string()),
)
.await;
let actual = op_load_inner(&mut state, "internal:///.tsbuildinfo")
.expect("should have invoked op")
.expect("load should have succeeded");
assert_eq!(
serde_json::to_value(actual).unwrap(),
json!({
"data": "some content",
"version": null,
"scriptKind": 0,
"isCjs": false,
})
);
}
#[tokio::test]
async fn test_load_missing_specifier() {
let mut state = setup(None, None, None).await;
let actual = op_load_inner(&mut state, "https://deno.land/x/mod.ts")
.expect("should have invoked op");
assert_eq!(serde_json::to_value(actual).unwrap(), json!(null));
}
#[tokio::test]
async fn test_resolve() {
let mut state = setup(
Some(ModuleSpecifier::parse("https://deno.land/x/a.ts").unwrap()),
None,
None,
)
.await;
let actual = op_resolve_inner(
&mut state,
ResolveArgs {
base: "https://deno.land/x/a.ts".to_string(),
specifiers: vec![(false, "./b.ts".to_string())],
},
)
.expect("should have invoked op");
assert_eq!(
actual,
vec![("https://deno.land/x/b.ts".into(), Some(".ts"))]
);
}
#[tokio::test]
async fn test_resolve_empty() {
let mut state = setup(
Some(ModuleSpecifier::parse("https://deno.land/x/a.ts").unwrap()),
None,
None,
)
.await;
let actual = op_resolve_inner(
&mut state,
ResolveArgs {
base: "https://deno.land/x/a.ts".to_string(),
specifiers: vec![(false, "./bad.ts".to_string())],
},
)
.expect("should have not errored");
assert_eq!(
actual,
vec![(MISSING_DEPENDENCY_SPECIFIER.into(), Some(".d.ts"))]
);
}
#[tokio::test]
async fn test_respond() {
let mut state = setup(None, None, None).await;
let args = serde_json::from_value(json!({
"diagnostics": [
{
"messageText": "Unknown compiler option 'invalid'.",
"category": 1,
"code": 5023
}
],
"stats": [["a", 12]],
"ambientModules": []
}))
.unwrap();
op_respond_inner(&mut state, args);
let state = state.borrow::<State>();
assert_eq!(
state.maybe_response,
Some(RespondArgs {
diagnostics: Diagnostics::new(vec![Diagnostic {
category: DiagnosticCategory::Error,
code: 5023,
start: None,
end: None,
original_source_start: None,
message_text: Some(
"Unknown compiler option \'invalid\'.".to_string()
),
message_chain: None,
source: None,
source_line: None,
file_name: None,
related_information: None,
reports_deprecated: None,
reports_unnecessary: None,
other: Default::default(),
missing_specifier: None,
}]),
ambient_modules: vec![],
stats: Stats(vec![("a".to_string(), 12)])
})
);
}
#[tokio::test]
async fn test_exec_basic() {
let specifier = ModuleSpecifier::parse("https://deno.land/x/a.ts").unwrap();
let actual = test_exec(&specifier)
.await
.expect("exec should not have errored");
assert!(!actual.diagnostics.has_diagnostic());
assert!(actual.maybe_tsbuildinfo.is_some());
assert_eq!(actual.stats.0.len(), 12);
}
#[tokio::test]
async fn test_exec_reexport_dts() {
let specifier = ModuleSpecifier::parse("file:///reexports.ts").unwrap();
let actual = test_exec(&specifier)
.await
.expect("exec should not have errored");
assert!(!actual.diagnostics.has_diagnostic());
assert!(actual.maybe_tsbuildinfo.is_some());
assert_eq!(actual.stats.0.len(), 12);
}
#[tokio::test]
async fn fix_lib_ref() {
let specifier = ModuleSpecifier::parse("file:///libref.ts").unwrap();
let actual = test_exec(&specifier)
.await
.expect("exec should not have errored");
assert!(!actual.diagnostics.has_diagnostic());
}
pub type SpecifierWithType = (ModuleSpecifier, CodeCacheType);
#[derive(Default)]
struct TestExtCodeCache {
cache: Mutex<HashMap<(SpecifierWithType, u64), Vec<u8>>>,
hits: Mutex<HashMap<SpecifierWithType, usize>>,
misses: Mutex<HashMap<SpecifierWithType, usize>>,
}
impl deno_runtime::code_cache::CodeCache for TestExtCodeCache {
fn get_sync(
&self,
specifier: &ModuleSpecifier,
code_cache_type: CodeCacheType,
source_hash: u64,
) -> Option<Vec<u8>> {
let result = self
.cache
.lock()
.get(&((specifier.clone(), code_cache_type), source_hash))
.cloned();
if result.is_some() {
*self
.hits
.lock()
.entry((specifier.clone(), code_cache_type))
.or_default() += 1;
} else {
*self
.misses
.lock()
.entry((specifier.clone(), code_cache_type))
.or_default() += 1;
}
result
}
fn set_sync(
&self,
specifier: ModuleSpecifier,
code_cache_type: CodeCacheType,
source_hash: u64,
data: &[u8],
) {
self
.cache
.lock()
.insert(((specifier, code_cache_type), source_hash), data.to_vec());
}
}
#[tokio::test]
async fn test_exec_code_cache() {
let code_cache = Arc::new(TestExtCodeCache::default());
let specifier = ModuleSpecifier::parse("https://deno.land/x/a.ts").unwrap();
let actual = test_exec_with_cache(&specifier, Some(code_cache.clone()))
.await
.expect("exec should not have errored");
assert!(!actual.diagnostics.has_diagnostic());
let expect = [
(
"ext:deno_cli_tsc/99_main_compiler.js",
CodeCacheType::EsModule,
),
("ext:deno_cli_tsc/98_lsp.js", CodeCacheType::EsModule),
("ext:deno_cli_tsc/97_ts_host.js", CodeCacheType::EsModule),
("ext:deno_cli_tsc/00_typescript.js", CodeCacheType::Script),
];
{
let mut files = HashMap::new();
for (((specifier, ty), _), _) in code_cache.cache.lock().iter() {
let specifier = specifier.to_string();
if files.contains_key(&specifier) {
panic!("should have only 1 entry per specifier");
}
files.insert(specifier, *ty);
}
// 99_main_compiler, 98_lsp, 97_ts_host, 00_typescript
assert_eq!(files.len(), 4);
assert_eq!(code_cache.hits.lock().len(), 0);
assert_eq!(code_cache.misses.lock().len(), 4);
for (specifier, ty) in &expect {
assert_eq!(files.get(*specifier), Some(ty));
}
code_cache.hits.lock().clear();
code_cache.misses.lock().clear();
}
{
let _ = test_exec_with_cache(&specifier, Some(code_cache.clone()))
.await
.expect("exec should not have errored");
// 99_main_compiler, 98_lsp, 97_ts_host, 00_typescript
assert_eq!(code_cache.hits.lock().len(), 4);
assert_eq!(code_cache.misses.lock().len(), 0);
for (specifier, ty) in expect {
let url = ModuleSpecifier::parse(specifier).unwrap();
assert_eq!(code_cache.hits.lock().get(&(url, ty)), Some(&1));
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -37,6 +38,7 @@ use crate::graph_util::BuildFastCheckGraphOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
use crate::graph_util::module_error_for_tsc_diagnostic; use crate::graph_util::module_error_for_tsc_diagnostic;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::node::CliPackageJsonResolver;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::tsc; use crate::tsc;
@ -103,9 +105,11 @@ pub struct TypeChecker {
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_resolver: CliNpmResolver, npm_resolver: CliNpmResolver,
package_json_resolver: Arc<CliPackageJsonResolver>,
sys: CliSys, sys: CliSys,
compiler_options_resolver: Arc<CompilerOptionsResolver>, compiler_options_resolver: Arc<CompilerOptionsResolver>,
code_cache: Option<Arc<crate::cache::CodeCache>>, code_cache: Option<Arc<crate::cache::CodeCache>>,
tsgo_path: Option<PathBuf>,
} }
impl TypeChecker { impl TypeChecker {
@ -117,9 +121,11 @@ impl TypeChecker {
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_resolver: CliNpmResolver, npm_resolver: CliNpmResolver,
package_json_resolver: Arc<CliPackageJsonResolver>,
sys: CliSys, sys: CliSys,
compiler_options_resolver: Arc<CompilerOptionsResolver>, compiler_options_resolver: Arc<CompilerOptionsResolver>,
code_cache: Option<Arc<crate::cache::CodeCache>>, code_cache: Option<Arc<crate::cache::CodeCache>>,
tsgo_path: Option<PathBuf>,
) -> Self { ) -> Self {
Self { Self {
caches, caches,
@ -128,9 +134,11 @@ impl TypeChecker {
module_graph_builder, module_graph_builder,
node_resolver, node_resolver,
npm_resolver, npm_resolver,
package_json_resolver,
sys, sys,
compiler_options_resolver, compiler_options_resolver,
code_cache, code_cache,
tsgo_path,
} }
} }
@ -233,6 +241,7 @@ impl TypeChecker {
cjs_tracker: &self.cjs_tracker, cjs_tracker: &self.cjs_tracker,
node_resolver: &self.node_resolver, node_resolver: &self.node_resolver,
npm_resolver: &self.npm_resolver, npm_resolver: &self.npm_resolver,
package_json_resolver: &self.package_json_resolver,
compiler_options_resolver: &self.compiler_options_resolver, compiler_options_resolver: &self.compiler_options_resolver,
log_level: self.cli_options.log_level(), log_level: self.cli_options.log_level(),
npm_check_state_hash: check_state_hash(&self.npm_resolver), npm_check_state_hash: check_state_hash(&self.npm_resolver),
@ -244,6 +253,8 @@ impl TypeChecker {
options, options,
seen_diagnotics: Default::default(), seen_diagnotics: Default::default(),
code_cache: self.code_cache.clone(), code_cache: self.code_cache.clone(),
tsgo_path: self.tsgo_path.clone(),
initial_cwd: self.cli_options.initial_cwd().to_path_buf(),
}), }),
)) ))
} }
@ -362,6 +373,7 @@ struct DiagnosticsByFolderRealIterator<'a> {
cjs_tracker: &'a Arc<TypeCheckingCjsTracker>, cjs_tracker: &'a Arc<TypeCheckingCjsTracker>,
node_resolver: &'a Arc<CliNodeResolver>, node_resolver: &'a Arc<CliNodeResolver>,
npm_resolver: &'a CliNpmResolver, npm_resolver: &'a CliNpmResolver,
package_json_resolver: &'a Arc<CliPackageJsonResolver>,
compiler_options_resolver: &'a CompilerOptionsResolver, compiler_options_resolver: &'a CompilerOptionsResolver,
type_check_cache: TypeCheckCache, type_check_cache: TypeCheckCache,
groups: Vec<CheckGroup<'a>>, groups: Vec<CheckGroup<'a>>,
@ -371,6 +383,8 @@ struct DiagnosticsByFolderRealIterator<'a> {
seen_diagnotics: HashSet<String>, seen_diagnotics: HashSet<String>,
options: CheckOptions, options: CheckOptions,
code_cache: Option<Arc<crate::cache::CodeCache>>, code_cache: Option<Arc<crate::cache::CodeCache>>,
tsgo_path: Option<PathBuf>,
initial_cwd: PathBuf,
} }
impl Iterator for DiagnosticsByFolderRealIterator<'_> { impl Iterator for DiagnosticsByFolderRealIterator<'_> {
@ -522,12 +536,15 @@ impl DiagnosticsByFolderRealIterator<'_> {
cjs_tracker: self.cjs_tracker.clone(), cjs_tracker: self.cjs_tracker.clone(),
node_resolver: self.node_resolver.clone(), node_resolver: self.node_resolver.clone(),
npm_resolver: self.npm_resolver.clone(), npm_resolver: self.npm_resolver.clone(),
package_json_resolver: self.package_json_resolver.clone(),
}), }),
maybe_tsbuildinfo, maybe_tsbuildinfo,
root_names, root_names,
check_mode: self.options.type_check_mode, check_mode: self.options.type_check_mode,
initial_cwd: self.initial_cwd.clone(),
}, },
code_cache, code_cache,
self.tsgo_path.as_deref(),
)?; )?;
let ambient_modules = response.ambient_modules; let ambient_modules = response.ambient_modules;

39
docs/tsgo.md Normal file
View file

@ -0,0 +1,39 @@
# Typescript-Go Integration
Currently only integrated with deno check, though in the future it will also be
integrated with our LSP implementation.
In the CLI, we have a small abstraction over the tsc backend in
[cli/tsc/mod.rs](../cli/tsc/mod.rs). Along with some shared types and
functionality, the main piece is the `exec` function, which takes a "request" to
be served by the typescript compiler and returns the result. This now has two
different "backend" which can serve the request the current tsc, which runs in
an isolate and communicates via ops, and typescript-go which runs in a
subprocess and uses IPC.
From a high level, the way the tsgo backend works is that we download a
typescript-go binary from
[github releases](https://github.com/denoland/typescript-go/releases) into the
deno cache dir. To actually interface with tsgo, we spawn it in a subprocess and
write messages over stdin/stdout (similar to the Language Server Protocol). The
format is a mixture of binary data (for the header and other protocol level
details) followed by json encoded values for RPC calls. The rust implementation
of the IPC protocol is in the
[deno_typescript_go_client_rust crate](../libs/typescript_go_client/src/lib.rs).
We currently maintain a
[fork of typescript-go](https://github.com/denoland/typescript-go) with the
following changes:
- Special handling of the global symbol tables to account for the fact that we
have two slightly different sets of globals: one for node contexts (in npm
packages), and one for deno contexts. At this point, the main difference is
the type returned by `setTimeout`. With node globals `setTimeout` returns an
object, and with deno globals it returns a number (just like the web
standard).
- Symbol table logic to prevent @types/node from creating type errors by
introducing incompatible definitions for globals
- Additional hooks to allow us to provide our own resolution, determine whether
a file is esm/cjs, etc.
- Additional APIs exposed from the IPC server
- Support for deno's custom libs (`deno.window`, `deno.worker`, etc)

View file

@ -8,6 +8,7 @@ use std::path::PathBuf;
use deno_package_json::PackageJson; use deno_package_json::PackageJson;
use deno_package_json::PackageJsonCacheResult; use deno_package_json::PackageJsonCacheResult;
use deno_package_json::PackageJsonRc; use deno_package_json::PackageJsonRc;
use sys_traits::FsMetadata;
use sys_traits::FsRead; use sys_traits::FsRead;
use crate::errors::PackageJsonLoadError; use crate::errors::PackageJsonLoadError;
@ -76,12 +77,12 @@ pub type PackageJsonResolverRc<TSys> =
deno_maybe_sync::MaybeArc<PackageJsonResolver<TSys>>; deno_maybe_sync::MaybeArc<PackageJsonResolver<TSys>>;
#[derive(Debug)] #[derive(Debug)]
pub struct PackageJsonResolver<TSys: FsRead> { pub struct PackageJsonResolver<TSys: FsRead + FsMetadata> {
sys: TSys, sys: TSys,
loader_cache: Option<PackageJsonCacheRc>, loader_cache: Option<PackageJsonCacheRc>,
} }
impl<TSys: FsRead> PackageJsonResolver<TSys> { impl<TSys: FsRead + FsMetadata> PackageJsonResolver<TSys> {
pub fn new(sys: TSys, loader_cache: Option<PackageJsonCacheRc>) -> Self { pub fn new(sys: TSys, loader_cache: Option<PackageJsonCacheRc>) -> Self {
Self { sys, loader_cache } Self { sys, loader_cache }
} }
@ -124,12 +125,14 @@ impl<TSys: FsRead> PackageJsonResolver<TSys> {
} }
} }
pub struct ClosestPackageJsonsIterator<'a, TSys: FsRead> { pub struct ClosestPackageJsonsIterator<'a, TSys: FsRead + FsMetadata> {
current_path: &'a Path, current_path: &'a Path,
resolver: &'a PackageJsonResolver<TSys>, resolver: &'a PackageJsonResolver<TSys>,
} }
impl<'a, TSys: FsRead> Iterator for ClosestPackageJsonsIterator<'a, TSys> { impl<'a, TSys: FsRead + FsMetadata> Iterator
for ClosestPackageJsonsIterator<'a, TSys>
{
type Item = Result<PackageJsonRc, PackageJsonLoadError>; type Item = Result<PackageJsonRc, PackageJsonLoadError>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {

View file

@ -87,7 +87,7 @@ impl NodeAnalysisCache for NullNodeAnalysisCache {
#[sys_traits::auto_impl] #[sys_traits::auto_impl]
pub trait DenoCjsCodeAnalyzerSys: pub trait DenoCjsCodeAnalyzerSys:
sys_traits::FsRead + MaybeSend + MaybeSync + 'static sys_traits::FsRead + sys_traits::FsMetadata + MaybeSend + MaybeSync + 'static
{ {
} }

View file

@ -6,6 +6,7 @@ use node_resolver::InNpmPackageChecker;
use node_resolver::PackageJsonResolverRc; use node_resolver::PackageJsonResolverRc;
use node_resolver::ResolutionMode; use node_resolver::ResolutionMode;
use node_resolver::errors::PackageJsonLoadError; use node_resolver::errors::PackageJsonLoadError;
use sys_traits::FsMetadata;
use sys_traits::FsRead; use sys_traits::FsRead;
use url::Url; use url::Url;
@ -21,12 +22,15 @@ pub type CjsTrackerRc<TInNpmPackageChecker, TSys> =
/// be CJS or ESM after they're loaded based on their contents. So these /// be CJS or ESM after they're loaded based on their contents. So these
/// files will be "maybe CJS" until they're loaded. /// files will be "maybe CJS" until they're loaded.
#[derive(Debug)] #[derive(Debug)]
pub struct CjsTracker<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead> { pub struct CjsTracker<
TInNpmPackageChecker: InNpmPackageChecker,
TSys: FsRead + FsMetadata,
> {
is_cjs_resolver: IsCjsResolver<TInNpmPackageChecker, TSys>, is_cjs_resolver: IsCjsResolver<TInNpmPackageChecker, TSys>,
known: MaybeDashMap<Url, ResolutionMode>, known: MaybeDashMap<Url, ResolutionMode>,
} }
impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead> impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead + FsMetadata>
CjsTracker<TInNpmPackageChecker, TSys> CjsTracker<TInNpmPackageChecker, TSys>
{ {
pub fn new( pub fn new(
@ -155,14 +159,14 @@ pub enum IsCjsResolutionMode {
#[derive(Debug)] #[derive(Debug)]
pub struct IsCjsResolver< pub struct IsCjsResolver<
TInNpmPackageChecker: InNpmPackageChecker, TInNpmPackageChecker: InNpmPackageChecker,
TSys: FsRead, TSys: FsRead + FsMetadata,
> { > {
in_npm_pkg_checker: TInNpmPackageChecker, in_npm_pkg_checker: TInNpmPackageChecker,
pkg_json_resolver: PackageJsonResolverRc<TSys>, pkg_json_resolver: PackageJsonResolverRc<TSys>,
mode: IsCjsResolutionMode, mode: IsCjsResolutionMode,
} }
impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead> impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead + FsMetadata>
IsCjsResolver<TInNpmPackageChecker, TSys> IsCjsResolver<TInNpmPackageChecker, TSys>
{ {
pub fn new( pub fn new(

View file

@ -49,7 +49,7 @@ pub enum ByonmResolvePkgFolderFromDenoReqError {
JsrReqUnsupported { req: PackageReq }, JsrReqUnsupported { req: PackageReq },
} }
pub struct ByonmNpmResolverCreateOptions<TSys: FsRead> { pub struct ByonmNpmResolverCreateOptions<TSys: FsRead + FsMetadata> {
// todo(dsherret): investigate removing this // todo(dsherret): investigate removing this
pub root_node_modules_dir: Option<PathBuf>, pub root_node_modules_dir: Option<PathBuf>,
pub sys: NodeResolutionSys<TSys>, pub sys: NodeResolutionSys<TSys>,

View file

@ -0,0 +1,17 @@
# Copyright 2018-2025 the Deno authors. MIT license.
[package]
name = "deno_typescript_go_client_rust"
version = "0.1.0"
edition = "2024"
license = "MIT"
description = "Rust library to communicate with TSGO binary over IPC"
[dependencies]
indexmap = { version = "2", features = ["serde"] }
log = "0.4.28"
rmp = "0.8.14"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_repr = "0.1"
thiserror = "2"

View file

@ -0,0 +1,77 @@
// Copyright 2018-2025 the Deno authors. MIT license.
// Partially extracted / adapted from https://github.com/microsoft/libsyncrpc
// Copyright 2024 Microsoft Corporation. MIT license.
use std::io::BufRead;
use std::io::Result;
use std::io::Write;
use std::io::{self};
/// Lower-level wrapper around RPC-related messaging and process management.
pub struct RpcConnection<R: BufRead, W: Write> {
reader: R,
writer: W,
}
impl<R: BufRead, W: Write> RpcConnection<R, W> {
pub fn new(reader: R, writer: W) -> Result<Self> {
Ok(Self { reader, writer })
}
pub fn write(&mut self, ty: u8, name: &[u8], payload: &[u8]) -> Result<()> {
let w = &mut self.writer;
rmp::encode::write_array_len(w, 3)?;
rmp::encode::write_u8(w, ty)?;
rmp::encode::write_bin(w, name)?;
rmp::encode::write_bin(w, payload)?;
w.flush()?;
Ok(())
}
pub fn read(&mut self) -> Result<(u8, Vec<u8>, Vec<u8>)> {
let r = &mut self.reader;
assert_eq!(
rmp::decode::read_array_len(r).map_err(to_io)?,
3,
"Message components must be a valid 3-part messagepack array."
);
Ok((
rmp::decode::read_int(r).map_err(to_io)?,
self.read_bin()?,
self.read_bin()?,
))
}
fn read_bin(&mut self) -> Result<Vec<u8>> {
let r = &mut self.reader;
let payload_len = rmp::decode::read_bin_len(r).map_err(to_io)?;
let mut payload = vec![0u8; payload_len as usize];
r.read_exact(&mut payload)?;
Ok(payload)
}
// Helper method to create an error
pub fn create_error(
&self,
name: &str,
payload: Vec<u8>,
expected_method: &str,
) -> io::Error {
if name == expected_method {
let payload = match String::from_utf8(payload) {
Ok(payload) => payload,
Err(err) => return io::Error::other(format!("{err}")),
};
io::Error::other(payload)
} else {
io::Error::other(format!(
"name mismatch for response: expected `{expected_method}`, got `{name}`"
))
}
}
}
fn to_io<T: std::error::Error>(err: T) -> io::Error {
io::Error::other(format!("{err}"))
}

View file

@ -0,0 +1,320 @@
// Copyright 2018-2025 the Deno authors. MIT license.
// Partially extracted / adapted from https://github.com/microsoft/libsyncrpc
// Copyright 2024 Microsoft Corporation. MIT license.
pub mod connection;
pub mod types;
use std::collections::HashSet;
use std::ffi::OsStr;
use std::io::BufReader;
use std::io::BufWriter;
use std::process::Child;
use std::process::ChildStdin;
use std::process::ChildStdout;
use connection::RpcConnection;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to spawn process: {0}")]
ProcessSpawn(#[source] std::io::Error),
#[error("Failed to kill process: {0}")]
ProcessKill(#[source] std::io::Error),
#[error("Error in RPC connection: {0}")]
RpcConnection(#[source] std::io::Error),
#[error("Error encoding {obj} as {ty}: {source}")]
Encoding {
obj: &'static str,
ty: &'static str,
source: Box<Error>,
},
#[error("Error decoding UTF-8: {0}")]
Utf8(#[source] std::string::FromUtf8Error),
#[error("Invalid message type: {0}")]
InvalidMessageType(MessageType),
#[error("{0}")]
AdHoc(String),
#[error("serde json error: {0}")]
Json(#[from] serde_json::Error),
}
impl Error {
pub fn from_reason<S: Into<String>>(reason: S) -> Self {
Self::AdHoc(reason.into())
}
}
pub trait CallbackHandler {
fn supported_callbacks(&self) -> &'static [&'static str];
fn handle_callback(
&self,
name: &str,
payload: String,
) -> Result<String, Error>;
}
/// A synchronous RPC channel that allows JavaScript to synchronously call out
/// to a child process and get a response over a line-based protocol,
/// including handling of JavaScript-side callbacks before the call completes.
///
/// #### Protocol
///
/// Requests follow a MessagePack-based "tuple"/array protocol with 3 items:
/// `(<type>, <name>, <payload>)`. All items are binary arrays of 8-bit
/// integers, including the `<type>` and `<name>`, to avoid unnecessary
/// encoding/decoding at the protocol level.
///
/// For specific message types and their corresponding protocol behavior, please
/// see `MessageType` below.
pub struct SyncRpcChannel<T> {
child: Child,
conn: RpcConnection<BufReader<ChildStdout>, BufWriter<ChildStdin>>,
callback_handler: T,
supported_callbacks: HashSet<&'static str>,
}
impl<T: CallbackHandler> SyncRpcChannel<T> {
/// Constructs a new `SyncRpcChannel` by spawning a child process with the
/// given `exe` executable, and a given set of `args`.
pub fn new<I, S>(
exe: impl AsRef<OsStr>,
args: I,
callback_handler: T,
) -> Result<Self, Error>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut child = std::process::Command::new(exe)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
.args(args)
.spawn()
.map_err(Error::ProcessSpawn)?;
let supported_callbacks = callback_handler.supported_callbacks();
Ok(Self {
conn: RpcConnection::new(
BufReader::new(child.stdout.take().expect("Where did ChildStdout go?")),
BufWriter::new(child.stdin.take().expect("Where did ChildStdin go?")),
)
.map_err(Error::RpcConnection)?,
supported_callbacks: supported_callbacks.iter().copied().collect(),
callback_handler,
child,
})
}
/// Send a request to the child process and wait for a response. The method
/// will not return, synchronously, until a response is received or an error
/// occurs.
///
/// This method will take care of encoding and decoding the binary payload to
/// and from a JS string automatically and suitable for smaller payloads.
pub fn request_sync(
&mut self,
method: &str,
payload: String,
) -> Result<String, Error> {
self
.request_bytes_sync(method, payload.as_bytes())
.and_then(|arr| {
String::from_utf8((&arr[..]).into()).map_err(|e| Error::Encoding {
obj: "response",
ty: "string",
source: Box::new(Error::Utf8(e)),
})
})
}
/// Send a request to the child process and wait for a response. The method
/// will not return, synchronously, until a response is received or an error
/// occurs.
///
/// Unlike `requestSync`, this method will not do any of its own encoding or
/// decoding of payload data. Everything will be as sent/received through the
/// underlying protocol.
pub fn request_bytes_sync(
&mut self,
method: &str,
payload: &[u8],
) -> Result<Vec<u8>, Error> {
log::trace!("request_bytes_sync: {method}");
let method_bytes = method.as_bytes();
self
.conn
.write(MessageType::Request as u8, method_bytes, payload)
.map_err(Error::RpcConnection)?;
loop {
let (ty, name, payload) =
self.conn.read().map_err(Error::RpcConnection)?;
match ty.try_into().map_err(Error::from_reason)? {
MessageType::Response => {
if name == method_bytes {
return Ok(payload);
} else {
let name = String::from_utf8_lossy(&name);
return Err(Error::from_reason(format!(
"name mismatch for response: expected `{method}`, got `{name}`"
)));
}
}
MessageType::Error => {
return Err(Error::RpcConnection(self.conn.create_error(
&String::from_utf8_lossy(&name),
payload,
method,
)));
}
MessageType::Call => {
self.handle_call(&String::from_utf8_lossy(&name), payload)?;
}
_ => {
return Err(Error::from_reason(format!(
"Invalid message type from child: {ty:?}"
)));
}
}
}
}
// Closes the channel, terminating its underlying process.
pub fn close(&mut self) -> Result<(), Error> {
self.child.kill().map_err(Error::ProcessKill)?;
Ok(())
}
// Helper method to handle callback calls
fn handle_call(&mut self, name: &str, payload: Vec<u8>) -> Result<(), Error> {
if !self.supported_callbacks.contains(name) {
self.conn.write(MessageType::CallError as u8, name.as_bytes(), format!("unknown callback: `{name}`. Please make sure to register it on the JavaScript side before invoking it.").as_bytes())
.map_err(Error::RpcConnection)?;
return Err(Error::from_reason(format!(
"no callback named `{name}` found"
)));
}
let res = self
.callback_handler
.handle_callback(name, String::from_utf8(payload).map_err(Error::Utf8)?);
match res {
Ok(res) => {
self
.conn
.write(
MessageType::CallResponse as u8,
name.as_bytes(),
res.as_bytes(),
)
.map_err(Error::RpcConnection)?;
}
Err(e) => {
self
.conn
.write(
MessageType::CallError as u8,
name.as_bytes(),
format!("{e}").trim().as_bytes(),
)
.map_err(Error::RpcConnection)?;
return Err(Error::from_reason(format!(
"Error calling callback `{name}`: {}",
e
)));
}
}
Ok(())
}
}
/// Messages types exchanged between the channel and its child. All messages
/// have an associated `<name>` and `<payload>`, which will both be arrays of
/// 8-bit integers (`Uint8Array`s).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum MessageType {
// --- Sent by channel---
/// A request to the child with the given raw byte `<payload>`, with
/// `<name>` as the method name. The child may send back any number of
/// `MessageType.Call` messages and must then close the request with either a
/// `MessageType.Response`, or a `MessageType.Error`. message.
Request = 1,
/// A response to a `MessageType.Call` message that the child previously sent.
/// The `<payload>` is the return value from invoking the JavaScript callback
/// associated with it. If the callback errors, `MessageType.CallError` will
/// be sent to the child.
CallResponse,
/// Informs the child that an error occurred. The `<payload>` will be the
/// binary representation of the stringified error, as UTF-8 bytes, not
/// necessarily in JSON format. The method linked to this message will also
/// throw an error after sending this message to its child and terminate the
/// request call.
CallError,
// --- Sent by child ---
/// A response to a request that the call was for. `<name>` MUST match the
/// `MessageType.Request` message's `<name>` argument.
Response,
/// A response that denotes some error occurred while processing the request
/// on the child side. The `<payload>` will simply be the binary
/// representation of the stringified error, as UTF-8 bytes, not necessarily
/// in JSON format. The method associated with this call will also throw an
/// error after receiving this message from the child.
Error,
/// A request to invoke a pre-registered JavaScript callback (see
/// `SyncRpcChannel#registerCallback`). `<name>` is the name of the callback,
/// and `<payload>` is an encoded UTF-8 string that the callback will be
/// called with. The child should then listen for `MessageType.CallResponse`
/// and `MessageType.CallError` messages.
Call,
// NOTE: Do NOT put any variants below this one, always add them _before_ it.
// See comment in TryFrom impl, and remove this when `variant_count` stabilizes.
_UnusedPlaceholderVariant,
// NOTHING SHOULD GO BELOW HERE
}
impl std::fmt::Display for MessageType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageType::Request => write!(f, "MessageType::Request"),
MessageType::CallResponse => write!(f, "MessageType::CallResponse"),
MessageType::CallError => write!(f, "MessageType::CallError"),
MessageType::Response => write!(f, "MessageType::Response"),
MessageType::Error => write!(f, "MessageType::Error"),
MessageType::Call => write!(f, "MessageType::Call"),
MessageType::_UnusedPlaceholderVariant => {
write!(f, "MessageType::_UnusedPlaceholderVariant")
}
}
}
}
impl TryFrom<u8> for MessageType {
type Error = String;
fn try_from(
value: u8,
) -> std::result::Result<Self, <MessageType as TryFrom<u8>>::Error> {
// TODO: change to the following line when `variant_count` stabilizes
// (https://github.com/rust-lang/rust/issues/73662) and remove `_UnusedPlaceholderVariant`
//
// if (1..=std::mem::variant_count::<MessageType>()) {
if (1..(MessageType::_UnusedPlaceholderVariant as u8)).contains(&value) {
// SAFETY: This is safe as long as the above holds true. It'll be fully
// safe once `variant_count` stabilizes.
Ok(unsafe { std::mem::transmute::<u8, MessageType>(value) })
} else {
Err(format!("Invalid message type: {value}"))
}
}
}

View file

@ -0,0 +1,134 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::marker::PhantomData;
use indexmap::IndexMap;
#[derive(serde::Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub struct Position {
pub line: u64,
pub character: u64,
}
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub file_name: String,
pub start: Position,
pub end: Position,
pub start_pos: u32,
pub end_pos: u32,
pub code: u32,
pub category: String,
pub message: String,
pub message_chain: Vec<Diagnostic>,
pub related_information: Vec<Diagnostic>,
pub reports_unnecessary: bool,
pub reports_deprecated: bool,
pub skipped_on_no_emit: bool,
pub source_line: String,
}
pub type DiagnosticId = u32;
#[derive(
serde_repr::Deserialize_repr, serde_repr::Serialize_repr, Debug, Clone, Copy,
)]
#[repr(u32)]
pub enum ResolutionMode {
None = 0,
CommonJS = 1,
ESM = 99,
}
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ResolveModuleNamePayload {
pub module_name: String,
pub containing_file: String,
pub resolution_mode: ResolutionMode,
// redirected_reference: Handle<Project>,
}
#[derive(serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResolveTypeReferenceDirectivePayload {
pub type_reference_directive_name: String,
pub containing_file: String,
pub resolution_mode: ResolutionMode,
}
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(from = "String", into = "String")]
pub struct Handle<T> {
pub id: String,
#[serde(skip)]
_phantom: PhantomData<T>,
}
impl<T> std::fmt::Debug for Handle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Handle").field(&self.id).finish()
}
}
impl<T> From<Handle<T>> for String {
fn from(value: Handle<T>) -> Self {
value.id
}
}
impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
_phantom: PhantomData,
}
}
}
impl<T> From<String> for Handle<T> {
fn from(id: String) -> Self {
Self {
id,
_phantom: PhantomData,
}
}
}
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Project {
pub id: Handle<Self>,
pub config_file_name: String,
pub root_files: Vec<String>,
pub compiler_options: IndexMap<String, serde_json::Value>,
}
#[derive(
Debug, Clone, serde_repr::Deserialize_repr, serde_repr::Serialize_repr,
)]
#[repr(u32)]
pub enum ModuleKind {
None = 0,
CommonJS = 1,
AMD = 2,
UMD = 3,
System = 4,
ES2015 = 5,
ES2020 = 6,
ES2022 = 7,
ESNext = 99,
Node16 = 100,
Node18 = 101,
NodeNext = 199,
Preserve = 200,
}
#[derive(serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GetImpliedNodeFormatForFilePayload {
pub file_name: String,
pub package_json_type: String,
}

View file

@ -232,4 +232,12 @@ pub static FEATURE_DESCRIPTIONS: &[UnstableFeatureDescription] = &[
config_option: ConfigFileOption::SameAsFlagName, config_option: ConfigFileOption::SameAsFlagName,
env_var: None, env_var: None,
}, },
UnstableFeatureDescription {
name: "tsgo",
help_text: "Enable unstable TypeScript Go integration",
show_in_help: true,
kind: UnstableFeatureKind::Cli,
config_option: ConfigFileOption::SameAsFlagName,
env_var: Some("DENO_UNSTABLE_TSGO"),
},
]; ];

View file

@ -21,8 +21,8 @@ export const unstableIds = {
process: 17, process: 17,
rawImports: 18, rawImports: 18,
temporal: 21, temporal: 21,
unsafeProto: 22, unsafeProto: 23,
vsock: 23, vsock: 24,
webgpu: 24, webgpu: 25,
workerOptions: 25, workerOptions: 26,
}; };

View file

@ -205,12 +205,21 @@ pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[
kind: UnstableFeatureKind::Runtime, kind: UnstableFeatureKind::Runtime,
config_file_option: "temporal", config_file_option: "temporal",
}, },
UnstableFeatureDefinition {
name: "tsgo",
flag_name: "unstable-tsgo",
help_text: "Enable unstable TypeScript Go integration",
show_in_help: true,
id: 22,
kind: UnstableFeatureKind::Cli,
config_file_option: "tsgo",
},
UnstableFeatureDefinition { UnstableFeatureDefinition {
name: "unsafe-proto", name: "unsafe-proto",
flag_name: "unstable-unsafe-proto", flag_name: "unstable-unsafe-proto",
help_text: "Enable unsafe __proto__ support. This is a security risk.", help_text: "Enable unsafe __proto__ support. This is a security risk.",
show_in_help: true, show_in_help: true,
id: 22, id: 23,
kind: UnstableFeatureKind::Runtime, kind: UnstableFeatureKind::Runtime,
config_file_option: "unsafe-proto", config_file_option: "unsafe-proto",
}, },
@ -219,7 +228,7 @@ pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[
flag_name: "unstable-vsock", flag_name: "unstable-vsock",
help_text: "Enable unstable VSOCK APIs", help_text: "Enable unstable VSOCK APIs",
show_in_help: false, show_in_help: false,
id: 23, id: 24,
kind: UnstableFeatureKind::Runtime, kind: UnstableFeatureKind::Runtime,
config_file_option: "vsock", config_file_option: "vsock",
}, },
@ -228,7 +237,7 @@ pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[
flag_name: "unstable-webgpu", flag_name: "unstable-webgpu",
help_text: "Enable unstable WebGPU APIs", help_text: "Enable unstable WebGPU APIs",
show_in_help: true, show_in_help: true,
id: 24, id: 25,
kind: UnstableFeatureKind::Runtime, kind: UnstableFeatureKind::Runtime,
config_file_option: "webgpu", config_file_option: "webgpu",
}, },
@ -237,7 +246,7 @@ pub static UNSTABLE_FEATURES: &[UnstableFeatureDefinition] = &[
flag_name: "unstable-worker-options", flag_name: "unstable-worker-options",
help_text: "Enable unstable Web Worker APIs", help_text: "Enable unstable Web Worker APIs",
show_in_help: true, show_in_help: true,
id: 25, id: 26,
kind: UnstableFeatureKind::Runtime, kind: UnstableFeatureKind::Runtime,
config_file_option: "worker-options", config_file_option: "worker-options",
}, },
@ -250,6 +259,7 @@ pub struct UnstableEnvVarNames {
pub raw_imports: &'static str, pub raw_imports: &'static str,
pub sloppy_imports: &'static str, pub sloppy_imports: &'static str,
pub subdomain_wildcards: &'static str, pub subdomain_wildcards: &'static str,
pub tsgo: &'static str,
} }
pub static UNSTABLE_ENV_VAR_NAMES: UnstableEnvVarNames = UnstableEnvVarNames { pub static UNSTABLE_ENV_VAR_NAMES: UnstableEnvVarNames = UnstableEnvVarNames {
bare_node_builtins: "DENO_UNSTABLE_BARE_NODE_BUILTINS", bare_node_builtins: "DENO_UNSTABLE_BARE_NODE_BUILTINS",
@ -259,4 +269,5 @@ pub static UNSTABLE_ENV_VAR_NAMES: UnstableEnvVarNames = UnstableEnvVarNames {
raw_imports: "DENO_UNSTABLE_RAW_IMPORTS", raw_imports: "DENO_UNSTABLE_RAW_IMPORTS",
sloppy_imports: "DENO_UNSTABLE_SLOPPY_IMPORTS", sloppy_imports: "DENO_UNSTABLE_SLOPPY_IMPORTS",
subdomain_wildcards: "DENO_UNSTABLE_SUBDOMAIN_WILDCARDS", subdomain_wildcards: "DENO_UNSTABLE_SUBDOMAIN_WILDCARDS",
tsgo: "DENO_UNSTABLE_TSGO",
}; };

View file

@ -6,6 +6,10 @@ use util::TestContextBuilder;
use util::assert_not_contains; use util::assert_not_contains;
use util::testdata_path; use util::testdata_path;
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn compile_basic() { fn compile_basic() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -57,6 +61,10 @@ fn compile_basic() {
output.assert_matches_text("Welcome to Deno!\n"); output.assert_matches_text("Welcome to Deno!\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_args() { fn standalone_args() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -88,6 +96,10 @@ fn standalone_args() {
.assert_matches_text("a\nb\nfoo\n--bar\n--unstable\n"); .assert_matches_text("a\nb\nfoo\n--bar\n--unstable\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_load_datauri() { fn standalone_load_datauri() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -117,6 +129,10 @@ fn standalone_load_datauri() {
} }
// https://github.com/denoland/deno/issues/13704 // https://github.com/denoland/deno/issues/13704
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_follow_redirects() { fn standalone_follow_redirects() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -262,6 +278,10 @@ fn compile_and_overwrite_file() {
} }
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_runtime_flags() { fn standalone_runtime_flags() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -298,6 +318,10 @@ fn standalone_runtime_flags() {
); );
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_ext_flag_ts() { fn standalone_ext_flag_ts() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -329,6 +353,10 @@ fn standalone_ext_flag_ts() {
.assert_matches_text("executing typescript with no extension\n"); .assert_matches_text("executing typescript with no extension\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_ext_flag_js() { fn standalone_ext_flag_js() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -359,6 +387,10 @@ fn standalone_ext_flag_js() {
.assert_matches_text("executing javascript with no extension\n"); .assert_matches_text("executing javascript with no extension\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_import_map() { fn standalone_import_map() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -390,6 +422,10 @@ fn standalone_import_map() {
.assert_exit_code(0); .assert_exit_code(0);
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn standalone_import_map_config_file() { fn standalone_import_map_config_file() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
@ -421,6 +457,10 @@ fn standalone_import_map_config_file() {
.assert_exit_code(0); .assert_exit_code(0);
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
// https://github.com/denoland/deno/issues/12670 // https://github.com/denoland/deno/issues/12670
fn skip_rebundle() { fn skip_rebundle() {
@ -500,6 +540,10 @@ fn check_local_by_default2() {
); );
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn workers_basic() { fn workers_basic() {
let context = TestContext::with_http_server(); let context = TestContext::with_http_server();
@ -530,6 +574,10 @@ fn workers_basic() {
.assert_matches_file("./compile/workers/basic.out"); .assert_matches_file("./compile/workers/basic.out");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn workers_not_in_module_map() { fn workers_not_in_module_map() {
let context = TestContext::with_http_server(); let context = TestContext::with_http_server();
@ -559,6 +607,10 @@ fn workers_not_in_module_map() {
)); ));
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn workers_with_include_flag() { fn workers_with_include_flag() {
let context = TestContext::with_http_server(); let context = TestContext::with_http_server();
@ -590,6 +642,10 @@ fn workers_with_include_flag() {
.assert_matches_text("Hello from worker!\nReceived 42\nClosing\n"); .assert_matches_text("Hello from worker!\nReceived 42\nClosing\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn dynamic_import() { fn dynamic_import() {
let context = TestContext::with_http_server(); let context = TestContext::with_http_server();
@ -620,6 +676,10 @@ fn dynamic_import() {
.assert_exit_code(0); .assert_exit_code(0);
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn dynamic_import_unanalyzable() { fn dynamic_import_unanalyzable() {
let context = TestContext::with_http_server(); let context = TestContext::with_http_server();
@ -772,6 +832,10 @@ testing[WILDCARD]this
.assert_matches_text("2\n"); .assert_matches_text("2\n");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn compile_npm_bin_esm() { fn compile_npm_bin_esm() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
@ -788,6 +852,10 @@ fn compile_npm_bin_esm() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn compile_npm_bin_cjs() { fn compile_npm_bin_cjs() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
input_specifier: "npm:@denotest/bin/cli-cjs", input_specifier: "npm:@denotest/bin/cli-cjs",
@ -803,6 +871,10 @@ fn compile_npm_bin_cjs() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn compile_npm_cowsay_main() { fn compile_npm_cowsay_main() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
input_specifier: "npm:cowsay@1.5.0", input_specifier: "npm:cowsay@1.5.0",
@ -818,6 +890,10 @@ fn compile_npm_cowsay_main() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn compile_npm_no_permissions() { fn compile_npm_no_permissions() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
input_specifier: "npm:@denotest/cli-with-permissions@1.0.0", input_specifier: "npm:@denotest/cli-with-permissions@1.0.0",
@ -833,6 +909,10 @@ fn compile_npm_no_permissions() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn compile_npm_cowsay_explicit() { fn compile_npm_cowsay_explicit() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
input_specifier: "npm:cowsay@1.5.0/cowsay", input_specifier: "npm:cowsay@1.5.0/cowsay",
@ -848,6 +928,10 @@ fn compile_npm_cowsay_explicit() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn compile_npm_cowthink() { fn compile_npm_cowthink() {
run_npm_bin_compile_test(RunNpmBinCompileOptions { run_npm_bin_compile_test(RunNpmBinCompileOptions {
input_specifier: "npm:cowsay@1.5.0/cowthink", input_specifier: "npm:cowsay@1.5.0/cowthink",
@ -921,6 +1005,10 @@ fn run_npm_bin_compile_test(opts: RunNpmBinCompileOptions) {
output.assert_matches_file(opts.output_file); output.assert_matches_file(opts.output_file);
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn compile_node_modules_symlink_outside() { fn compile_node_modules_symlink_outside() {
// this code is using a canonicalized temp dir because otherwise // this code is using a canonicalized temp dir because otherwise
@ -989,6 +1077,10 @@ fn compile_node_modules_symlink_outside() {
output.assert_matches_file("compile/node_modules_symlink_outside/main.out"); output.assert_matches_file("compile/node_modules_symlink_outside/main.out");
} }
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
#[test] #[test]
fn compile_node_modules_symlink_non_existent() { fn compile_node_modules_symlink_non_existent() {
let context = TestContextBuilder::for_npm().use_temp_cwd().build(); let context = TestContextBuilder::for_npm().use_temp_cwd().build();
@ -1033,6 +1125,10 @@ Embedded Files
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn dynamic_imports_tmp_lit() { fn dynamic_imports_tmp_lit() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
let dir = context.temp_dir(); let dir = context.temp_dir();
@ -1057,6 +1153,10 @@ fn dynamic_imports_tmp_lit() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn granular_unstable_features() { fn granular_unstable_features() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
let dir = context.temp_dir(); let dir = context.temp_dir();
@ -1084,6 +1184,10 @@ fn granular_unstable_features() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn granular_unstable_features_config_file() { fn granular_unstable_features_config_file() {
let context = TestContextBuilder::new().use_temp_cwd().build(); let context = TestContextBuilder::new().use_temp_cwd().build();
let dir = context.temp_dir(); let dir = context.temp_dir();
@ -1121,6 +1225,10 @@ fn granular_unstable_features_config_file() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn dynamic_import_bad_data_uri() { fn dynamic_import_bad_data_uri() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
let dir = context.temp_dir(); let dir = context.temp_dir();
@ -1150,6 +1258,10 @@ fn dynamic_import_bad_data_uri() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn standalone_config_file_respects_compiler_options() { fn standalone_config_file_respects_compiler_options() {
let context = TestContextBuilder::new().build(); let context = TestContextBuilder::new().build();
let dir = context.temp_dir(); let dir = context.temp_dir();
@ -1179,6 +1291,10 @@ fn standalone_config_file_respects_compiler_options() {
} }
#[test] #[test]
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", debug_assertions),
ignore // TODO: fix this, see https://github.com/denoland/sui/issues/43
)]
fn standalone_jsr_dynamic_import() { fn standalone_jsr_dynamic_import() {
let context = TestContextBuilder::for_jsr().build(); let context = TestContextBuilder::for_jsr().build();
let dir = context.temp_dir(); let dir = context.temp_dir();

View file

@ -1,18 +1,36 @@
{ {
"steps": [{ "steps": [
"args": "check foo.ts", {
"output": "foo.out" "args": "check foo.ts",
}, { "output": "foo.out"
"args": "check bar.ts", },
"output": "bar.out", {
"exitCode": 1 "args": "check bar.ts",
}, { "output": "bar.out",
"args": "run foo.ts", "exitCode": 1
"output": "run.out", },
"exitCode": 1 {
}, { "args": "run foo.ts",
"args": "run bar.ts", "output": "run.out",
"output": "run.out", "exitCode": 1
"exitCode": 1 },
}] {
"args": "run bar.ts",
"output": "run.out",
"exitCode": 1
}
],
"variants": {
// TODO(nathanwhit): not implemented yet, needs extra api to get ambient modules on
// the tsgo side
// "tsgo": {
// "use_tsgo": "1"
// },
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,3 @@
// https://github.com/denoland/deno/issues/30116
{ {
"tests": { "tests": {
"import_map": { "import_map": {
@ -11,5 +10,16 @@
"output": "check_no_import_map.out", "output": "check_no_import_map.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "check.out" "output": "check.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,21 @@
{ {
"args": "check --unstable-raw-imports main.ts", "args": "check --unstable-raw-imports main.ts",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
// TODO(nathanwhit): not implemented yet, needs tsgo changes
// see appendRawImportFragment in cli/tsc/97_ts_host.js and the code that calls it.
// it will need tsgo changes because the import kind is not easily available on the tsgo side
// during resolution.
//
// "tsgo": {
// "use_tsgo": "1"
// },
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --allow-import --quiet --all check_all.ts", "args": "check --allow-import --quiet --all check_all.ts",
"output": "check_all.out", "output": "check_all.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --allow-import --quiet check_all_local.ts", "args": "check --allow-import --quiet check_all_local.ts",
"output": "", "output": "",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet broadcast_channel.ts", "args": "check --quiet broadcast_channel.ts",
"output": "", "output": "",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet deno_not_found/main.ts", "args": "check --quiet deno_not_found/main.ts",
"output": "deno_not_found/main.out", "output": "deno_not_found/main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -0,0 +1,5 @@
{
"compilerOptions": {
"lib": []
}
}

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet dts/check_dts.d.ts", "args": "check --quiet dts/check_dts.d.ts",
"output": "dts/check_dts.out", "output": "dts/check_dts.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -15,5 +15,16 @@
"output": "exclude_option.ts.error.out", "output": "exclude_option.ts.error.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet --config exclude_option/deno.exclude_dir.json exclude_option/index.ts", "args": "check --quiet --config exclude_option/deno.exclude_dir.json exclude_option/index.ts",
"output": "exclude_option/exclude_option.ts.error.out", "output": "exclude_option/exclude_option.ts.error.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet --config jsximportsource_importmap_config/deno.json jsximportsource_importmap_config/main.tsx", "args": "check --quiet --config jsximportsource_importmap_config/deno.json jsximportsource_importmap_config/main.tsx",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet no_error_truncation/main.ts --config no_error_truncation/deno.json", "args": "check --quiet no_error_truncation/main.ts --config no_error_truncation/deno.json",
"output": "no_error_truncation/main.out", "output": "no_error_truncation/main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -2,13 +2,26 @@
"tests": { "tests": {
"js": { "js": {
"args": "check --quiet mod.js", "args": "check --quiet mod.js",
"output": "mod.js.out", "output": "mod.js${out_suffix}",
"exitCode": 1 "exitCode": 1
}, },
"ts": { "ts": {
"args": "check --quiet mod.ts", "args": "check --quiet mod.ts",
"output": "mod.ts.out", "output": "mod.ts${out_suffix}",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1",
"out_suffix": ".tsgo.out"
},
"tsc": {
"use_tsgo": "",
"out_suffix": ".out"
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -0,0 +1,11 @@
TS2769 [ERROR]: No overload matches this call.
const _data = fs.readFileSync("./node_builtin.js", 123);
~~~
at [WILDLINE]/mod.js:3:52
TS2771 [ERROR]: The last overload is declared here.
export function readFileSync(
~~~~~~~~~~~~
at [WILDLINE]/fs.d.ts:2925:21
error: Type checking failed.

View file

@ -0,0 +1,18 @@
TS2769 [ERROR]: No overload matches this call.
const _data = fs.readFileSync("./node_builtin.js", 123);
~~~
at [WILDLINE]/mod.ts:2:52
TS2771 [ERROR]: The last overload is declared here.
export function readFileSync(
~~~~~~~~~~~~
at [WILDLINE]/fs.d.ts:2925:21
TS4104 [ERROR]: The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
const _testString: number[] = builtinModules;
~~~~~~~~~~~
at [WILDLINE]/mod.ts:9:7
Found 2 errors.
error: Type checking failed.

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet", "args": "check --quiet",
"output": "", "output": "",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet npm_install_diagnostics/main.ts", "args": "check --quiet npm_install_diagnostics/main.ts",
"output": "npm_install_diagnostics/main.out", "output": "npm_install_diagnostics/main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet response_json.ts", "args": "check --quiet response_json.ts",
"output": "", "output": "",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "main.out", "output": "main.out",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check lib/types.d.ts", "args": "check lib/types.d.ts",
"output": "check.out" "output": "check.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -10,5 +10,16 @@
"output": "check_config_flag.out", "output": "check_config_flag.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -9,5 +9,16 @@
"output": "unmapped.out", "output": "unmapped.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet subdir/mod.ts", "args": "check --quiet subdir/mod.ts",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -49,5 +49,16 @@
} }
] ]
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,27 +1,46 @@
{ {
"steps": [{ "steps": [
"args": "check exists.ts", {
"output": "exists.out" "args": "check exists.ts",
}, { "output": "exists.out"
"args": "run --check exists.ts", },
"output": "exists_run_with_check.out", {
"exitCode": 1 "args": "run --check exists.ts",
}, { "output": "exists_run_with_check.out",
"args": "check not_exists.ts", "exitCode": 1
"output": "not_exists.out", },
"exitCode": 1 {
}, { "args": "check not_exists.ts",
"args": "run --check not_exists.ts", "output": "not_exists.out",
"output": "not_exists_run.out", "exitCode": 1
"exitCode": 1 },
}, { {
"args": "check exists_and_try_uses.ts", "args": "run --check not_exists.ts",
"output": "exists_and_try_uses.out" "output": "not_exists_run.out",
}, { "exitCode": 1
"args": "check exists_dynamic_import.ts", },
"output": "Check file:///[WILDCARD]exists_dynamic_import.ts\n" {
}, { "args": "check exists_and_try_uses.ts",
"args": "run --check --reload exists_dynamic_import.ts", "output": "exists_and_try_uses.out"
"output": "Check file:///[WILDCARD]exists_dynamic_import.ts\n" },
}] {
"args": "check exists_dynamic_import.ts",
"output": "Check file:///[WILDCARD]exists_dynamic_import.ts\n"
},
{
"args": "run --check --reload exists_dynamic_import.ts",
"output": "Check file:///[WILDCARD]exists_dynamic_import.ts\n"
}
],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet declaration_header_file_with_no_exports.ts", "args": "check --quiet declaration_header_file_with_no_exports.ts",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,7 +1,8 @@
{ {
"tempDir": true, "tempDir": true,
"envs": { "envs": {
"RUST_BACKTRACE": "0" "RUST_BACKTRACE": "0",
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}, },
"tests": { "tests": {
"node_modules_dir_auto": { "node_modules_dir_auto": {
@ -72,5 +73,13 @@
} }
] ]
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check index.js", "args": "check index.js",
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "error.out", "output": "error.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,10 +1,23 @@
{ {
"steps": [{ "steps": [
"args": "check main.ts", {
"output": "main.out" "args": "check main.ts",
}, { "output": "main.out"
// ensure diagnostic is not cached },
"args": "check main.ts", {
"output": "main.out" "args": "check main.ts",
}] "output": "main.out"
}
],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --reload --all http://localhost:4545/subdir/no_js_ext", "args": "check --reload --all http://localhost:4545/subdir/no_js_ext",
"output": "check.out" "output": "check.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -20,5 +20,16 @@
"output": "Check [WILDLINE]sub_dir/main.ts\nTS2322[WILDCARD]", "output": "Check [WILDLINE]sub_dir/main.ts\nTS2322[WILDCARD]",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -49,5 +49,16 @@
} }
] ]
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -10,5 +10,16 @@
"output": "check_all.out", "output": "check_all.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "[WILDCARD]", "output": "[WILDCARD]",
"exitCode": 0 // should be successful "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --allow-import main.js", "args": "check --allow-import main.js",
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,10 +1,24 @@
{ {
"tempDir": true, "tempDir": true,
"steps": [{ "steps": [
"args": "install", {
"output": "[WILDCARD]" "args": "install",
}, { "output": "[WILDCARD]"
"args": "check main.tsx", },
"output": "check.out" {
}] "args": "check main.tsx",
"output": "check.out"
}
],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "main.out" "output": "main.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --allow-import --all main.tsx", "args": "check --allow-import --all main.tsx",
"output": "main.out" "output": "main.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --allow-import --all main.tsx", "args": "check --allow-import --all main.tsx",
"output": "main.out" "output": "main.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check jsx_not_checked/main.jsx", "args": "check jsx_not_checked/main.jsx",
"output": "jsx_not_checked/main.out", "output": "jsx_not_checked/main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -2,30 +2,48 @@
"tempDir": true, "tempDir": true,
"tests": { "tests": {
"check": { "check": {
"steps": [{ "steps": [
"args": "check --frozen=true", {
"output": "[WILDCARD]The lockfile is out of date.[WILDCARD]", "args": "check --frozen=true",
"exitCode": 1 "output": "[WILDCARD]The lockfile is out of date.[WILDCARD]",
}, { "exitCode": 1
"args": "check --frozen=false", },
"output": "[WILDCARD]", {
"exitCode": 0 "args": "check --frozen=false",
}, { "output": "[WILDCARD]",
"args": "check --frozen=true", "exitCode": 0
"output": "[WILDCARD]", },
"exitCode": 0 {
}] "args": "check --frozen=true",
"output": "[WILDCARD]",
"exitCode": 0
}
]
}, },
"install": { "install": {
"steps": [{ "steps": [
"args": "install --frozen=false --entrypoint main.ts", {
"output": "[WILDCARD]", "args": "install --frozen=false --entrypoint main.ts",
"exitCode": 0 "output": "[WILDCARD]",
}, { "exitCode": 0
"args": "check --frozen=true", },
"output": "[WILDCARD]", {
"exitCode": 0 "args": "check --frozen=true",
}] "output": "[WILDCARD]",
"exitCode": 0
}
]
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --frozen=true", "args": "check --frozen=true",
"output": "[WILDCARD]", "output": "[WILDCARD]",
"exitCode": 0 "exitCode": 0,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,6 +1,18 @@
// Regression test for https://github.com/denoland/deno/issues/27411.
{ {
"args": "check --quiet message_chain_formatting.ts", "args": "check --quiet message_chain_formatting.ts",
"output": "message_chain_formatting.out", "output": "${out}",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1",
"out": "message_chain_formatting_tsgo.out"
},
"tsc": {
"use_tsgo": "",
"out": "message_chain_formatting.out"
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -0,0 +1,11 @@
TS2769 [ERROR]: No overload matches this call.
foo("hello", 42);
~~~~~~~
at [WILDLINE]/message_chain_formatting.ts:8:5
TS2771 [ERROR]: The last overload is declared here.
function foo(ss: string[], b: Date): void;
~~~
at [WILDLINE]/message_chain_formatting.ts:3:10
error: Type checking failed.

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet module_detection_force/main.ts", "args": "check --quiet module_detection_force/main.ts",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -20,5 +20,16 @@
"output": "missing_remote_root.out", "output": "missing_remote_root.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,17 @@
{ {
"args": "check --all main.ts", "args": "check --all main.ts",
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1,
"variants": {
// currently failing
// "tsgo": {
// "use_tsgo": "1"
// },
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -15,5 +15,16 @@
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1
} }
},
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
} }
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check main.ts", "args": "check main.ts",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,10 +1,24 @@
{ {
"steps": [{ "steps": [
"args": "check index.ts", {
"output": "check.out", "args": "check index.ts",
"exitCode": 1 "output": "check.out",
}, { "exitCode": 1
"args": "run index.ts", },
"output": "run.out" {
}] "args": "run index.ts",
"output": "run.out"
}
],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -9,5 +9,16 @@
"args": "check main.ts", "args": "check main.ts",
"output": "check.out" "output": "check.out"
} }
] ],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -5,5 +5,16 @@
"args": "check main.ts", "args": "check main.ts",
"output": "check.out" "output": "check.out"
} }
] ],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -10,5 +10,16 @@
"output": "fail_check.out", "output": "fail_check.out",
"exitCode": 1 "exitCode": 1
} }
] ],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -10,5 +10,16 @@
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1
} }
] ],
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --reload --all http://localhost:4545/subdir/no_js_ext@1.0.0", "args": "check --reload --all http://localhost:4545/subdir/no_js_ext@1.0.0",
"output": "output.out" "output": "output.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check ./main.ts", "args": "check ./main.ts",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --allow-import --all main.ts", "args": "check --allow-import --all main.ts",
"output": "Download [WILDLINE]\nCheck [WILDLINE]\n" "output": "Download [WILDLINE]\nCheck [WILDLINE]\n",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet types.d.ts", "args": "check --quiet types.d.ts",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check", "args": "check",
"output": "check.out" "output": "check.out",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --all main.ts", "args": "check --all main.ts",
"output": "check.out", "output": "check.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,4 +1,15 @@
{ {
"args": "check --quiet", "args": "check --quiet",
"output": "" "output": "",
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet", "args": "check --quiet",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

View file

@ -1,5 +1,16 @@
{ {
"args": "check --quiet", "args": "check --quiet",
"output": "main.out", "output": "main.out",
"exitCode": 1 "exitCode": 1,
"variants": {
"tsgo": {
"use_tsgo": "1"
},
"tsc": {
"use_tsgo": ""
}
},
"envs": {
"DENO_UNSTABLE_TSGO": "${use_tsgo}"
}
} }

Some files were not shown because too many files have changed in this diff Show more