diff --git a/Cargo.lock b/Cargo.lock index 29ca2adb85..ba909b8212 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,11 +1508,12 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.43.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c4c11bd51ef6738cabfc3c53f16c209a0b8615cb1e4e5bf3b14e3b5deebfe21" +checksum = "47a47412627aa0d08414eca0e8329128013ab70bdb2cdfdc5456c2214cf24c8f" dependencies = [ "boxed_error", + "capacity_builder 0.5.0", "deno_error", "deno_package_json", "deno_path_util", @@ -2085,9 +2086,9 @@ dependencies = [ [[package]] name = "deno_npm" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23" +checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27" dependencies = [ "async-trait", "capacity_builder 0.5.0", diff --git a/Cargo.toml b/Cargo.toml index 223a9c98e7..291d2c61ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,10 +51,10 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.330.0" } deno_bench_util = { version = "0.179.0", path = "./bench_util" } -deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.45.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } -deno_npm = "=0.27.0" +deno_npm = "=0.27.2" deno_path_util = "=0.3.0" deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } deno_runtime = { version = "0.193.0", path = "./runtime" } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index ebd321a20a..4ba85f1706 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -56,7 +56,6 @@ use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url::Url; -use deno_error::JsErrorBox; use deno_graph::GraphKind; pub use deno_json::check_warn_tsconfig; use deno_lint::linter::LintConfig as DenoLintConfig; @@ -842,8 +841,6 @@ impl CliOptions { } else { &[] }; - let config_parse_options = - deno_config::deno_json::ConfigParseOptions::default(); let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled && !flags.no_npm && !has_flag_env_var("DENO_NO_PACKAGE_JSON"); @@ -854,7 +851,6 @@ impl CliOptions { deno_json_cache: None, pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache), workspace_cache: None, - config_parse_options, additional_config_file_names, discover_pkg_json, maybe_vendor_override, @@ -1103,11 +1099,11 @@ impl CliOptions { } }; Ok(self.workspace().create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map: cli_arg_specified_import_map, }, - |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), )?) } @@ -2126,12 +2122,7 @@ mod test { let cwd = &std::env::current_dir().unwrap(); let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( Some("import-map.json"), Some(&config_file), @@ -2150,12 +2141,7 @@ mod test { let config_text = r#"{}"#; let config_specifier = ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new( - config_text, - config_specifier, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let actual = resolve_import_map_specifier( None, Some(&config_file), diff --git a/cli/factory.rs b/cli/factory.rs index 5cc99830bc..e3873b5164 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -11,8 +11,10 @@ use deno_core::error::AnyError; use deno_core::futures::FutureExt; use deno_core::FeatureChecker; use deno_error::JsErrorBox; +use deno_npm_cache::NpmCacheSetting; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::sloppy_imports::SloppyImportsCachedFs; @@ -67,19 +69,27 @@ use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; use crate::npm::create_cli_npm_resolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; +use crate::npm::CliNpmTarballCache; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CjsTracker; use crate::resolver::CliDenoResolver; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::NpmModuleLoader; use crate::standalone::binary::DenoCompileBinaryWriter; use crate::sys::CliSys; @@ -188,6 +198,7 @@ struct CliFactoryServices { emitter: Deferred>, feature_checker: Deferred>, file_fetcher: Deferred>, + found_pkg_json_dep_flag: Arc, fs: Deferred>, global_http_cache: Deferred>, http_cache: Deferred>, @@ -202,9 +213,18 @@ struct CliFactoryServices { module_load_preparer: Deferred>, node_code_translator: Deferred>, node_resolver: Deferred>, + npm_cache: Deferred>, npm_cache_dir: Deferred>, + npm_cache_http_client: Deferred>, + npm_graph_resolver: Deferred>, + npm_installer: Deferred>, + npm_registry_info_provider: Deferred>, npm_req_resolver: Deferred>, + npm_resolution: Arc, + npm_resolution_initializer: Deferred>, + npm_resolution_installer: Deferred>, npm_resolver: Deferred>, + npm_tarball_cache: Deferred>, parsed_source_cache: Deferred>, permission_desc_parser: Deferred>>, @@ -398,6 +418,18 @@ impl CliFactory { }) } + pub fn npm_cache(&self) -> Result<&Arc, AnyError> { + self.services.npm_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmCache::new( + self.npm_cache_dir()?.clone(), + self.sys(), + NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()), + cli_options.npmrc().clone(), + ))) + }) + } + pub fn npm_cache_dir(&self) -> Result<&Arc, AnyError> { self.services.npm_cache_dir.get_or_try_init(|| { let global_path = self.deno_dir()?.npm_folder_path(); @@ -410,6 +442,123 @@ impl CliFactory { }) } + pub fn npm_cache_http_client(&self) -> &Arc { + self.services.npm_cache_http_client.get_or_init(|| { + Arc::new(CliNpmCacheHttpClient::new( + self.http_client_provider().clone(), + self.text_only_progress_bar().clone(), + )) + }) + } + + pub fn npm_graph_resolver( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_graph_resolver.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmGraphResolver::new( + self.npm_installer_if_managed()?.cloned(), + self.services.found_pkg_json_dep_flag.clone(), + cli_options.unstable_bare_node_builtins(), + cli_options.default_npm_caching_strategy(), + ))) + }) + } + + pub fn npm_installer_if_managed( + &self, + ) -> Result>, AnyError> { + let options = self.cli_options()?; + if options.use_byonm() || options.no_npm() { + Ok(None) + } else { + Ok(Some(self.npm_installer()?)) + } + } + + pub fn npm_installer(&self) -> Result<&Arc, AnyError> { + self.services.npm_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmInstaller::new( + self.npm_cache()?.clone(), + Arc::new(NpmInstallDepsProvider::from_workspace( + cli_options.workspace(), + )), + self.npm_resolution().clone(), + self.npm_resolution_initializer()?.clone(), + self.npm_resolution_installer()?.clone(), + self.text_only_progress_bar(), + self.sys(), + self.npm_tarball_cache()?.clone(), + cli_options.maybe_lockfile().cloned(), + cli_options.node_modules_dir_path().cloned(), + cli_options.lifecycle_scripts_config(), + cli_options.npm_system_info(), + ))) + }) + } + + pub fn npm_registry_info_provider( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_registry_info_provider + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmRegistryInfoProvider::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + cli_options.npmrc().clone(), + ))) + }) + } + + pub fn npm_resolution(&self) -> &Arc { + &self.services.npm_resolution + } + + pub fn npm_resolution_initializer( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .npm_resolution_initializer + .get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInitializer::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + match cli_options.resolve_npm_resolution_snapshot()? { + Some(snapshot) => { + CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) + } + None => match cli_options.maybe_lockfile() { + Some(lockfile) => { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( + lockfile.clone(), + ) + } + None => CliNpmResolverManagedSnapshotOption::Specified(None), + }, + }, + ))) + }) + } + + pub fn npm_resolution_installer( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_resolution_installer.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(NpmResolutionInstaller::new( + self.npm_registry_info_provider()?.clone(), + self.npm_resolution().clone(), + cli_options.maybe_lockfile().cloned(), + ))) + }) + } + pub async fn npm_resolver( &self, ) -> Result<&Arc, AnyError> { @@ -419,7 +568,7 @@ impl CliFactory { .get_or_try_init_async( async { let cli_options = self.cli_options()?; - create_cli_npm_resolver(if cli_options.use_byonm() { + Ok(create_cli_npm_resolver(if cli_options.use_byonm() { CliNpmResolverCreateOptions::Byonm( CliByonmNpmResolverCreateOptions { sys: self.sys(), @@ -438,52 +587,43 @@ impl CliFactory { }, ) } else { + self + .npm_resolution_initializer()? + .ensure_initialized() + .await?; CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - http_client_provider: self.http_client_provider().clone(), - npm_install_deps_provider: Arc::new( - NpmInstallDepsProvider::from_workspace( - cli_options.workspace(), - ), - ), sys: self.sys(), - snapshot: match cli_options.resolve_npm_resolution_snapshot()? { - Some(snapshot) => { - CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )) - } - None => match cli_options.maybe_lockfile() { - Some(lockfile) => { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( - lockfile.clone(), - ) - } - None => { - CliNpmResolverManagedSnapshotOption::Specified(None) - } - }, - }, - maybe_lockfile: cli_options.maybe_lockfile().cloned(), + npm_resolution: self.npm_resolution().clone(), npm_cache_dir: self.npm_cache_dir()?.clone(), - cache_setting: cli_options.cache_setting(), - text_only_progress_bar: self.text_only_progress_bar().clone(), maybe_node_modules_path: cli_options .node_modules_dir_path() .cloned(), npm_system_info: cli_options.npm_system_info(), npmrc: cli_options.npmrc().clone(), - lifecycle_scripts: cli_options.lifecycle_scripts_config(), }, ) - }) - .await + })) } .boxed_local(), ) .await } + pub fn npm_tarball_cache( + &self, + ) -> Result<&Arc, AnyError> { + self.services.npm_tarball_cache.get_or_try_init(|| { + let cli_options = self.cli_options()?; + Ok(Arc::new(CliNpmTarballCache::new( + self.npm_cache()?.clone(), + self.npm_cache_http_client().clone(), + self.sys(), + cli_options.npmrc().clone(), + ))) + }) + } + pub fn sloppy_imports_resolver( &self, ) -> Result>, AnyError> { @@ -571,17 +711,10 @@ impl CliFactory { .resolver .get_or_try_init_async( async { - let cli_options = self.cli_options()?; - Ok(Arc::new(CliResolver::new(CliResolverOptions { - npm_resolver: if cli_options.no_npm() { - None - } else { - Some(self.npm_resolver().await?.clone()) - }, - bare_node_builtins_enabled: cli_options - .unstable_bare_node_builtins(), - deno_resolver: self.deno_resolver().await?.clone(), - }))) + Ok(Arc::new(CliResolver::new( + self.deno_resolver().await?.clone(), + self.services.found_pkg_json_dep_flag.clone(), + ))) } .boxed_local(), ) @@ -670,6 +803,7 @@ impl CliFactory { .into_npm_pkg_folder_resolver(), self.pkg_json_resolver().clone(), self.sys(), + node_resolver::ConditionsFromResolutionMode::default(), ))) } .boxed_local(), @@ -751,6 +885,7 @@ impl CliFactory { cli_options.clone(), self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.sys(), ))) @@ -776,6 +911,8 @@ impl CliFactory { cli_options.maybe_lockfile().cloned(), self.maybe_file_watcher_reporter().clone(), self.module_info_cache()?.clone(), + self.npm_graph_resolver()?.clone(), + self.npm_installer_if_managed()?.cloned(), self.npm_resolver().await?.clone(), self.parsed_source_cache().clone(), self.resolver().await?.clone(), @@ -796,7 +933,7 @@ impl CliFactory { let cli_options = self.cli_options()?; Ok(Arc::new(ModuleGraphCreator::new( cli_options.clone(), - self.npm_resolver().await?.clone(), + self.npm_installer_if_managed()?.cloned(), self.module_graph_builder().await?.clone(), self.type_checker().await?.clone(), ))) @@ -996,6 +1133,7 @@ impl CliFactory { self.sys(), )), node_resolver.clone(), + self.npm_installer_if_managed()?.cloned(), npm_resolver.clone(), pkg_json_resolver, self.root_cert_store_provider().clone(), diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 84beee027e..17d4fef553 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use deno_config::deno_json; use deno_config::deno_json::JsxImportSourceConfig; +use deno_config::deno_json::NodeModulesDirMode; use deno_config::workspace::JsrPackageConfig; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -36,7 +37,6 @@ use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::SmallStackString; -use import_map::ImportMapError; use node_resolver::InNpmPackageChecker; use crate::args::config_to_deno_graph_workspace_member; @@ -52,8 +52,11 @@ use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; use crate::file_fetcher::CliFileFetcher; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::resolver::CjsTracker; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliResolver; use crate::resolver::CliSloppyImportsResolver; use crate::sys::CliSys; @@ -264,7 +267,7 @@ pub struct CreateGraphOptions<'a> { pub struct ModuleGraphCreator { options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, } @@ -272,13 +275,13 @@ pub struct ModuleGraphCreator { impl ModuleGraphCreator { pub fn new( options: Arc, - npm_resolver: Arc, + npm_installer: Option>, module_graph_builder: Arc, type_checker: Arc, ) -> Self { Self { options, - npm_resolver, + npm_installer, module_graph_builder, type_checker, } @@ -401,9 +404,9 @@ impl ModuleGraphCreator { .build_graph_with_npm_resolution(&mut graph, options) .await?; - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier && self.options.type_check_mode().is_true() { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } @@ -498,6 +501,8 @@ pub struct ModuleGraphBuilder { lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, parsed_source_cache: Arc, resolver: Arc, @@ -517,6 +522,8 @@ impl ModuleGraphBuilder { lockfile: Option>, maybe_file_watcher_reporter: Option, module_info_cache: Arc, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, parsed_source_cache: Arc, resolver: Arc, @@ -533,6 +540,8 @@ impl ModuleGraphBuilder { lockfile, maybe_file_watcher_reporter, module_info_cache, + npm_graph_resolver, + npm_installer, npm_resolver, parsed_source_cache, resolver, @@ -631,10 +640,7 @@ impl ModuleGraphBuilder { Some(loader) => MutLoaderRef::Borrowed(loader), None => MutLoaderRef::Owned(self.create_graph_loader()), }; - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = - cli_resolver.create_graph_npm_resolver(options.npm_caching); let maybe_file_watcher_reporter = self .maybe_file_watcher_reporter .as_ref() @@ -655,7 +661,7 @@ impl ModuleGraphBuilder { executor: Default::default(), file_system: &self.sys, jsr_url_provider: &CliJsrUrlProvider, - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), module_analyzer: &analyzer, reporter: maybe_file_watcher_reporter, resolver: Some(&graph_resolver), @@ -679,16 +685,15 @@ impl ModuleGraphBuilder { if self .cli_options .node_modules_dir()? - .map(|m| m.uses_node_modules_dir()) + .map(|m| m == NodeModulesDirMode::Auto) .unwrap_or(false) { - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &self.npm_installer { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } @@ -776,11 +781,7 @@ impl ModuleGraphBuilder { None }; let parser = self.parsed_source_cache.as_capturing_parser(); - let cli_resolver = &self.resolver; let graph_resolver = self.create_graph_resolver()?; - let graph_npm_resolver = cli_resolver.create_graph_npm_resolver( - self.cli_options.default_npm_caching_strategy(), - ); graph.build_fast_check_type_graph( deno_graph::BuildFastCheckTypeGraphOptions { @@ -789,7 +790,7 @@ impl ModuleGraphBuilder { fast_check_dts: false, jsr_url_provider: &CliJsrUrlProvider, resolver: Some(&graph_resolver), - npm_resolver: Some(&graph_npm_resolver), + npm_resolver: Some(self.npm_graph_resolver.as_ref()), workspace_fast_check: options.workspace_fast_check, }, ); @@ -1024,14 +1025,11 @@ fn get_resolution_error_bare_specifier( { Some(specifier.as_str()) } else if let ResolutionError::ResolverError { error, .. } = error { - if let ResolveError::Other(error) = (*error).as_ref() { - if let Some(import_map::ImportMapErrorKind::UnmappedBareSpecifier( + if let ResolveError::ImportMap(error) = (*error).as_ref() { + if let import_map::ImportMapErrorKind::UnmappedBareSpecifier( specifier, _, - )) = error - .as_any() - .downcast_ref::() - .map(|e| &**e) + ) = error.as_kind() { Some(specifier.as_str()) } else { @@ -1324,7 +1322,7 @@ mod test { let specifier = ModuleSpecifier::parse("file:///file.ts").unwrap(); let err = import_map.resolve(input, &specifier).err().unwrap(); let err = ResolutionError::ResolverError { - error: Arc::new(ResolveError::Other(JsErrorBox::from_err(err))), + error: Arc::new(ResolveError::ImportMap(err)), specifier: input.to_string(), range: Range { specifier, diff --git a/cli/js/40_lint.js b/cli/js/40_lint.js index 4adbc1baa1..9f85f0871d 100644 --- a/cli/js/40_lint.js +++ b/cli/js/40_lint.js @@ -8,10 +8,27 @@ import { splitSelectors, } from "ext:cli/40_lint_selector.js"; import { core, internals } from "ext:core/mod.js"; + const { op_lint_create_serialized_ast, } = core.ops; +// Keep these in sync with Rust +const AST_IDX_INVALID = 0; +const AST_GROUP_TYPE = 1; +/// +/// +/// +/// +/// +const NODE_SIZE = 1 + 4 + 4 + 4 + 4; +const PROP_OFFSET = 1; +const CHILD_OFFSET = 1 + 4; +const NEXT_OFFSET = 1 + 4 + 4; +const PARENT_OFFSET = 1 + 4 + 4 + 4; +// Span size in buffer: u32 + u32 +const SPAN_SIZE = 4 + 4; + // Keep in sync with Rust // These types are expected to be present on every node. Note that this // isn't set in stone. We could revise this at a future point. @@ -34,12 +51,21 @@ const PropFlags = { * the string table that was included in the message. */ String: 2, + /** + * A numnber field. Numbers are represented as strings internally. + */ + Number: 3, /** This value is either 0 = false, or 1 = true */ - Bool: 3, + Bool: 4, /** No value, it's null */ - Null: 4, + Null: 5, /** No value, it's undefined */ - Undefined: 5, + Undefined: 6, + /** An object */ + Obj: 7, + Regex: 8, + BigInt: 9, + Array: 10, }; /** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */ @@ -51,6 +77,7 @@ const PropFlags = { /** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */ /** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformFn */ /** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */ +/** @typedef {import("./40_lint_types.d.ts").Node} Node */ /** @type {LintState} */ const state = { @@ -100,17 +127,17 @@ export function installPlugin(plugin) { /** * @param {AstContext} ctx - * @param {number} offset - * @returns + * @param {number} idx + * @returns {FacadeNode | null} */ -function getNode(ctx, offset) { - if (offset === 0) return null; - const cached = ctx.nodes.get(offset); - if (cached !== undefined) return cached; +function getNode(ctx, idx) { + if (idx === AST_IDX_INVALID) return null; + const cached = ctx.nodes.get(idx); + if (cached !== undefined) return /** @type {*} */ (cached); - const node = new Node(ctx, offset); - ctx.nodes.set(offset, /** @type {*} */ (cached)); - return node; + const node = new FacadeNode(ctx, idx); + ctx.nodes.set(idx, /** @type {*} */ (node)); + return /** @type {*} */ (node); } /** @@ -122,31 +149,22 @@ function getNode(ctx, offset) { * @returns {number} */ function findPropOffset(buf, offset, search) { - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const propCount = buf[offset]; + const count = buf[offset]; offset += 1; - for (let i = 0; i < propCount; i++) { + for (let i = 0; i < count; i++) { const maybe = offset; const prop = buf[offset++]; const kind = buf[offset++]; if (prop === search) return maybe; - if (kind === PropFlags.Ref) { - offset += 4; - } else if (kind === PropFlags.RefArr) { + if (kind === PropFlags.Obj) { const len = readU32(buf, offset); - offset += 4 + (len * 4); - } else if (kind === PropFlags.String) { offset += 4; - } else if (kind === PropFlags.Bool) { - offset++; - } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { - // No value + // prop + kind + value + offset += len * (1 + 1 + 4); } else { - offset++; + offset += 4; } } @@ -154,23 +172,23 @@ function findPropOffset(buf, offset, search) { } const INTERNAL_CTX = Symbol("ctx"); -const INTERNAL_OFFSET = Symbol("offset"); +const INTERNAL_IDX = Symbol("offset"); // This class is a facade for all materialized nodes. Instead of creating a // unique class per AST node, we have one class with getters for every // possible node property. This allows us to lazily materialize child node // only when they are needed. -class Node { +class FacadeNode { [INTERNAL_CTX]; - [INTERNAL_OFFSET]; + [INTERNAL_IDX]; /** * @param {AstContext} ctx - * @param {number} offset + * @param {number} idx */ - constructor(ctx, offset) { + constructor(ctx, idx) { this[INTERNAL_CTX] = ctx; - this[INTERNAL_OFFSET] = offset; + this[INTERNAL_IDX] = idx; } /** @@ -186,12 +204,12 @@ class Node { * @returns {string} */ [Symbol.for("Deno.customInspect")](_, options) { - const json = toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + const json = nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); return Deno.inspect(json, options); } [Symbol.for("Deno.lint.toJsValue")]() { - return toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]); + return nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]); } } @@ -212,125 +230,243 @@ function setNodeGetters(ctx) { const name = getString(ctx.strTable, id); - Object.defineProperty(Node.prototype, name, { + Object.defineProperty(FacadeNode.prototype, name, { get() { - return readValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET], i); + return readValue( + this[INTERNAL_CTX], + this[INTERNAL_IDX], + i, + getNode, + ); }, }); } } /** - * Serialize a node recursively to plain JSON * @param {AstContext} ctx - * @param {number} offset - * @returns {*} + * @param {number} idx */ -function toJsValue(ctx, offset) { - const { buf } = ctx; - +function nodeToJson(ctx, idx) { /** @type {Record} */ const node = { - type: readValue(ctx, offset, AST_PROP_TYPE), - range: readValue(ctx, offset, AST_PROP_RANGE), + type: readValue(ctx, idx, AST_PROP_TYPE, nodeToJson), + range: readValue(ctx, idx, AST_PROP_RANGE, nodeToJson), }; - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; + const { buf } = ctx; + let offset = readPropOffset(ctx, idx); const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const prop = buf[offset++]; - const kind = buf[offset++]; - const name = getString(ctx.strTable, ctx.strByProp[prop]); - if (kind === PropFlags.Ref) { - const v = readU32(buf, offset); - offset += 4; - node[name] = v === 0 ? null : toJsValue(ctx, v); - } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; - const nodes = new Array(len); - for (let i = 0; i < len; i++) { - const v = readU32(buf, offset); - if (v === 0) continue; - nodes[i] = toJsValue(ctx, v); - offset += 4; - } - node[name] = nodes; - } else if (kind === PropFlags.Bool) { - const v = buf[offset++]; - node[name] = v === 1; - } else if (kind === PropFlags.String) { - const v = readU32(buf, offset); - offset += 4; - node[name] = getString(ctx.strTable, v); - } else if (kind === PropFlags.Null) { - node[name] = null; - } else if (kind === PropFlags.Undefined) { - node[name] = undefined; - } + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const _kind = buf[offset + 1]; + + const name = getString(ctx.strTable, ctx.strByProp[prop]); + node[name] = readProperty(ctx, offset, nodeToJson); + + // prop + type + value + offset += 1 + 1 + 4; } return node; } /** - * Read a specific property from a node + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readType(buf, idx) { + return buf[idx * NODE_SIZE]; +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {Node["range"]} + */ +function readSpan(ctx, idx) { + let offset = ctx.spansOffset + (idx * SPAN_SIZE); + const start = readU32(ctx.buf, offset); + offset += 4; + const end = readU32(ctx.buf, offset); + + return [start, end]; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readRawPropOffset(buf, idx) { + const offset = (idx * NODE_SIZE) + PROP_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext} ctx + * @param {number} idx + * @returns {number} + */ +function readPropOffset(ctx, idx) { + return readRawPropOffset(ctx.buf, idx) + ctx.propsOffset; +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readChild(buf, idx) { + const offset = (idx * NODE_SIZE) + CHILD_OFFSET; + return readU32(buf, offset); +} +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readNext(buf, idx) { + const offset = (idx * NODE_SIZE) + NEXT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["buf"]} buf + * @param {number} idx + * @returns {number} + */ +function readParent(buf, idx) { + const offset = (idx * NODE_SIZE) + PARENT_OFFSET; + return readU32(buf, offset); +} + +/** + * @param {AstContext["strTable"]} strTable + * @param {number} strId + * @returns {RegExp} + */ +function readRegex(strTable, strId) { + const raw = getString(strTable, strId); + const idx = raw.lastIndexOf("/"); + const pattern = raw.slice(1, idx); + const flags = idx < raw.length - 1 ? raw.slice(idx + 1) : undefined; + + return new RegExp(pattern, flags); +} + +/** * @param {AstContext} ctx * @param {number} offset - * @param {number} search - * @returns {*} + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {Record} */ -function readValue(ctx, offset, search) { - const { buf } = ctx; - const type = buf[offset]; +function readObject(ctx, offset, parseNode) { + const { buf, strTable, strByProp } = ctx; - if (search === AST_PROP_TYPE) { - return getString(ctx.strTable, ctx.strByType[type]); - } else if (search === AST_PROP_RANGE) { - const start = readU32(buf, offset + 1 + 4); - const end = readU32(buf, offset + 1 + 4 + 4); - return [start, end]; - } else if (search === AST_PROP_PARENT) { - const pos = readU32(buf, offset + 1); - return getNode(ctx, pos); + /** @type {Record} */ + const obj = {}; + + const count = readU32(buf, offset); + offset += 4; + + for (let i = 0; i < count; i++) { + const prop = buf[offset]; + const name = getString(strTable, strByProp[prop]); + obj[name] = readProperty(ctx, offset, parseNode); + // name + kind + value + offset += 1 + 1 + 4; } - offset = findPropOffset(ctx.buf, offset, search); - if (offset === -1) return undefined; + return obj; +} - const kind = buf[offset + 1]; - offset += 2; +/** + * @param {AstContext} ctx + * @param {number} offset + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {any} + */ +function readProperty(ctx, offset, parseNode) { + const { buf } = ctx; + + // skip over name + const _name = buf[offset++]; + const kind = buf[offset++]; if (kind === PropFlags.Ref) { const value = readU32(buf, offset); - return getNode(ctx, value); + return parseNode(ctx, value); } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; + const groupId = readU32(buf, offset); - const nodes = new Array(len); - for (let i = 0; i < len; i++) { - nodes[i] = getNode(ctx, readU32(buf, offset)); - offset += 4; + const nodes = []; + let next = readChild(buf, groupId); + while (next > AST_IDX_INVALID) { + nodes.push(parseNode(ctx, next)); + next = readNext(buf, next); } + return nodes; } else if (kind === PropFlags.Bool) { - return buf[offset] === 1; + const v = readU32(buf, offset); + return v === 1; } else if (kind === PropFlags.String) { const v = readU32(buf, offset); return getString(ctx.strTable, v); + } else if (kind === PropFlags.Number) { + const v = readU32(buf, offset); + return Number(getString(ctx.strTable, v)); + } else if (kind === PropFlags.BigInt) { + const v = readU32(buf, offset); + return BigInt(getString(ctx.strTable, v)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(ctx.strTable, v); } else if (kind === PropFlags.Null) { return null; } else if (kind === PropFlags.Undefined) { return undefined; + } else if (kind === PropFlags.Obj) { + const objOffset = readU32(buf, offset) + ctx.propsOffset; + return readObject(ctx, objOffset, parseNode); } throw new Error(`Unknown prop kind: ${kind}`); } +/** + * Read a specific property from a node + * @param {AstContext} ctx + * @param {number} idx + * @param {number} search + * @param {(ctx: AstContext, idx: number) => any} parseNode + * @returns {*} + */ +function readValue(ctx, idx, search, parseNode) { + const { buf } = ctx; + + if (search === AST_PROP_TYPE) { + const type = readType(buf, idx); + return getString(ctx.strTable, ctx.strByType[type]); + } else if (search === AST_PROP_RANGE) { + return readSpan(ctx, idx); + } else if (search === AST_PROP_PARENT) { + const parent = readParent(buf, idx); + return getNode(ctx, parent); + } + + const propOffset = readPropOffset(ctx, idx); + + const offset = findPropOffset(ctx.buf, propOffset, search); + if (offset === -1) return undefined; + + return readProperty(ctx, offset, parseNode); +} + const DECODER = new TextDecoder(); /** @@ -359,322 +495,149 @@ function getString(strTable, id) { return name; } -/** - * @param {AstContext["buf"]} buf - * @param {number} child - * @returns {null | [number, number]} - */ -function findChildOffset(buf, child) { - let offset = readU32(buf, child + 1); - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const propCount = buf[offset++]; - for (let i = 0; i < propCount; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const start = offset; - const value = readU32(buf, offset); - offset += 4; - if (value === child) { - return [start, -1]; - } - break; - } - case PropFlags.RefArr: { - const start = offset; - - const len = readU32(buf, offset); - offset += 4; - - for (let j = 0; j < len; j++) { - const value = readU32(buf, offset); - offset += 4; - if (value === child) { - return [start, j]; - } - } - - break; - } - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return null; -} - /** @implements {MatchContext} */ class MatchCtx { /** - * @param {AstContext["buf"]} buf - * @param {AstContext["strTable"]} strTable - * @param {AstContext["strByType"]} strByType + * @param {AstContext} ctx */ - constructor(buf, strTable, strByType) { - this.buf = buf; - this.strTable = strTable; - this.strByType = strByType; + constructor(ctx) { + this.ctx = ctx; } /** - * @param {number} offset - * @returns {number} - */ - getParent(offset) { - return readU32(this.buf, offset + 1); - } - - /** - * @param {number} offset - * @returns {number} - */ - getType(offset) { - return this.buf[offset]; - } - - /** - * @param {number} offset - * @param {number[]} propIds * @param {number} idx + * @returns {number} + */ + getParent(idx) { + return readParent(this.ctx.buf, idx); + } + + /** + * @param {number} idx + * @returns {number} + */ + getType(idx) { + return readType(this.ctx.buf, idx); + } + + /** + * @param {number} idx - Node idx + * @param {number[]} propIds + * @param {number} propIdx * @returns {unknown} */ - getAttrPathValue(offset, propIds, idx) { - const { buf } = this; + getAttrPathValue(idx, propIds, propIdx) { + if (idx === 0) throw -1; - const propId = propIds[idx]; + const { buf, strTable, strByType } = this.ctx; + + const propId = propIds[propIdx]; switch (propId) { case AST_PROP_TYPE: { - const type = this.getType(offset); - return getString(this.strTable, this.strByType[type]); + const type = readType(buf, idx); + return getString(strTable, strByType[type]); } case AST_PROP_PARENT: case AST_PROP_RANGE: - throw new Error(`Not supported`); + throw -1; } + let offset = readPropOffset(this.ctx, idx); + offset = findPropOffset(buf, offset, propId); - if (offset === -1) return undefined; + if (offset === -1) throw -1; const _prop = buf[offset++]; const kind = buf[offset++]; if (kind === PropFlags.Ref) { const value = readU32(buf, offset); // Checks need to end with a value, not a node - if (idx === propIds.length - 1) return undefined; - return this.getAttrPathValue(value, propIds, idx + 1); + if (propIdx === propIds.length - 1) throw -1; + return this.getAttrPathValue(value, propIds, propIdx + 1); } else if (kind === PropFlags.RefArr) { - const count = readU32(buf, offset); + const arrIdx = readU32(buf, offset); offset += 4; - if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { + let count = 0; + let child = readChild(buf, arrIdx); + while (child > AST_IDX_INVALID) { + count++; + child = readNext(buf, child); + } + + if ( + propIdx < propIds.length - 1 && propIds[propIdx + 1] === AST_PROP_LENGTH + ) { return count; } // TODO(@marvinhagemeister): Allow traversing into array children? + throw -1; + } else if (kind === PropFlags.Obj) { + // TODO(@marvinhagemeister) } // Cannot traverse into primitives further - if (idx < propIds.length - 1) return undefined; + if (propIdx < propIds.length - 1) throw -1; if (kind === PropFlags.String) { const s = readU32(buf, offset); - return getString(this.strTable, s); + return getString(strTable, s); + } else if (kind === PropFlags.Number) { + const s = readU32(buf, offset); + return Number(getString(strTable, s)); + } else if (kind === PropFlags.Regex) { + const v = readU32(buf, offset); + return readRegex(strTable, v); } else if (kind === PropFlags.Bool) { - return buf[offset] === 1; + return readU32(buf, offset) === 1; } else if (kind === PropFlags.Null) { return null; } else if (kind === PropFlags.Undefined) { return undefined; } - return undefined; + throw -1; } /** - * @param {number} offset - * @param {number[]} propIds * @param {number} idx - * @returns {boolean} - */ - hasAttrPath(offset, propIds, idx) { - const { buf } = this; - - const propId = propIds[idx]; - // If propId is 0 then the property doesn't exist in the AST - if (propId === 0) return false; - - switch (propId) { - case AST_PROP_TYPE: - case AST_PROP_PARENT: - case AST_PROP_RANGE: - return true; - } - - offset = findPropOffset(buf, offset, propId); - if (offset === -1) return false; - if (idx === propIds.length - 1) return true; - - const _prop = buf[offset++]; - const kind = buf[offset++]; - if (kind === PropFlags.Ref) { - const value = readU32(buf, offset); - return this.hasAttrPath(value, propIds, idx + 1); - } else if (kind === PropFlags.RefArr) { - const _count = readU32(buf, offset); - offset += 4; - - if (idx < propIds.length - 1 && propIds[idx + 1] === AST_PROP_LENGTH) { - return true; - } - - // TODO(@marvinhagemeister): Allow traversing into array children? - } - - // Primitives cannot be traversed further. This means we - // didn't found the attribute. - if (idx < propIds.length - 1) return false; - - return true; - } - - /** - * @param {number} offset * @returns {number} */ - getFirstChild(offset) { - const { buf } = this; - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const v = readU32(buf, offset); - offset += 4; - return v; - } - case PropFlags.RefArr: { - const len = readU32(buf, offset); - offset += 4; - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - offset += 4; - return v; - } - - return len; - } - - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return -1; + getFirstChild(idx) { + const siblings = this.getSiblings(idx); + return siblings[0] ?? -1; } /** - * @param {number} offset + * @param {number} idx * @returns {number} */ - getLastChild(offset) { - const { buf } = this; - - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; - - let last = -1; - - const count = buf[offset++]; - for (let i = 0; i < count; i++) { - const _prop = buf[offset++]; - const kind = buf[offset++]; - - switch (kind) { - case PropFlags.Ref: { - const v = readU32(buf, offset); - offset += 4; - last = v; - break; - } - case PropFlags.RefArr: { - const len = readU32(buf, offset); - offset += 4; - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - last = v; - offset += 4; - } - - break; - } - - case PropFlags.String: - offset += 4; - break; - case PropFlags.Bool: - offset++; - break; - case PropFlags.Null: - case PropFlags.Undefined: - break; - } - } - - return last; + getLastChild(idx) { + const siblings = this.getSiblings(idx); + return siblings.at(-1) ?? -1; } /** - * @param {number} id + * @param {number} idx * @returns {number[]} */ - getSiblings(id) { - const { buf } = this; + getSiblings(idx) { + const { buf } = this.ctx; + const parent = readParent(buf, idx); - const result = findChildOffset(buf, id); - // Happens for program nodes - if (result === null) return []; - - if (result[1] === -1) { - return [id]; + // Only RefArrays have siblings + const parentType = readType(buf, parent); + if (parentType !== AST_GROUP_TYPE) { + return []; } - let offset = result[0]; - const count = readU32(buf, offset); - offset += 4; - - /** @type {number[]} */ const out = []; - for (let i = 0; i < count; i++) { - const v = readU32(buf, offset); - offset += 4; - out.push(v); + let child = readChild(buf, parent); + while (child > AST_IDX_INVALID) { + out.push(child); + child = readNext(buf, child); } return out; @@ -683,7 +646,7 @@ class MatchCtx { /** * @param {Uint8Array} buf - * @param {AstContext} buf + * @returns {AstContext} */ function createAstContext(buf) { /** @type {Map} */ @@ -691,6 +654,8 @@ function createAstContext(buf) { // The buffer has a few offsets at the end which allows us to easily // jump to the relevant sections of the message. + const propsOffset = readU32(buf, buf.length - 24); + const spansOffset = readU32(buf, buf.length - 20); const typeMapOffset = readU32(buf, buf.length - 16); const propMapOffset = readU32(buf, buf.length - 12); const strTableOffset = readU32(buf, buf.length - 8); @@ -702,9 +667,7 @@ function createAstContext(buf) { const stringCount = readU32(buf, offset); offset += 4; - // TODO(@marvinhagemeister): We could lazily decode the strings on an as needed basis. - // Not sure if this matters much in practice though. - let id = 0; + let strId = 0; for (let i = 0; i < stringCount; i++) { const len = readU32(buf, offset); offset += 4; @@ -712,8 +675,8 @@ function createAstContext(buf) { const strBytes = buf.slice(offset, offset + len); offset += len; const s = DECODER.decode(strBytes); - strTable.set(id, s); - id++; + strTable.set(strId, s); + strId++; } if (strTable.size !== stringCount) { @@ -755,14 +718,17 @@ function createAstContext(buf) { buf, strTable, rootOffset, + spansOffset, + propsOffset, nodes: new Map(), strTableOffset, strByProp, strByType, typeByStr, propByStr, - matcher: new MatchCtx(buf, strTable, strByType), + matcher: /** @type {*} */ (null), }; + ctx.matcher = new MatchCtx(ctx); setNodeGetters(ctx); @@ -903,76 +869,53 @@ export function runPluginsForFile(fileName, serializedAst) { /** * @param {AstContext} ctx * @param {CompiledVisitor[]} visitors - * @param {number} offset + * @param {number} idx */ -function traverse(ctx, visitors, offset) { - // The 0 offset is used to denote an empty/placeholder node - if (offset === 0) return; - - const originalOffset = offset; +function traverse(ctx, visitors, idx) { + if (idx === AST_IDX_INVALID) return; const { buf } = ctx; + const nodeType = readType(ctx.buf, idx); /** @type {VisitorFn[] | null} */ let exits = null; - for (let i = 0; i < visitors.length; i++) { - const v = visitors[i]; - - if (v.matcher(ctx.matcher, offset)) { - if (v.info.exit !== NOOP) { - if (exits === null) { - exits = [v.info.exit]; - } else { - exits.push(v.info.exit); + // Only visit if it's an actual node + if (nodeType !== AST_GROUP_TYPE) { + // Loop over visitors and check if any selector matches + for (let i = 0; i < visitors.length; i++) { + const v = visitors[i]; + if (v.matcher(ctx.matcher, idx)) { + if (v.info.exit !== NOOP) { + if (exits === null) { + exits = [v.info.exit]; + } else { + exits.push(v.info.exit); + } } - } - if (v.info.enter !== NOOP) { - const node = /** @type {*} */ (getNode(ctx, offset)); - v.info.enter(node); + if (v.info.enter !== NOOP) { + const node = /** @type {*} */ (getNode(ctx, idx)); + v.info.enter(node); + } } } } - // Search for node references in the properties of the current node. All - // other properties can be ignored. try { - // type + parentId + SpanLo + SpanHi - offset += 1 + 4 + 4 + 4; + const childIdx = readChild(buf, idx); + if (childIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, childIdx); + } - const propCount = buf[offset]; - offset += 1; - - for (let i = 0; i < propCount; i++) { - const kind = buf[offset + 1]; - offset += 2; // propId + propFlags - - if (kind === PropFlags.Ref) { - const next = readU32(buf, offset); - offset += 4; - traverse(ctx, visitors, next); - } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; - - for (let j = 0; j < len; j++) { - const child = readU32(buf, offset); - offset += 4; - traverse(ctx, visitors, child); - } - } else if (kind === PropFlags.String) { - offset += 4; - } else if (kind === PropFlags.Bool) { - offset += 1; - } else if (kind === PropFlags.Null || kind === PropFlags.Undefined) { - // No value - } + const nextIdx = readNext(buf, idx); + if (nextIdx > AST_IDX_INVALID) { + traverse(ctx, visitors, nextIdx); } } finally { if (exits !== null) { for (let i = 0; i < exits.length; i++) { - const node = /** @type {*} */ (getNode(ctx, originalOffset)); + const node = /** @type {*} */ (getNode(ctx, idx)); exits[i](node); } } @@ -1009,38 +952,46 @@ function _dump(ctx) { // deno-lint-ignore no-console console.log(); - let offset = 0; + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(); - while (offset < strTableOffset) { - const type = buf[offset]; - const name = getString(ctx.strTable, ctx.strByType[type]); + let idx = 0; + while (idx < (strTableOffset / NODE_SIZE)) { + const type = readType(buf, idx); + const child = readChild(buf, idx); + const next = readNext(buf, idx); + const parent = readParent(buf, idx); + const range = readSpan(ctx, idx); + + const name = type === AST_IDX_INVALID + ? "" + : type === AST_GROUP_TYPE + ? "" + : getString(ctx.strTable, ctx.strByType[type]); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(`${name}, offset: ${offset}, type: ${type}`); - offset += 1; + console.log(`${name}, idx: ${idx}, type: ${type}`); - const parent = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` parent: ${parent}`); - - const start = readU32(buf, offset); - offset += 4; - const end = readU32(buf, offset); - offset += 4; + console.log(` child: ${child}, next: ${next}, parent: ${parent}`); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` range: ${start} -> ${end}`); + console.log(` range: ${range[0]}, ${range[1]}`); - const count = buf[offset++]; + const rawOffset = readRawPropOffset(ctx.buf, idx); + let propOffset = readPropOffset(ctx, idx); + const count = buf[propOffset++]; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` prop count: ${count}`); + console.log( + ` prop count: ${count}, prop offset: ${propOffset} raw offset: ${rawOffset}`, + ); for (let i = 0; i < count; i++) { - const prop = buf[offset++]; - const kind = buf[offset++]; + const prop = buf[propOffset++]; + const kind = buf[propOffset++]; const name = getString(ctx.strTable, ctx.strByProp[prop]); let kindName = "unknown"; @@ -1051,40 +1002,36 @@ function _dump(ctx) { } } + const v = readU32(buf, propOffset); + propOffset += 4; + if (kind === PropFlags.Ref) { - const v = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: ${v} (${kindName}, ${prop})`); } else if (kind === PropFlags.RefArr) { - const len = readU32(buf, offset); - offset += 4; // @ts-ignore dump fn // deno-lint-ignore no-console - console.log(` ${name}: Array(${len}) (${kindName}, ${prop})`); - - for (let j = 0; j < len; j++) { - const v = readU32(buf, offset); - offset += 4; - // @ts-ignore dump fn - // deno-lint-ignore no-console - console.log(` - ${v} (${prop})`); - } + console.log(` ${name}: RefArray: ${v}, (${kindName}, ${prop})`); } else if (kind === PropFlags.Bool) { - const v = buf[offset]; - offset += 1; // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: ${v} (${kindName}, ${prop})`); } else if (kind === PropFlags.String) { - const v = readU32(buf, offset); - offset += 4; + const raw = getString(ctx.strTable, v); // @ts-ignore dump fn // deno-lint-ignore no-console - console.log( - ` ${name}: ${getString(ctx.strTable, v)} (${kindName}, ${prop})`, - ); + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Number) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Regex) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); } else if (kind === PropFlags.Null) { // @ts-ignore dump fn // deno-lint-ignore no-console @@ -1093,8 +1040,27 @@ function _dump(ctx) { // @ts-ignore dump fn // deno-lint-ignore no-console console.log(` ${name}: undefined (${kindName}, ${prop})`); + } else if (kind === PropFlags.BigInt) { + const raw = getString(ctx.strTable, v); + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log(` ${name}: ${raw} (${kindName}, ${prop})`); + } else if (kind === PropFlags.Obj) { + let offset = v + ctx.propsOffset; + const count = readU32(ctx.buf, offset); + offset += 4; + + // @ts-ignore dump fn + // deno-lint-ignore no-console + console.log( + ` ${name}: Object (${count}) (${kindName}, ${prop}), raw offset ${v}`, + ); + + // TODO(@marvinhagemeister): Show object } } + + idx++; } } @@ -1107,9 +1073,10 @@ function _dump(ctx) { */ function runLintPlugin(plugin, fileName, sourceText) { installPlugin(plugin); - const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); try { + const serializedAst = op_lint_create_serialized_ast(fileName, sourceText); + runPluginsForFile(fileName, serializedAst); } finally { // During testing we don't want to keep plugins around diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js index c4cd523f9f..362130076a 100644 --- a/cli/js/40_lint_selector.js +++ b/cli/js/40_lint_selector.js @@ -744,8 +744,7 @@ export function compileSelector(selector) { fn = matchNthChild(node, fn); break; case PSEUDO_HAS: - // FIXME - // fn = matchIs(part, fn); + // TODO(@marvinhagemeister) throw new Error("TODO: :has"); case PSEUDO_NOT: fn = matchNot(node.selectors, fn); @@ -767,8 +766,7 @@ export function compileSelector(selector) { */ function matchFirstChild(next) { return (ctx, id) => { - const parent = ctx.getParent(id); - const first = ctx.getFirstChild(parent); + const first = ctx.getFirstChild(id); return first === id && next(ctx, first); }; } @@ -779,8 +777,7 @@ function matchFirstChild(next) { */ function matchLastChild(next) { return (ctx, id) => { - const parent = ctx.getParent(id); - const last = ctx.getLastChild(parent); + const last = ctx.getLastChild(id); return last === id && next(ctx, id); }; } @@ -955,7 +952,9 @@ function matchElem(part, next) { else if (part.elem === 0) return false; const type = ctx.getType(id); - if (type > 0 && type === part.elem) return next(ctx, id); + if (type > 0 && type === part.elem) { + return next(ctx, id); + } return false; }; @@ -968,7 +967,16 @@ function matchElem(part, next) { */ function matchAttrExists(attr, next) { return (ctx, id) => { - return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false; + try { + ctx.getAttrPathValue(id, attr.prop, 0); + return next(ctx, id); + } catch (err) { + if (err === -1) { + return false; + } + + throw err; + } }; } @@ -979,9 +987,15 @@ function matchAttrExists(attr, next) { */ function matchAttrBin(attr, next) { return (ctx, id) => { - if (!ctx.hasAttrPath(id, attr.prop, 0)) return false; - const value = ctx.getAttrPathValue(id, attr.prop, 0); - if (!matchAttrValue(attr, value)) return false; + try { + const value = ctx.getAttrPathValue(id, attr.prop, 0); + if (!matchAttrValue(attr, value)) return false; + } catch (err) { + if (err === -1) { + return false; + } + throw err; + } return next(ctx, id); }; } diff --git a/cli/js/40_lint_types.d.ts b/cli/js/40_lint_types.d.ts index db2202981a..f07d16581e 100644 --- a/cli/js/40_lint_types.d.ts +++ b/cli/js/40_lint_types.d.ts @@ -12,6 +12,8 @@ export interface AstContext { strTableOffset: number; rootOffset: number; nodes: Map; + spansOffset: number; + propsOffset: number; strByType: number[]; strByProp: number[]; typeByStr: Map; @@ -19,6 +21,12 @@ export interface AstContext { matcher: MatchContext; } +export interface Node { + range: Range; +} + +export type Range = [number, number]; + // TODO(@marvinhagemeister) Remove once we land "official" types export interface RuleContext { id: string; @@ -121,7 +129,6 @@ export interface MatchContext { getSiblings(id: number): number[]; getParent(id: number): number; getType(id: number): number; - hasAttrPath(id: number, propIds: number[], idx: number): boolean; getAttrPathValue(id: number, propIds: number[], idx: number): unknown; } diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 7841ee0783..ba57502298 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -41,7 +41,6 @@ use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; -use deno_error::JsErrorBox; use deno_lint::linter::LintConfig as DenoLintConfig; use deno_npm::npm_rc::ResolvedNpmRc; use deno_package_json::PackageJsonCache; @@ -1247,7 +1246,6 @@ impl ConfigData { pkg_json_cache: Some(pkg_json_cache), workspace_cache: Some(workspace_cache), discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), - config_parse_options: Default::default(), maybe_vendor_override: None, }, ) @@ -1572,11 +1570,11 @@ impl ConfigData { let resolver = member_dir .workspace .create_resolver( + &CliSys::default(), CreateResolverOptions { pkg_json_dep_resolution, specified_import_map, }, - |path| std::fs::read_to_string(path).map_err(JsErrorBox::from_err), ) .inspect_err(|err| { lsp_warn!( @@ -2078,7 +2076,6 @@ impl deno_config::workspace::WorkspaceCache for WorkspaceMemCache { #[cfg(test)] mod tests { - use deno_config::deno_json::ConfigParseOptions; use deno_core::resolve_url; use deno_core::serde_json; use deno_core::serde_json::json; @@ -2352,12 +2349,7 @@ mod tests { config .tree .inject_config_file( - ConfigFile::new( - "{}", - root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), - ) - .unwrap(), + ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), ) .await; assert!(config.specifier_enabled(&root_uri)); @@ -2413,7 +2405,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2439,7 +2430,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -2457,7 +2447,6 @@ mod tests { }) .to_string(), root_uri.join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 42a1a0c52a..fe8dc4c8d5 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1695,12 +1695,7 @@ mod tests { let mut config = Config::new_with_roots([root_uri.clone()]); if let Some((relative_path, json_string)) = maybe_import_map { let base_url = root_uri.join(relative_path).unwrap(); - let config_file = ConfigFile::new( - json_string, - base_url, - &deno_config::deno_json::ConfigParseOptions::default(), - ) - .unwrap(); + let config_file = ConfigFile::new(json_string, base_url).unwrap(); config.tree.inject_config_file(config_file).await; } let resolver = diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index f31353d436..6a7a08b5a1 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -480,7 +480,7 @@ impl Document { let is_cjs_resolver = resolver.as_is_cjs_resolver(self.file_referrer.as_ref()); let npm_resolver = - resolver.create_graph_npm_resolver(self.file_referrer.as_ref()); + resolver.as_graph_npm_resolver(self.file_referrer.as_ref()); let config_data = resolver.as_config_data(self.file_referrer.as_ref()); let jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); @@ -503,7 +503,7 @@ impl Document { s, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), ), ) }) @@ -513,7 +513,7 @@ impl Document { Arc::new(d.with_new_resolver( &CliJsrUrlProvider, Some(&resolver), - Some(&npm_resolver), + Some(npm_resolver.as_ref()), )) }); is_script = self.is_script; @@ -1702,7 +1702,7 @@ fn analyze_module( ) -> (ModuleResult, ResolutionMode) { match parsed_source_result { Ok(parsed_source) => { - let npm_resolver = resolver.create_graph_npm_resolver(file_referrer); + let npm_resolver = resolver.as_graph_npm_resolver(file_referrer); let cli_resolver = resolver.as_cli_resolver(file_referrer); let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer); let config_data = resolver.as_config_data(file_referrer); @@ -1731,7 +1731,7 @@ fn analyze_module( file_system: &deno_graph::source::NullFileSystem, jsr_url_provider: &CliJsrUrlProvider, maybe_resolver: Some(&resolver), - maybe_npm_resolver: Some(&npm_resolver), + maybe_npm_resolver: Some(npm_resolver.as_ref()), }, )), module_resolution_mode, @@ -1767,7 +1767,6 @@ fn bytes_to_content( #[cfg(test)] mod tests { use deno_config::deno_json::ConfigFile; - use deno_config::deno_json::ConfigParseOptions; use deno_core::serde_json; use deno_core::serde_json::json; use pretty_assertions::assert_eq; @@ -1924,7 +1923,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) @@ -1968,7 +1966,6 @@ console.log(b, "hello deno"); }) .to_string(), config.root_uri().unwrap().join("deno.json").unwrap(), - &ConfigParseOptions::default(), ) .unwrap(), ) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 35f5374efe..0f3bfcdf59 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1419,18 +1419,16 @@ impl Inner { // the file path is only used to determine what formatter should // be used to format the file, so give the filepath an extension // that matches what the user selected as the language - let file_path = document + let ext = document .maybe_language_id() - .and_then(|id| id.as_extension()) - .map(|ext| file_path.with_extension(ext)) - .unwrap_or(file_path); + .and_then(|id| id.as_extension().map(|s| s.to_string())); // it's not a js/ts file, so attempt to format its contents format_file( &file_path, document.content(), &fmt_options, &unstable_options, - None, + ext, ) } }; @@ -3623,8 +3621,6 @@ impl Inner { deno_json_cache: None, pkg_json_cache: None, workspace_cache: None, - config_parse_options: - deno_config::deno_json::ConfigParseOptions::default(), additional_config_file_names: &[], discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"), maybe_vendor_override: if force_global_cache { diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 57ef2e6a3c..af4ab6571f 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use dashmap::DashMap; use deno_ast::MediaType; -use deno_cache_dir::file_fetcher::CacheSetting; use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::HttpCache; use deno_config::deno_json::JsxImportSourceConfig; @@ -21,9 +20,11 @@ use deno_graph::GraphImport; use deno_graph::ModuleSpecifier; use deno_graph::Range; use deno_npm::NpmSystemInfo; +use deno_npm_cache::TarballCache; use deno_path_util::url_to_file_path; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::DenoResolverOptions; @@ -42,6 +43,8 @@ use super::cache::LspCache; use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmCachingStrategy; use crate::args::NpmInstallDepsProvider; use crate::factory::Deferred; use crate::graph_util::to_node_resolution_kind; @@ -53,19 +56,25 @@ use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; -use crate::npm::create_cli_npm_resolver_for_lsp; +use crate::npm::create_cli_npm_resolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::NpmResolutionInstaller; use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmCacheHttpClient; +use crate::npm::CliNpmRegistryInfoProvider; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::ManagedCliNpmResolver; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CliDenoResolver; +use crate::resolver::CliNpmGraphResolver; use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; -use crate::resolver::CliResolverOptions; +use crate::resolver::FoundPackageJsonDepFlag; use crate::resolver::IsCjsResolver; -use crate::resolver::WorkerCliNpmGraphResolver; use crate::sys::CliSys; use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; @@ -77,6 +86,8 @@ struct LspScopeResolver { in_npm_pkg_checker: Arc, is_cjs_resolver: Arc, jsr_resolver: Option>, + npm_graph_resolver: Arc, + npm_installer: Option>, npm_resolver: Option>, node_resolver: Option>, npm_pkg_req_resolver: Option>, @@ -96,6 +107,8 @@ impl Default for LspScopeResolver { in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: None, + npm_graph_resolver: factory.npm_graph_resolver().clone(), + npm_installer: None, npm_resolver: None, node_resolver: None, npm_pkg_req_resolver: None, @@ -121,6 +134,7 @@ impl LspScopeResolver { } let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone(); let npm_resolver = factory.npm_resolver().cloned(); + let npm_installer = factory.npm_installer().cloned(); let node_resolver = factory.node_resolver().cloned(); let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned(); let cli_resolver = factory.cli_resolver().clone(); @@ -133,8 +147,7 @@ impl LspScopeResolver { cache.for_specifier(config_data.map(|d| d.scope.as_ref())), config_data.and_then(|d| d.lockfile.clone()), ))); - let npm_graph_resolver = cli_resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager); + let npm_graph_resolver = factory.npm_graph_resolver(); let maybe_jsx_import_source_config = config_data.and_then(|d| d.maybe_jsx_import_source_config()); let graph_imports = config_data @@ -156,7 +169,7 @@ impl LspScopeResolver { imports, &CliJsrUrlProvider, Some(&resolver), - Some(&npm_graph_resolver), + Some(npm_graph_resolver.as_ref()), ); (referrer, graph_import) }) @@ -207,8 +220,10 @@ impl LspScopeResolver { in_npm_pkg_checker, is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver, + npm_graph_resolver: factory.npm_graph_resolver().clone(), npm_pkg_req_resolver, npm_resolver, + npm_installer, node_resolver, pkg_json_resolver, redirect_resolver, @@ -231,6 +246,9 @@ impl LspScopeResolver { in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(), jsr_resolver: self.jsr_resolver.clone(), + npm_graph_resolver: factory.npm_graph_resolver().clone(), + // npm installer isn't necessary for a snapshot + npm_installer: None, npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), npm_resolver: factory.npm_resolver().cloned(), node_resolver: factory.node_resolver().cloned(), @@ -318,14 +336,12 @@ impl LspResolver { if let Some(dep_info) = dep_info { *resolver.dep_info.lock() = dep_info.clone(); } - if let Some(npm_resolver) = resolver.npm_resolver.as_ref() { - if let Some(npm_resolver) = npm_resolver.as_managed() { - let reqs = dep_info - .map(|i| i.npm_reqs.iter().cloned().collect::>()) - .unwrap_or_default(); - if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { - lsp_warn!("Could not set npm package requirements: {:#}", err); - } + if let Some(npm_installer) = resolver.npm_installer.as_ref() { + let reqs = dep_info + .map(|i| i.npm_reqs.iter().cloned().collect::>()) + .unwrap_or_default(); + if let Err(err) = npm_installer.set_package_reqs(&reqs).await { + lsp_warn!("Could not set npm package requirements: {:#}", err); } } } @@ -339,14 +355,12 @@ impl LspResolver { resolver.resolver.as_ref() } - pub fn create_graph_npm_resolver( + pub fn as_graph_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> WorkerCliNpmGraphResolver { + ) -> &Arc { let resolver = self.get_scope_resolver(file_referrer); - resolver - .resolver - .create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager) + &resolver.npm_graph_resolver } pub fn as_is_cjs_resolver( @@ -590,9 +604,12 @@ pub struct ScopeDepInfo { #[derive(Default)] struct ResolverFactoryServices { cli_resolver: Deferred>, + found_pkg_json_dep_flag: Arc, in_npm_pkg_checker: Deferred>, is_cjs_resolver: Deferred>, node_resolver: Deferred>>, + npm_graph_resolver: Deferred>, + npm_installer: Option>, npm_pkg_req_resolver: Deferred>>, npm_resolver: Option>, } @@ -616,6 +633,10 @@ impl<'a> ResolverFactory<'a> { } } + // todo(dsherret): probably this method could be removed in the future + // and instead just `npm_resolution_initializer.ensure_initialized()` could + // be called. The reason this exists is because creating the npm resolvers + // used to be async. async fn init_npm_resolver( &mut self, http_client_provider: &Arc, @@ -645,11 +666,31 @@ impl<'a> ResolverFactory<'a> { cache.deno_dir().npm_folder_path(), npmrc.get_all_known_registries_urls(), )); - CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { - http_client_provider: http_client_provider.clone(), - // only used for top level install, so we can ignore this - npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), - snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { + let npm_cache = Arc::new(CliNpmCache::new( + npm_cache_dir.clone(), + sys.clone(), + // Use an "only" cache setting in order to make the + // user do an explicit "cache" command and prevent + // the cache from being filled with lots of packages while + // the user is typing. + deno_npm_cache::NpmCacheSetting::Only, + npmrc.clone(), + )); + let pb = ProgressBar::new(ProgressBarStyle::TextOnly); + let npm_client = Arc::new(CliNpmCacheHttpClient::new( + http_client_provider.clone(), + pb.clone(), + )); + let registry_info_provider = Arc::new(CliNpmRegistryInfoProvider::new( + npm_cache.clone(), + npm_client.clone(), + npmrc.clone(), + )); + let npm_resolution = Arc::new(NpmResolutionCell::default()); + let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new( + registry_info_provider.clone(), + npm_resolution.clone(), + match self.config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( lockfile.clone(), @@ -657,26 +698,62 @@ impl<'a> ResolverFactory<'a> { } None => CliNpmResolverManagedSnapshotOption::Specified(None), }, + )); + // Don't provide the lockfile. We don't want these resolvers + // updating it. Only the cache request should update the lockfile. + let maybe_lockfile: Option> = None; + let maybe_node_modules_path = + self.config_data.and_then(|d| d.node_modules_dir.clone()); + let tarball_cache = Arc::new(TarballCache::new( + npm_cache.clone(), + npm_client.clone(), + sys.clone(), + npmrc.clone(), + )); + let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new( + registry_info_provider, + npm_resolution.clone(), + maybe_lockfile.clone(), + )); + let npm_installer = Arc::new(NpmInstaller::new( + npm_cache.clone(), + Arc::new(NpmInstallDepsProvider::empty()), + npm_resolution.clone(), + npm_resolution_initializer.clone(), + npm_resolution_installer, + &pb, + sys.clone(), + tarball_cache.clone(), + maybe_lockfile, + maybe_node_modules_path.clone(), + LifecycleScriptsConfig::default(), + NpmSystemInfo::default(), + )); + self.set_npm_installer(npm_installer); + // spawn due to the lsp's `Send` requirement + deno_core::unsync::spawn(async move { + if let Err(err) = npm_resolution_initializer.ensure_initialized().await + { + log::warn!("failed to initialize npm resolution: {}", err); + } + }) + .await + .unwrap(); + + CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { sys: CliSys::default(), npm_cache_dir, - // Use an "only" cache setting in order to make the - // user do an explicit "cache" command and prevent - // the cache from being filled with lots of packages while - // the user is typing. - cache_setting: CacheSetting::Only, - text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), - // Don't provide the lockfile. We don't want these resolvers - // updating it. Only the cache request should update the lockfile. - maybe_lockfile: None, - maybe_node_modules_path: self - .config_data - .and_then(|d| d.node_modules_dir.clone()), + maybe_node_modules_path, npmrc, + npm_resolution, npm_system_info: NpmSystemInfo::default(), - lifecycle_scripts: Default::default(), }) }; - self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await); + self.set_npm_resolver(create_cli_npm_resolver(options)); + } + + pub fn set_npm_installer(&mut self, npm_installer: Arc) { + self.services.npm_installer = Some(npm_installer); } pub fn set_npm_resolver(&mut self, npm_resolver: Arc) { @@ -720,13 +797,27 @@ impl<'a> ResolverFactory<'a> { is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false), maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()), })); - Arc::new(CliResolver::new(CliResolverOptions { + Arc::new(CliResolver::new( deno_resolver, - npm_resolver: self.npm_resolver().cloned(), - bare_node_builtins_enabled: self + self.services.found_pkg_json_dep_flag.clone(), + )) + }) + } + + pub fn npm_installer(&self) -> Option<&Arc> { + self.services.npm_installer.as_ref() + } + + pub fn npm_graph_resolver(&self) -> &Arc { + self.services.npm_graph_resolver.get_or_init(|| { + Arc::new(CliNpmGraphResolver::new( + None, + self.services.found_pkg_json_dep_flag.clone(), + self .config_data .is_some_and(|d| d.unstable.contains("bare-node-builtins")), - })) + NpmCachingStrategy::Eager, + )) }) } @@ -783,6 +874,7 @@ impl<'a> ResolverFactory<'a> { npm_resolver.clone().into_npm_pkg_folder_resolver(), self.pkg_json_resolver.clone(), self.sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), ))) }) .as_ref() diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index cd1a724f5e..e2a0fc430d 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -4509,11 +4509,12 @@ fn op_release( #[op2] #[serde] +#[allow(clippy::type_complexity)] fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result>, deno_core::url::ParseError> { +) -> Result)>>, deno_core::url::ParseError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -4595,10 +4596,11 @@ async fn op_poll_requests( } #[inline] +#[allow(clippy::type_complexity)] fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result>, deno_core::url::ParseError> { +) -> Result)>>, deno_core::url::ParseError> { let state = state.borrow_mut::(); let mark = state.performance.mark_with_args("tsc.op.op_resolve", &args); let referrer = state.specifier_map.normalize(&args.base)?; @@ -4611,7 +4613,11 @@ fn op_resolve_inner( o.map(|(s, mt)| { ( state.specifier_map.denormalize(&s), - mt.as_ts_extension().to_string(), + if matches!(mt, MediaType::Unknown) { + None + } else { + Some(mt.as_ts_extension().to_string()) + }, ) }) }) @@ -5583,7 +5589,6 @@ mod tests { }) .to_string(), temp_dir.url().join("deno.json").unwrap(), - &Default::default(), ) .unwrap(), ) @@ -6461,7 +6466,7 @@ mod tests { resolved, vec![Some(( temp_dir.url().join("b.ts").unwrap().to_string(), - MediaType::TypeScript.as_ts_extension().to_string() + Some(MediaType::TypeScript.as_ts_extension().to_string()) ))] ); } diff --git a/cli/lsp/urls.rs b/cli/lsp/urls.rs index 91c04f11c0..2aadaf5352 100644 --- a/cli/lsp/urls.rs +++ b/cli/lsp/urls.rs @@ -282,24 +282,26 @@ impl LspUrlMap { } } -/// Convert a e.g. `deno-notebook-cell:` specifier to a `file:` specifier. +/// Convert a e.g. `vscode-notebook-cell:` specifier to a `file:` specifier. /// ```rust /// assert_eq!( /// file_like_to_file_specifier( -/// &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), +/// &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), /// ), -/// Some(Url::parse("file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc").unwrap()), +/// Some(Url::parse("file:///path/to/file.ipynb?scheme=untitled#abc").unwrap()), /// ); fn file_like_to_file_specifier(specifier: &Url) -> Option { - if matches!(specifier.scheme(), "untitled" | "deno-notebook-cell") { + if matches!( + specifier.scheme(), + "untitled" | "vscode-notebook-cell" | "deno-notebook-cell" + ) { if let Ok(mut s) = ModuleSpecifier::parse(&format!( - "file://{}", + "file:///{}", &specifier.as_str()[deno_core::url::quirks::internal_components(specifier) - .host_end as usize..], + .host_end as usize..].trim_start_matches('/'), )) { s.query_pairs_mut() .append_pair("scheme", specifier.scheme()); - s.set_path(&format!("{}.ts", s.path())); return Some(s); } } @@ -432,11 +434,11 @@ mod tests { fn test_file_like_to_file_specifier() { assert_eq!( file_like_to_file_specifier( - &Url::parse("deno-notebook-cell:/path/to/file.ipynb#abc").unwrap(), + &Url::parse("vscode-notebook-cell:/path/to/file.ipynb#abc").unwrap(), ), Some( Url::parse( - "file:///path/to/file.ipynb.ts?scheme=deno-notebook-cell#abc" + "file:///path/to/file.ipynb?scheme=vscode-notebook-cell#abc" ) .unwrap() ), @@ -446,8 +448,7 @@ mod tests { &Url::parse("untitled:/path/to/file.ipynb#123").unwrap(), ), Some( - Url::parse("file:///path/to/file.ipynb.ts?scheme=untitled#123") - .unwrap() + Url::parse("file:///path/to/file.ipynb?scheme=untitled#123").unwrap() ), ); } diff --git a/cli/main.rs b/cli/main.rs index 6bbefcf956..f97ea81e5d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -40,7 +40,6 @@ use deno_core::error::AnyError; use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::unsync::JoinHandle; -use deno_npm::resolution::SnapshotFromLockfileError; use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_runtime::fmt_errors::format_js_error; @@ -52,6 +51,7 @@ use factory::CliFactory; use standalone::MODULE_NOT_FOUND; use standalone::UNSUPPORTED_SCHEME; +use self::npm::ResolveSnapshotError; use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::args::Flags; @@ -376,13 +376,15 @@ fn exit_for_error(error: AnyError) -> ! { util::result::any_and_jserrorbox_downcast_ref::(&error) { error_string = format_js_error(e); - } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = - util::result::any_and_jserrorbox_downcast_ref::( + } else if let Some(e @ ResolveSnapshotError { .. }) = + util::result::any_and_jserrorbox_downcast_ref::( &error, ) { - error_string = e.to_string(); - error_code = 10; + if let Some(e) = e.maybe_integrity_check_error() { + error_string = e.to_string(); + error_code = 10; + } } exit_with_message(&error_string, error_code); diff --git a/cli/npm/managed/installers/common/bin_entries.rs b/cli/npm/installer/common/bin_entries.rs similarity index 99% rename from cli/npm/managed/installers/common/bin_entries.rs rename to cli/npm/installer/common/bin_entries.rs index bc69786b6c..2f7bed285a 100644 --- a/cli/npm/managed/installers/common/bin_entries.rs +++ b/cli/npm/installer/common/bin_entries.rs @@ -8,8 +8,7 @@ use std::path::PathBuf; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageId; - -use crate::npm::managed::NpmResolutionPackage; +use deno_npm::NpmResolutionPackage; #[derive(Default)] pub struct BinEntries<'a> { diff --git a/cli/npm/managed/installers/common/lifecycle_scripts.rs b/cli/npm/installer/common/lifecycle_scripts.rs similarity index 100% rename from cli/npm/managed/installers/common/lifecycle_scripts.rs rename to cli/npm/installer/common/lifecycle_scripts.rs diff --git a/cli/npm/managed/installers/common/mod.rs b/cli/npm/installer/common/mod.rs similarity index 79% rename from cli/npm/managed/installers/common/mod.rs rename to cli/npm/installer/common/mod.rs index 9659649a2e..bd22a58f03 100644 --- a/cli/npm/managed/installers/common/mod.rs +++ b/cli/npm/installer/common/mod.rs @@ -3,14 +3,14 @@ use async_trait::async_trait; use deno_error::JsErrorBox; -use crate::npm::PackageCaching; +use super::PackageCaching; pub mod bin_entries; pub mod lifecycle_scripts; /// Part of the resolution that interacts with the file system. #[async_trait(?Send)] -pub trait NpmPackageFsInstaller: Send + Sync { +pub trait NpmPackageFsInstaller: std::fmt::Debug + Send + Sync { async fn cache_packages<'a>( &self, caching: PackageCaching<'a>, diff --git a/cli/npm/managed/installers/global.rs b/cli/npm/installer/global.rs similarity index 97% rename from cli/npm/managed/installers/global.rs rename to cli/npm/installer/global.rs index 477d73dbfb..a6b296c6d8 100644 --- a/cli/npm/managed/installers/global.rs +++ b/cli/npm/installer/global.rs @@ -11,14 +11,14 @@ use deno_core::futures::StreamExt; use deno_error::JsErrorBox; use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::NpmPackageFsInstaller; +use super::PackageCaching; use crate::args::LifecycleScriptsConfig; use crate::cache::FastInsecureHasher; use crate::colors; -use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; @@ -27,25 +27,25 @@ use crate::npm::CliNpmTarballCache; pub struct GlobalNpmPackageInstaller { cache: Arc, tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, + resolution: Arc, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, } impl GlobalNpmPackageInstaller { pub fn new( cache: Arc, tarball_cache: Arc, - resolution: Arc, - system_info: NpmSystemInfo, + resolution: Arc, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, ) -> Self { Self { cache, tarball_cache, resolution, - system_info, lifecycle_scripts, + system_info, } } } diff --git a/cli/npm/managed/installers/local.rs b/cli/npm/installer/local.rs similarity index 99% rename from cli/npm/managed/installers/local.rs rename to cli/npm/installer/local.rs index a8efaaeb00..87288c6c8e 100644 --- a/cli/npm/managed/installers/local.rs +++ b/cli/npm/installer/local.rs @@ -25,7 +25,7 @@ use deno_npm::NpmResolutionPackage; use deno_npm::NpmSystemInfo; use deno_path_util::fs::atomic_write_file_with_retries; use deno_resolver::npm::get_package_folder_id_folder_name; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_semver::package::PackageNv; use deno_semver::StackString; use serde::Deserialize; @@ -33,11 +33,11 @@ use serde::Serialize; use super::common::bin_entries; use super::common::NpmPackageFsInstaller; +use super::PackageCaching; use crate::args::LifecycleScriptsConfig; use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; use crate::colors; -use crate::npm::managed::PackageCaching; use crate::npm::CliNpmCache; use crate::npm::CliNpmTarballCache; use crate::sys::CliSys; @@ -54,12 +54,12 @@ pub struct LocalNpmPackageInstaller { cache: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, sys: CliSys, tarball_cache: Arc, + lifecycle_scripts: LifecycleScriptsConfig, root_node_modules_path: PathBuf, system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, } impl LocalNpmPackageInstaller { @@ -68,12 +68,12 @@ impl LocalNpmPackageInstaller { cache: Arc, npm_install_deps_provider: Arc, progress_bar: ProgressBar, - resolution: Arc, + resolution: Arc, sys: CliSys, tarball_cache: Arc, node_modules_folder: PathBuf, - system_info: NpmSystemInfo, lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, ) -> Self { Self { cache, @@ -82,9 +82,9 @@ impl LocalNpmPackageInstaller { resolution, tarball_cache, sys, + lifecycle_scripts, root_node_modules_path: node_modules_folder, system_info, - lifecycle_scripts, } } } diff --git a/cli/npm/installer/mod.rs b/cli/npm/installer/mod.rs new file mode 100644 index 0000000000..58b9cb1bc7 --- /dev/null +++ b/cli/npm/installer/mod.rs @@ -0,0 +1,283 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_core::error::AnyError; +use deno_core::unsync::sync::AtomicFlag; +use deno_error::JsErrorBox; +use deno_npm::registry::NpmPackageInfo; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_runtime::colors; +use deno_semver::package::PackageReq; + +pub use self::common::NpmPackageFsInstaller; +use self::global::GlobalNpmPackageInstaller; +use self::local::LocalNpmPackageInstaller; +pub use self::resolution::AddPkgReqsResult; +pub use self::resolution::NpmResolutionInstaller; +use super::NpmResolutionInitializer; +use crate::args::CliLockfile; +use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; +use crate::args::PackageJsonDepValueParseWithLocationError; +use crate::npm::CliNpmCache; +use crate::npm::CliNpmTarballCache; +use crate::sys::CliSys; +use crate::util::progress_bar::ProgressBar; + +mod common; +mod global; +mod local; +mod resolution; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PackageCaching<'a> { + Only(Cow<'a, [PackageReq]>), + All, +} + +#[derive(Debug)] +pub struct NpmInstaller { + fs_installer: Arc, + npm_install_deps_provider: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + maybe_lockfile: Option>, + npm_resolution: Arc, + top_level_install_flag: AtomicFlag, +} + +impl NpmInstaller { + #[allow(clippy::too_many_arguments)] + pub fn new( + npm_cache: Arc, + npm_install_deps_provider: Arc, + npm_resolution: Arc, + npm_resolution_initializer: Arc, + npm_resolution_installer: Arc, + progress_bar: &ProgressBar, + sys: CliSys, + tarball_cache: Arc, + maybe_lockfile: Option>, + maybe_node_modules_path: Option, + lifecycle_scripts: LifecycleScriptsConfig, + system_info: NpmSystemInfo, + ) -> Self { + let fs_installer: Arc = + match maybe_node_modules_path { + Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( + npm_cache, + npm_install_deps_provider.clone(), + progress_bar.clone(), + npm_resolution.clone(), + sys, + tarball_cache, + node_modules_folder, + lifecycle_scripts, + system_info, + )), + None => Arc::new(GlobalNpmPackageInstaller::new( + npm_cache, + tarball_cache, + npm_resolution.clone(), + lifecycle_scripts, + system_info, + )), + }; + Self { + fs_installer, + npm_install_deps_provider, + npm_resolution, + npm_resolution_initializer, + npm_resolution_installer, + maybe_lockfile, + top_level_install_flag: Default::default(), + } + } + + /// Adds package requirements to the resolver and ensures everything is setup. + /// This includes setting up the `node_modules` directory, if applicable. + pub async fn add_and_cache_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw( + packages, + Some(PackageCaching::Only(packages.into())), + ) + .await + .dependencies_result + } + + pub async fn add_package_reqs_no_cache( + &self, + packages: &[PackageReq], + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self + .add_package_reqs_raw(packages, None) + .await + .dependencies_result + } + + pub async fn add_package_reqs( + &self, + packages: &[PackageReq], + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self + .add_package_reqs_raw(packages, Some(caching)) + .await + .dependencies_result + } + + pub async fn add_package_reqs_raw<'a>( + &self, + packages: &[PackageReq], + caching: Option>, + ) -> AddPkgReqsResult { + if packages.is_empty() { + return AddPkgReqsResult { + dependencies_result: Ok(()), + results: vec![], + }; + } + + #[cfg(debug_assertions)] + self.npm_resolution_initializer.debug_assert_initialized(); + + let mut result = self + .npm_resolution_installer + .add_package_reqs(packages) + .await; + + if result.dependencies_result.is_ok() { + if let Some(lockfile) = self.maybe_lockfile.as_ref() { + result.dependencies_result = lockfile.error_if_changed(); + } + } + if result.dependencies_result.is_ok() { + if let Some(caching) = caching { + result.dependencies_result = self.cache_packages(caching).await; + } + } + + result + } + + /// Sets package requirements to the resolver, removing old requirements and adding new ones. + /// + /// This will retrieve and resolve package information, but not cache any package files. + pub async fn set_package_reqs( + &self, + packages: &[PackageReq], + ) -> Result<(), AnyError> { + self + .npm_resolution_installer + .set_package_reqs(packages) + .await + } + + pub async fn inject_synthetic_types_node_package( + &self, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + let reqs = &[PackageReq::from_str("@types/node").unwrap()]; + // add and ensure this isn't added to the lockfile + self + .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) + .await?; + + Ok(()) + } + + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + self + .npm_resolution_installer + .cache_package_info(package_name) + .await + } + + pub async fn cache_packages( + &self, + caching: PackageCaching<'_>, + ) -> Result<(), JsErrorBox> { + self.npm_resolution_initializer.ensure_initialized().await?; + self.fs_installer.cache_packages(caching).await + } + + pub fn ensure_no_pkg_json_dep_errors( + &self, + ) -> Result<(), Box> { + for err in self.npm_install_deps_provider.pkg_json_dep_errors() { + match err.source.as_kind() { + deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { + return Err(Box::new(err.clone())); + } + deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { + .. + } => { + // only warn for this one + log::warn!( + "{} {}\n at {}", + colors::yellow("Warning"), + err.source, + err.location, + ) + } + } + } + Ok(()) + } + + /// Ensures that the top level `package.json` dependencies are installed. + /// This may set up the `node_modules` directory. + /// + /// Returns `true` if the top level packages are already installed. A + /// return value of `false` means that new packages were added to the NPM resolution. + pub async fn ensure_top_level_package_json_install( + &self, + ) -> Result { + if !self.top_level_install_flag.raise() { + return Ok(true); // already did this + } + + self.npm_resolution_initializer.ensure_initialized().await?; + + let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); + if pkg_json_remote_pkgs.is_empty() { + return Ok(true); + } + + // check if something needs resolving before bothering to load all + // the package information (which is slow) + if pkg_json_remote_pkgs.iter().all(|pkg| { + self + .npm_resolution + .resolve_pkg_id_from_pkg_req(&pkg.req) + .is_ok() + }) { + log::debug!( + "All package.json deps resolvable. Skipping top level install." + ); + return Ok(true); // everything is already resolvable + } + + let pkg_reqs = pkg_json_remote_pkgs + .iter() + .map(|pkg| pkg.req.clone()) + .collect::>(); + self.add_package_reqs_no_cache(&pkg_reqs).await?; + + Ok(false) + } +} diff --git a/cli/npm/managed/installer.rs b/cli/npm/installer/resolution.rs similarity index 92% rename from cli/npm/managed/installer.rs rename to cli/npm/installer/resolution.rs index 9f1cc05968..06bbcd4f76 100644 --- a/cli/npm/managed/installer.rs +++ b/cli/npm/installer/resolution.rs @@ -8,12 +8,14 @@ use deno_core::error::AnyError; use deno_error::JsErrorBox; use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo; +use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmRegistryApi; +use deno_npm::registry::NpmRegistryPackageInfoLoadError; use deno_npm::resolution::AddPkgReqsOptions; use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmResolutionPackage; -use deno_resolver::npm::managed::NpmResolution; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; @@ -35,9 +37,10 @@ pub struct AddPkgReqsResult { } /// Updates the npm resolution with the provided package requirements. +#[derive(Debug)] pub struct NpmResolutionInstaller { registry_info_provider: Arc, - resolution: Arc, + resolution: Arc, maybe_lockfile: Option>, update_queue: TaskQueue, } @@ -45,7 +48,7 @@ pub struct NpmResolutionInstaller { impl NpmResolutionInstaller { pub fn new( registry_info_provider: Arc, - resolution: Arc, + resolution: Arc, maybe_lockfile: Option>, ) -> Self { Self { @@ -56,6 +59,14 @@ impl NpmResolutionInstaller { } } + pub async fn cache_package_info( + &self, + package_name: &str, + ) -> Result, NpmRegistryPackageInfoLoadError> { + // this will internally cache the package information + self.registry_info_provider.package_info(package_name).await + } + pub async fn add_package_reqs( &self, package_reqs: &[PackageReq], diff --git a/cli/npm/managed.rs b/cli/npm/managed.rs new file mode 100644 index 0000000000..0d4c209acc --- /dev/null +++ b/cli/npm/managed.rs @@ -0,0 +1,496 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_ast::ModuleSpecifier; +use deno_cache_dir::npm::NpmCacheDir; +use deno_core::parking_lot::Mutex; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_error::JsError; +use deno_error::JsErrorBox; +use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::registry::NpmRegistryApi; +use deno_npm::resolution::NpmResolutionSnapshot; +use deno_npm::resolution::PackageReqNotFoundError; +use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; +use deno_npm::NpmPackageId; +use deno_npm::NpmResolutionPackage; +use deno_npm::NpmSystemInfo; +use deno_resolver::npm::managed::NpmResolutionCell; +use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; +use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; +use deno_resolver::npm::managed::ResolvePkgIdFromSpecifierError; +use deno_resolver::npm::ByonmOrManagedNpmResolver; +use deno_resolver::npm::ManagedNpmResolver; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use node_resolver::NpmPackageFolderResolver; +use sys_traits::FsMetadata; +use thiserror::Error; + +use super::CliNpmRegistryInfoProvider; +use super::CliNpmResolver; +use super::InnerCliNpmResolverRef; +use crate::args::CliLockfile; +use crate::args::NpmProcessState; +use crate::args::NpmProcessStateKind; +use crate::cache::FastInsecureHasher; +use crate::sys::CliSys; + +#[derive(Debug, Clone)] +pub enum CliNpmResolverManagedSnapshotOption { + ResolveFromLockfile(Arc), + Specified(Option), +} + +#[derive(Debug)] +enum SyncState { + Pending(Option), + Err(ResolveSnapshotError), + Success, +} + +#[derive(Debug)] +pub struct NpmResolutionInitializer { + npm_registry_info_provider: Arc, + npm_resolution: Arc, + queue: tokio::sync::Mutex<()>, + sync_state: Mutex, +} + +impl NpmResolutionInitializer { + pub fn new( + npm_registry_info_provider: Arc, + npm_resolution: Arc, + snapshot_option: CliNpmResolverManagedSnapshotOption, + ) -> Self { + Self { + npm_registry_info_provider, + npm_resolution, + queue: tokio::sync::Mutex::new(()), + sync_state: Mutex::new(SyncState::Pending(Some(snapshot_option))), + } + } + + #[cfg(debug_assertions)] + pub fn debug_assert_initialized(&self) { + if !matches!(*self.sync_state.lock(), SyncState::Success) { + panic!("debug assert: npm resolution must be initialized before calling this code"); + } + } + + pub async fn ensure_initialized(&self) -> Result<(), JsErrorBox> { + // fast exit if not pending + { + match &*self.sync_state.lock() { + SyncState::Pending(_) => {} + SyncState::Err(err) => return Err(JsErrorBox::from_err(err.clone())), + SyncState::Success => return Ok(()), + } + } + + // only allow one task in here at a time + let _guard = self.queue.lock().await; + + let snapshot_option = { + let mut sync_state = self.sync_state.lock(); + match &mut *sync_state { + SyncState::Pending(snapshot_option) => { + // this should never panic, but if it does it means that a + // previous future was dropped while initialization occurred... + // that should never happen because this is initialized during + // startup + snapshot_option.take().unwrap() + } + // another thread updated the state while we were waiting + SyncState::Err(resolve_snapshot_error) => { + return Err(JsErrorBox::from_err(resolve_snapshot_error.clone())); + } + SyncState::Success => { + return Ok(()); + } + } + }; + + match resolve_snapshot(&self.npm_registry_info_provider, snapshot_option) + .await + { + Ok(maybe_snapshot) => { + if let Some(snapshot) = maybe_snapshot { + self + .npm_resolution + .set_snapshot(NpmResolutionSnapshot::new(snapshot)); + } + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Success; + Ok(()) + } + Err(err) => { + let mut sync_state = self.sync_state.lock(); + *sync_state = SyncState::Err(err.clone()); + Err(JsErrorBox::from_err(err)) + } + } + } +} + +pub struct CliManagedNpmResolverCreateOptions { + pub npm_cache_dir: Arc, + pub sys: CliSys, + pub maybe_node_modules_path: Option, + pub npm_system_info: NpmSystemInfo, + pub npmrc: Arc, + pub npm_resolution: Arc, +} + +pub fn create_managed_npm_resolver( + options: CliManagedNpmResolverCreateOptions, +) -> Arc { + let managed_npm_resolver = + Arc::new(ManagedNpmResolver::::new::( + &options.npm_cache_dir, + &options.npmrc, + options.npm_resolution.clone(), + options.sys.clone(), + options.maybe_node_modules_path, + )); + Arc::new(ManagedCliNpmResolver::new( + managed_npm_resolver, + options.npm_cache_dir, + options.npmrc, + options.npm_resolution, + options.sys, + options.npm_system_info, + )) +} + +#[derive(Debug, Error, Clone, JsError)] +#[error("failed reading lockfile '{}'", lockfile_path.display())] +#[class(inherit)] +pub struct ResolveSnapshotError { + lockfile_path: PathBuf, + #[inherit] + #[source] + source: SnapshotFromLockfileError, +} + +impl ResolveSnapshotError { + pub fn maybe_integrity_check_error( + &self, + ) -> Option<&deno_npm::resolution::IntegrityCheckFailedError> { + match &self.source { + SnapshotFromLockfileError::SnapshotFromLockfile( + deno_npm::resolution::SnapshotFromLockfileError::IntegrityCheckFailed( + err, + ), + ) => Some(err), + _ => None, + } + } +} + +async fn resolve_snapshot( + registry_info_provider: &Arc, + snapshot: CliNpmResolverManagedSnapshotOption, +) -> Result, ResolveSnapshotError> +{ + match snapshot { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { + if !lockfile.overwrite() { + let snapshot = snapshot_from_lockfile( + lockfile.clone(), + ®istry_info_provider.as_npm_registry_api(), + ) + .await + .map_err(|source| ResolveSnapshotError { + lockfile_path: lockfile.filename.clone(), + source, + })?; + Ok(Some(snapshot)) + } else { + Ok(None) + } + } + CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), + } +} + +#[derive(Debug, Error, Clone, JsError)] +pub enum SnapshotFromLockfileError { + #[error(transparent)] + #[class(inherit)] + IncompleteError( + #[from] deno_npm::resolution::IncompleteSnapshotFromLockfileError, + ), + #[error(transparent)] + #[class(inherit)] + SnapshotFromLockfile(#[from] deno_npm::resolution::SnapshotFromLockfileError), +} + +async fn snapshot_from_lockfile( + lockfile: Arc, + api: &dyn NpmRegistryApi, +) -> Result { + let (incomplete_snapshot, skip_integrity_check) = { + let lock = lockfile.lock(); + ( + deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, + lock.overwrite, + ) + }; + let snapshot = deno_npm::resolution::snapshot_from_lockfile( + deno_npm::resolution::SnapshotFromLockfileParams { + incomplete_snapshot, + api, + skip_integrity_check, + }, + ) + .await?; + Ok(snapshot) +} + +/// An npm resolver where the resolution is managed by Deno rather than +/// the user bringing their own node_modules (BYONM) on the file system. +pub struct ManagedCliNpmResolver { + managed_npm_resolver: Arc>, + npm_cache_dir: Arc, + npm_rc: Arc, + sys: CliSys, + resolution: Arc, + system_info: NpmSystemInfo, +} + +impl std::fmt::Debug for ManagedCliNpmResolver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ManagedCliNpmResolver") + .field("", &"") + .finish() + } +} + +impl ManagedCliNpmResolver { + #[allow(clippy::too_many_arguments)] + pub fn new( + managed_npm_resolver: Arc>, + npm_cache_dir: Arc, + npm_rc: Arc, + resolution: Arc, + sys: CliSys, + system_info: NpmSystemInfo, + ) -> Self { + Self { + managed_npm_resolver, + npm_cache_dir, + npm_rc, + resolution, + sys, + system_info, + } + } + + pub fn resolve_pkg_folder_from_pkg_id( + &self, + pkg_id: &NpmPackageId, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(pkg_id) + } + + /// Resolves the package id from the provided specifier. + pub fn resolve_pkg_id_from_specifier( + &self, + specifier: &ModuleSpecifier, + ) -> Result, ResolvePkgIdFromSpecifierError> { + self + .managed_npm_resolver + .resolve_pkg_id_from_specifier(specifier) + } + + pub fn resolve_pkg_reqs_from_pkg_id( + &self, + id: &NpmPackageId, + ) -> Vec { + self.resolution.resolve_pkg_reqs_from_pkg_id(id) + } + + pub fn all_system_packages( + &self, + system_info: &NpmSystemInfo, + ) -> Vec { + self.resolution.all_system_packages(system_info) + } + + /// Checks if the provided package req's folder is cached. + pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { + self + .resolve_pkg_id_from_pkg_req(req) + .ok() + .and_then(|id| { + self + .managed_npm_resolver + .resolve_pkg_folder_from_pkg_id(&id) + .ok() + }) + .map(|folder| self.sys.fs_exists_no_err(folder)) + .unwrap_or(false) + } + + pub fn snapshot(&self) -> NpmResolutionSnapshot { + self.resolution.snapshot() + } + + pub fn top_package_req_for_name(&self, name: &str) -> Option { + let package_reqs = self.resolution.package_reqs(); + let mut entries = package_reqs + .iter() + .filter(|(_, nv)| nv.name == name) + .collect::>(); + entries.sort_by_key(|(_, nv)| &nv.version); + Some(entries.last()?.0.clone()) + } + + pub fn serialized_valid_snapshot_for_system( + &self, + system_info: &NpmSystemInfo, + ) -> ValidSerializedNpmResolutionSnapshot { + self + .resolution + .serialized_valid_snapshot_for_system(system_info) + } + + pub fn resolve_pkg_folder_from_deno_module( + &self, + nv: &PackageNv, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_deno_module(nv) + } + + pub fn resolve_pkg_id_from_pkg_req( + &self, + req: &PackageReq, + ) -> Result { + self.resolution.resolve_pkg_id_from_pkg_req(req) + } + + pub fn maybe_node_modules_path(&self) -> Option<&Path> { + self.managed_npm_resolver.node_modules_path() + } + + pub fn global_cache_root_path(&self) -> &Path { + self.npm_cache_dir.root_dir() + } + + pub fn global_cache_root_url(&self) -> &Url { + self.npm_cache_dir.root_dir_url() + } +} + +pub fn npm_process_state( + snapshot: ValidSerializedNpmResolutionSnapshot, + node_modules_path: Option<&Path>, +) -> String { + serde_json::to_string(&NpmProcessState { + kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), + local_node_modules_path: node_modules_path + .map(|p| p.to_string_lossy().to_string()), + }) + .unwrap() +} + +impl NpmProcessStateProvider for ManagedCliNpmResolver { + fn get_npm_process_state(&self) -> String { + npm_process_state( + self.resolution.serialized_valid_snapshot(), + self.managed_npm_resolver.node_modules_path(), + ) + } +} + +impl CliNpmResolver for ManagedCliNpmResolver { + fn into_npm_pkg_folder_resolver( + self: Arc, + ) -> Arc { + self.managed_npm_resolver.clone() + } + + fn into_process_state_provider( + self: Arc, + ) -> Arc { + self + } + + fn into_byonm_or_managed( + self: Arc, + ) -> ByonmOrManagedNpmResolver { + ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) + } + + fn clone_snapshotted(&self) -> Arc { + // create a new snapshotted npm resolution and resolver + let npm_resolution = + Arc::new(NpmResolutionCell::new(self.resolution.snapshot())); + + Arc::new(ManagedCliNpmResolver::new( + Arc::new(ManagedNpmResolver::::new::( + &self.npm_cache_dir, + &self.npm_rc, + npm_resolution.clone(), + self.sys.clone(), + self.root_node_modules_path().map(ToOwned::to_owned), + )), + self.npm_cache_dir.clone(), + self.npm_rc.clone(), + npm_resolution, + self.sys.clone(), + self.system_info.clone(), + )) + } + + fn as_inner(&self) -> InnerCliNpmResolverRef { + InnerCliNpmResolverRef::Managed(self) + } + + fn root_node_modules_path(&self) -> Option<&Path> { + self.managed_npm_resolver.node_modules_path() + } + + fn check_state_hash(&self) -> Option { + // We could go further and check all the individual + // npm packages, but that's probably overkill. + let mut package_reqs = self + .resolution + .package_reqs() + .into_iter() + .collect::>(); + package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism + let mut hasher = FastInsecureHasher::new_without_deno_version(); + // ensure the cache gets busted when turning nodeModulesDir on or off + // as this could cause changes in resolution + hasher + .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); + for (pkg_req, pkg_nv) in package_reqs { + hasher.write_hashable(&pkg_req); + hasher.write_hashable(&pkg_nv); + } + Some(hasher.finish()) + } + + fn resolve_pkg_folder_from_deno_module_req( + &self, + req: &PackageReq, + referrer: &Url, + ) -> Result { + self + .managed_npm_resolver + .resolve_pkg_folder_from_deno_module_req(req, referrer) + .map_err(ResolvePkgFolderFromDenoReqError::Managed) + } +} diff --git a/cli/npm/managed/installers/mod.rs b/cli/npm/managed/installers/mod.rs deleted file mode 100644 index 39e0d6f77c..0000000000 --- a/cli/npm/managed/installers/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::path::PathBuf; -use std::sync::Arc; - -use deno_npm::NpmSystemInfo; -use deno_resolver::npm::managed::NpmResolution; - -pub use self::common::NpmPackageFsInstaller; -use self::global::GlobalNpmPackageInstaller; -use self::local::LocalNpmPackageInstaller; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::npm::CliNpmCache; -use crate::npm::CliNpmTarballCache; -use crate::sys::CliSys; -use crate::util::progress_bar::ProgressBar; - -mod common; -mod global; -mod local; - -#[allow(clippy::too_many_arguments)] -pub fn create_npm_fs_installer( - npm_cache: Arc, - npm_install_deps_provider: &Arc, - progress_bar: &ProgressBar, - resolution: Arc, - sys: CliSys, - tarball_cache: Arc, - maybe_node_modules_path: Option, - system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - match maybe_node_modules_path { - Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new( - npm_cache, - npm_install_deps_provider.clone(), - progress_bar.clone(), - resolution, - sys, - tarball_cache, - node_modules_folder, - system_info, - lifecycle_scripts, - )), - None => Arc::new(GlobalNpmPackageInstaller::new( - npm_cache, - tarball_cache, - resolution, - system_info, - lifecycle_scripts, - )), - } -} diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs deleted file mode 100644 index 8d2ede7e67..0000000000 --- a/cli/npm/managed/mod.rs +++ /dev/null @@ -1,768 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::borrow::Cow; -use std::path::Path; -use std::path::PathBuf; -use std::sync::Arc; - -use deno_ast::ModuleSpecifier; -use deno_cache_dir::npm::NpmCacheDir; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_error::JsErrorBox; -use deno_npm::npm_rc::ResolvedNpmRc; -use deno_npm::registry::NpmPackageInfo; -use deno_npm::registry::NpmRegistryApi; -use deno_npm::resolution::NpmResolutionSnapshot; -use deno_npm::resolution::PackageReqNotFoundError; -use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; -use deno_npm::NpmPackageId; -use deno_npm::NpmResolutionPackage; -use deno_npm::NpmSystemInfo; -use deno_npm_cache::NpmCacheSetting; -use deno_resolver::npm::managed::NpmResolution; -use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError; -use deno_resolver::npm::ByonmOrManagedNpmResolver; -use deno_resolver::npm::ManagedNpmResolver; -use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; -use deno_runtime::colors; -use deno_runtime::ops::process::NpmProcessStateProvider; -use deno_semver::package::PackageNv; -use deno_semver::package::PackageReq; -use installer::AddPkgReqsResult; -use installer::NpmResolutionInstaller; -use installers::create_npm_fs_installer; -use installers::NpmPackageFsInstaller; -use node_resolver::NpmPackageFolderResolver; - -use super::CliNpmCache; -use super::CliNpmCacheHttpClient; -use super::CliNpmRegistryInfoProvider; -use super::CliNpmResolver; -use super::CliNpmTarballCache; -use super::InnerCliNpmResolverRef; -use crate::args::CliLockfile; -use crate::args::LifecycleScriptsConfig; -use crate::args::NpmInstallDepsProvider; -use crate::args::NpmProcessState; -use crate::args::NpmProcessStateKind; -use crate::args::PackageJsonDepValueParseWithLocationError; -use crate::cache::FastInsecureHasher; -use crate::sys::CliSys; -use crate::util::progress_bar::ProgressBar; -use crate::util::sync::AtomicFlag; - -mod installer; -mod installers; - -pub enum CliNpmResolverManagedSnapshotOption { - ResolveFromLockfile(Arc), - Specified(Option), -} - -pub struct CliManagedNpmResolverCreateOptions { - pub snapshot: CliNpmResolverManagedSnapshotOption, - pub maybe_lockfile: Option>, - pub http_client_provider: Arc, - pub npm_cache_dir: Arc, - pub sys: CliSys, - pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting, - pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, - pub maybe_node_modules_path: Option, - pub npm_system_info: NpmSystemInfo, - pub npm_install_deps_provider: Arc, - pub npmrc: Arc, - pub lifecycle_scripts: LifecycleScriptsConfig, -} - -pub async fn create_managed_npm_resolver_for_lsp( - options: CliManagedNpmResolverCreateOptions, -) -> Arc { - let npm_cache = create_cache(&options); - let http_client = Arc::new(CliNpmCacheHttpClient::new( - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )); - let npm_api = create_api(npm_cache.clone(), http_client.clone(), &options); - // spawn due to the lsp's `Send` requirement - deno_core::unsync::spawn(async move { - let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await { - Ok(snapshot) => snapshot, - Err(err) => { - log::warn!("failed to resolve snapshot: {}", err); - None - } - }; - create_inner( - http_client, - npm_cache, - options.npm_cache_dir, - options.npm_install_deps_provider, - npm_api, - options.sys, - options.text_only_progress_bar, - options.maybe_lockfile, - options.npmrc, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - ) - }) - .await - .unwrap() -} - -pub async fn create_managed_npm_resolver( - options: CliManagedNpmResolverCreateOptions, -) -> Result, AnyError> { - let npm_cache = create_cache(&options); - let http_client = Arc::new(CliNpmCacheHttpClient::new( - options.http_client_provider.clone(), - options.text_only_progress_bar.clone(), - )); - let api = create_api(npm_cache.clone(), http_client.clone(), &options); - let snapshot = resolve_snapshot(&api, options.snapshot).await?; - Ok(create_inner( - http_client, - npm_cache, - options.npm_cache_dir, - options.npm_install_deps_provider, - api, - options.sys, - options.text_only_progress_bar, - options.maybe_lockfile, - options.npmrc, - options.maybe_node_modules_path, - options.npm_system_info, - snapshot, - options.lifecycle_scripts, - )) -} - -#[allow(clippy::too_many_arguments)] -fn create_inner( - http_client: Arc, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - registry_info_provider: Arc, - sys: CliSys, - text_only_progress_bar: crate::util::progress_bar::ProgressBar, - maybe_lockfile: Option>, - npm_rc: Arc, - node_modules_dir_path: Option, - npm_system_info: NpmSystemInfo, - snapshot: Option, - lifecycle_scripts: LifecycleScriptsConfig, -) -> Arc { - let resolution = Arc::new(NpmResolution::from_serialized(snapshot)); - let tarball_cache = Arc::new(CliNpmTarballCache::new( - npm_cache.clone(), - http_client, - sys.clone(), - npm_rc.clone(), - )); - - let fs_installer = create_npm_fs_installer( - npm_cache.clone(), - &npm_install_deps_provider, - &text_only_progress_bar, - resolution.clone(), - sys.clone(), - tarball_cache.clone(), - node_modules_dir_path.clone(), - npm_system_info.clone(), - lifecycle_scripts.clone(), - ); - let managed_npm_resolver = - Arc::new(ManagedNpmResolver::::new::( - &npm_cache_dir, - &npm_rc, - resolution.clone(), - sys.clone(), - node_modules_dir_path, - )); - Arc::new(ManagedCliNpmResolver::new( - fs_installer, - maybe_lockfile, - managed_npm_resolver, - npm_cache, - npm_cache_dir, - npm_install_deps_provider, - npm_rc, - registry_info_provider, - resolution, - sys, - tarball_cache, - text_only_progress_bar, - npm_system_info, - lifecycle_scripts, - )) -} - -fn create_cache( - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmCache::new( - options.npm_cache_dir.clone(), - options.sys.clone(), - NpmCacheSetting::from_cache_setting(&options.cache_setting), - options.npmrc.clone(), - )) -} - -fn create_api( - cache: Arc, - http_client: Arc, - options: &CliManagedNpmResolverCreateOptions, -) -> Arc { - Arc::new(CliNpmRegistryInfoProvider::new( - cache, - http_client, - options.npmrc.clone(), - )) -} - -async fn resolve_snapshot( - registry_info_provider: &Arc, - snapshot: CliNpmResolverManagedSnapshotOption, -) -> Result, AnyError> { - match snapshot { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => { - if !lockfile.overwrite() { - let snapshot = snapshot_from_lockfile( - lockfile.clone(), - ®istry_info_provider.as_npm_registry_api(), - ) - .await - .with_context(|| { - format!("failed reading lockfile '{}'", lockfile.filename.display()) - })?; - Ok(Some(snapshot)) - } else { - Ok(None) - } - } - CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot), - } -} - -async fn snapshot_from_lockfile( - lockfile: Arc, - api: &dyn NpmRegistryApi, -) -> Result { - let (incomplete_snapshot, skip_integrity_check) = { - let lock = lockfile.lock(); - ( - deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?, - lock.overwrite, - ) - }; - let snapshot = deno_npm::resolution::snapshot_from_lockfile( - deno_npm::resolution::SnapshotFromLockfileParams { - incomplete_snapshot, - api, - skip_integrity_check, - }, - ) - .await?; - Ok(snapshot) -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum PackageCaching<'a> { - Only(Cow<'a, [PackageReq]>), - All, -} - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum ResolvePkgFolderFromDenoModuleError { - #[class(inherit)] - #[error(transparent)] - PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError), - #[class(inherit)] - #[error(transparent)] - ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), -} - -/// An npm resolver where the resolution is managed by Deno rather than -/// the user bringing their own node_modules (BYONM) on the file system. -pub struct ManagedCliNpmResolver { - fs_installer: Arc, - maybe_lockfile: Option>, - registry_info_provider: Arc, - managed_npm_resolver: Arc>, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - npm_rc: Arc, - sys: CliSys, - resolution: Arc, - resolution_installer: NpmResolutionInstaller, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - top_level_install_flag: AtomicFlag, - lifecycle_scripts: LifecycleScriptsConfig, -} - -impl std::fmt::Debug for ManagedCliNpmResolver { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ManagedCliNpmResolver") - .field("", &"") - .finish() - } -} - -impl ManagedCliNpmResolver { - #[allow(clippy::too_many_arguments)] - pub fn new( - fs_installer: Arc, - maybe_lockfile: Option>, - managed_npm_resolver: Arc>, - npm_cache: Arc, - npm_cache_dir: Arc, - npm_install_deps_provider: Arc, - npm_rc: Arc, - registry_info_provider: Arc, - resolution: Arc, - sys: CliSys, - tarball_cache: Arc, - text_only_progress_bar: ProgressBar, - npm_system_info: NpmSystemInfo, - lifecycle_scripts: LifecycleScriptsConfig, - ) -> Self { - let resolution_installer = NpmResolutionInstaller::new( - registry_info_provider.clone(), - resolution.clone(), - maybe_lockfile.clone(), - ); - Self { - fs_installer, - maybe_lockfile, - managed_npm_resolver, - npm_cache, - npm_cache_dir, - npm_install_deps_provider, - npm_rc, - registry_info_provider, - text_only_progress_bar, - resolution, - resolution_installer, - sys, - tarball_cache, - npm_system_info, - top_level_install_flag: Default::default(), - lifecycle_scripts, - } - } - - pub fn resolve_pkg_folder_from_pkg_id( - &self, - pkg_id: &NpmPackageId, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(pkg_id) - } - - /// Resolves the package id from the provided specifier. - pub fn resolve_pkg_id_from_specifier( - &self, - specifier: &ModuleSpecifier, - ) -> Result, AnyError> { - let Some(cache_folder_id) = self - .managed_npm_resolver - .resolve_package_cache_folder_id_from_specifier(specifier)? - else { - return Ok(None); - }; - Ok(Some( - self - .resolution - .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, - )) - } - - pub fn resolve_pkg_reqs_from_pkg_id( - &self, - id: &NpmPackageId, - ) -> Vec { - self.resolution.resolve_pkg_reqs_from_pkg_id(id) - } - - /// Attempts to get the package size in bytes. - pub fn package_size( - &self, - package_id: &NpmPackageId, - ) -> Result { - let package_folder = self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(package_id)?; - Ok(crate::util::fs::dir_size(&package_folder)?) - } - - pub fn all_system_packages( - &self, - system_info: &NpmSystemInfo, - ) -> Vec { - self.resolution.all_system_packages(system_info) - } - - /// Checks if the provided package req's folder is cached. - pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool { - self - .resolve_pkg_id_from_pkg_req(req) - .ok() - .and_then(|id| { - self - .managed_npm_resolver - .resolve_pkg_folder_from_pkg_id(&id) - .ok() - }) - .map(|folder| folder.exists()) - .unwrap_or(false) - } - - /// Adds package requirements to the resolver and ensures everything is setup. - /// This includes setting up the `node_modules` directory, if applicable. - pub async fn add_and_cache_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw( - packages, - Some(PackageCaching::Only(packages.into())), - ) - .await - .dependencies_result - } - - pub async fn add_package_reqs_no_cache( - &self, - packages: &[PackageReq], - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw(packages, None) - .await - .dependencies_result - } - - pub async fn add_package_reqs( - &self, - packages: &[PackageReq], - caching: PackageCaching<'_>, - ) -> Result<(), JsErrorBox> { - self - .add_package_reqs_raw(packages, Some(caching)) - .await - .dependencies_result - } - - pub async fn add_package_reqs_raw<'a>( - &self, - packages: &[PackageReq], - caching: Option>, - ) -> AddPkgReqsResult { - if packages.is_empty() { - return AddPkgReqsResult { - dependencies_result: Ok(()), - results: vec![], - }; - } - - let mut result = self.resolution_installer.add_package_reqs(packages).await; - - if result.dependencies_result.is_ok() { - if let Some(lockfile) = self.maybe_lockfile.as_ref() { - result.dependencies_result = lockfile.error_if_changed(); - } - } - if result.dependencies_result.is_ok() { - if let Some(caching) = caching { - result.dependencies_result = self.cache_packages(caching).await; - } - } - - result - } - - /// Sets package requirements to the resolver, removing old requirements and adding new ones. - /// - /// This will retrieve and resolve package information, but not cache any package files. - pub async fn set_package_reqs( - &self, - packages: &[PackageReq], - ) -> Result<(), AnyError> { - self.resolution_installer.set_package_reqs(packages).await - } - - pub fn snapshot(&self) -> NpmResolutionSnapshot { - self.resolution.snapshot() - } - - pub fn top_package_req_for_name(&self, name: &str) -> Option { - let package_reqs = self.resolution.package_reqs(); - let mut entries = package_reqs - .iter() - .filter(|(_, nv)| nv.name == name) - .collect::>(); - entries.sort_by_key(|(_, nv)| &nv.version); - Some(entries.last()?.0.clone()) - } - - pub fn serialized_valid_snapshot_for_system( - &self, - system_info: &NpmSystemInfo, - ) -> ValidSerializedNpmResolutionSnapshot { - self - .resolution - .serialized_valid_snapshot_for_system(system_info) - } - - pub async fn inject_synthetic_types_node_package( - &self, - ) -> Result<(), JsErrorBox> { - let reqs = &[PackageReq::from_str("@types/node").unwrap()]; - // add and ensure this isn't added to the lockfile - self - .add_package_reqs(reqs, PackageCaching::Only(reqs.into())) - .await?; - - Ok(()) - } - - pub async fn cache_packages( - &self, - caching: PackageCaching<'_>, - ) -> Result<(), JsErrorBox> { - self.fs_installer.cache_packages(caching).await - } - - pub fn resolve_pkg_folder_from_deno_module( - &self, - nv: &PackageNv, - ) -> Result { - let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; - Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) - } - - pub fn resolve_pkg_id_from_pkg_req( - &self, - req: &PackageReq, - ) -> Result { - self.resolution.resolve_pkg_id_from_pkg_req(req) - } - - pub fn ensure_no_pkg_json_dep_errors( - &self, - ) -> Result<(), Box> { - for err in self.npm_install_deps_provider.pkg_json_dep_errors() { - match err.source.as_kind() { - deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => { - return Err(Box::new(err.clone())); - } - deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported { - .. - } => { - // only warn for this one - log::warn!( - "{} {}\n at {}", - colors::yellow("Warning"), - err.source, - err.location, - ) - } - } - } - Ok(()) - } - - /// Ensures that the top level `package.json` dependencies are installed. - /// This may set up the `node_modules` directory. - /// - /// Returns `true` if the top level packages are already installed. A - /// return value of `false` means that new packages were added to the NPM resolution. - pub async fn ensure_top_level_package_json_install( - &self, - ) -> Result { - if !self.top_level_install_flag.raise() { - return Ok(true); // already did this - } - - let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); - if pkg_json_remote_pkgs.is_empty() { - return Ok(true); - } - - // check if something needs resolving before bothering to load all - // the package information (which is slow) - if pkg_json_remote_pkgs.iter().all(|pkg| { - self - .resolution - .resolve_pkg_id_from_pkg_req(&pkg.req) - .is_ok() - }) { - log::debug!( - "All package.json deps resolvable. Skipping top level install." - ); - return Ok(true); // everything is already resolvable - } - - let pkg_reqs = pkg_json_remote_pkgs - .iter() - .map(|pkg| pkg.req.clone()) - .collect::>(); - self.add_package_reqs_no_cache(&pkg_reqs).await?; - - Ok(false) - } - - pub async fn cache_package_info( - &self, - package_name: &str, - ) -> Result, AnyError> { - // this will internally cache the package information - self - .registry_info_provider - .package_info(package_name) - .await - .map_err(|err| err.into()) - } - - pub fn maybe_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - pub fn global_cache_root_path(&self) -> &Path { - self.npm_cache_dir.root_dir() - } - - pub fn global_cache_root_url(&self) -> &Url { - self.npm_cache_dir.root_dir_url() - } -} - -fn npm_process_state( - snapshot: ValidSerializedNpmResolutionSnapshot, - node_modules_path: Option<&Path>, -) -> String { - serde_json::to_string(&NpmProcessState { - kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()), - local_node_modules_path: node_modules_path - .map(|p| p.to_string_lossy().to_string()), - }) - .unwrap() -} - -impl NpmProcessStateProvider for ManagedCliNpmResolver { - fn get_npm_process_state(&self) -> String { - npm_process_state( - self.resolution.serialized_valid_snapshot(), - self.managed_npm_resolver.node_modules_path(), - ) - } -} - -impl CliNpmResolver for ManagedCliNpmResolver { - fn into_npm_pkg_folder_resolver( - self: Arc, - ) -> Arc { - self.managed_npm_resolver.clone() - } - - fn into_process_state_provider( - self: Arc, - ) -> Arc { - self - } - - fn into_byonm_or_managed( - self: Arc, - ) -> ByonmOrManagedNpmResolver { - ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone()) - } - - fn clone_snapshotted(&self) -> Arc { - // create a new snapshotted npm resolution and resolver - let npm_resolution = - Arc::new(NpmResolution::new(self.resolution.snapshot())); - - Arc::new(ManagedCliNpmResolver::new( - create_npm_fs_installer( - self.npm_cache.clone(), - &self.npm_install_deps_provider, - &self.text_only_progress_bar, - npm_resolution.clone(), - self.sys.clone(), - self.tarball_cache.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - ), - self.maybe_lockfile.clone(), - Arc::new(ManagedNpmResolver::::new::( - &self.npm_cache_dir, - &self.npm_rc, - npm_resolution.clone(), - self.sys.clone(), - self.root_node_modules_path().map(ToOwned::to_owned), - )), - self.npm_cache.clone(), - self.npm_cache_dir.clone(), - self.npm_install_deps_provider.clone(), - self.npm_rc.clone(), - self.registry_info_provider.clone(), - npm_resolution, - self.sys.clone(), - self.tarball_cache.clone(), - self.text_only_progress_bar.clone(), - self.npm_system_info.clone(), - self.lifecycle_scripts.clone(), - )) - } - - fn as_inner(&self) -> InnerCliNpmResolverRef { - InnerCliNpmResolverRef::Managed(self) - } - - fn root_node_modules_path(&self) -> Option<&Path> { - self.managed_npm_resolver.node_modules_path() - } - - fn check_state_hash(&self) -> Option { - // We could go further and check all the individual - // npm packages, but that's probably overkill. - let mut package_reqs = self - .resolution - .package_reqs() - .into_iter() - .collect::>(); - package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism - let mut hasher = FastInsecureHasher::new_without_deno_version(); - // ensure the cache gets busted when turning nodeModulesDir on or off - // as this could cause changes in resolution - hasher - .write_hashable(self.managed_npm_resolver.node_modules_path().is_some()); - for (pkg_req, pkg_nv) in package_reqs { - hasher.write_hashable(&pkg_req); - hasher.write_hashable(&pkg_nv); - } - Some(hasher.finish()) - } - - fn resolve_pkg_folder_from_deno_module_req( - &self, - req: &PackageReq, - referrer: &Url, - ) -> Result { - self - .managed_npm_resolver - .resolve_pkg_folder_from_deno_module_req(req, referrer) - .map_err(ResolvePkgFolderFromDenoReqError::Managed) - } -} diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 052a98e6cf..6ad7ad610e 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. mod byonm; +pub mod installer; mod managed; mod permission_checker; @@ -9,7 +10,6 @@ use std::path::PathBuf; use std::sync::Arc; use dashmap::DashMap; -use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; use deno_error::JsErrorBox; @@ -30,8 +30,8 @@ pub use self::byonm::CliByonmNpmResolverCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::ManagedCliNpmResolver; -pub use self::managed::PackageCaching; -pub use self::managed::ResolvePkgFolderFromDenoModuleError; +pub use self::managed::NpmResolutionInitializer; +pub use self::managed::ResolveSnapshotError; pub use self::permission_checker::NpmRegistryReadPermissionChecker; pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; use crate::file_fetcher::CliFileFetcher; @@ -109,28 +109,16 @@ pub enum CliNpmResolverCreateOptions { Byonm(CliByonmNpmResolverCreateOptions), } -pub async fn create_cli_npm_resolver_for_lsp( +pub fn create_cli_npm_resolver( options: CliNpmResolverCreateOptions, ) -> Arc { use CliNpmResolverCreateOptions::*; match options { - Managed(options) => { - managed::create_managed_npm_resolver_for_lsp(options).await - } + Managed(options) => managed::create_managed_npm_resolver(options), Byonm(options) => Arc::new(ByonmNpmResolver::new(options)), } } -pub async fn create_cli_npm_resolver( - options: CliNpmResolverCreateOptions, -) -> Result, AnyError> { - use CliNpmResolverCreateOptions::*; - match options { - Managed(options) => managed::create_managed_npm_resolver(options).await, - Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))), - } -} - pub enum InnerCliNpmResolverRef<'a> { Managed(&'a ManagedCliNpmResolver), #[allow(dead_code)] diff --git a/cli/resolver.rs b/cli/resolver.rs index 1d12d5f8b7..2f3d42e9e1 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -33,8 +33,8 @@ use thiserror::Error; use crate::args::NpmCachingStrategy; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; -use crate::npm::CliNpmResolver; -use crate::npm::InnerCliNpmResolverRef; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::sys::CliSys; use crate::util::sync::AtomicFlag; use crate::util::text_encoding::from_utf8_lossy_cow; @@ -164,48 +164,30 @@ impl NpmModuleLoader { } } -pub struct CliResolverOptions { - pub deno_resolver: Arc, - pub npm_resolver: Option>, - pub bare_node_builtins_enabled: bool, -} +#[derive(Debug, Default)] +pub struct FoundPackageJsonDepFlag(AtomicFlag); /// A resolver that takes care of resolution, taking into account loaded /// import map, JSX settings. #[derive(Debug)] pub struct CliResolver { deno_resolver: Arc, - npm_resolver: Option>, - found_package_json_dep_flag: AtomicFlag, - bare_node_builtins_enabled: bool, + found_package_json_dep_flag: Arc, warned_pkgs: DashSet, } impl CliResolver { - pub fn new(options: CliResolverOptions) -> Self { + pub fn new( + deno_resolver: Arc, + found_package_json_dep_flag: Arc, + ) -> Self { Self { - deno_resolver: options.deno_resolver, - npm_resolver: options.npm_resolver, - found_package_json_dep_flag: Default::default(), - bare_node_builtins_enabled: options.bare_node_builtins_enabled, + deno_resolver, + found_package_json_dep_flag, warned_pkgs: Default::default(), } } - // todo(dsherret): move this off CliResolver as CliResolver is acting - // like a factory by doing this (it's beyond its responsibility) - pub fn create_graph_npm_resolver( - &self, - npm_caching: NpmCachingStrategy, - ) -> WorkerCliNpmGraphResolver { - WorkerCliNpmGraphResolver { - npm_resolver: self.npm_resolver.as_ref(), - found_package_json_dep_flag: &self.found_package_json_dep_flag, - bare_node_builtins_enabled: self.bare_node_builtins_enabled, - npm_caching, - } - } - pub fn resolve( &self, raw_specifier: &str, @@ -233,7 +215,7 @@ impl CliResolver { if resolution.found_package_json_dep { // mark that we need to do an "npm install" later - self.found_package_json_dep_flag.raise(); + self.found_package_json_dep_flag.0.raise(); } if let Some(diagnostic) = resolution.maybe_diagnostic { @@ -260,15 +242,31 @@ impl CliResolver { } #[derive(Debug)] -pub struct WorkerCliNpmGraphResolver<'a> { - npm_resolver: Option<&'a Arc>, - found_package_json_dep_flag: &'a AtomicFlag, +pub struct CliNpmGraphResolver { + npm_installer: Option>, + found_package_json_dep_flag: Arc, bare_node_builtins_enabled: bool, npm_caching: NpmCachingStrategy, } +impl CliNpmGraphResolver { + pub fn new( + npm_installer: Option>, + found_package_json_dep_flag: Arc, + bare_node_builtins_enabled: bool, + npm_caching: NpmCachingStrategy, + ) -> Self { + Self { + npm_installer, + found_package_json_dep_flag, + bare_node_builtins_enabled, + npm_caching, + } + } +} + #[async_trait(?Send)] -impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { +impl deno_graph::source::NpmResolver for CliNpmGraphResolver { fn resolve_builtin_node_module( &self, specifier: &ModuleSpecifier, @@ -298,17 +296,12 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { } fn load_and_cache_npm_package_info(&self, package_name: &str) { - match self.npm_resolver { - Some(npm_resolver) if npm_resolver.as_managed().is_some() => { - let npm_resolver = npm_resolver.clone(); - let package_name = package_name.to_string(); - deno_core::unsync::spawn(async move { - if let Some(managed) = npm_resolver.as_managed() { - let _ignore = managed.cache_package_info(&package_name).await; - } - }); - } - _ => {} + if let Some(npm_installer) = &self.npm_installer { + let npm_installer = npm_installer.clone(); + let package_name = package_name.to_string(); + deno_core::unsync::spawn(async move { + let _ignore = npm_installer.cache_package_info(&package_name).await; + }); } } @@ -316,17 +309,11 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { &self, package_reqs: &[PackageReq], ) -> NpmResolvePkgReqsResult { - match &self.npm_resolver { - Some(npm_resolver) => { - let npm_resolver = match npm_resolver.as_inner() { - InnerCliNpmResolverRef::Managed(npm_resolver) => npm_resolver, - // if we are using byonm, then this should never be called because - // we don't use deno_graph's npm resolution in this case - InnerCliNpmResolverRef::Byonm(_) => unreachable!(), - }; - - let top_level_result = if self.found_package_json_dep_flag.is_raised() { - npm_resolver + match &self.npm_installer { + Some(npm_installer) => { + let top_level_result = if self.found_package_json_dep_flag.0.is_raised() + { + npm_installer .ensure_top_level_package_json_install() .await .map(|_| ()) @@ -334,15 +321,13 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { Ok(()) }; - let result = npm_resolver + let result = npm_installer .add_package_reqs_raw( package_reqs, match self.npm_caching { - NpmCachingStrategy::Eager => { - Some(crate::npm::PackageCaching::All) - } + NpmCachingStrategy::Eager => Some(PackageCaching::All), NpmCachingStrategy::Lazy => { - Some(crate::npm::PackageCaching::Only(package_reqs.into())) + Some(PackageCaching::Only(package_reqs.into())) } NpmCachingStrategy::Manual => None, }, diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index f07c645d89..ff0343e27f 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -73,6 +73,7 @@ use super::serialization::SourceMapStore; use super::virtual_fs::output_vfs; use super::virtual_fs::BuiltVfs; use super::virtual_fs::FileBackedVfs; +use super::virtual_fs::FileSystemCaseSensitivity; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VfsFileSubDataKind; use super::virtual_fs::VfsRoot; @@ -202,6 +203,7 @@ pub struct Metadata { pub node_modules: Option, pub unstable_config: UnstableConfig, pub otel_config: OtelConfig, + pub vfs_case_sensitivity: FileSystemCaseSensitivity, } #[allow(clippy::too_many_arguments)] @@ -379,7 +381,11 @@ pub fn extract_standalone( root_path: root_path.clone(), start_file_offset: 0, }; - Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root)) + Arc::new(FileBackedVfs::new( + Cow::Borrowed(vfs_files_data), + fs_root, + metadata.vfs_case_sensitivity, + )) }; Ok(Some(StandaloneData { metadata, @@ -851,6 +857,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(), }, otel_config: self.cli_options.otel_config(), + vfs_case_sensitivity: vfs.case_sensitivity, }; write_binary_bytes( @@ -984,11 +991,15 @@ impl<'a> DenoCompileBinaryWriter<'a> { // it's better to not expose the user's cache directory, so take it out // of there + let case_sensitivity = vfs.case_sensitivity(); let parent = global_cache_root_path.parent().unwrap(); let parent_dir = vfs.get_dir_mut(parent).unwrap(); let index = parent_dir .entries - .binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME) + .binary_search( + DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME, + case_sensitivity, + ) .unwrap(); let npm_global_cache_dir_entry = parent_dir.entries.remove(index); @@ -996,9 +1007,19 @@ impl<'a> DenoCompileBinaryWriter<'a> { // this is not as optimized as it could be let mut last_name = Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME); - for ancestor in parent.ancestors() { - let dir = vfs.get_dir_mut(ancestor).unwrap(); - if let Ok(index) = dir.entries.binary_search(&last_name) { + for ancestor in + parent.ancestors().map(Some).chain(std::iter::once(None)) + { + let dir = if let Some(ancestor) = ancestor { + vfs.get_dir_mut(ancestor).unwrap() + } else if cfg!(windows) { + vfs.get_system_root_dir_mut() + } else { + break; + }; + if let Ok(index) = + dir.entries.binary_search(&last_name, case_sensitivity) + { dir.entries.remove(index); } last_name = Cow::Owned(dir.name.clone()); @@ -1009,7 +1030,9 @@ impl<'a> DenoCompileBinaryWriter<'a> { // now build the vfs and add the global cache dir entry there let mut built_vfs = vfs.build(); - built_vfs.entries.insert(npm_global_cache_dir_entry); + built_vfs + .entries + .insert(npm_global_cache_dir_entry, case_sensitivity); built_vfs } InnerCliNpmResolverRef::Byonm(_) => vfs.build(), diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 961e8d6b51..4ca82f6b7d 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -37,10 +37,12 @@ use deno_core::ResolutionKind; use deno_core::SourceCodeCacheInfo; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; +use deno_npm::resolution::NpmResolutionSnapshot; use deno_package_json::PackageJsonDepValue; use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::npm::create_in_npm_pkg_checker; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; +use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::NpmReqResolverOptions; use deno_runtime::deno_fs; @@ -91,6 +93,7 @@ use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionCheckerMode; +use crate::npm::NpmResolutionInitializer; use crate::resolver::CjsTracker; use crate::resolver::CliNpmReqResolver; use crate::resolver::NpmModuleLoader; @@ -687,18 +690,12 @@ pub async fn run( ca_data: metadata.ca_data.map(CaData::Bytes), cell: Default::default(), }); - let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly); - let http_client_provider = Arc::new(HttpClientProvider::new( - Some(root_cert_store_provider.clone()), - metadata.unsafely_ignore_certificate_errors.clone(), - )); // use a dummy npm registry url let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap(); let root_dir_url = Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap()); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); - let cache_setting = CacheSetting::Only; let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); let npm_registry_permission_checker = { let mode = match &metadata.node_modules { @@ -743,29 +740,19 @@ pub async fn run( maybe_node_modules_path: maybe_node_modules_path.as_deref(), }, )); + let npm_resolution = + Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot))); let npm_resolver = create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( - snapshot, - )), - maybe_lockfile: None, - http_client_provider: http_client_provider.clone(), + npm_resolution, npm_cache_dir, - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), sys: sys.clone(), - text_only_progress_bar: progress_bar, - cache_setting, maybe_node_modules_path, npm_system_info: Default::default(), npmrc, - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } Some(binary::NodeModules::Byonm { @@ -781,8 +768,7 @@ pub async fn run( pkg_json_resolver: pkg_json_resolver.clone(), root_node_modules_dir, }), - ) - .await?; + ); (in_npm_pkg_checker, npm_resolver) } None => { @@ -801,27 +787,18 @@ pub async fn run( maybe_node_modules_path: None, }, )); + let npm_resolution = Arc::new(NpmResolutionCell::default()); let npm_resolver = create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( CliManagedNpmResolverCreateOptions { - snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), - http_client_provider: http_client_provider.clone(), - npm_install_deps_provider: Arc::new( - // this is only used for installing packages, which isn't necessary with deno compile - NpmInstallDepsProvider::empty(), - ), + npm_resolution, sys: sys.clone(), - cache_setting, - text_only_progress_bar: progress_bar, npm_cache_dir, - maybe_lockfile: None, maybe_node_modules_path: None, npm_system_info: Default::default(), npmrc: create_default_npmrc(), - lifecycle_scripts: Default::default(), }, - )) - .await?; + )); (in_npm_pkg_checker, npm_resolver) } }; @@ -833,6 +810,7 @@ pub async fn run( npm_resolver.clone().into_npm_pkg_folder_resolver(), pkg_json_resolver.clone(), sys.clone(), + node_resolver::ConditionsFromResolutionMode::default(), )); let cjs_tracker = Arc::new(CjsTracker::new( in_npm_pkg_checker.clone(), @@ -994,6 +972,7 @@ pub async fn run( None, Box::new(module_loader_factory), node_resolver, + None, npm_resolver, pkg_json_resolver, root_cert_store_provider, diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index 238ef44fd0..ae2e411b5f 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -27,6 +27,7 @@ use indexmap::IndexMap; use super::binary::Metadata; use super::virtual_fs::BuiltVfs; +use super::virtual_fs::FileSystemCaseSensitivity; use super::virtual_fs::VfsBuilder; use super::virtual_fs::VirtualDirectoryEntries; use crate::standalone::virtual_fs::VirtualDirectory; diff --git a/cli/standalone/virtual_fs.rs b/cli/standalone/virtual_fs.rs index e167b153a7..0b1f33259d 100644 --- a/cli/standalone/virtual_fs.rs +++ b/cli/standalone/virtual_fs.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::cmp::Ordering; use std::collections::HashMap; use std::collections::HashSet; use std::fs::File; @@ -74,6 +75,7 @@ impl WindowsSystemRootablePath { #[derive(Debug)] pub struct BuiltVfs { pub root_path: WindowsSystemRootablePath, + pub case_sensitivity: FileSystemCaseSensitivity, pub entries: VirtualDirectoryEntries, pub files: Vec>, } @@ -95,6 +97,7 @@ pub struct VfsBuilder { file_offsets: HashMap, /// The minimum root directory that should be included in the VFS. min_root_dir: Option, + case_sensitivity: FileSystemCaseSensitivity, } impl VfsBuilder { @@ -108,9 +111,23 @@ impl VfsBuilder { current_offset: 0, file_offsets: Default::default(), min_root_dir: Default::default(), + // This is not exactly correct because file systems on these OSes + // may be case-sensitive or not based on the directory, but this + // is a good enough approximation and limitation. In the future, + // we may want to store this information per directory instead + // depending on the feedback we get. + case_sensitivity: if cfg!(windows) || cfg!(target_os = "macos") { + FileSystemCaseSensitivity::Insensitive + } else { + FileSystemCaseSensitivity::Sensitive + }, } } + pub fn case_sensitivity(&self) -> FileSystemCaseSensitivity { + self.case_sensitivity + } + /// Add a directory that might be the minimum root directory /// of the VFS. /// @@ -215,7 +232,10 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let index = match current_dir.entries.binary_search(&name) { + let index = match current_dir + .entries + .binary_search(&name, self.case_sensitivity) + { Ok(index) => index, Err(insert_index) => { current_dir.entries.0.insert( @@ -252,7 +272,9 @@ impl VfsBuilder { continue; } let name = component.as_os_str().to_string_lossy(); - let entry = current_dir.entries.get_mut_by_name(&name)?; + let entry = current_dir + .entries + .get_mut_by_name(&name, self.case_sensitivity)?; match entry { VfsEntry::Dir(dir) => { current_dir = dir; @@ -304,6 +326,7 @@ impl VfsBuilder { ) -> Result<(), AnyError> { log::debug!("Adding file '{}'", path.display()); let checksum = util::checksum::gen(&[&data]); + let case_sensitivity = self.case_sensitivity; let offset = if let Some(offset) = self.file_offsets.get(&checksum) { // duplicate file, reuse an old offset *offset @@ -318,7 +341,7 @@ impl VfsBuilder { offset, len: data.len() as u64, }; - match dir.entries.binary_search(&name) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(index) => { let entry = &mut dir.entries.0[index]; match entry { @@ -379,10 +402,11 @@ impl VfsBuilder { std::fs::read_link(path) .with_context(|| format!("Reading symlink '{}'", path.display()))?, ); + let case_sensitivity = self.case_sensitivity; let target = normalize_path(path.parent().unwrap().join(&target)); let dir = self.add_dir_raw(path.parent().unwrap()); let name = path.file_name().unwrap().to_string_lossy(); - match dir.entries.binary_search(&name) { + match dir.entries.binary_search(&name, case_sensitivity) { Ok(_) => {} // previously inserted Err(insert_index) => { dir.entries.0.insert( @@ -478,6 +502,7 @@ impl VfsBuilder { } BuiltVfs { root_path: current_path, + case_sensitivity: self.case_sensitivity, entries: current_dir.entries, files: self.files, } @@ -906,6 +931,14 @@ impl VfsEntry { } } +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum FileSystemCaseSensitivity { + #[serde(rename = "s")] + Sensitive, + #[serde(rename = "i")] + Insensitive, +} + #[derive(Debug, Default, Serialize, Deserialize)] pub struct VirtualDirectoryEntries(Vec); @@ -928,23 +961,54 @@ impl VirtualDirectoryEntries { self.0.len() } - pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> { - self.binary_search(name).ok().map(|index| &self.0[index]) + pub fn get_by_name( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&VfsEntry> { + self + .binary_search(name, case_sensitivity) + .ok() + .map(|index| &self.0[index]) } - pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> { + pub fn get_mut_by_name( + &mut self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Option<&mut VfsEntry> { self - .binary_search(name) + .binary_search(name, case_sensitivity) .ok() .map(|index| &mut self.0[index]) } - pub fn binary_search(&self, name: &str) -> Result { - self.0.binary_search_by(|e| e.name().cmp(name)) + pub fn binary_search( + &self, + name: &str, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Result { + match case_sensitivity { + FileSystemCaseSensitivity::Sensitive => { + self.0.binary_search_by(|e| e.name().cmp(name)) + } + FileSystemCaseSensitivity::Insensitive => self.0.binary_search_by(|e| { + e.name() + .chars() + .zip(name.chars()) + .map(|(a, b)| a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase())) + .find(|&ord| ord != Ordering::Equal) + .unwrap_or_else(|| e.name().len().cmp(&name.len())) + }), + } } - pub fn insert(&mut self, entry: VfsEntry) { - match self.binary_search(entry.name()) { + pub fn insert( + &mut self, + entry: VfsEntry, + case_sensitivity: FileSystemCaseSensitivity, + ) { + match self.binary_search(entry.name(), case_sensitivity) { Ok(index) => { self.0[index] = entry; } @@ -1039,19 +1103,21 @@ impl VfsRoot { fn find_entry<'a>( &'a self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { - self.find_entry_inner(path, &mut HashSet::new()) + self.find_entry_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let mut path = Cow::Borrowed(path); loop { let (resolved_path, entry) = - self.find_entry_no_follow_inner(&path, seen)?; + self.find_entry_no_follow_inner(&path, seen, case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { if !seen.insert(path.to_path_buf()) { @@ -1072,14 +1138,16 @@ impl VfsRoot { fn find_entry_no_follow( &self, path: &Path, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef)> { - self.find_entry_no_follow_inner(path, &mut HashSet::new()) + self.find_entry_no_follow_inner(path, &mut HashSet::new(), case_sensitivity) } fn find_entry_no_follow_inner<'a>( &'a self, path: &Path, seen: &mut HashSet, + case_sensitivity: FileSystemCaseSensitivity, ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> { let relative_path = match path.strip_prefix(&self.root_path) { Ok(p) => p, @@ -1101,7 +1169,8 @@ impl VfsRoot { } VfsEntryRef::Symlink(symlink) => { let dest = symlink.resolve_dest_from_root(&self.root_path); - let (resolved_path, entry) = self.find_entry_inner(&dest, seen)?; + let (resolved_path, entry) = + self.find_entry_inner(&dest, seen, case_sensitivity)?; final_path = resolved_path; // overwrite with the new resolved path match entry { VfsEntryRef::Dir(dir) => { @@ -1126,7 +1195,7 @@ impl VfsRoot { let component = component.to_string_lossy(); current_entry = current_dir .entries - .get_by_name(&component) + .get_by_name(&component, case_sensitivity) .ok_or_else(|| { std::io::Error::new(std::io::ErrorKind::NotFound, "path not found") })? @@ -1392,13 +1461,19 @@ impl FileBackedVfsMetadata { pub struct FileBackedVfs { vfs_data: Cow<'static, [u8]>, fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, } impl FileBackedVfs { - pub fn new(data: Cow<'static, [u8]>, fs_root: VfsRoot) -> Self { + pub fn new( + data: Cow<'static, [u8]>, + fs_root: VfsRoot, + case_sensitivity: FileSystemCaseSensitivity, + ) -> Self { Self { vfs_data: data, fs_root, + case_sensitivity, } } @@ -1451,7 +1526,9 @@ impl FileBackedVfs { } pub fn read_link(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; match entry { VfsEntryRef::Symlink(symlink) => { Ok(symlink.resolve_dest_from_root(&self.fs_root.root_path)) @@ -1464,17 +1541,19 @@ impl FileBackedVfs { } pub fn lstat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry_no_follow(path)?; + let (_, entry) = self + .fs_root + .find_entry_no_follow(path, self.case_sensitivity)?; Ok(entry.as_metadata()) } pub fn stat(&self, path: &Path) -> std::io::Result { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; Ok(entry.as_metadata()) } pub fn canonicalize(&self, path: &Path) -> std::io::Result { - let (path, _) = self.fs_root.find_entry(path)?; + let (path, _) = self.fs_root.find_entry(path, self.case_sensitivity)?; Ok(path) } @@ -1536,7 +1615,7 @@ impl FileBackedVfs { } pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(dir) => Ok(dir), VfsEntryRef::Symlink(_) => unreachable!(), @@ -1548,7 +1627,7 @@ impl FileBackedVfs { } pub fn file_entry(&self, path: &Path) -> std::io::Result<&VirtualFile> { - let (_, entry) = self.fs_root.find_entry(path)?; + let (_, entry) = self.fs_root.find_entry(path, self.case_sensitivity)?; match entry { VfsEntryRef::Dir(_) => Err(std::io::Error::new( std::io::ErrorKind::Other, @@ -1754,6 +1833,7 @@ mod test { root_path: dest_path.to_path_buf(), start_file_offset: 0, }, + FileSystemCaseSensitivity::Sensitive, ), ) } diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 53fd5c5db9..c3a285a9b2 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -34,6 +34,7 @@ use crate::graph_util::maybe_additional_sloppy_imports_message; use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::ModuleGraphBuilder; use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::tsc; @@ -109,6 +110,7 @@ pub struct TypeChecker { cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, + npm_installer: Option>, node_resolver: Arc, npm_resolver: Arc, sys: CliSys, @@ -136,12 +138,14 @@ pub enum CheckError { } impl TypeChecker { + #[allow(clippy::too_many_arguments)] pub fn new( caches: Arc, cjs_tracker: Arc, cli_options: Arc, module_graph_builder: Arc, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, sys: CliSys, ) -> Self { @@ -151,6 +155,7 @@ impl TypeChecker { cli_options, module_graph_builder, node_resolver, + npm_installer, npm_resolver, sys, } @@ -191,9 +196,9 @@ impl TypeChecker { // node built-in specifiers use the @types/node package to determine // types, so inject that now (the caller should do this after the lockfile // has been written) - if let Some(npm_resolver) = self.npm_resolver.as_managed() { + if let Some(npm_installer) = &self.npm_installer { if graph.has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 81b0af0a66..50faeda7d3 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -386,8 +386,11 @@ impl NpmInfo { npm_snapshot: &'a NpmResolutionSnapshot, ) { self.packages.insert(package.id.clone(), package.clone()); - if let Ok(size) = npm_resolver.package_size(&package.id) { - self.package_sizes.insert(package.id.clone(), size); + if let Ok(folder) = npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id) + { + if let Ok(size) = crate::util::fs::dir_size(&folder) { + self.package_sizes.insert(package.id.clone(), size); + } } for id in package.dependencies.values() { if !self.packages.contains_key(id) { diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index ff5744c07a..596d087589 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -300,8 +300,8 @@ async fn install_local( InstallFlagsLocal::TopLevel => { let factory = CliFactory::from_flags(flags); // surface any errors in the package.json - if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { - npm_resolver.ensure_no_pkg_json_dep_errors()?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + npm_installer.ensure_no_pkg_json_dep_errors()?; } crate::tools::registry::cache_top_level_deps(&factory, None).await?; diff --git a/cli/tools/jupyter/mod.rs b/cli/tools/jupyter/mod.rs index 78b7675420..67c604118c 100644 --- a/cli/tools/jupyter/mod.rs +++ b/cli/tools/jupyter/mod.rs @@ -67,7 +67,7 @@ pub async fn kernel( // TODO(bartlomieju): should we run with all permissions? let permissions = PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone()); - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let worker_factory = factory.create_cli_main_worker_factory().await?; let (stdio_tx, stdio_rx) = mpsc::unbounded_channel(); @@ -115,7 +115,7 @@ pub async fn kernel( let worker = worker.into_main_worker(); let mut repl_session = repl::ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module, diff --git a/cli/tools/lint/ast_buffer/buffer.rs b/cli/tools/lint/ast_buffer/buffer.rs index 517c3b14dc..a884ee24f9 100644 --- a/cli/tools/lint/ast_buffer/buffer.rs +++ b/cli/tools/lint/ast_buffer/buffer.rs @@ -14,9 +14,14 @@ pub enum PropFlags { Ref, RefArr, String, + Number, Bool, Null, Undefined, + Object, + Regex, + BigInt, + Array, } impl From for u8 { @@ -33,21 +38,29 @@ impl TryFrom for PropFlags { 0 => Ok(PropFlags::Ref), 1 => Ok(PropFlags::RefArr), 2 => Ok(PropFlags::String), - 3 => Ok(PropFlags::Bool), - 4 => Ok(PropFlags::Null), - 5 => Ok(PropFlags::Undefined), + 3 => Ok(PropFlags::Number), + 4 => Ok(PropFlags::Bool), + 5 => Ok(PropFlags::Null), + 6 => Ok(PropFlags::Undefined), + 7 => Ok(PropFlags::Object), + 8 => Ok(PropFlags::Regex), + 9 => Ok(PropFlags::BigInt), + 10 => Ok(PropFlags::Array), _ => Err("Unknown Prop flag"), } } } +pub type Index = u32; + +const GROUP_KIND: u8 = 1; const MASK_U32_1: u32 = 0b11111111_00000000_00000000_00000000; const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000; const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000; const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111; -// TODO: There is probably a native Rust function to do this. -pub fn append_u32(result: &mut Vec, value: u32) { +#[inline] +fn append_u32(result: &mut Vec, value: u32) { let v1: u8 = ((value & MASK_U32_1) >> 24) as u8; let v2: u8 = ((value & MASK_U32_2) >> 16) as u8; let v3: u8 = ((value & MASK_U32_3) >> 8) as u8; @@ -59,25 +72,11 @@ pub fn append_u32(result: &mut Vec, value: u32) { result.push(v4); } -pub fn append_usize(result: &mut Vec, value: usize) { +fn append_usize(result: &mut Vec, value: usize) { let raw = u32::try_from(value).unwrap(); append_u32(result, raw); } -pub fn write_usize(result: &mut [u8], value: usize, idx: usize) { - let raw = u32::try_from(value).unwrap(); - - let v1: u8 = ((raw & MASK_U32_1) >> 24) as u8; - let v2: u8 = ((raw & MASK_U32_2) >> 16) as u8; - let v3: u8 = ((raw & MASK_U32_3) >> 8) as u8; - let v4: u8 = (raw & MASK_U32_4) as u8; - - result[idx] = v1; - result[idx + 1] = v2; - result[idx + 2] = v3; - result[idx + 3] = v4; -} - #[derive(Debug)] pub struct StringTable { id: usize, @@ -119,71 +118,47 @@ impl StringTable { } #[derive(Debug, Clone, Copy, PartialEq)] -pub struct NodeRef(pub usize); - -/// Represents an offset to a node whose schema hasn't been committed yet +pub struct NodeRef(pub Index); #[derive(Debug, Clone, Copy, PartialEq)] -pub struct PendingNodeRef(pub NodeRef); +pub struct PendingRef(pub Index); -#[derive(Debug)] -pub struct BoolPos(pub usize); -#[derive(Debug)] -pub struct FieldPos(pub usize); -#[derive(Debug)] -pub struct FieldArrPos(pub usize); -#[derive(Debug)] -pub struct StrPos(pub usize); -#[derive(Debug)] -pub struct UndefPos(pub usize); -#[derive(Debug)] -pub struct NullPos(pub usize); - -#[derive(Debug)] -pub enum NodePos { - Bool(BoolPos), - #[allow(dead_code)] - Field(FieldPos), - #[allow(dead_code)] - FieldArr(FieldArrPos), - Str(StrPos), - Undef(UndefPos), - #[allow(dead_code)] - Null(NullPos), +pub trait AstBufSerializer { + fn serialize(&mut self) -> Vec; } -pub trait AstBufSerializer -where - K: Into + Display, - P: Into + Display, -{ - fn header(&mut self, kind: K, parent: NodeRef, span: &Span) - -> PendingNodeRef; - fn ref_field(&mut self, prop: P) -> FieldPos; - fn ref_vec_field(&mut self, prop: P, len: usize) -> FieldArrPos; - fn str_field(&mut self, prop: P) -> StrPos; - fn bool_field(&mut self, prop: P) -> BoolPos; - fn undefined_field(&mut self, prop: P) -> UndefPos; - #[allow(dead_code)] - fn null_field(&mut self, prop: P) -> NullPos; - fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef; - - fn write_ref(&mut self, pos: FieldPos, value: NodeRef); - fn write_maybe_ref(&mut self, pos: FieldPos, value: Option); - fn write_refs(&mut self, pos: FieldArrPos, value: Vec); - fn write_str(&mut self, pos: StrPos, value: &str); - fn write_bool(&mut self, pos: BoolPos, value: bool); - - fn serialize(&mut self) -> Vec; +/// +/// +/// +/// +/// +#[derive(Debug)] +struct Node { + kind: u8, + prop_offset: u32, + child: u32, + next: u32, + parent: u32, } #[derive(Debug)] pub struct SerializeCtx { - buf: Vec, - start_buf: NodeRef, + root_idx: Index, + + nodes: Vec, + prop_stack: Vec>, + field_count: Vec, + field_buf: Vec, + prev_sibling_stack: Vec, + + /// Vec of spans + spans: Vec, + + /// Maps string id to the actual string str_table: StringTable, - kind_map: Vec, - prop_map: Vec, - field_count: u8, + /// Maps kind id to string id + kind_name_map: Vec, + /// Maps prop id to string id + prop_name_map: Vec, } /// This is the internal context used to allocate and fill the buffer. The point @@ -198,20 +173,24 @@ impl SerializeCtx { let kind_size = kind_len as usize; let prop_size = prop_len as usize; let mut ctx = Self { - start_buf: NodeRef(0), - buf: vec![], + spans: Vec::with_capacity(512), + root_idx: 0, + nodes: Vec::with_capacity(512), + prop_stack: vec![vec![]], + prev_sibling_stack: vec![0], + field_count: vec![0], + field_buf: Vec::with_capacity(1024), str_table: StringTable::new(), - kind_map: vec![0; kind_size], - prop_map: vec![0; prop_size], - field_count: 0, + kind_name_map: vec![0; kind_size], + prop_name_map: vec![0; prop_size], }; let empty_str = ctx.str_table.insert(""); // Placeholder node is always 0 - ctx.append_node(0, NodeRef(0), &DUMMY_SP, 0); - ctx.kind_map[0] = empty_str; - ctx.start_buf = NodeRef(ctx.buf.len()); + ctx.append_node(0, &DUMMY_SP); + ctx.kind_name_map[0] = empty_str; + ctx.kind_name_map[1] = empty_str; // Insert default props that are always present let type_str = ctx.str_table.insert("type"); @@ -220,258 +199,306 @@ impl SerializeCtx { let length_str = ctx.str_table.insert("length"); // These values are expected to be in this order on the JS side - ctx.prop_map[0] = empty_str; - ctx.prop_map[1] = type_str; - ctx.prop_map[2] = parent_str; - ctx.prop_map[3] = range_str; - ctx.prop_map[4] = length_str; + ctx.prop_name_map[0] = empty_str; + ctx.prop_name_map[1] = type_str; + ctx.prop_name_map[2] = parent_str; + ctx.prop_name_map[3] = range_str; + ctx.prop_name_map[4] = length_str; ctx } + pub fn set_root_idx(&mut self, idx: Index) { + self.root_idx = idx; + } + /// Allocate a node's header - fn field_header

(&mut self, prop: P, prop_flags: PropFlags) -> usize + fn field_header

(&mut self, prop: P, prop_flags: PropFlags) where P: Into + Display + Clone, { - self.field_count += 1; - - let offset = self.buf.len(); - + let flags: u8 = prop_flags.into(); let n: u8 = prop.clone().into(); - self.buf.push(n); - if let Some(v) = self.prop_map.get::(n.into()) { + if let Some(v) = self.prop_name_map.get::(n.into()) { if *v == 0 { let id = self.str_table.insert(&format!("{prop}")); - self.prop_map[n as usize] = id; + self.prop_name_map[n as usize] = id; } } - let flags: u8 = prop_flags.into(); - self.buf.push(flags); + // Increment field counter + let idx = self.field_count.len() - 1; + let count = self.field_count[idx]; + self.field_count[idx] = count + 1; - offset + let buf = self.prop_stack.last_mut().unwrap(); + buf.push(n); + buf.push(flags); } - /// Allocate a property pointing to another node. - fn field

(&mut self, prop: P, prop_flags: PropFlags) -> usize + fn get_node(&mut self, id: Index) -> &mut Node { + self.nodes.get_mut(id as usize).unwrap() + } + + fn set_parent(&mut self, child_id: Index, parent_id: Index) { + let child = self.get_node(child_id); + child.parent = parent_id; + } + + fn set_child(&mut self, parent_id: Index, child_id: Index) { + let parent = self.get_node(parent_id); + parent.child = child_id; + } + + fn set_next(&mut self, node_id: Index, next_id: Index) { + let node = self.get_node(node_id); + node.next = next_id; + } + + fn update_ref_links(&mut self, parent_id: Index, child_id: Index) { + let last_idx = self.prev_sibling_stack.len() - 1; + let parent = self.get_node(parent_id); + if parent.child == 0 { + parent.child = child_id; + } else { + let prev_id = self.prev_sibling_stack[last_idx]; + self.set_next(prev_id, child_id); + } + + self.prev_sibling_stack[last_idx] = child_id; + self.set_parent(child_id, parent_id); + } + + pub fn append_node(&mut self, kind: K, span: &Span) -> PendingRef where - P: Into + Display + Clone, + K: Into + Display + Clone, { - let offset = self.field_header(prop, prop_flags); - - append_usize(&mut self.buf, 0); - - offset + self.append_inner(kind, span.lo.0, span.hi.0) } - fn append_node( + pub fn append_inner( &mut self, - kind: u8, - parent: NodeRef, - span: &Span, - prop_count: usize, - ) -> PendingNodeRef { - let offset = self.buf.len(); - - // Node type fits in a u8 - self.buf.push(kind); - - // Offset to the parent node. Will be 0 if none exists - append_usize(&mut self.buf, parent.0); - - // Span, the start and end location of this node - append_u32(&mut self.buf, span.lo.0); - append_u32(&mut self.buf, span.hi.0); - - // No node has more than <10 properties - debug_assert!(prop_count < 10); - self.buf.push(prop_count as u8); - - PendingNodeRef(NodeRef(offset)) - } - - pub fn commit_schema(&mut self, node_ref: PendingNodeRef) -> NodeRef { - let mut offset = node_ref.0 .0; - - // type + parentId + span lo + span hi - offset += 1 + 4 + 4 + 4; - - self.buf[offset] = self.field_count; - self.field_count = 0; - - node_ref.0 - } - - /// Allocate the node header. It's always the same for every node. - /// - /// - /// - /// - /// (There is no node with more than 10 properties) - pub fn header( - &mut self, - kind: N, - parent: NodeRef, - span: &Span, - ) -> PendingNodeRef + kind: K, + span_lo: u32, + span_hi: u32, + ) -> PendingRef where - N: Into + Display + Clone, + K: Into + Display + Clone, { - let n: u8 = kind.clone().into(); + let kind_u8: u8 = kind.clone().into(); - if let Some(v) = self.kind_map.get::(n.into()) { + let id: Index = self.nodes.len() as u32; + + self.nodes.push(Node { + kind: kind_u8, + prop_offset: 0, + child: 0, + next: 0, + parent: 0, + }); + + if let Some(v) = self.kind_name_map.get::(kind_u8.into()) { if *v == 0 { - let id = self.str_table.insert(&format!("{kind}")); - self.kind_map[n as usize] = id; + let s_id = self.str_table.insert(&format!("{kind}")); + self.kind_name_map[kind_u8 as usize] = s_id; } } - // Prop count will be filled with the actual value when the - // schema is committed. - self.append_node(n, parent, span, 0) + self.field_count.push(0); + self.prop_stack.push(vec![]); + self.prev_sibling_stack.push(0); + + // write spans + self.spans.push(span_lo); + self.spans.push(span_hi); + + PendingRef(id) } - /// Allocate a reference property that will hold the offset of - /// another node. - pub fn ref_field

(&mut self, prop: P) -> usize + pub fn commit_node(&mut self, id: PendingRef) -> NodeRef { + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + + // All nodes have <10 fields + self.field_buf.push(count as u8); + self.field_buf.append(&mut buf); + + let node = self.nodes.get_mut(id.0 as usize).unwrap(); + node.prop_offset = offset as u32; + + self.prev_sibling_stack.pop(); + + NodeRef(id.0) + } + + // Allocate an object field + pub fn open_obj(&mut self) { + self.field_count.push(0); + self.prop_stack.push(vec![]); + } + + pub fn commit_obj

(&mut self, prop: P) where P: Into + Display + Clone, { - self.field(prop, PropFlags::Ref) + let mut buf = self.prop_stack.pop().unwrap(); + let count = self.field_count.pop().unwrap(); + let offset = self.field_buf.len(); + append_usize(&mut self.field_buf, count); + self.field_buf.append(&mut buf); + + self.field_header(prop, PropFlags::Object); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, offset); } - /// Allocate a property that is a vec of node offsets pointing to other - /// nodes. - pub fn ref_vec_field

(&mut self, prop: P, len: usize) -> usize + /// Allocate an null field + pub fn write_null

(&mut self, prop: P) where P: Into + Display + Clone, { - let offset = self.field(prop, PropFlags::RefArr); + self.field_header(prop, PropFlags::Null); - for _ in 0..len { - append_u32(&mut self.buf, 0); - } - - offset + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, 0); } - // Allocate a property representing a string. Strings are deduplicated - // in the message and the property will only contain the string id. - pub fn str_field

(&mut self, prop: P) -> usize + /// Allocate an null field + pub fn write_undefined

(&mut self, prop: P) where P: Into + Display + Clone, { - self.field(prop, PropFlags::String) + self.field_header(prop, PropFlags::Undefined); + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, 0); } - /// Allocate a bool field - pub fn bool_field

(&mut self, prop: P) -> usize + /// Allocate a number field + pub fn write_num

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - let offset = self.field_header(prop, PropFlags::Bool); - self.buf.push(0); - offset + self.field_header(prop, PropFlags::Number); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } - /// Allocate an undefined field - pub fn undefined_field

(&mut self, prop: P) -> usize + /// Allocate a bigint field + pub fn write_bigint

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - self.field_header(prop, PropFlags::Undefined) + self.field_header(prop, PropFlags::BigInt); + + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } - /// Allocate an undefined field - #[allow(dead_code)] - pub fn null_field

(&mut self, prop: P) -> usize + /// Allocate a RegExp field + pub fn write_regex

(&mut self, prop: P, value: &str) where P: Into + Display + Clone, { - self.field_header(prop, PropFlags::Null) - } + self.field_header(prop, PropFlags::Regex); - /// Replace the placeholder of a reference field with the actual offset - /// to the node we want to point to. - pub fn write_ref(&mut self, field_offset: usize, value: NodeRef) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { - panic!("Trying to write a ref into a non-ref field") - } - } - - write_usize(&mut self.buf, value.0, field_offset + 2); - } - - /// Helper for writing optional node offsets - pub fn write_maybe_ref( - &mut self, - field_offset: usize, - value: Option, - ) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Ref { - panic!("Trying to write a ref into a non-ref field") - } - } - - let ref_value = if let Some(v) = value { v } else { NodeRef(0) }; - write_usize(&mut self.buf, ref_value.0, field_offset + 2); - } - - /// Write a vec of node offsets into the property. The necessary space - /// has been reserved earlier. - pub fn write_refs(&mut self, field_offset: usize, value: Vec) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::RefArr { - panic!("Trying to write a ref into a non-ref array field") - } - } - - let mut offset = field_offset + 2; - write_usize(&mut self.buf, value.len(), offset); - offset += 4; - - for item in value { - write_usize(&mut self.buf, item.0, offset); - offset += 4; - } + let id = self.str_table.insert(value); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } /// Store the string in our string table and save the id of the string /// in the current field. - pub fn write_str(&mut self, field_offset: usize, value: &str) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::String { - panic!("Trying to write a ref into a non-string field") - } - } + pub fn write_str

(&mut self, prop: P, value: &str) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::String); let id = self.str_table.insert(value); - write_usize(&mut self.buf, id, field_offset + 2); + let buf = self.prop_stack.last_mut().unwrap(); + append_usize(buf, id); } /// Write a bool to a field. - pub fn write_bool(&mut self, field_offset: usize, value: bool) { - #[cfg(debug_assertions)] - { - let value_kind = self.buf[field_offset + 1]; - if PropFlags::try_from(value_kind).unwrap() != PropFlags::Bool { - panic!("Trying to write a ref into a non-bool field") - } - } + pub fn write_bool

(&mut self, prop: P, value: bool) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Bool); - self.buf[field_offset + 2] = if value { 1 } else { 0 }; + let n = if value { 1 } else { 0 }; + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, n); + } + + /// Replace the placeholder of a reference field with the actual offset + /// to the node we want to point to. + pub fn write_ref

(&mut self, prop: P, parent: &PendingRef, value: NodeRef) + where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::Ref); + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, value.0); + + if parent.0 > 0 { + self.update_ref_links(parent.0, value.0); + } + } + + /// Helper for writing optional node offsets + pub fn write_maybe_ref

( + &mut self, + prop: P, + parent: &PendingRef, + value: Option, + ) where + P: Into + Display + Clone, + { + if let Some(v) = value { + self.write_ref(prop, parent, v); + } else { + self.write_null(prop); + }; + } + + /// Write a vec of node offsets into the property. The necessary space + /// has been reserved earlier. + pub fn write_ref_vec

( + &mut self, + prop: P, + parent_ref: &PendingRef, + value: Vec, + ) where + P: Into + Display + Clone, + { + self.field_header(prop, PropFlags::RefArr); + let group_id = self.append_node(GROUP_KIND, &DUMMY_SP); + let group_id = self.commit_node(group_id).0; + + let buf = self.prop_stack.last_mut().unwrap(); + append_u32(buf, group_id); + + self.update_ref_links(parent_ref.0, group_id); + + let mut prev_id = 0; + for (i, item) in value.iter().enumerate() { + self.set_parent(item.0, group_id); + + if i == 0 { + self.set_child(group_id, item.0); + } else { + self.set_next(prev_id, item.0); + } + + prev_id = item.0; + } } /// Serialize all information we have into a buffer that can be sent to JS. @@ -481,6 +508,8 @@ impl SerializeCtx { /// /// <- node kind id maps to string id /// <- node property id maps to string id + /// <- List of spans, rarely needed + /// /// /// /// @@ -490,7 +519,13 @@ impl SerializeCtx { // The buffer starts with the serialized AST first, because that // contains absolute offsets. By butting this at the start of the // message we don't have to waste time updating any offsets. - buf.append(&mut self.buf); + for node in &self.nodes { + buf.push(node.kind); + append_u32(&mut buf, node.prop_offset); + append_u32(&mut buf, node.child); + append_u32(&mut buf, node.next); + append_u32(&mut buf, node.parent); + } // Next follows the string table. We'll keep track of the offset // in the message of where the string table begins @@ -507,8 +542,8 @@ impl SerializeCtx { // Write the total number of entries in the kind -> str mapping table // TODO: make this a u8 - append_usize(&mut buf, self.kind_map.len()); - for v in &self.kind_map { + append_usize(&mut buf, self.kind_name_map.len()); + for v in &self.kind_name_map { append_usize(&mut buf, *v); } @@ -517,19 +552,35 @@ impl SerializeCtx { // as u8. let offset_prop_map = buf.len(); // Write the total number of entries in the kind -> str mapping table - append_usize(&mut buf, self.prop_map.len()); - for v in &self.prop_map { + append_usize(&mut buf, self.prop_name_map.len()); + for v in &self.prop_name_map { append_usize(&mut buf, *v); } + // Spans are rarely needed, so they're stored in a separate array. + // They're indexed by the node id. + let offset_spans = buf.len(); + for v in &self.spans { + append_u32(&mut buf, *v); + } + + // The field value table. They're detached from nodes as they're not + // as frequently needed as the nodes themselves. The most common + // operation is traversal and we can traverse nodes without knowing + // about the fields. + let offset_props = buf.len(); + buf.append(&mut self.field_buf); + // Putting offsets of relevant parts of the buffer at the end. This // allows us to hop to the relevant part by merely looking at the last // for values in the message. Each value represents an offset into the // buffer. + append_usize(&mut buf, offset_props); + append_usize(&mut buf, offset_spans); append_usize(&mut buf, offset_kind_map); append_usize(&mut buf, offset_prop_map); append_usize(&mut buf, offset_str_table); - append_usize(&mut buf, self.start_buf.0); + append_u32(&mut buf, self.root_idx); buf } diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index 7652756c9b..925d1bcd17 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -2,10 +2,13 @@ use deno_ast::swc::ast::AssignTarget; use deno_ast::swc::ast::AssignTargetPat; +use deno_ast::swc::ast::BindingIdent; use deno_ast::swc::ast::BlockStmtOrExpr; use deno_ast::swc::ast::Callee; use deno_ast::swc::ast::ClassMember; use deno_ast::swc::ast::Decl; +use deno_ast::swc::ast::Decorator; +use deno_ast::swc::ast::DefaultDecl; use deno_ast::swc::ast::ExportSpecifier; use deno_ast::swc::ast::Expr; use deno_ast::swc::ast::ExprOrSpread; @@ -14,13 +17,13 @@ use deno_ast::swc::ast::ForHead; use deno_ast::swc::ast::Function; use deno_ast::swc::ast::Ident; use deno_ast::swc::ast::IdentName; +use deno_ast::swc::ast::ImportSpecifier; use deno_ast::swc::ast::JSXAttrName; use deno_ast::swc::ast::JSXAttrOrSpread; use deno_ast::swc::ast::JSXAttrValue; use deno_ast::swc::ast::JSXElement; use deno_ast::swc::ast::JSXElementChild; use deno_ast::swc::ast::JSXElementName; -use deno_ast::swc::ast::JSXEmptyExpr; use deno_ast::swc::ast::JSXExpr; use deno_ast::swc::ast::JSXExprContainer; use deno_ast::swc::ast::JSXFragment; @@ -28,12 +31,14 @@ use deno_ast::swc::ast::JSXMemberExpr; use deno_ast::swc::ast::JSXNamespacedName; use deno_ast::swc::ast::JSXObject; use deno_ast::swc::ast::JSXOpeningElement; +use deno_ast::swc::ast::Key; use deno_ast::swc::ast::Lit; use deno_ast::swc::ast::MemberExpr; use deno_ast::swc::ast::MemberProp; use deno_ast::swc::ast::ModuleDecl; use deno_ast::swc::ast::ModuleExportName; use deno_ast::swc::ast::ModuleItem; +use deno_ast::swc::ast::ObjectLit; use deno_ast::swc::ast::ObjectPatProp; use deno_ast::swc::ast::OptChainBase; use deno_ast::swc::ast::Param; @@ -47,14 +52,18 @@ use deno_ast::swc::ast::PropOrSpread; use deno_ast::swc::ast::SimpleAssignTarget; use deno_ast::swc::ast::Stmt; use deno_ast::swc::ast::SuperProp; -use deno_ast::swc::ast::Tpl; use deno_ast::swc::ast::TsEntityName; use deno_ast::swc::ast::TsEnumMemberId; +use deno_ast::swc::ast::TsExprWithTypeArgs; use deno_ast::swc::ast::TsFnOrConstructorType; use deno_ast::swc::ast::TsFnParam; use deno_ast::swc::ast::TsIndexSignature; use deno_ast::swc::ast::TsLit; use deno_ast::swc::ast::TsLitType; +use deno_ast::swc::ast::TsModuleName; +use deno_ast::swc::ast::TsModuleRef; +use deno_ast::swc::ast::TsNamespaceBody; +use deno_ast::swc::ast::TsParamPropParam; use deno_ast::swc::ast::TsThisTypeOrIdent; use deno_ast::swc::ast::TsType; use deno_ast::swc::ast::TsTypeAnn; @@ -71,7 +80,7 @@ use deno_ast::swc::common::SyntaxContext; use deno_ast::view::Accessibility; use deno_ast::view::AssignOp; use deno_ast::view::BinaryOp; -use deno_ast::view::TruePlusMinus; +use deno_ast::view::MethodKind; use deno_ast::view::TsKeywordTypeKind; use deno_ast::view::TsTypeOperatorOp; use deno_ast::view::UnaryOp; @@ -80,53 +89,39 @@ use deno_ast::view::VarDeclKind; use deno_ast::ParsedSource; use super::buffer::AstBufSerializer; -use super::buffer::BoolPos; -use super::buffer::NodePos; use super::buffer::NodeRef; -use super::buffer::StrPos; use super::ts_estree::AstNode; -use super::ts_estree::AstProp; use super::ts_estree::TsEsTreeBuilder; +use super::ts_estree::TsKeywordKind; pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { let mut ctx = TsEsTreeBuilder::new(); let program = &parsed_source.program(); - let raw = ctx.header(AstNode::Program, NodeRef(0), &program.span()); - let source_type_pos = ctx.str_field(AstProp::SourceType); - match program.as_ref() { Program::Module(module) => { - let body_pos = ctx.ref_vec_field(AstProp::Body, module.body.len()); - let pos = ctx.commit_schema(raw); - let children = module .body .iter() .map(|item| match item { ModuleItem::ModuleDecl(module_decl) => { - serialize_module_decl(&mut ctx, module_decl, pos) + serialize_module_decl(&mut ctx, module_decl) } - ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt, pos), + ModuleItem::Stmt(stmt) => serialize_stmt(&mut ctx, stmt), }) .collect::>(); - ctx.write_str(source_type_pos, "module"); - ctx.write_refs(body_pos, children); + ctx.write_program(&module.span, "module", children); } Program::Script(script) => { - let body_pos = ctx.ref_vec_field(AstProp::Body, script.body.len()); - let pos = ctx.commit_schema(raw); - let children = script .body .iter() - .map(|stmt| serialize_stmt(&mut ctx, stmt, pos)) + .map(|stmt| serialize_stmt(&mut ctx, stmt)) .collect::>(); - ctx.write_str(source_type_pos, "script"); - ctx.write_refs(body_pos, children); + ctx.write_program(&script.span, "script", children); } } @@ -136,539 +131,521 @@ pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec { fn serialize_module_decl( ctx: &mut TsEsTreeBuilder, module_decl: &ModuleDecl, - parent: NodeRef, ) -> NodeRef { match module_decl { ModuleDecl::Import(node) => { - let raw = ctx.header(AstNode::ImportExpression, parent, &node.span); - ctx.commit_schema(raw) - } - ModuleDecl::ExportDecl(node) => { - let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); - let decl_pos = ctx.ref_field(AstProp::Declarations); - let pos = ctx.commit_schema(raw); + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); - let decl = serialize_decl(ctx, &node.decl, pos); - - ctx.write_ref(decl_pos, decl); - - pos - } - ModuleDecl::ExportNamed(node) => { - let raw = ctx.header(AstNode::ExportNamedDeclaration, parent, &node.span); - let src_pos = ctx.ref_field(AstProp::Source); - let spec_pos = - ctx.ref_vec_field(AstProp::Specifiers, node.specifiers.len()); - let id = ctx.commit_schema(raw); - - // FIXME: Flags - // let mut flags = FlagValue::new(); - // flags.set(Flag::ExportType); - - let src_id = node - .src - .as_ref() - .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()), id)); - - let spec_ids = node + let specifiers = node .specifiers .iter() - .map(|spec| { - match spec { - ExportSpecifier::Named(child) => { - let raw = ctx.header(AstNode::ExportSpecifier, id, &child.span); - let local_pos = ctx.ref_field(AstProp::Local); - let exp_pos = ctx.ref_field(AstProp::Exported); - let spec_pos = ctx.commit_schema(raw); - - // let mut flags = FlagValue::new(); - // flags.set(Flag::ExportType); - - let local = - serialize_module_exported_name(ctx, &child.orig, spec_pos); - - let exported = child.exported.as_ref().map(|exported| { - serialize_module_exported_name(ctx, exported, spec_pos) + .map(|spec| match spec { + ImportSpecifier::Named(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + let imported = spec + .imported + .as_ref() + .map_or(serialize_ident(ctx, &spec.local, None), |v| { + serialize_module_export_name(ctx, v) }); - - // ctx.write_flags(&flags); - ctx.write_ref(local_pos, local); - ctx.write_maybe_ref(exp_pos, exported); - - spec_pos - } - - // These two aren't syntactically valid - ExportSpecifier::Namespace(_) => todo!(), - ExportSpecifier::Default(_) => todo!(), + ctx.write_import_spec( + &spec.span, + spec.is_type_only, + local, + imported, + ) + } + ImportSpecifier::Default(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_default_spec(&spec.span, local) + } + ImportSpecifier::Namespace(spec) => { + let local = serialize_ident(ctx, &spec.local, None); + ctx.write_import_ns_spec(&spec.span, local) } }) .collect::>(); - // ctx.write_flags(&flags); - ctx.write_maybe_ref(src_pos, src_id); - ctx.write_refs(spec_pos, spec_ids); + ctx.write_import_decl(&node.span, node.type_only, src, specifiers, attrs) + } + ModuleDecl::ExportDecl(node) => { + let decl = serialize_decl(ctx, &node.decl); + ctx.write_export_decl(&node.span, decl) + } + ModuleDecl::ExportNamed(node) => { + let attrs = serialize_import_attrs(ctx, &node.with); + let source = node + .src + .as_ref() + .map(|src| serialize_lit(ctx, &Lit::Str(*src.clone()))); - id + if let Some(ExportSpecifier::Namespace(ns)) = node.specifiers.first() { + let exported = serialize_module_export_name(ctx, &ns.name); + ctx.write_export_all_decl( + &node.span, + node.type_only, + exported, + source, + attrs, + ) + } else { + let specifiers = node + .specifiers + .iter() + .map(|spec| { + match spec { + ExportSpecifier::Named(spec) => { + let local = serialize_module_export_name(ctx, &spec.orig); + + let exported = spec.exported.as_ref().map_or( + serialize_module_export_name(ctx, &spec.orig), + |exported| serialize_module_export_name(ctx, exported), + ); + + ctx.write_export_spec( + &spec.span, + spec.is_type_only, + local, + exported, + ) + } + + // Already handled earlier + ExportSpecifier::Namespace(_) => unreachable!(), + // this is not syntactically valid + ExportSpecifier::Default(_) => unreachable!(), + } + }) + .collect::>(); + + ctx.write_export_named_decl(&node.span, specifiers, source, attrs) + } } ModuleDecl::ExportDefaultDecl(node) => { - let raw = - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let (is_type_only, decl) = match &node.decl { + DefaultDecl::Class(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + let decl = ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ); + + (false, decl) + } + DefaultDecl::Fn(node) => { + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let fn_obj = node.function.as_ref(); + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); + + let params = fn_obj + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = + maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); + let body = fn_obj + .body + .as_ref() + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); + + let decl = ctx.write_fn_decl( + &fn_obj.span, + false, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + return_type, + body, + params, + ); + + (false, decl) + } + DefaultDecl::TsInterfaceDecl(node) => { + let ident_id = serialize_ident(ctx, &node.id, None); + let type_param = + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); + + let extend_ids = node + .extends + .iter() + .map(|item| { + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + + ctx.write_ts_interface_heritage(&item.span, expr, type_args) + }) + .collect::>(); + + let body_elem_ids = node + .body + .body + .iter() + .map(|item| serialize_ts_type_elem(ctx, item)) + .collect::>(); + + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + + let decl = ctx.write_ts_interface_decl( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ); + + (true, decl) + } + }; + + ctx.write_export_default_decl(&node.span, is_type_only, decl) } ModuleDecl::ExportDefaultExpr(node) => { - let raw = - ctx.header(AstNode::ExportDefaultDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_default_decl(&node.span, false, expr) } ModuleDecl::ExportAll(node) => { - let raw = ctx.header(AstNode::ExportAllDeclaration, parent, &node.span); - ctx.commit_schema(raw) + let src = serialize_lit(ctx, &Lit::Str(node.src.as_ref().clone())); + let attrs = serialize_import_attrs(ctx, &node.with); + + ctx.write_export_all_decl(&node.span, node.type_only, src, None, attrs) } ModuleDecl::TsImportEquals(node) => { - let raw = ctx.header(AstNode::TsImportEquals, parent, &node.span); - ctx.commit_schema(raw) + let ident = serialize_ident(ctx, &node.id, None); + let module_ref = match &node.module_ref { + TsModuleRef::TsEntityName(entity) => { + serialize_ts_entity_name(ctx, entity) + } + TsModuleRef::TsExternalModuleRef(external) => { + let expr = serialize_lit(ctx, &Lit::Str(external.expr.clone())); + ctx.write_ts_external_mod_ref(&external.span, expr) + } + }; + + ctx.write_export_ts_import_equals( + &node.span, + node.is_type_only, + ident, + module_ref, + ) } ModuleDecl::TsExportAssignment(node) => { - let raw = ctx.header(AstNode::TsExportAssignment, parent, &node.span); - ctx.commit_schema(raw) + let expr = serialize_expr(ctx, &node.expr); + ctx.write_export_assign(&node.span, expr) } ModuleDecl::TsNamespaceExport(node) => { - let raw = ctx.header(AstNode::TsNamespaceExport, parent, &node.span); - ctx.commit_schema(raw) + let decl = serialize_ident(ctx, &node.id, None); + ctx.write_export_ts_namespace(&node.span, decl) } } } -fn serialize_stmt( +fn serialize_import_attrs( ctx: &mut TsEsTreeBuilder, - stmt: &Stmt, - parent: NodeRef, -) -> NodeRef { + raw_attrs: &Option>, +) -> Vec { + raw_attrs.as_ref().map_or(vec![], |obj| { + obj + .props + .iter() + .map(|prop| { + let (key, value) = match prop { + // Invalid syntax + PropOrSpread::Spread(_) => unreachable!(), + PropOrSpread::Prop(prop) => { + match prop.as_ref() { + Prop::Shorthand(ident) => ( + serialize_ident(ctx, ident, None), + serialize_ident(ctx, ident, None), + ), + Prop::KeyValue(kv) => ( + serialize_prop_name(ctx, &kv.key), + serialize_expr(ctx, kv.value.as_ref()), + ), + // Invalid syntax + Prop::Assign(_) + | Prop::Getter(_) + | Prop::Setter(_) + | Prop::Method(_) => unreachable!(), + } + } + }; + + ctx.write_import_attr(&prop.span(), key, value) + }) + .collect::>() + }) +} + +fn serialize_stmt(ctx: &mut TsEsTreeBuilder, stmt: &Stmt) -> NodeRef { match stmt { Stmt::Block(node) => { - let raw = ctx.header(AstNode::BlockStatement, parent, &node.span); - let body_pos = ctx.ref_vec_field(AstProp::Body, node.stmts.len()); - let pos = ctx.commit_schema(raw); - let children = node .stmts .iter() - .map(|stmt| serialize_stmt(ctx, stmt, pos)) + .map(|stmt| serialize_stmt(ctx, stmt)) .collect::>(); - ctx.write_refs(body_pos, children); - - pos + ctx.write_block_stmt(&node.span, children) } Stmt::Empty(_) => NodeRef(0), - Stmt::Debugger(node) => { - let raw = ctx.header(AstNode::DebuggerStatement, parent, &node.span); - ctx.commit_schema(raw) + Stmt::Debugger(node) => ctx.write_debugger_stmt(&node.span), + Stmt::With(node) => { + let obj = serialize_expr(ctx, &node.obj); + let body = serialize_stmt(ctx, &node.body); + + ctx.write_with_stmt(&node.span, obj, body) } - Stmt::With(_) => todo!(), Stmt::Return(node) => { - let raw = ctx.header(AstNode::ReturnStatement, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg, pos)); - ctx.write_maybe_ref(arg_pos, arg); - - pos + let arg = node.arg.as_ref().map(|arg| serialize_expr(ctx, arg)); + ctx.write_return_stmt(&node.span, arg) } Stmt::Labeled(node) => { - let raw = ctx.header(AstNode::LabeledStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let ident = serialize_ident(ctx, &node.label, None); + let stmt = serialize_stmt(ctx, &node.body); - let ident = serialize_ident(ctx, &node.label, pos); - let stmt = serialize_stmt(ctx, &node.body, pos); - - ctx.write_ref(label_pos, ident); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_labeled_stmt(&node.span, ident, stmt) } Stmt::Break(node) => { - let raw = ctx.header(AstNode::BreakStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let pos = ctx.commit_schema(raw); - let arg = node .label .as_ref() - .map(|label| serialize_ident(ctx, label, pos)); - - ctx.write_maybe_ref(label_pos, arg); - - pos + .map(|label| serialize_ident(ctx, label, None)); + ctx.write_break_stmt(&node.span, arg) } Stmt::Continue(node) => { - let raw = ctx.header(AstNode::ContinueStatement, parent, &node.span); - let label_pos = ctx.ref_field(AstProp::Label); - let pos = ctx.commit_schema(raw); - let arg = node .label .as_ref() - .map(|label| serialize_ident(ctx, label, pos)); + .map(|label| serialize_ident(ctx, label, None)); - ctx.write_maybe_ref(label_pos, arg); - - pos + ctx.write_continue_stmt(&node.span, arg) } Stmt::If(node) => { - let raw = ctx.header(AstNode::IfStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = ctx.ref_field(AstProp::Consequent); - let alt_pos = ctx.ref_field(AstProp::Alternate); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_stmt(ctx, node.cons.as_ref()); + let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt)); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let cons = serialize_stmt(ctx, node.cons.as_ref(), pos); - let alt = node.alt.as_ref().map(|alt| serialize_stmt(ctx, alt, pos)); - - ctx.write_ref(test_pos, test); - ctx.write_ref(cons_pos, cons); - ctx.write_maybe_ref(alt_pos, alt); - - pos + ctx.write_if_stmt(&node.span, test, cons, alt) } Stmt::Switch(node) => { - let raw = ctx.header(AstNode::SwitchStatement, parent, &node.span); - let disc_pos = ctx.ref_field(AstProp::Discriminant); - let cases_pos = ctx.ref_vec_field(AstProp::Cases, node.cases.len()); - let pos = ctx.commit_schema(raw); - - let disc = serialize_expr(ctx, &node.discriminant, pos); + let disc = serialize_expr(ctx, &node.discriminant); let cases = node .cases .iter() .map(|case| { - let raw = ctx.header(AstNode::SwitchCase, pos, &case.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = - ctx.ref_vec_field(AstProp::Consequent, case.cons.len()); - let case_pos = ctx.commit_schema(raw); - - let test = case - .test - .as_ref() - .map(|test| serialize_expr(ctx, test, case_pos)); + let test = case.test.as_ref().map(|test| serialize_expr(ctx, test)); let cons = case .cons .iter() - .map(|cons| serialize_stmt(ctx, cons, case_pos)) + .map(|cons| serialize_stmt(ctx, cons)) .collect::>(); - ctx.write_maybe_ref(test_pos, test); - ctx.write_refs(cons_pos, cons); - - case_pos + ctx.write_switch_case(&case.span, test, cons) }) .collect::>(); - ctx.write_ref(disc_pos, disc); - ctx.write_refs(cases_pos, cases); - - pos + ctx.write_switch_stmt(&node.span, disc, cases) } Stmt::Throw(node) => { - let raw = ctx.header(AstNode::ThrowStatement, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, &node.arg, pos); - ctx.write_ref(arg_pos, arg); - - pos + let arg = serialize_expr(ctx, &node.arg); + ctx.write_throw_stmt(&node.span, arg) } Stmt::Try(node) => { - let raw = ctx.header(AstNode::TryStatement, parent, &node.span); - let block_pos = ctx.ref_field(AstProp::Block); - let handler_pos = ctx.ref_field(AstProp::Handler); - let finalizer_pos = ctx.ref_field(AstProp::Finalizer); - let pos = ctx.commit_schema(raw); - - let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone()), pos); + let block = serialize_stmt(ctx, &Stmt::Block(node.block.clone())); let handler = node.handler.as_ref().map(|catch| { - let raw = ctx.header(AstNode::CatchClause, pos, &catch.span); - let param_pos = ctx.ref_field(AstProp::Param); - let body_pos = ctx.ref_field(AstProp::Body); - let clause_pos = ctx.commit_schema(raw); + let param = catch.param.as_ref().map(|param| serialize_pat(ctx, param)); - let param = catch - .param - .as_ref() - .map(|param| serialize_pat(ctx, param, clause_pos)); + let body = serialize_stmt(ctx, &Stmt::Block(catch.body.clone())); - let body = - serialize_stmt(ctx, &Stmt::Block(catch.body.clone()), clause_pos); - - ctx.write_maybe_ref(param_pos, param); - ctx.write_ref(body_pos, body); - - clause_pos + ctx.write_catch_clause(&catch.span, param, body) }); - let finalizer = node.finalizer.as_ref().map(|finalizer| { - serialize_stmt(ctx, &Stmt::Block(finalizer.clone()), pos) - }); + let finalizer = node + .finalizer + .as_ref() + .map(|finalizer| serialize_stmt(ctx, &Stmt::Block(finalizer.clone()))); - ctx.write_ref(block_pos, block); - ctx.write_maybe_ref(handler_pos, handler); - ctx.write_maybe_ref(finalizer_pos, finalizer); - - pos + ctx.write_try_stmt(&node.span, block, handler, finalizer) } Stmt::While(node) => { - let raw = ctx.header(AstNode::WhileStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(test_pos, test); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_while_stmt(&node.span, test, stmt) } Stmt::DoWhile(node) => { - let raw = ctx.header(AstNode::DoWhileStatement, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.test.as_ref()); + let stmt = serialize_stmt(ctx, node.body.as_ref()); - let expr = serialize_expr(ctx, node.test.as_ref(), pos); - let stmt = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(test_pos, expr); - ctx.write_ref(body_pos, stmt); - - pos + ctx.write_do_while_stmt(&node.span, expr, stmt) } Stmt::For(node) => { - let raw = ctx.header(AstNode::ForStatement, parent, &node.span); - let init_pos = ctx.ref_field(AstProp::Init); - let test_pos = ctx.ref_field(AstProp::Test); - let update_pos = ctx.ref_field(AstProp::Update); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - let init = node.init.as_ref().map(|init| match init { VarDeclOrExpr::VarDecl(var_decl) => { - serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone())), pos) + serialize_stmt(ctx, &Stmt::Decl(Decl::Var(var_decl.clone()))) } - VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + VarDeclOrExpr::Expr(expr) => serialize_expr(ctx, expr), }); - let test = node - .test - .as_ref() - .map(|expr| serialize_expr(ctx, expr, pos)); - let update = node - .update - .as_ref() - .map(|expr| serialize_expr(ctx, expr, pos)); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); + let test = node.test.as_ref().map(|expr| serialize_expr(ctx, expr)); + let update = node.update.as_ref().map(|expr| serialize_expr(ctx, expr)); + let body = serialize_stmt(ctx, node.body.as_ref()); - ctx.write_maybe_ref(init_pos, init); - ctx.write_maybe_ref(test_pos, test); - ctx.write_maybe_ref(update_pos, update); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_stmt(&node.span, init, test, update, body) } Stmt::ForIn(node) => { - let raw = ctx.header(AstNode::ForInStatement, parent, &node.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); - let left = serialize_for_head(ctx, &node.left, pos); - let right = serialize_expr(ctx, node.right.as_ref(), pos); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_in_stmt(&node.span, left, right, body) } Stmt::ForOf(node) => { - let raw = ctx.header(AstNode::ForOfStatement, parent, &node.span); - let await_pos = ctx.bool_field(AstProp::Await); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); + let left = serialize_for_head(ctx, &node.left); + let right = serialize_expr(ctx, node.right.as_ref()); + let body = serialize_stmt(ctx, node.body.as_ref()); - let left = serialize_for_head(ctx, &node.left, pos); - let right = serialize_expr(ctx, node.right.as_ref(), pos); - let body = serialize_stmt(ctx, node.body.as_ref(), pos); - - ctx.write_bool(await_pos, node.is_await); - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - ctx.write_ref(body_pos, body); - - pos + ctx.write_for_of_stmt(&node.span, node.is_await, left, right, body) } - Stmt::Decl(node) => serialize_decl(ctx, node, parent), + Stmt::Decl(node) => serialize_decl(ctx, node), Stmt::Expr(node) => { - let raw = ctx.header(AstNode::ExpressionStatement, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - ctx.write_ref(expr_pos, expr); - - pos + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_expr_stmt(&node.span, expr) } } } -fn serialize_expr( - ctx: &mut TsEsTreeBuilder, - expr: &Expr, - parent: NodeRef, -) -> NodeRef { +fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef { match expr { - Expr::This(node) => { - let raw = ctx.header(AstNode::ThisExpression, parent, &node.span); - ctx.commit_schema(raw) - } + Expr::This(node) => ctx.write_this_expr(&node.span), Expr::Array(node) => { - let raw = ctx.header(AstNode::ArrayExpression, parent, &node.span); - let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); - let pos = ctx.commit_schema(raw); - let elems = node .elems .iter() .map(|item| { item .as_ref() - .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item, pos)) + .map_or(NodeRef(0), |item| serialize_expr_or_spread(ctx, item)) }) .collect::>(); - ctx.write_refs(elems_pos, elems); - - pos + ctx.write_arr_expr(&node.span, elems) } Expr::Object(node) => { - let raw = ctx.header(AstNode::ObjectExpression, parent, &node.span); - let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); - let pos = ctx.commit_schema(raw); - - let prop_ids = node + let props = node .props .iter() - .map(|prop| serialize_prop_or_spread(ctx, prop, pos)) + .map(|prop| serialize_prop_or_spread(ctx, prop)) .collect::>(); - ctx.write_refs(props_pos, prop_ids); - - pos + ctx.write_obj_expr(&node.span, props) } Expr::Fn(node) => { let fn_obj = node.function.as_ref(); - let raw = ctx.header(AstNode::FunctionExpression, parent, &fn_obj.span); - - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let id_pos = ctx.ref_field(AstProp::Id); - let tparams_pos = ctx.ref_field(AstProp::TypeParameters); - let params_pos = ctx.ref_vec_field(AstProp::Params, fn_obj.params.len()); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - let ident = node .ident .as_ref() - .map(|ident| serialize_ident(ctx, ident, pos)); + .map(|ident| serialize_ident(ctx, ident, None)); let type_params = - maybe_serialize_ts_type_param(ctx, &fn_obj.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &fn_obj.type_params); let params = fn_obj .params .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .map(|param| serialize_pat(ctx, ¶m.pat)) .collect::>(); - let return_id = - maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type, pos); + let return_id = maybe_serialize_ts_type_ann(ctx, &fn_obj.return_type); let body = fn_obj .body .as_ref() - .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()), pos)); + .map(|block| serialize_stmt(ctx, &Stmt::Block(block.clone()))); - ctx.write_bool(async_pos, fn_obj.is_async); - ctx.write_bool(gen_pos, fn_obj.is_generator); - ctx.write_maybe_ref(id_pos, ident); - ctx.write_maybe_ref(tparams_pos, type_params); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_pos, return_id); - ctx.write_maybe_ref(body_pos, body); - - pos + ctx.write_fn_expr( + &fn_obj.span, + fn_obj.is_async, + fn_obj.is_generator, + ident, + type_params, + params, + return_id, + body, + ) } Expr::Unary(node) => { - let raw = ctx.header(AstNode::UnaryExpression, parent, &node.span); - let flag_pos = ctx.str_field(AstProp::Operator); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let arg = serialize_expr(ctx, &node.arg); + let op = match node.op { + UnaryOp::Minus => "-", + UnaryOp::Plus => "+", + UnaryOp::Bang => "!", + UnaryOp::Tilde => "~", + UnaryOp::TypeOf => "typeof", + UnaryOp::Void => "void", + UnaryOp::Delete => "delete", + }; - let arg = serialize_expr(ctx, &node.arg, pos); - - ctx.write_str( - flag_pos, - match node.op { - UnaryOp::Minus => "-", - UnaryOp::Plus => "+", - UnaryOp::Bang => "!", - UnaryOp::Tilde => "~", - UnaryOp::TypeOf => "typeof", - UnaryOp::Void => "void", - UnaryOp::Delete => "delete", - }, - ); - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_unary_expr(&node.span, op, arg) } Expr::Update(node) => { - let raw = ctx.header(AstNode::UpdateExpression, parent, &node.span); - let prefix_pos = ctx.bool_field(AstProp::Prefix); - let arg_pos = ctx.ref_field(AstProp::Argument); - let op_ops = ctx.str_field(AstProp::Operator); - let pos = ctx.commit_schema(raw); + let arg = serialize_expr(ctx, node.arg.as_ref()); + let op = match node.op { + UpdateOp::PlusPlus => "++", + UpdateOp::MinusMinus => "--", + }; - let arg = serialize_expr(ctx, node.arg.as_ref(), pos); - - ctx.write_bool(prefix_pos, node.prefix); - ctx.write_ref(arg_pos, arg); - ctx.write_str( - op_ops, - match node.op { - UpdateOp::PlusPlus => "++", - UpdateOp::MinusMinus => "--", - }, - ); - - pos + ctx.write_update_expr(&node.span, node.prefix, op, arg) } Expr::Bin(node) => { let (node_type, flag_str) = match node.op { @@ -699,501 +676,361 @@ fn serialize_expr( BinaryOp::Exp => (AstNode::BinaryExpression, "**"), }; - let raw = ctx.header(node_type, parent, &node.span); - let op_pos = ctx.str_field(AstProp::Operator); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); + let left = serialize_expr(ctx, node.left.as_ref()); + let right = serialize_expr(ctx, node.right.as_ref()); - let left_id = serialize_expr(ctx, node.left.as_ref(), pos); - let right_id = serialize_expr(ctx, node.right.as_ref(), pos); - - ctx.write_str(op_pos, flag_str); - ctx.write_ref(left_pos, left_id); - ctx.write_ref(right_pos, right_id); - - pos + match node_type { + AstNode::LogicalExpression => { + ctx.write_logical_expr(&node.span, flag_str, left, right) + } + AstNode::BinaryExpression => { + ctx.write_bin_expr(&node.span, flag_str, left, right) + } + _ => unreachable!(), + } } Expr::Assign(node) => { - let raw = ctx.header(AstNode::AssignmentExpression, parent, &node.span); - let op_pos = ctx.str_field(AstProp::Operator); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); - let left = match &node.left { AssignTarget::Simple(simple_assign_target) => { match simple_assign_target { SimpleAssignTarget::Ident(target) => { - serialize_ident(ctx, &target.id, pos) + serialize_binding_ident(ctx, target) } SimpleAssignTarget::Member(target) => { - serialize_expr(ctx, &Expr::Member(target.clone()), pos) + serialize_expr(ctx, &Expr::Member(target.clone())) } SimpleAssignTarget::SuperProp(target) => { - serialize_expr(ctx, &Expr::SuperProp(target.clone()), pos) + serialize_expr(ctx, &Expr::SuperProp(target.clone())) } SimpleAssignTarget::Paren(target) => { - serialize_expr(ctx, &target.expr, pos) + serialize_expr(ctx, &target.expr) } SimpleAssignTarget::OptChain(target) => { - serialize_expr(ctx, &Expr::OptChain(target.clone()), pos) + serialize_expr(ctx, &Expr::OptChain(target.clone())) } SimpleAssignTarget::TsAs(target) => { - serialize_expr(ctx, &Expr::TsAs(target.clone()), pos) + serialize_expr(ctx, &Expr::TsAs(target.clone())) } SimpleAssignTarget::TsSatisfies(target) => { - serialize_expr(ctx, &Expr::TsSatisfies(target.clone()), pos) + serialize_expr(ctx, &Expr::TsSatisfies(target.clone())) } SimpleAssignTarget::TsNonNull(target) => { - serialize_expr(ctx, &Expr::TsNonNull(target.clone()), pos) + serialize_expr(ctx, &Expr::TsNonNull(target.clone())) } SimpleAssignTarget::TsTypeAssertion(target) => { - serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone()), pos) + serialize_expr(ctx, &Expr::TsTypeAssertion(target.clone())) } SimpleAssignTarget::TsInstantiation(target) => { - serialize_expr(ctx, &Expr::TsInstantiation(target.clone()), pos) + serialize_expr(ctx, &Expr::TsInstantiation(target.clone())) } SimpleAssignTarget::Invalid(_) => unreachable!(), } } AssignTarget::Pat(target) => match target { AssignTargetPat::Array(array_pat) => { - serialize_pat(ctx, &Pat::Array(array_pat.clone()), pos) + serialize_pat(ctx, &Pat::Array(array_pat.clone())) } AssignTargetPat::Object(object_pat) => { - serialize_pat(ctx, &Pat::Object(object_pat.clone()), pos) + serialize_pat(ctx, &Pat::Object(object_pat.clone())) } AssignTargetPat::Invalid(_) => unreachable!(), }, }; - let right = serialize_expr(ctx, node.right.as_ref(), pos); + let right = serialize_expr(ctx, node.right.as_ref()); - ctx.write_str( - op_pos, - match node.op { - AssignOp::Assign => "=", - AssignOp::AddAssign => "+=", - AssignOp::SubAssign => "-=", - AssignOp::MulAssign => "*=", - AssignOp::DivAssign => "/=", - AssignOp::ModAssign => "%=", - AssignOp::LShiftAssign => "<<=", - AssignOp::RShiftAssign => ">>=", - AssignOp::ZeroFillRShiftAssign => ">>>=", - AssignOp::BitOrAssign => "|=", - AssignOp::BitXorAssign => "^=", - AssignOp::BitAndAssign => "&=", - AssignOp::ExpAssign => "**=", - AssignOp::AndAssign => "&&=", - AssignOp::OrAssign => "||=", - AssignOp::NullishAssign => "??=", - }, - ); - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); + let op = match node.op { + AssignOp::Assign => "=", + AssignOp::AddAssign => "+=", + AssignOp::SubAssign => "-=", + AssignOp::MulAssign => "*=", + AssignOp::DivAssign => "/=", + AssignOp::ModAssign => "%=", + AssignOp::LShiftAssign => "<<=", + AssignOp::RShiftAssign => ">>=", + AssignOp::ZeroFillRShiftAssign => ">>>=", + AssignOp::BitOrAssign => "|=", + AssignOp::BitXorAssign => "^=", + AssignOp::BitAndAssign => "&=", + AssignOp::ExpAssign => "**=", + AssignOp::AndAssign => "&&=", + AssignOp::OrAssign => "||=", + AssignOp::NullishAssign => "??=", + }; - pos + ctx.write_assignment_expr(&node.span, op, left, right) } - Expr::Member(node) => serialize_member_expr(ctx, node, parent, false), + Expr::Member(node) => serialize_member_expr(ctx, node, false), Expr::SuperProp(node) => { - let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let obj_pos = ctx.ref_field(AstProp::Object); - let prop_pos = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::Super, pos, &node.obj.span); - let obj = ctx.commit_schema(raw); + let obj = ctx.write_super(&node.obj.span); let mut computed = false; let prop = match &node.prop { - SuperProp::Ident(ident_name) => { - serialize_ident_name(ctx, ident_name, pos) - } + SuperProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), SuperProp::Computed(prop) => { computed = true; - serialize_expr(ctx, &prop.expr, pos) + serialize_expr(ctx, &prop.expr) } }; - ctx.write_bool(computed_pos, computed); - ctx.write_ref(obj_pos, obj); - ctx.write_ref(prop_pos, prop); - - pos + ctx.write_member_expr(&node.span, false, computed, obj, prop) } Expr::Cond(node) => { - let raw = ctx.header(AstNode::ConditionalExpression, parent, &node.span); - let test_pos = ctx.ref_field(AstProp::Test); - let cons_pos = ctx.ref_field(AstProp::Consequent); - let alt_pos = ctx.ref_field(AstProp::Alternate); - let pos = ctx.commit_schema(raw); + let test = serialize_expr(ctx, node.test.as_ref()); + let cons = serialize_expr(ctx, node.cons.as_ref()); + let alt = serialize_expr(ctx, node.alt.as_ref()); - let test = serialize_expr(ctx, node.test.as_ref(), pos); - let cons = serialize_expr(ctx, node.cons.as_ref(), pos); - let alt = serialize_expr(ctx, node.alt.as_ref(), pos); - - ctx.write_ref(test_pos, test); - ctx.write_ref(cons_pos, cons); - ctx.write_ref(alt_pos, alt); - - pos + ctx.write_conditional_expr(&node.span, test, cons, alt) } Expr::Call(node) => { - let raw = ctx.header(AstNode::CallExpression, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = ctx.ref_vec_field(AstProp::Arguments, node.args.len()); - let pos = ctx.commit_schema(raw); + if let Callee::Import(_) = node.callee { + let source = node + .args + .first() + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); - let callee = match &node.callee { - Callee::Super(super_node) => { - let raw = ctx.header(AstNode::Super, pos, &super_node.span); - ctx.commit_schema(raw) - } - Callee::Import(_) => todo!(), - Callee::Expr(expr) => serialize_expr(ctx, expr, pos), - }; + let options = node + .args + .get(1) + .map_or(NodeRef(0), |arg| serialize_expr_or_spread(ctx, arg)); - let type_arg = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + ctx.write_import_expr(&node.span, source, options) + } else { + let callee = match &node.callee { + Callee::Super(super_node) => ctx.write_super(&super_node.span), + Callee::Import(_) => unreachable!("Already handled"), + Callee::Expr(expr) => serialize_expr(ctx, expr), + }; - let args = node - .args - .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) - .collect::>(); + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - ctx.write_bool(opt_pos, false); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_arg); - ctx.write_refs(args_pos, args); + let args = node + .args + .iter() + .map(|arg| serialize_expr_or_spread(ctx, arg)) + .collect::>(); - pos + ctx.write_call_expr(&node.span, false, callee, type_arg, args) + } } Expr::New(node) => { - let raw = ctx.header(AstNode::NewExpression, parent, &node.span); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = ctx.ref_vec_field( - AstProp::Arguments, - node.args.as_ref().map_or(0, |v| v.len()), - ); - let pos = ctx.commit_schema(raw); - - let callee = serialize_expr(ctx, node.callee.as_ref(), pos); + let callee = serialize_expr(ctx, node.callee.as_ref()); let args: Vec = node.args.as_ref().map_or(vec![], |args| { args .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .map(|arg| serialize_expr_or_spread(ctx, arg)) .collect::>() }); - let type_args = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + let type_args = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_args); - ctx.write_refs(args_pos, args); - - pos + ctx.write_new_expr(&node.span, callee, type_args, args) } Expr::Seq(node) => { - let raw = ctx.header(AstNode::SequenceExpression, parent, &node.span); - let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); - let pos = ctx.commit_schema(raw); - let children = node .exprs .iter() - .map(|expr| serialize_expr(ctx, expr, pos)) + .map(|expr| serialize_expr(ctx, expr)) .collect::>(); - ctx.write_refs(exprs_pos, children); - - pos + ctx.write_sequence_expr(&node.span, children) } - Expr::Ident(node) => serialize_ident(ctx, node, parent), - Expr::Lit(node) => serialize_lit(ctx, node, parent), + Expr::Ident(node) => serialize_ident(ctx, node, None), + Expr::Lit(node) => serialize_lit(ctx, node), Expr::Tpl(node) => { - let raw = ctx.header(AstNode::TemplateLiteral, parent, &node.span); - let quasis_pos = ctx.ref_vec_field(AstProp::Quasis, node.quasis.len()); - let exprs_pos = ctx.ref_vec_field(AstProp::Expressions, node.exprs.len()); - let pos = ctx.commit_schema(raw); - let quasis = node .quasis .iter() .map(|quasi| { - let raw = ctx.header(AstNode::TemplateElement, pos, &quasi.span); - let tail_pos = ctx.bool_field(AstProp::Tail); - let raw_pos = ctx.str_field(AstProp::Raw); - let cooked_pos = ctx.str_field(AstProp::Cooked); - let tpl_pos = ctx.commit_schema(raw); - - ctx.write_bool(tail_pos, quasi.tail); - ctx.write_str(raw_pos, &quasi.raw); - ctx.write_str( - cooked_pos, + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, &quasi .cooked .as_ref() .map_or("".to_string(), |v| v.to_string()), - ); - - tpl_pos + ) }) .collect::>(); let exprs = node .exprs .iter() - .map(|expr| serialize_expr(ctx, expr, pos)) + .map(|expr| serialize_expr(ctx, expr)) .collect::>(); - ctx.write_refs(quasis_pos, quasis); - ctx.write_refs(exprs_pos, exprs); - - pos + ctx.write_template_lit(&node.span, quasis, exprs) } Expr::TaggedTpl(node) => { - let raw = - ctx.header(AstNode::TaggedTemplateExpression, parent, &node.span); - let tag_pos = ctx.ref_field(AstProp::Tag); - let type_arg_pos = ctx.ref_field(AstProp::TypeArguments); - let quasi_pos = ctx.ref_field(AstProp::Quasi); - let pos = ctx.commit_schema(raw); - - let tag = serialize_expr(ctx, &node.tag, pos); - - let type_param_id = node + let tag = serialize_expr(ctx, &node.tag); + let type_param = node .type_params .clone() - .map(|params| serialize_ts_param_inst(ctx, params.as_ref(), pos)); - let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone()), pos); + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); + let quasi = serialize_expr(ctx, &Expr::Tpl(*node.tpl.clone())); - ctx.write_ref(tag_pos, tag); - ctx.write_maybe_ref(type_arg_pos, type_param_id); - ctx.write_ref(quasi_pos, quasi); - - pos + ctx.write_tagged_template_expr(&node.span, tag, type_param, quasi) } Expr::Arrow(node) => { - let raw = - ctx.header(AstNode::ArrowFunctionExpression, parent, &node.span); - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let type_param_pos = ctx.ref_field(AstProp::TypeParameters); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let body_pos = ctx.ref_field(AstProp::Body); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let pos = ctx.commit_schema(raw); - let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); let params = node .params .iter() - .map(|param| serialize_pat(ctx, param, pos)) + .map(|param| serialize_pat(ctx, param)) .collect::>(); let body = match node.body.as_ref() { BlockStmtOrExpr::BlockStmt(block_stmt) => { - serialize_stmt(ctx, &Stmt::Block(block_stmt.clone()), pos) + serialize_stmt(ctx, &Stmt::Block(block_stmt.clone())) } - BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref(), pos), + BlockStmtOrExpr::Expr(expr) => serialize_expr(ctx, expr.as_ref()), }; - let return_type = - maybe_serialize_ts_type_ann(ctx, &node.return_type, pos); + let return_type = maybe_serialize_ts_type_ann(ctx, &node.return_type); - ctx.write_bool(async_pos, node.is_async); - ctx.write_bool(gen_pos, node.is_generator); - ctx.write_maybe_ref(type_param_pos, type_param); - ctx.write_refs(params_pos, params); - ctx.write_ref(body_pos, body); - ctx.write_maybe_ref(return_type_pos, return_type); - - pos + ctx.write_arrow_fn_expr( + &node.span, + node.is_async, + node.is_generator, + type_param, + params, + return_type, + body, + ) } Expr::Class(node) => { - // FIXME - let raw = ctx.header(AstNode::ClassExpression, parent, &node.class.span); - ctx.commit_schema(raw) + let ident = node + .ident + .as_ref() + .map(|ident| serialize_ident(ctx, ident, None)); + + let super_class = node + .class + .super_class + .as_ref() + .map(|expr| serialize_expr(ctx, expr.as_ref())); + + let implements = node + .class + .implements + .iter() + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) + .collect::>(); + + let members = node + .class + .body + .iter() + .filter_map(|member| serialize_class_member(ctx, member)) + .collect::>(); + + let body = ctx.write_class_body(&node.class.span, members); + + ctx.write_class_expr( + &node.class.span, + false, + node.class.is_abstract, + ident, + super_class, + implements, + body, + ) } Expr::Yield(node) => { - let raw = ctx.header(AstNode::YieldExpression, parent, &node.span); - let delegate_pos = ctx.bool_field(AstProp::Delegate); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - let arg = node .arg .as_ref() - .map(|arg| serialize_expr(ctx, arg.as_ref(), pos)); + .map(|arg| serialize_expr(ctx, arg.as_ref())); - ctx.write_bool(delegate_pos, node.delegate); - ctx.write_maybe_ref(arg_pos, arg); - - pos + ctx.write_yield_expr(&node.span, node.delegate, arg) } Expr::MetaProp(node) => { - let raw = ctx.header(AstNode::MetaProp, parent, &node.span); - ctx.commit_schema(raw) + let prop = ctx.write_identifier(&node.span, "meta", false, None); + ctx.write_meta_prop(&node.span, prop) } Expr::Await(node) => { - let raw = ctx.header(AstNode::AwaitExpression, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, node.arg.as_ref(), pos); - - ctx.write_ref(arg_pos, arg); - - pos + let arg = serialize_expr(ctx, node.arg.as_ref()); + ctx.write_await_expr(&node.span, arg) } Expr::Paren(node) => { // Paren nodes are treated as a syntax only thing in TSEStree // and are never materialized to actual AST nodes. - serialize_expr(ctx, &node.expr, parent) + serialize_expr(ctx, &node.expr) } - Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node, parent), - Expr::JSXNamespacedName(node) => { - serialize_jsx_namespaced_name(ctx, node, parent) - } - Expr::JSXEmpty(node) => serialize_jsx_empty_expr(ctx, node, parent), - Expr::JSXElement(node) => serialize_jsx_element(ctx, node, parent), - Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node, parent), + Expr::JSXMember(node) => serialize_jsx_member_expr(ctx, node), + Expr::JSXNamespacedName(node) => serialize_jsx_namespaced_name(ctx, node), + Expr::JSXEmpty(node) => ctx.write_jsx_empty_expr(&node.span), + Expr::JSXElement(node) => serialize_jsx_element(ctx, node), + Expr::JSXFragment(node) => serialize_jsx_fragment(ctx, node), Expr::TsTypeAssertion(node) => { - let raw = ctx.header(AstNode::TSTypeAssertion, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, &node.expr); + let type_ann = serialize_ts_type(ctx, &node.type_ann); - let expr = serialize_expr(ctx, &node.expr, parent); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_type_assertion(&node.span, expr, type_ann) } Expr::TsConstAssertion(node) => { - let raw = ctx.header(AstNode::TsConstAssertion, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); - let arg = serialize_expr(ctx, node.expr.as_ref(), pos); + let type_name = ctx.write_identifier(&node.span, "const", false, None); + let type_ann = ctx.write_ts_type_ref(&node.span, type_name, None); - // FIXME - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_ts_as_expr(&node.span, expr, type_ann) } Expr::TsNonNull(node) => { - let raw = ctx.header(AstNode::TSNonNullExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - - let expr_id = serialize_expr(ctx, node.expr.as_ref(), pos); - - ctx.write_ref(expr_pos, expr_id); - - pos + let expr = serialize_expr(ctx, node.expr.as_ref()); + ctx.write_ts_non_null(&node.span, expr) } Expr::TsAs(node) => { - let raw = ctx.header(AstNode::TSAsExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_as_expr(&node.span, expr, type_ann) } - Expr::TsInstantiation(node) => { - let raw = ctx.header(AstNode::TsInstantiation, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - - let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - - let type_arg = serialize_ts_param_inst(ctx, node.type_args.as_ref(), pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_ref(type_args_pos, type_arg); - - pos + Expr::TsInstantiation(_) => { + // Invalid syntax + unreachable!() } Expr::TsSatisfies(node) => { - let raw = ctx.header(AstNode::TSSatisfiesExpression, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, node.expr.as_ref()); + let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref()); - let epxr = serialize_expr(ctx, node.expr.as_ref(), pos); - let type_ann = serialize_ts_type(ctx, node.type_ann.as_ref(), pos); - - ctx.write_ref(expr_pos, epxr); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_satisfies_expr(&node.span, expr, type_ann) } - Expr::PrivateName(node) => serialize_private_name(ctx, node, parent), + Expr::PrivateName(node) => serialize_private_name(ctx, node), Expr::OptChain(node) => { - let raw = ctx.header(AstNode::ChainExpression, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let arg = match node.base.as_ref() { + let expr = match node.base.as_ref() { OptChainBase::Member(member_expr) => { - serialize_member_expr(ctx, member_expr, pos, true) + serialize_member_expr(ctx, member_expr, true) } OptChainBase::Call(opt_call) => { - let raw = ctx.header(AstNode::CallExpression, pos, &opt_call.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let callee_pos = ctx.ref_field(AstProp::Callee); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let args_pos = - ctx.ref_vec_field(AstProp::Arguments, opt_call.args.len()); - let call_pos = ctx.commit_schema(raw); + let callee = serialize_expr(ctx, &opt_call.callee); - let callee = serialize_expr(ctx, &opt_call.callee, pos); - - let type_param_id = opt_call.type_args.clone().map(|params| { - serialize_ts_param_inst(ctx, params.as_ref(), call_pos) - }); + let type_param_id = opt_call + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); let args = opt_call .args .iter() - .map(|arg| serialize_expr_or_spread(ctx, arg, pos)) + .map(|arg| serialize_expr_or_spread(ctx, arg)) .collect::>(); - ctx.write_bool(opt_pos, true); - ctx.write_ref(callee_pos, callee); - ctx.write_maybe_ref(type_args_pos, type_param_id); - ctx.write_refs(args_pos, args); - - call_pos + ctx.write_call_expr(&opt_call.span, true, callee, type_param_id, args) } }; - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_chain_expr(&node.span, expr) } Expr::Invalid(_) => { unreachable!() @@ -1204,69 +1041,49 @@ fn serialize_expr( fn serialize_prop_or_spread( ctx: &mut TsEsTreeBuilder, prop: &PropOrSpread, - parent: NodeRef, ) -> NodeRef { match prop { PropOrSpread::Spread(spread_element) => serialize_spread( ctx, spread_element.expr.as_ref(), &spread_element.dot3_token, - parent, ), PropOrSpread::Prop(prop) => { - let raw = ctx.header(AstNode::Property, parent, &prop.span()); - - let shorthand_pos = ctx.bool_field(AstProp::Shorthand); - let computed_pos = ctx.bool_field(AstProp::Computed); - let method_pos = ctx.bool_field(AstProp::Method); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - let mut shorthand = false; let mut computed = false; let mut method = false; let mut kind = "init"; - // FIXME: optional - let (key_id, value_id) = match prop.as_ref() { + let (key, value) = match prop.as_ref() { Prop::Shorthand(ident) => { shorthand = true; - let value = serialize_ident(ctx, ident, pos); - (value, value) + let value = serialize_ident(ctx, ident, None); + let value2 = serialize_ident(ctx, ident, None); + (value, value2) } Prop::KeyValue(key_value_prop) => { if let PropName::Computed(_) = key_value_prop.key { computed = true; } - let key = serialize_prop_name(ctx, &key_value_prop.key, pos); - let value = serialize_expr(ctx, key_value_prop.value.as_ref(), pos); + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_expr(ctx, key_value_prop.value.as_ref()); (key, value) } Prop::Assign(assign_prop) => { - let raw = - ctx.header(AstNode::AssignmentPattern, pos, &assign_prop.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let child_pos = ctx.commit_schema(raw); + let left = serialize_ident(ctx, &assign_prop.key, None); + let right = serialize_expr(ctx, assign_prop.value.as_ref()); - let left = serialize_ident(ctx, &assign_prop.key, child_pos); - let right = - serialize_expr(ctx, assign_prop.value.as_ref(), child_pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); + let child_pos = ctx.write_assign_pat(&assign_prop.span, left, right); (left, child_pos) } Prop::Getter(getter_prop) => { kind = "get"; - let key = serialize_prop_name(ctx, &getter_prop.key, pos); + let key = serialize_prop_name(ctx, &getter_prop.key); let value = serialize_expr( ctx, @@ -1280,11 +1097,10 @@ fn serialize_prop_or_spread( body: getter_prop.body.clone(), is_generator: false, is_async: false, - type_params: None, // FIXME - return_type: None, + type_params: None, + return_type: getter_prop.type_ann.clone(), }), }), - pos, ); (key, value) @@ -1292,7 +1108,7 @@ fn serialize_prop_or_spread( Prop::Setter(setter_prop) => { kind = "set"; - let key_id = serialize_prop_name(ctx, &setter_prop.key, pos); + let key_id = serialize_prop_name(ctx, &setter_prop.key); let param = Param::from(*setter_prop.param.clone()); @@ -1312,7 +1128,6 @@ fn serialize_prop_or_spread( return_type: None, }), }), - pos, ); (key_id, value_id) @@ -1320,7 +1135,7 @@ fn serialize_prop_or_spread( Prop::Method(method_prop) => { method = true; - let key_id = serialize_prop_name(ctx, &method_prop.key, pos); + let key_id = serialize_prop_name(ctx, &method_prop.key); let value_id = serialize_expr( ctx, @@ -1328,21 +1143,21 @@ fn serialize_prop_or_spread( ident: None, function: method_prop.function.clone(), }), - pos, ); (key_id, value_id) } }; - ctx.write_bool(shorthand_pos, shorthand); - ctx.write_bool(computed_pos, computed); - ctx.write_bool(method_pos, method); - ctx.write_str(kind_pos, kind); - ctx.write_ref(key_pos, key_id); - ctx.write_ref(value_pos, value_id); - - pos + ctx.write_property( + &prop.span(), + shorthand, + computed, + method, + kind, + key, + value, + ) } } } @@ -1350,396 +1165,190 @@ fn serialize_prop_or_spread( fn serialize_member_expr( ctx: &mut TsEsTreeBuilder, node: &MemberExpr, - parent: NodeRef, optional: bool, ) -> NodeRef { - let raw = ctx.header(AstNode::MemberExpression, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let computed_pos = ctx.bool_field(AstProp::Computed); - let obj_pos = ctx.ref_field(AstProp::Object); - let prop_pos = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - - let obj = serialize_expr(ctx, node.obj.as_ref(), pos); - let mut computed = false; + let obj = serialize_expr(ctx, node.obj.as_ref()); let prop = match &node.prop { - MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name, pos), + MemberProp::Ident(ident_name) => serialize_ident_name(ctx, ident_name), MemberProp::PrivateName(private_name) => { - serialize_private_name(ctx, private_name, pos) + serialize_private_name(ctx, private_name) } MemberProp::Computed(computed_prop_name) => { computed = true; - serialize_expr(ctx, computed_prop_name.expr.as_ref(), pos) + serialize_expr(ctx, computed_prop_name.expr.as_ref()) } }; - ctx.write_bool(opt_pos, optional); - ctx.write_bool(computed_pos, computed); - ctx.write_ref(obj_pos, obj); - ctx.write_ref(prop_pos, prop); - - pos -} - -fn serialize_class_member( - ctx: &mut TsEsTreeBuilder, - member: &ClassMember, - parent: NodeRef, -) -> NodeRef { - match member { - ClassMember::Constructor(constructor) => { - let raw = - ctx.header(AstNode::MethodDefinition, parent, &constructor.span); - let key_pos = ctx.ref_field(AstProp::Key); - let body_pos = ctx.ref_field(AstProp::Body); - let args_pos = - ctx.ref_vec_field(AstProp::Arguments, constructor.params.len()); - let acc_pos = if constructor.accessibility.is_some() { - NodePos::Str(ctx.str_field(AstProp::Accessibility)) - } else { - NodePos::Undef(ctx.undefined_field(AstProp::Accessibility)) - }; - let member_id = ctx.commit_schema(raw); - - // FIXME flags - - let key = serialize_prop_name(ctx, &constructor.key, member_id); - let body = constructor - .body - .as_ref() - .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id)); - - let params = constructor - .params - .iter() - .map(|param| match param { - ParamOrTsParamProp::TsParamProp(_) => { - todo!() - } - ParamOrTsParamProp::Param(param) => { - serialize_pat(ctx, ¶m.pat, member_id) - } - }) - .collect::>(); - - if let Some(acc) = constructor.accessibility { - if let NodePos::Str(str_pos) = acc_pos { - ctx.write_str(str_pos, &accessibility_to_str(acc)); - } - } - - ctx.write_ref(key_pos, key); - ctx.write_maybe_ref(body_pos, body); - // FIXME - ctx.write_refs(args_pos, params); - - member_id - } - ClassMember::Method(method) => { - let raw = ctx.header(AstNode::MethodDefinition, parent, &method.span); - - let member_id = ctx.commit_schema(raw); - - // let mut flags = FlagValue::new(); - // flags.set(Flag::ClassMethod); - if method.function.is_async { - // FIXME - } - - // accessibility_to_flag(&mut flags, method.accessibility); - - let _key_id = serialize_prop_name(ctx, &method.key, member_id); - - let _body_id = - method.function.body.as_ref().map(|body| { - serialize_stmt(ctx, &Stmt::Block(body.clone()), member_id) - }); - - let _params = method - .function - .params - .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, member_id)) - .collect::>(); - - // ctx.write_node(member_id, ); - // ctx.write_flags(&flags); - // ctx.write_id(key_id); - // ctx.write_id(body_id); - // ctx.write_ids(AstProp::Params, params); - - member_id - } - ClassMember::PrivateMethod(_) => todo!(), - ClassMember::ClassProp(_) => todo!(), - ClassMember::PrivateProp(_) => todo!(), - ClassMember::TsIndexSignature(member) => { - serialize_ts_index_sig(ctx, member, parent) - } - ClassMember::Empty(_) => unreachable!(), - ClassMember::StaticBlock(_) => todo!(), - ClassMember::AutoAccessor(_) => todo!(), - } + ctx.write_member_expr(&node.span, optional, computed, obj, prop) } fn serialize_expr_or_spread( ctx: &mut TsEsTreeBuilder, arg: &ExprOrSpread, - parent: NodeRef, ) -> NodeRef { if let Some(spread) = &arg.spread { - serialize_spread(ctx, &arg.expr, spread, parent) + serialize_spread(ctx, &arg.expr, spread) } else { - serialize_expr(ctx, arg.expr.as_ref(), parent) + serialize_expr(ctx, arg.expr.as_ref()) } } fn serialize_ident( ctx: &mut TsEsTreeBuilder, ident: &Ident, - parent: NodeRef, + type_ann: Option, ) -> NodeRef { - let raw = ctx.header(AstNode::Identifier, parent, &ident.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, ident.sym.as_str()); - - pos + ctx.write_identifier( + &ident.span, + ident.sym.as_str(), + ident.optional, + type_ann, + ) } -fn serialize_module_exported_name( +fn serialize_module_export_name( ctx: &mut TsEsTreeBuilder, name: &ModuleExportName, - parent: NodeRef, ) -> NodeRef { match &name { - ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, parent), - ModuleExportName::Str(lit) => { - serialize_lit(ctx, &Lit::Str(lit.clone()), parent) - } + ModuleExportName::Ident(ident) => serialize_ident(ctx, ident, None), + ModuleExportName::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone())), } } -fn serialize_decl( - ctx: &mut TsEsTreeBuilder, - decl: &Decl, - parent: NodeRef, -) -> NodeRef { +fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { match decl { Decl::Class(node) => { - let raw = ctx.header(AstNode::ClassDeclaration, parent, &node.class.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let abstract_pos = ctx.bool_field(AstProp::Abstract); - let id_pos = ctx.ref_field(AstProp::Id); - let body_pos = ctx.ref_field(AstProp::Body); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let super_pos = ctx.ref_field(AstProp::SuperClass); - let super_type_pos = ctx.ref_field(AstProp::SuperTypeArguments); - let impl_pos = - ctx.ref_vec_field(AstProp::Implements, node.class.implements.len()); - let id = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::ClassBody, id, &node.class.span); - let body_body_pos = - ctx.ref_vec_field(AstProp::Body, node.class.body.len()); - let body_id = ctx.commit_schema(body_raw); - - let ident = serialize_ident(ctx, &node.ident, id); - let type_params = - maybe_serialize_ts_type_param(ctx, &node.class.type_params, id); + let ident = serialize_ident(ctx, &node.ident, None); let super_class = node .class .super_class .as_ref() - .map(|super_class| serialize_expr(ctx, super_class, id)); + .map(|expr| serialize_expr(ctx, expr.as_ref())); - let super_type_params = node - .class - .super_type_params - .as_ref() - .map(|super_params| serialize_ts_param_inst(ctx, super_params, id)); - - let implement_ids = node + let implements = node .class .implements .iter() - .map(|implements| { - let raw = - ctx.header(AstNode::TSClassImplements, id, &implements.span); - - let expr_pos = ctx.ref_field(AstProp::Expression); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let child_pos = ctx.commit_schema(raw); - - let type_args = implements - .type_args - .clone() - .map(|args| serialize_ts_param_inst(ctx, &args, child_pos)); - - let expr = serialize_expr(ctx, &implements.expr, child_pos); - - ctx.write_ref(expr_pos, expr); - ctx.write_maybe_ref(type_args_pos, type_args); - - child_pos - }) + .map(|item| serialize_ts_expr_with_type_args(ctx, item)) .collect::>(); - let member_ids = node + let members = node .class .body .iter() - .map(|member| serialize_class_member(ctx, member, parent)) + .filter_map(|member| serialize_class_member(ctx, member)) .collect::>(); - ctx.write_ref(body_pos, body_id); + let body = ctx.write_class_body(&node.class.span, members); - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(abstract_pos, node.class.is_abstract); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(type_params_pos, type_params); - ctx.write_maybe_ref(super_pos, super_class); - ctx.write_maybe_ref(super_type_pos, super_type_params); - ctx.write_refs(impl_pos, implement_ids); - - // body - ctx.write_refs(body_body_pos, member_ids); - - id + ctx.write_class_decl( + &node.class.span, + false, + node.class.is_abstract, + Some(ident), + super_class, + implements, + body, + ) } Decl::Fn(node) => { - let raw = - ctx.header(AstNode::FunctionDeclaration, parent, &node.function.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let async_pos = ctx.bool_field(AstProp::Async); - let gen_pos = ctx.bool_field(AstProp::Generator); - let id_pos = ctx.ref_field(AstProp::Id); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let body_pos = ctx.ref_field(AstProp::Body); - let params_pos = - ctx.ref_vec_field(AstProp::Params, node.function.params.len()); - let pos = ctx.commit_schema(raw); - - let ident_id = serialize_ident(ctx, &node.ident, parent); + let ident_id = serialize_ident(ctx, &node.ident, None); let type_param_id = - maybe_serialize_ts_type_param(ctx, &node.function.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.function.type_params); let return_type = - maybe_serialize_ts_type_ann(ctx, &node.function.return_type, pos); + maybe_serialize_ts_type_ann(ctx, &node.function.return_type); let body = node .function .body .as_ref() - .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()), pos)); + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); let params = node .function .params .iter() - .map(|param| serialize_pat(ctx, ¶m.pat, pos)) + .map(|param| serialize_pat(ctx, ¶m.pat)) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(async_pos, node.function.is_async); - ctx.write_bool(gen_pos, node.function.is_generator); - ctx.write_ref(id_pos, ident_id); - ctx.write_maybe_ref(type_params_pos, type_param_id); - ctx.write_maybe_ref(return_pos, return_type); - ctx.write_maybe_ref(body_pos, body); - ctx.write_refs(params_pos, params); - - pos + ctx.write_fn_decl( + &node.function.span, + node.declare, + node.function.is_async, + node.function.is_generator, + Some(ident_id), + type_param_id, + return_type, + body, + params, + ) } Decl::Var(node) => { - let raw = ctx.header(AstNode::VariableDeclaration, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let kind_pos = ctx.str_field(AstProp::Kind); - let decls_pos = - ctx.ref_vec_field(AstProp::Declarations, node.decls.len()); - let id = ctx.commit_schema(raw); + let children = node + .decls + .iter() + .map(|decl| { + let ident = serialize_pat(ctx, &decl.name); + let init = decl + .init + .as_ref() + .map(|init| serialize_expr(ctx, init.as_ref())); + + ctx.write_var_declarator(&decl.span, ident, init) + }) + .collect::>(); + + let kind = match node.kind { + VarDeclKind::Var => "var", + VarDeclKind::Let => "let", + VarDeclKind::Const => "const", + }; + + ctx.write_var_decl(&node.span, node.declare, kind, children) + } + Decl::Using(node) => { + let kind = if node.is_await { + "await using" + } else { + "using" + }; let children = node .decls .iter() .map(|decl| { - let raw = ctx.header(AstNode::VariableDeclarator, id, &decl.span); - let id_pos = ctx.ref_field(AstProp::Id); - let init_pos = ctx.ref_field(AstProp::Init); - let child_id = ctx.commit_schema(raw); - - // FIXME: Definite? - - let ident = serialize_pat(ctx, &decl.name, child_id); - + let ident = serialize_pat(ctx, &decl.name); let init = decl .init .as_ref() - .map(|init| serialize_expr(ctx, init.as_ref(), child_id)); + .map(|init| serialize_expr(ctx, init.as_ref())); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(init_pos, init); - - child_id + ctx.write_var_declarator(&decl.span, ident, init) }) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_str( - kind_pos, - match node.kind { - VarDeclKind::Var => "var", - VarDeclKind::Let => "let", - VarDeclKind::Const => "const", - }, - ); - ctx.write_refs(decls_pos, children); - - id - } - Decl::Using(_) => { - todo!(); + ctx.write_var_decl(&node.span, false, kind, children) } Decl::TsInterface(node) => { - let raw = ctx.header(AstNode::TSInterface, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let id_pos = ctx.ref_field(AstProp::Id); - let extends_pos = ctx.ref_vec_field(AstProp::Extends, node.extends.len()); - let type_param_pos = ctx.ref_field(AstProp::TypeParameters); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::TSInterfaceBody, pos, &node.body.span); - let body_body_pos = - ctx.ref_vec_field(AstProp::Body, node.body.body.len()); - let body_id = ctx.commit_schema(body_raw); - - let ident_id = serialize_ident(ctx, &node.id, pos); + let ident_id = serialize_ident(ctx, &node.id, None); let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); let extend_ids = node .extends .iter() .map(|item| { - let raw = ctx.header(AstNode::TSInterfaceHeritage, pos, &item.span); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let expr_pos = ctx.ref_field(AstProp::Expression); - let child_pos = ctx.commit_schema(raw); + let expr = serialize_expr(ctx, &item.expr); + let type_args = item + .type_args + .clone() + .map(|params| serialize_ts_param_inst(ctx, params.as_ref())); - let expr = serialize_expr(ctx, &item.expr, child_pos); - let type_args = item.type_args.clone().map(|params| { - serialize_ts_param_inst(ctx, params.as_ref(), child_pos) - }); - - ctx.write_ref(expr_pos, expr); - ctx.write_maybe_ref(type_args_pos, type_args); - - child_pos + ctx.write_ts_interface_heritage(&item.span, expr, type_args) }) .collect::>(); @@ -1747,269 +1356,211 @@ fn serialize_decl( .body .body .iter() - .map(|item| match item { - TsTypeElement::TsCallSignatureDecl(ts_call) => { - let raw = ctx.header( - AstNode::TsCallSignatureDeclaration, - pos, - &ts_call.span, - ); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let params_pos = - ctx.ref_vec_field(AstProp::Params, ts_call.params.len()); - let return_pos = ctx.ref_field(AstProp::ReturnType); - let item_id = ctx.commit_schema(raw); - - let type_param = - maybe_serialize_ts_type_param(ctx, &ts_call.type_params, pos); - let return_type = - maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann, pos); - let params = ts_call - .params - .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) - .collect::>(); - - ctx.write_maybe_ref(type_ann_pos, type_param); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_pos, return_type); - - item_id - } - TsTypeElement::TsConstructSignatureDecl(_) => todo!(), - TsTypeElement::TsPropertySignature(sig) => { - let raw = ctx.header(AstNode::TSPropertySignature, pos, &sig.span); - - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let key_pos = ctx.ref_field(AstProp::Key); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, &sig.key, item_pos); - let type_ann = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, sig.computed); - ctx.write_bool(optional_pos, sig.optional); - ctx.write_bool(readonly_pos, sig.readonly); - ctx.write_ref(key_pos, key); - ctx.write_maybe_ref(type_ann_pos, type_ann); - - item_pos - } - TsTypeElement::TsGetterSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let return_type = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "getter"); - ctx.write_maybe_ref(return_type_pos, return_type); - ctx.write_ref(key_pos, key); - - item_pos - } - TsTypeElement::TsSetterSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let params_pos = ctx.ref_vec_field(AstProp::Params, 1); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let params = serialize_ts_fn_param(ctx, &sig.param, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "setter"); - ctx.write_ref(key_pos, key); - ctx.write_refs(params_pos, vec![params]); - - item_pos - } - TsTypeElement::TsMethodSignature(sig) => { - let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - let computed_pos = ctx.bool_field(AstProp::Computed); - let optional_pos = ctx.bool_field(AstProp::Optional); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let _static_bos = ctx.bool_field(AstProp::Static); - let kind_pos = ctx.str_field(AstProp::Kind); - let key_pos = ctx.ref_field(AstProp::Key); - let params_pos = - ctx.ref_vec_field(AstProp::Params, sig.params.len()); - let return_type_pos = ctx.ref_field(AstProp::ReturnType); - let item_pos = ctx.commit_schema(raw); - - let key = serialize_expr(ctx, sig.key.as_ref(), item_pos); - let params = sig - .params - .iter() - .map(|param| serialize_ts_fn_param(ctx, param, item_pos)) - .collect::>(); - let return_type = - maybe_serialize_ts_type_ann(ctx, &sig.type_ann, item_pos); - - ctx.write_bool(computed_pos, false); - ctx.write_bool(optional_pos, false); - ctx.write_bool(readonly_pos, false); - ctx.write_str(kind_pos, "method"); - ctx.write_ref(key_pos, key); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(return_type_pos, return_type); - - item_pos - } - TsTypeElement::TsIndexSignature(sig) => { - serialize_ts_index_sig(ctx, sig, pos) - } - }) + .map(|item| serialize_ts_type_elem(ctx, item)) .collect::>(); - ctx.write_bool(declare_pos, node.declare); - ctx.write_ref(id_pos, ident_id); - ctx.write_maybe_ref(type_param_pos, type_param); - ctx.write_refs(extends_pos, extend_ids); - ctx.write_ref(body_pos, body_id); - - // Body - ctx.write_refs(body_body_pos, body_elem_ids); - - pos + let body_pos = + ctx.write_ts_interface_body(&node.body.span, body_elem_ids); + ctx.write_ts_interface( + &node.span, + node.declare, + ident_id, + type_param, + extend_ids, + body_pos, + ) } Decl::TsTypeAlias(node) => { - let raw = ctx.header(AstNode::TsTypeAlias, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let id_pos = ctx.ref_field(AstProp::Id); - let type_params_pos = ctx.ref_field(AstProp::TypeParameters); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let ident = serialize_ident(ctx, &node.id, pos); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); + let ident = serialize_ident(ctx, &node.id, None); + let type_ann = serialize_ts_type(ctx, &node.type_ann); let type_param = - maybe_serialize_ts_type_param(ctx, &node.type_params, pos); + maybe_serialize_ts_type_param_decl(ctx, &node.type_params); - ctx.write_bool(declare_pos, node.declare); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(type_params_pos, type_param); - ctx.write_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_type_alias( + &node.span, + node.declare, + ident, + type_param, + type_ann, + ) } Decl::TsEnum(node) => { - let raw = ctx.header(AstNode::TSEnumDeclaration, parent, &node.span); - let declare_pos = ctx.bool_field(AstProp::Declare); - let const_pos = ctx.bool_field(AstProp::Const); - let id_pos = ctx.ref_field(AstProp::Id); - let body_pos = ctx.ref_field(AstProp::Body); - let pos = ctx.commit_schema(raw); - - let body_raw = ctx.header(AstNode::TSEnumBody, pos, &node.span); - let members_pos = ctx.ref_vec_field(AstProp::Members, node.members.len()); - let body = ctx.commit_schema(body_raw); - - let ident_id = serialize_ident(ctx, &node.id, parent); + let id = serialize_ident(ctx, &node.id, None); let members = node .members .iter() .map(|member| { - let raw = ctx.header(AstNode::TSEnumMember, body, &member.span); - let id_pos = ctx.ref_field(AstProp::Id); - let init_pos = ctx.ref_field(AstProp::Initializer); - let member_id = ctx.commit_schema(raw); - let ident = match &member.id { - TsEnumMemberId::Ident(ident) => { - serialize_ident(ctx, ident, member_id) - } + TsEnumMemberId::Ident(ident) => serialize_ident(ctx, ident, None), TsEnumMemberId::Str(lit_str) => { - serialize_lit(ctx, &Lit::Str(lit_str.clone()), member_id) + serialize_lit(ctx, &Lit::Str(lit_str.clone())) } }; - let init = member - .init - .as_ref() - .map(|init| serialize_expr(ctx, init, member_id)); + let init = member.init.as_ref().map(|init| serialize_expr(ctx, init)); - ctx.write_ref(id_pos, ident); - ctx.write_maybe_ref(init_pos, init); - - member_id + ctx.write_ts_enum_member(&member.span, ident, init) }) .collect::>(); - ctx.write_refs(members_pos, members); - - ctx.write_bool(declare_pos, node.declare); - ctx.write_bool(const_pos, node.is_const); - ctx.write_ref(id_pos, ident_id); - ctx.write_ref(body_pos, body); - - pos + let body = ctx.write_ts_enum_body(&node.span, members); + ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body) } - Decl::TsModule(ts_module_decl) => { - let raw = ctx.header(AstNode::TsModule, parent, &ts_module_decl.span); - ctx.commit_schema(raw) + Decl::TsModule(node) => { + let ident = match &node.id { + TsModuleName::Ident(ident) => serialize_ident(ctx, ident, None), + TsModuleName::Str(str_lit) => { + serialize_lit(ctx, &Lit::Str(str_lit.clone())) + } + }; + + let body = node + .body + .as_ref() + .map(|body| serialize_ts_namespace_body(ctx, body)); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + body, + ) } } } +fn serialize_ts_namespace_body( + ctx: &mut TsEsTreeBuilder, + node: &TsNamespaceBody, +) -> NodeRef { + match node { + TsNamespaceBody::TsModuleBlock(mod_block) => { + let items = mod_block + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(decl) => serialize_module_decl(ctx, decl), + ModuleItem::Stmt(stmt) => serialize_stmt(ctx, stmt), + }) + .collect::>(); + + ctx.write_ts_module_block(&mod_block.span, items) + } + TsNamespaceBody::TsNamespaceDecl(node) => { + let ident = serialize_ident(ctx, &node.id, None); + let body = serialize_ts_namespace_body(ctx, &node.body); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + Some(body), + ) + } + } +} + +fn serialize_ts_type_elem( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeElement, +) -> NodeRef { + match node { + TsTypeElement::TsCallSignatureDecl(ts_call) => { + let type_ann = + maybe_serialize_ts_type_param_decl(ctx, &ts_call.type_params); + let return_type = maybe_serialize_ts_type_ann(ctx, &ts_call.type_ann); + let params = ts_call + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + ctx.write_ts_call_sig_decl(&ts_call.span, type_ann, params, return_type) + } + TsTypeElement::TsConstructSignatureDecl(sig) => { + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + // Must be present + let return_type = + maybe_serialize_ts_type_ann(ctx, &sig.type_ann).unwrap(); + + ctx.write_ts_construct_sig(&sig.span, type_params, params, return_type) + } + TsTypeElement::TsPropertySignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_ann = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_property_sig( + &sig.span, + sig.computed, + sig.optional, + sig.readonly, + key, + type_ann, + ) + } + TsTypeElement::TsGetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_getter_sig(&sig.span, key, return_type) + } + TsTypeElement::TsSetterSignature(sig) => { + let key = serialize_expr(ctx, sig.key.as_ref()); + let param = serialize_ts_fn_param(ctx, &sig.param); + + ctx.write_ts_setter_sig(&sig.span, key, param) + } + TsTypeElement::TsMethodSignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_parms = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); + + ctx.write_ts_method_sig( + &sig.span, + sig.computed, + sig.optional, + key, + type_parms, + params, + return_type, + ) + } + TsTypeElement::TsIndexSignature(sig) => serialize_ts_index_sig(ctx, sig), + } +} + fn serialize_ts_index_sig( ctx: &mut TsEsTreeBuilder, node: &TsIndexSignature, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSMethodSignature, parent, &node.span); - let readonly_pos = ctx.bool_field(AstProp::Readonly); - // TODO: where is this coming from? - let static_pos = ctx.bool_field(AstProp::Static); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); - let params = node .params .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .map(|param| serialize_ts_fn_param(ctx, param)) .collect::>(); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); - ctx.write_bool(readonly_pos, false); - ctx.write_bool(static_pos, node.is_static); - ctx.write_refs(params_pos, params); - ctx.write_maybe_ref(type_ann_pos, type_ann); - - pos + ctx.write_ts_index_sig(&node.span, node.readonly, params, type_ann) } -fn accessibility_to_str(accessibility: Accessibility) -> String { +fn accessibility_to_str(accessibility: &Accessibility) -> String { match accessibility { Accessibility::Public => "public".to_string(), Accessibility::Protected => "protected".to_string(), @@ -2020,106 +1571,53 @@ fn accessibility_to_str(accessibility: Accessibility) -> String { fn serialize_private_name( ctx: &mut TsEsTreeBuilder, node: &PrivateName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::PrivateIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, node.name.as_str()); - - pos + ctx.write_private_identifier(&node.span, node.name.as_str()) } fn serialize_jsx_element( ctx: &mut TsEsTreeBuilder, node: &JSXElement, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXElement, parent, &node.span); - let open_pos = ctx.ref_field(AstProp::OpeningElement); - let close_pos = ctx.ref_field(AstProp::ClosingElement); - let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); - let pos = ctx.commit_schema(raw); - - let open = serialize_jsx_opening_element(ctx, &node.opening, pos); + let open = serialize_jsx_opening_element(ctx, &node.opening); let close = node.closing.as_ref().map(|closing| { - let raw = ctx.header(AstNode::JSXClosingElement, pos, &closing.span); - let name_pos = ctx.ref_field(AstProp::Name); - let closing_pos = ctx.commit_schema(raw); - - let name = serialize_jsx_element_name(ctx, &closing.name, closing_pos); - ctx.write_ref(name_pos, name); - - closing_pos + let name = serialize_jsx_element_name(ctx, &closing.name); + ctx.write_jsx_closing_elem(&closing.span, name) }); - let children = serialize_jsx_children(ctx, &node.children, pos); + let children = serialize_jsx_children(ctx, &node.children); - ctx.write_ref(open_pos, open); - ctx.write_maybe_ref(close_pos, close); - ctx.write_refs(children_pos, children); - - pos + ctx.write_jsx_elem(&node.span, open, close, children) } fn serialize_jsx_fragment( ctx: &mut TsEsTreeBuilder, node: &JSXFragment, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXFragment, parent, &node.span); + let opening = ctx.write_jsx_opening_frag(&node.opening.span); + let closing = ctx.write_jsx_closing_frag(&node.closing.span); + let children = serialize_jsx_children(ctx, &node.children); - let opening_pos = ctx.ref_field(AstProp::OpeningFragment); - let closing_pos = ctx.ref_field(AstProp::ClosingFragment); - let children_pos = ctx.ref_vec_field(AstProp::Children, node.children.len()); - let pos = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::JSXOpeningFragment, pos, &node.opening.span); - let opening_id = ctx.commit_schema(raw); - - let raw = ctx.header(AstNode::JSXClosingFragment, pos, &node.closing.span); - let closing_id = ctx.commit_schema(raw); - - let children = serialize_jsx_children(ctx, &node.children, pos); - - ctx.write_ref(opening_pos, opening_id); - ctx.write_ref(closing_pos, closing_id); - ctx.write_refs(children_pos, children); - - pos + ctx.write_jsx_frag(&node.span, opening, closing, children) } fn serialize_jsx_children( ctx: &mut TsEsTreeBuilder, children: &[JSXElementChild], - parent: NodeRef, ) -> Vec { children .iter() .map(|child| { match child { JSXElementChild::JSXText(text) => { - let raw = ctx.header(AstNode::JSXText, parent, &text.span); - let raw_pos = ctx.str_field(AstProp::Raw); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - - ctx.write_str(raw_pos, &text.raw); - ctx.write_str(value_pos, &text.value); - - pos + ctx.write_jsx_text(&text.span, &text.raw, &text.value) } JSXElementChild::JSXExprContainer(container) => { - serialize_jsx_container_expr(ctx, container, parent) - } - JSXElementChild::JSXElement(el) => { - serialize_jsx_element(ctx, el, parent) - } - JSXElementChild::JSXFragment(frag) => { - serialize_jsx_fragment(ctx, frag, parent) + serialize_jsx_container_expr(ctx, container) } + JSXElementChild::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXElementChild::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), // No parser supports this JSXElementChild::JSXSpreadChild(_) => unreachable!(), } @@ -2130,42 +1628,28 @@ fn serialize_jsx_children( fn serialize_jsx_member_expr( ctx: &mut TsEsTreeBuilder, node: &JSXMemberExpr, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXMemberExpression, parent, &node.span); - let obj_ref = ctx.ref_field(AstProp::Object); - let prop_ref = ctx.ref_field(AstProp::Property); - let pos = ctx.commit_schema(raw); - let obj = match &node.obj { - JSXObject::JSXMemberExpr(member) => { - serialize_jsx_member_expr(ctx, member, pos) - } - JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident, parent), + JSXObject::JSXMemberExpr(member) => serialize_jsx_member_expr(ctx, member), + JSXObject::Ident(ident) => serialize_jsx_identifier(ctx, ident), }; - let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop, pos); + let prop = serialize_ident_name_as_jsx_identifier(ctx, &node.prop); - ctx.write_ref(obj_ref, obj); - ctx.write_ref(prop_ref, prop); - - pos + ctx.write_jsx_member_expr(&node.span, obj, prop) } fn serialize_jsx_element_name( ctx: &mut TsEsTreeBuilder, node: &JSXElementName, - parent: NodeRef, ) -> NodeRef { match &node { - JSXElementName::Ident(ident) => { - serialize_jsx_identifier(ctx, ident, parent) - } + JSXElementName::Ident(ident) => serialize_jsx_identifier(ctx, ident), JSXElementName::JSXMemberExpr(member) => { - serialize_jsx_member_expr(ctx, member, parent) + serialize_jsx_member_expr(ctx, member) } JSXElementName::JSXNamespacedName(ns) => { - serialize_jsx_namespaced_name(ctx, ns, parent) + serialize_jsx_namespaced_name(ctx, ns) } } } @@ -2173,294 +1657,183 @@ fn serialize_jsx_element_name( fn serialize_jsx_opening_element( ctx: &mut TsEsTreeBuilder, node: &JSXOpeningElement, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXOpeningElement, parent, &node.span); - let sclose_pos = ctx.bool_field(AstProp::SelfClosing); - let name_pos = ctx.ref_field(AstProp::Name); - let attrs_pos = ctx.ref_vec_field(AstProp::Attributes, node.attrs.len()); - let pos = ctx.commit_schema(raw); + let name = serialize_jsx_element_name(ctx, &node.name); - let name = serialize_jsx_element_name(ctx, &node.name, pos); - - // FIXME: type args + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); let attrs = node .attrs .iter() .map(|attr| match attr { JSXAttrOrSpread::JSXAttr(attr) => { - let raw = ctx.header(AstNode::JSXAttribute, pos, &attr.span); - let name_pos = ctx.ref_field(AstProp::Name); - let value_pos = ctx.ref_field(AstProp::Value); - let attr_pos = ctx.commit_schema(raw); - let name = match &attr.name { JSXAttrName::Ident(name) => { - serialize_ident_name_as_jsx_identifier(ctx, name, attr_pos) + serialize_ident_name_as_jsx_identifier(ctx, name) } JSXAttrName::JSXNamespacedName(node) => { - serialize_jsx_namespaced_name(ctx, node, attr_pos) + serialize_jsx_namespaced_name(ctx, node) } }; let value = attr.value.as_ref().map(|value| match value { - JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit, attr_pos), + JSXAttrValue::Lit(lit) => serialize_lit(ctx, lit), JSXAttrValue::JSXExprContainer(container) => { - serialize_jsx_container_expr(ctx, container, attr_pos) - } - JSXAttrValue::JSXElement(el) => { - serialize_jsx_element(ctx, el, attr_pos) - } - JSXAttrValue::JSXFragment(frag) => { - serialize_jsx_fragment(ctx, frag, attr_pos) + serialize_jsx_container_expr(ctx, container) } + JSXAttrValue::JSXElement(el) => serialize_jsx_element(ctx, el), + JSXAttrValue::JSXFragment(frag) => serialize_jsx_fragment(ctx, frag), }); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(value_pos, value); - - attr_pos + ctx.write_jsx_attr(&attr.span, name, value) } JSXAttrOrSpread::SpreadElement(spread) => { - let raw = ctx.header(AstNode::JSXAttribute, pos, &spread.dot3_token); - let arg_pos = ctx.ref_field(AstProp::Argument); - let attr_pos = ctx.commit_schema(raw); - - let arg = serialize_expr(ctx, &spread.expr, attr_pos); - - ctx.write_ref(arg_pos, arg); - - attr_pos + let arg = serialize_expr(ctx, &spread.expr); + ctx.write_jsx_spread_attr(&spread.dot3_token, arg) } }) .collect::>(); - ctx.write_bool(sclose_pos, node.self_closing); - ctx.write_ref(name_pos, name); - ctx.write_refs(attrs_pos, attrs); - - pos + ctx.write_jsx_opening_elem( + &node.span, + node.self_closing, + name, + attrs, + type_args, + ) } fn serialize_jsx_container_expr( ctx: &mut TsEsTreeBuilder, node: &JSXExprContainer, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXExpressionContainer, parent, &node.span); - let expr_pos = ctx.ref_field(AstProp::Expression); - let pos = ctx.commit_schema(raw); - let expr = match &node.expr { - JSXExpr::JSXEmptyExpr(expr) => serialize_jsx_empty_expr(ctx, expr, pos), - JSXExpr::Expr(expr) => serialize_expr(ctx, expr, pos), + JSXExpr::JSXEmptyExpr(expr) => ctx.write_jsx_empty_expr(&expr.span), + JSXExpr::Expr(expr) => serialize_expr(ctx, expr), }; - ctx.write_ref(expr_pos, expr); - - pos -} - -fn serialize_jsx_empty_expr( - ctx: &mut TsEsTreeBuilder, - node: &JSXEmptyExpr, - parent: NodeRef, -) -> NodeRef { - let raw = ctx.header(AstNode::JSXEmptyExpression, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_jsx_expr_container(&node.span, expr) } fn serialize_jsx_namespaced_name( ctx: &mut TsEsTreeBuilder, node: &JSXNamespacedName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXNamespacedName, parent, &node.span); - let ns_pos = ctx.ref_field(AstProp::Namespace); - let name_pos = ctx.ref_field(AstProp::Name); - let pos = ctx.commit_schema(raw); + let ns = ctx.write_jsx_identifier(&node.ns.span, &node.ns.sym); + let name = ctx.write_jsx_identifier(&node.name.span, &node.name.sym); - let ns_id = serialize_ident_name_as_jsx_identifier(ctx, &node.ns, pos); - let name_id = serialize_ident_name_as_jsx_identifier(ctx, &node.name, pos); - - ctx.write_ref(ns_pos, ns_id); - ctx.write_ref(name_pos, name_id); - - pos + ctx.write_jsx_namespaced_name(&node.span, ns, name) } fn serialize_ident_name_as_jsx_identifier( ctx: &mut TsEsTreeBuilder, node: &IdentName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, &node.sym); - - pos + ctx.write_jsx_identifier(&node.span, &node.sym) } fn serialize_jsx_identifier( ctx: &mut TsEsTreeBuilder, node: &Ident, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::JSXIdentifier, parent, &node.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, &node.sym); - - pos + ctx.write_jsx_identifier(&node.span, &node.sym) } -fn serialize_pat( - ctx: &mut TsEsTreeBuilder, - pat: &Pat, - parent: NodeRef, -) -> NodeRef { +fn serialize_pat(ctx: &mut TsEsTreeBuilder, pat: &Pat) -> NodeRef { match pat { - Pat::Ident(node) => serialize_ident(ctx, &node.id, parent), + Pat::Ident(node) => serialize_binding_ident(ctx, node), Pat::Array(node) => { - let raw = ctx.header(AstNode::ArrayPattern, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let type_pos = ctx.ref_field(AstProp::TypeAnnotation); - let elems_pos = ctx.ref_vec_field(AstProp::Elements, node.elems.len()); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); let children = node .elems .iter() - .map(|pat| { - pat - .as_ref() - .map_or(NodeRef(0), |v| serialize_pat(ctx, v, pos)) - }) + .map(|pat| pat.as_ref().map_or(NodeRef(0), |v| serialize_pat(ctx, v))) .collect::>(); - ctx.write_bool(opt_pos, node.optional); - ctx.write_maybe_ref(type_pos, type_ann); - ctx.write_refs(elems_pos, children); - - pos + ctx.write_arr_pat(&node.span, node.optional, type_ann, children) } Pat::Rest(node) => { - let raw = ctx.header(AstNode::RestElement, parent, &node.span); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + let arg = serialize_pat(ctx, &node.arg); - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); - let arg = serialize_pat(ctx, &node.arg, parent); - - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_ref(arg_pos, arg); - - pos + ctx.write_rest_elem(&node.span, type_ann, arg) } Pat::Object(node) => { - let raw = ctx.header(AstNode::ObjectPattern, parent, &node.span); - let opt_pos = ctx.bool_field(AstProp::Optional); - let props_pos = ctx.ref_vec_field(AstProp::Properties, node.props.len()); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); let children = node .props .iter() .map(|prop| match prop { ObjectPatProp::KeyValue(key_value_prop) => { - let raw = - ctx.header(AstNode::Property, pos, &key_value_prop.span()); - let computed_pos = ctx.bool_field(AstProp::Computed); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let child_pos = ctx.commit_schema(raw); - let computed = matches!(key_value_prop.key, PropName::Computed(_)); - let key = serialize_prop_name(ctx, &key_value_prop.key, child_pos); - let value = - serialize_pat(ctx, key_value_prop.value.as_ref(), child_pos); + let key = serialize_prop_name(ctx, &key_value_prop.key); + let value = serialize_pat(ctx, key_value_prop.value.as_ref()); - ctx.write_bool(computed_pos, computed); - ctx.write_ref(key_pos, key); - ctx.write_ref(value_pos, value); - - child_pos + ctx.write_property( + &key_value_prop.span(), + false, + computed, + false, + "init", + key, + value, + ) } ObjectPatProp::Assign(assign_pat_prop) => { - let raw = ctx.header(AstNode::Property, pos, &assign_pat_prop.span); - // TOOD: Doesn't seem to be present in SWC ast - let _computed_pos = ctx.bool_field(AstProp::Computed); - let key_pos = ctx.ref_field(AstProp::Key); - let value_pos = ctx.ref_field(AstProp::Value); - let child_pos = ctx.commit_schema(raw); - - let ident = serialize_ident(ctx, &assign_pat_prop.key.id, parent); + let ident = serialize_binding_ident(ctx, &assign_pat_prop.key); let value = assign_pat_prop .value .as_ref() - .map(|value| serialize_expr(ctx, value, child_pos)); + .map_or(NodeRef(0), |value| serialize_expr(ctx, value)); - ctx.write_ref(key_pos, ident); - ctx.write_maybe_ref(value_pos, value); - - child_pos + ctx.write_property( + &assign_pat_prop.span, + false, + false, + false, + "init", + ident, + value, + ) } ObjectPatProp::Rest(rest_pat) => { - serialize_pat(ctx, &Pat::Rest(rest_pat.clone()), parent) + serialize_pat(ctx, &Pat::Rest(rest_pat.clone())) } }) .collect::>(); - ctx.write_bool(opt_pos, node.optional); - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_refs(props_pos, children); - - pos + ctx.write_obj_pat(&node.span, node.optional, type_ann, children) } Pat::Assign(node) => { - let raw = ctx.header(AstNode::AssignmentPattern, parent, &node.span); - let left_pos = ctx.ref_field(AstProp::Left); - let right_pos = ctx.ref_field(AstProp::Right); - let pos = ctx.commit_schema(raw); + let left = serialize_pat(ctx, &node.left); + let right = serialize_expr(ctx, &node.right); - let left = serialize_pat(ctx, &node.left, pos); - let right = serialize_expr(ctx, &node.right, pos); - - ctx.write_ref(left_pos, left); - ctx.write_ref(right_pos, right); - - pos + ctx.write_assign_pat(&node.span, left, right) } Pat::Invalid(_) => unreachable!(), - Pat::Expr(node) => serialize_expr(ctx, node, parent), + Pat::Expr(node) => serialize_expr(ctx, node), } } fn serialize_for_head( ctx: &mut TsEsTreeBuilder, for_head: &ForHead, - parent: NodeRef, ) -> NodeRef { match for_head { ForHead::VarDecl(var_decl) => { - serialize_decl(ctx, &Decl::Var(var_decl.clone()), parent) + serialize_decl(ctx, &Decl::Var(var_decl.clone())) } ForHead::UsingDecl(using_decl) => { - serialize_decl(ctx, &Decl::Using(using_decl.clone()), parent) + serialize_decl(ctx, &Decl::Using(using_decl.clone())) } - ForHead::Pat(pat) => serialize_pat(ctx, pat, parent), + ForHead::Pat(pat) => serialize_pat(ctx, pat), } } @@ -2468,471 +1841,634 @@ fn serialize_spread( ctx: &mut TsEsTreeBuilder, expr: &Expr, span: &Span, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::SpreadElement, parent, span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let pos = ctx.commit_schema(raw); - - let expr_pos = serialize_expr(ctx, expr, parent); - ctx.write_ref(arg_pos, expr_pos); - - pos + let expr = serialize_expr(ctx, expr); + ctx.write_spread(span, expr) } fn serialize_ident_name( ctx: &mut TsEsTreeBuilder, ident_name: &IdentName, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::Identifier, parent, &ident_name.span); - let name_pos = ctx.str_field(AstProp::Name); - let pos = ctx.commit_schema(raw); - - ctx.write_str(name_pos, ident_name.sym.as_str()); - - pos + ctx.write_identifier(&ident_name.span, ident_name.sym.as_str(), false, None) } fn serialize_prop_name( ctx: &mut TsEsTreeBuilder, prop_name: &PropName, - parent: NodeRef, ) -> NodeRef { match prop_name { - PropName::Ident(ident_name) => { - serialize_ident_name(ctx, ident_name, parent) - } - PropName::Str(str_prop) => { - let raw = ctx.header(AstNode::StringLiteral, parent, &str_prop.span); - let value_pos = ctx.str_field(AstProp::Value); - ctx.write_str(value_pos, &str_prop.value); - ctx.commit_schema(raw) - } - PropName::Num(number) => { - serialize_lit(ctx, &Lit::Num(number.clone()), parent) - } - PropName::Computed(node) => serialize_expr(ctx, &node.expr, parent), + PropName::Ident(ident_name) => serialize_ident_name(ctx, ident_name), + PropName::Str(str_prop) => serialize_lit(ctx, &Lit::Str(str_prop.clone())), + PropName::Num(number) => serialize_lit(ctx, &Lit::Num(number.clone())), + PropName::Computed(node) => serialize_expr(ctx, &node.expr), PropName::BigInt(big_int) => { - serialize_lit(ctx, &Lit::BigInt(big_int.clone()), parent) + serialize_lit(ctx, &Lit::BigInt(big_int.clone())) } } } -fn serialize_lit( - ctx: &mut TsEsTreeBuilder, - lit: &Lit, - parent: NodeRef, -) -> NodeRef { +fn serialize_lit(ctx: &mut TsEsTreeBuilder, lit: &Lit) -> NodeRef { match lit { Lit::Str(node) => { - let raw = ctx.header(AstNode::StringLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; - ctx.write_str(value_pos, &node.value); - - pos - } - Lit::Bool(lit_bool) => { - let raw = ctx.header(AstNode::Bool, parent, &lit_bool.span); - let value_pos = ctx.bool_field(AstProp::Value); - let pos = ctx.commit_schema(raw); - - ctx.write_bool(value_pos, lit_bool.value); - - pos - } - Lit::Null(node) => { - let raw = ctx.header(AstNode::Null, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_str_lit(&node.span, &node.value, &raw_value) } + Lit::Bool(node) => ctx.write_bool_lit(&node.span, node.value), + Lit::Null(node) => ctx.write_null_lit(&node.span), Lit::Num(node) => { - let raw = ctx.header(AstNode::NumericLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; let value = node.raw.as_ref().unwrap(); - ctx.write_str(value_pos, value); - - pos + ctx.write_num_lit(&node.span, value, &raw_value) } Lit::BigInt(node) => { - let raw = ctx.header(AstNode::BigIntLiteral, parent, &node.span); - let value_pos = ctx.str_field(AstProp::Value); - let pos = ctx.commit_schema(raw); + let raw_bigint_value = if let Some(v) = &node.raw { + let mut s = v.to_string(); + s.pop(); + s.to_string() + } else { + format!("{}", node.value).to_string() + }; - ctx.write_str(value_pos, &node.value.to_string()); + let raw_value = if let Some(v) = &node.raw { + v.to_string() + } else { + format!("{}", node.value).to_string() + }; - pos + ctx.write_bigint_lit( + &node.span, + &node.value.to_string(), + &raw_value, + &raw_bigint_value, + ) } Lit::Regex(node) => { - let raw = ctx.header(AstNode::RegExpLiteral, parent, &node.span); - let pattern_pos = ctx.str_field(AstProp::Pattern); - let flags_pos = ctx.str_field(AstProp::Flags); - let pos = ctx.commit_schema(raw); + let raw = format!("/{}/{}", node.exp.as_str(), node.flags.as_str()); - ctx.write_str(pattern_pos, node.exp.as_str()); - ctx.write_str(flags_pos, node.flags.as_str()); - - pos + ctx.write_regex_lit( + &node.span, + node.exp.as_str(), + node.flags.as_str(), + &raw, + &raw, + ) } - Lit::JSXText(jsxtext) => { - let raw = ctx.header(AstNode::JSXText, parent, &jsxtext.span); - ctx.commit_schema(raw) + Lit::JSXText(node) => { + ctx.write_jsx_text(&node.span, &node.raw, &node.value) } } } +fn serialize_class_member( + ctx: &mut TsEsTreeBuilder, + member: &ClassMember, +) -> Option { + match member { + ClassMember::Constructor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let params = node + .params + .iter() + .map(|param| match param { + ParamOrTsParamProp::TsParamProp(prop) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = prop + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let paramter = match &prop.param { + TsParamPropParam::Ident(binding_ident) => { + serialize_binding_ident(ctx, binding_ident) + } + TsParamPropParam::Assign(assign_pat) => { + serialize_pat(ctx, &Pat::Assign(assign_pat.clone())) + } + }; + + ctx.write_ts_param_prop( + &prop.span, + prop.is_override, + prop.readonly, + a11y, + decorators, + paramter, + ) + } + ParamOrTsParamProp::Param(param) => serialize_pat(ctx, ¶m.pat), + }) + .collect::>(); + + let body = node + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = ctx.write_fn_expr( + &node.span, false, false, None, None, params, None, body, + ); + + Some(ctx.write_class_method( + &node.span, + false, + false, + node.is_optional, + false, + false, + "constructor", + a11y, + key, + value, + )) + } + ClassMember::Method(node) => { + let key = serialize_prop_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::PrivateMethod(node) => { + let key = serialize_private_name(ctx, &node.key); + + Some(serialize_class_method( + ctx, + &node.span, + node.is_abstract, + node.is_override, + node.is_optional, + node.is_static, + node.accessibility, + &node.kind, + key, + &node.function, + )) + } + ClassMember::ClassProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let key = serialize_prop_name(ctx, &node.key); + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + Some(ctx.write_class_prop( + &node.span, + node.declare, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::PrivateProp(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = serialize_private_name(ctx, &node.key); + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_class_prop( + &node.span, + false, + false, + node.is_optional, + node.is_override, + node.readonly, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + ClassMember::TsIndexSignature(node) => { + Some(serialize_ts_index_sig(ctx, node)) + } + ClassMember::Empty(_) => None, + ClassMember::StaticBlock(node) => { + let body = serialize_stmt(ctx, &Stmt::Block(node.body.clone())); + Some(ctx.write_static_block(&node.span, body)) + } + ClassMember::AutoAccessor(node) => { + let a11y = node.accessibility.as_ref().map(accessibility_to_str); + let decorators = node + .decorators + .iter() + .map(|deco| serialize_decorator(ctx, deco)) + .collect::>(); + + let key = match &node.key { + Key::Private(private_name) => serialize_private_name(ctx, private_name), + Key::Public(prop_name) => serialize_prop_name(ctx, prop_name), + }; + + let value = node.value.as_ref().map(|expr| serialize_expr(ctx, expr)); + + Some(ctx.write_accessor_property( + &node.span, + false, + false, + false, + node.is_override, + false, + node.is_static, + a11y, + decorators, + key, + value, + )) + } + } +} + +#[allow(clippy::too_many_arguments)] +fn serialize_class_method( + ctx: &mut TsEsTreeBuilder, + span: &Span, + is_abstract: bool, + is_override: bool, + is_optional: bool, + is_static: bool, + accessibility: Option, + method_kind: &MethodKind, + key: NodeRef, + function: &Function, +) -> NodeRef { + let kind = match method_kind { + MethodKind::Method => "method", + MethodKind::Getter => "getter", + MethodKind::Setter => "setter", + }; + + let type_params = + maybe_serialize_ts_type_param_decl(ctx, &function.type_params); + let params = function + .params + .iter() + .map(|param| serialize_pat(ctx, ¶m.pat)) + .collect::>(); + + let return_type = maybe_serialize_ts_type_ann(ctx, &function.return_type); + + let body = function + .body + .as_ref() + .map(|body| serialize_stmt(ctx, &Stmt::Block(body.clone()))); + + let value = if let Some(body) = body { + ctx.write_fn_expr( + &function.span, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + Some(body), + ) + } else { + ctx.write_ts_empty_body_fn_expr( + span, + false, + false, + function.is_async, + function.is_generator, + None, + type_params, + params, + return_type, + ) + }; + + let a11y = accessibility.as_ref().map(accessibility_to_str); + + if is_abstract { + ctx.write_ts_abstract_method_def( + span, + false, + is_optional, + is_override, + false, + a11y, + key, + value, + ) + } else { + ctx.write_class_method( + span, + false, + false, + is_optional, + is_override, + is_static, + kind, + a11y, + key, + value, + ) + } +} + +fn serialize_ts_expr_with_type_args( + ctx: &mut TsEsTreeBuilder, + node: &TsExprWithTypeArgs, +) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); + + ctx.write_ts_class_implements(&node.span, expr, type_args) +} + +fn serialize_decorator(ctx: &mut TsEsTreeBuilder, node: &Decorator) -> NodeRef { + let expr = serialize_expr(ctx, &node.expr); + ctx.write_decorator(&node.span, expr) +} + +fn serialize_binding_ident( + ctx: &mut TsEsTreeBuilder, + node: &BindingIdent, +) -> NodeRef { + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + ctx.write_identifier(&node.span, &node.sym, node.optional, type_ann) +} + fn serialize_ts_param_inst( ctx: &mut TsEsTreeBuilder, node: &TsTypeParamInstantiation, - parent: NodeRef, ) -> NodeRef { - let raw = - ctx.header(AstNode::TSTypeParameterInstantiation, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); - let params = node .params .iter() - .map(|param| serialize_ts_type(ctx, param, pos)) + .map(|param| serialize_ts_type(ctx, param)) .collect::>(); - ctx.write_refs(params_pos, params); - - pos + ctx.write_ts_type_param_inst(&node.span, params) } -fn serialize_ts_type( - ctx: &mut TsEsTreeBuilder, - node: &TsType, - parent: NodeRef, -) -> NodeRef { +fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { match node { TsType::TsKeywordType(node) => { let kind = match node.kind { - TsKeywordTypeKind::TsAnyKeyword => AstNode::TSAnyKeyword, - TsKeywordTypeKind::TsUnknownKeyword => AstNode::TSUnknownKeyword, - TsKeywordTypeKind::TsNumberKeyword => AstNode::TSNumberKeyword, - TsKeywordTypeKind::TsObjectKeyword => AstNode::TSObjectKeyword, - TsKeywordTypeKind::TsBooleanKeyword => AstNode::TSBooleanKeyword, - TsKeywordTypeKind::TsBigIntKeyword => AstNode::TSBigIntKeyword, - TsKeywordTypeKind::TsStringKeyword => AstNode::TSStringKeyword, - TsKeywordTypeKind::TsSymbolKeyword => AstNode::TSSymbolKeyword, - TsKeywordTypeKind::TsVoidKeyword => AstNode::TSVoidKeyword, - TsKeywordTypeKind::TsUndefinedKeyword => AstNode::TSUndefinedKeyword, - TsKeywordTypeKind::TsNullKeyword => AstNode::TSNullKeyword, - TsKeywordTypeKind::TsNeverKeyword => AstNode::TSNeverKeyword, - TsKeywordTypeKind::TsIntrinsicKeyword => AstNode::TSIntrinsicKeyword, + TsKeywordTypeKind::TsAnyKeyword => TsKeywordKind::Any, + TsKeywordTypeKind::TsUnknownKeyword => TsKeywordKind::Unknown, + TsKeywordTypeKind::TsNumberKeyword => TsKeywordKind::Number, + TsKeywordTypeKind::TsObjectKeyword => TsKeywordKind::Object, + TsKeywordTypeKind::TsBooleanKeyword => TsKeywordKind::Boolean, + TsKeywordTypeKind::TsBigIntKeyword => TsKeywordKind::BigInt, + TsKeywordTypeKind::TsStringKeyword => TsKeywordKind::String, + TsKeywordTypeKind::TsSymbolKeyword => TsKeywordKind::Symbol, + TsKeywordTypeKind::TsVoidKeyword => TsKeywordKind::Void, + TsKeywordTypeKind::TsUndefinedKeyword => TsKeywordKind::Undefined, + TsKeywordTypeKind::TsNullKeyword => TsKeywordKind::Null, + TsKeywordTypeKind::TsNeverKeyword => TsKeywordKind::Never, + TsKeywordTypeKind::TsIntrinsicKeyword => TsKeywordKind::Intrinsic, }; - let raw = ctx.header(kind, parent, &node.span); - ctx.commit_schema(raw) - } - TsType::TsThisType(node) => { - let raw = ctx.header(AstNode::TSThisType, parent, &node.span); - ctx.commit_schema(raw) + ctx.write_ts_keyword(kind, &node.span) } + TsType::TsThisType(node) => ctx.write_ts_this_type(&node.span), TsType::TsFnOrConstructorType(node) => match node { TsFnOrConstructorType::TsFnType(node) => { - let raw = ctx.header(AstNode::TSFunctionType, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); - let param_ids = node .params .iter() - .map(|param| serialize_ts_fn_param(ctx, param, pos)) + .map(|param| serialize_ts_fn_param(ctx, param)) .collect::>(); - ctx.write_refs(params_pos, param_ids); - - pos + ctx.write_ts_fn_type(&node.span, param_ids) } - TsFnOrConstructorType::TsConstructorType(_) => { - todo!() + TsFnOrConstructorType::TsConstructorType(node) => { + // interface Foo { new(arg1: any): any } + let type_params = node + .type_params + .as_ref() + .map(|param| serialize_ts_type_param_decl(ctx, param)); + + let params = node + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + + let return_type = serialize_ts_type_ann(ctx, node.type_ann.as_ref()); + + ctx.write_ts_construct_sig(&node.span, type_params, params, return_type) } }, TsType::TsTypeRef(node) => { - let raw = ctx.header(AstNode::TSTypeReference, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::TypeName); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - - let name = serialize_ts_entity_name(ctx, &node.type_name, pos); + let name = serialize_ts_entity_name(ctx, &node.type_name); let type_args = node .type_params .clone() - .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + .map(|param| serialize_ts_param_inst(ctx, ¶m)); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(type_args_pos, type_args); - - pos + ctx.write_ts_type_ref(&node.span, name, type_args) } TsType::TsTypeQuery(node) => { - let raw = ctx.header(AstNode::TSTypeQuery, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::ExprName); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let pos = ctx.commit_schema(raw); - let expr_name = match &node.expr_name { TsTypeQueryExpr::TsEntityName(entity) => { - serialize_ts_entity_name(ctx, entity, pos) + serialize_ts_entity_name(ctx, entity) } TsTypeQueryExpr::Import(child) => { - serialize_ts_type(ctx, &TsType::TsImportType(child.clone()), pos) + serialize_ts_type(ctx, &TsType::TsImportType(child.clone())) } }; let type_args = node .type_args .clone() - .map(|param| serialize_ts_param_inst(ctx, ¶m, pos)); + .map(|param| serialize_ts_param_inst(ctx, ¶m)); - ctx.write_ref(name_pos, expr_name); - ctx.write_maybe_ref(type_args_pos, type_args); - - pos + ctx.write_ts_type_query(&node.span, expr_name, type_args) } - TsType::TsTypeLit(_) => { - // TODO: Not sure what this is - todo!() + TsType::TsTypeLit(node) => { + let members = node + .members + .iter() + .map(|member| serialize_ts_type_elem(ctx, member)) + .collect::>(); + + ctx.write_ts_type_lit(&node.span, members) } TsType::TsArrayType(node) => { - let raw = ctx.header(AstNode::TSArrayType, parent, &node.span); - let elem_pos = ctx.ref_field(AstProp::ElementType); - let pos = ctx.commit_schema(raw); - - let elem = serialize_ts_type(ctx, &node.elem_type, pos); - - ctx.write_ref(elem_pos, elem); - - pos + let elem = serialize_ts_type(ctx, &node.elem_type); + ctx.write_ts_array_type(&node.span, elem) } TsType::TsTupleType(node) => { - let raw = ctx.header(AstNode::TSTupleType, parent, &node.span); - let children_pos = - ctx.ref_vec_field(AstProp::ElementTypes, node.elem_types.len()); - let pos = ctx.commit_schema(raw); - let children = node .elem_types .iter() .map(|elem| { if let Some(label) = &elem.label { - let raw = ctx.header(AstNode::TSNamedTupleMember, pos, &elem.span); - let label_pos = ctx.ref_field(AstProp::Label); - let type_pos = ctx.ref_field(AstProp::ElementType); - let child_pos = ctx.commit_schema(raw); + let label = serialize_pat(ctx, label); + let type_id = serialize_ts_type(ctx, elem.ty.as_ref()); - let label_id = serialize_pat(ctx, label, child_pos); - let type_id = serialize_ts_type(ctx, elem.ty.as_ref(), child_pos); - - ctx.write_ref(label_pos, label_id); - ctx.write_ref(type_pos, type_id); - - child_pos + ctx.write_ts_named_tuple_member(&elem.span, label, type_id) } else { - serialize_ts_type(ctx, elem.ty.as_ref(), pos) + serialize_ts_type(ctx, elem.ty.as_ref()) } }) .collect::>(); - ctx.write_refs(children_pos, children); - - pos + ctx.write_ts_tuple_type(&node.span, children) + } + TsType::TsOptionalType(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_optional_type(&node.span, type_ann) } - TsType::TsOptionalType(_) => todo!(), TsType::TsRestType(node) => { - let raw = ctx.header(AstNode::TSRestType, parent, &node.span); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(type_ann_pos, type_ann); - - pos + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_rest_type(&node.span, type_ann) } TsType::TsUnionOrIntersectionType(node) => match node { TsUnionOrIntersectionType::TsUnionType(node) => { - let raw = ctx.header(AstNode::TSUnionType, parent, &node.span); - let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); - let pos = ctx.commit_schema(raw); - let children = node .types .iter() - .map(|item| serialize_ts_type(ctx, item, pos)) + .map(|item| serialize_ts_type(ctx, item)) .collect::>(); - ctx.write_refs(types_pos, children); - - pos + ctx.write_ts_union_type(&node.span, children) } TsUnionOrIntersectionType::TsIntersectionType(node) => { - let raw = ctx.header(AstNode::TSIntersectionType, parent, &node.span); - let types_pos = ctx.ref_vec_field(AstProp::Types, node.types.len()); - let pos = ctx.commit_schema(raw); - let children = node .types .iter() - .map(|item| serialize_ts_type(ctx, item, pos)) + .map(|item| serialize_ts_type(ctx, item)) .collect::>(); - ctx.write_refs(types_pos, children); - - pos + ctx.write_ts_intersection_type(&node.span, children) } }, TsType::TsConditionalType(node) => { - let raw = ctx.header(AstNode::TSConditionalType, parent, &node.span); - let check_pos = ctx.ref_field(AstProp::CheckType); - let extends_pos = ctx.ref_field(AstProp::ExtendsType); - let true_pos = ctx.ref_field(AstProp::TrueType); - let false_pos = ctx.ref_field(AstProp::FalseType); - let pos = ctx.commit_schema(raw); + let check = serialize_ts_type(ctx, &node.check_type); + let extends = serialize_ts_type(ctx, &node.extends_type); + let v_true = serialize_ts_type(ctx, &node.true_type); + let v_false = serialize_ts_type(ctx, &node.false_type); - let check = serialize_ts_type(ctx, &node.check_type, pos); - let extends = serialize_ts_type(ctx, &node.extends_type, pos); - let v_true = serialize_ts_type(ctx, &node.true_type, pos); - let v_false = serialize_ts_type(ctx, &node.false_type, pos); - - ctx.write_ref(check_pos, check); - ctx.write_ref(extends_pos, extends); - ctx.write_ref(true_pos, v_true); - ctx.write_ref(false_pos, v_false); - - pos + ctx.write_ts_conditional_type(&node.span, check, extends, v_true, v_false) } TsType::TsInferType(node) => { - let raw = ctx.header(AstNode::TSInferType, parent, &node.span); - let param_pos = ctx.ref_field(AstProp::TypeParameter); - let pos = ctx.commit_schema(raw); - - let param = serialize_ts_type_param(ctx, &node.type_param, parent); - - ctx.write_ref(param_pos, param); - - pos + let param = serialize_ts_type_param(ctx, &node.type_param); + ctx.write_ts_infer_type(&node.span, param) + } + TsType::TsParenthesizedType(node) => { + // Not materialized in TSEstree + serialize_ts_type(ctx, &node.type_ann) } - TsType::TsParenthesizedType(_) => todo!(), TsType::TsTypeOperator(node) => { - let raw = ctx.header(AstNode::TSTypeOperator, parent, &node.span); - let operator_pos = ctx.str_field(AstProp::Operator); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); + let type_ann = serialize_ts_type(ctx, &node.type_ann); - let type_ann = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_str( - operator_pos, - match node.op { - TsTypeOperatorOp::KeyOf => "keyof", - TsTypeOperatorOp::Unique => "unique", - TsTypeOperatorOp::ReadOnly => "readonly", - }, - ); - ctx.write_ref(type_ann_pos, type_ann); - - pos - } - TsType::TsIndexedAccessType(node) => { - let raw = ctx.header(AstNode::TSIndexedAccessType, parent, &node.span); - let index_type_pos = ctx.ref_field(AstProp::IndexType); - let obj_type_pos = ctx.ref_field(AstProp::ObjectType); - let pos = ctx.commit_schema(raw); - - let index = serialize_ts_type(ctx, &node.index_type, pos); - let obj = serialize_ts_type(ctx, &node.obj_type, pos); - - ctx.write_ref(index_type_pos, index); - ctx.write_ref(obj_type_pos, obj); - - pos - } - TsType::TsMappedType(node) => { - let raw = ctx.header(AstNode::TSMappedType, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::NameType); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let type_param_pos = ctx.ref_field(AstProp::TypeParameter); - let pos = ctx.commit_schema(raw); - - let opt_pos = - create_true_plus_minus_field(ctx, AstProp::Optional, node.optional); - let readonly_pos = - create_true_plus_minus_field(ctx, AstProp::Readonly, node.readonly); - - let name_id = maybe_serialize_ts_type(ctx, &node.name_type, pos); - let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann, pos); - let type_param = serialize_ts_type_param(ctx, &node.type_param, pos); - - write_true_plus_minus(ctx, opt_pos, node.optional); - write_true_plus_minus(ctx, readonly_pos, node.readonly); - ctx.write_maybe_ref(name_pos, name_id); - ctx.write_maybe_ref(type_ann_pos, type_ann); - ctx.write_ref(type_param_pos, type_param); - - pos - } - TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node, parent), - TsType::TsTypePredicate(node) => { - let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); - let asserts_pos = ctx.bool_field(AstProp::Asserts); - let param_name_pos = ctx.ref_field(AstProp::ParameterName); - let type_ann_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let param_name = match &node.param_name { - TsThisTypeOrIdent::TsThisType(ts_this_type) => { - let raw = ctx.header(AstNode::TSThisType, pos, &ts_this_type.span); - ctx.commit_schema(raw) - } - TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, pos), + let op = match node.op { + TsTypeOperatorOp::KeyOf => "keyof", + TsTypeOperatorOp::Unique => "unique", + TsTypeOperatorOp::ReadOnly => "readonly", }; - let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann, pos); + ctx.write_ts_type_op(&node.span, op, type_ann) + } + TsType::TsIndexedAccessType(node) => { + let index = serialize_ts_type(ctx, &node.index_type); + let obj = serialize_ts_type(ctx, &node.obj_type); - ctx.write_bool(asserts_pos, node.asserts); - ctx.write_ref(param_name_pos, param_name); - ctx.write_maybe_ref(type_ann_pos, type_ann); + ctx.write_ts_indexed_access_type(&node.span, index, obj) + } + TsType::TsMappedType(node) => { + let name = maybe_serialize_ts_type(ctx, &node.name_type); + let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann); + let type_param = serialize_ts_type_param(ctx, &node.type_param); - pos + ctx.write_ts_mapped_type( + &node.span, + node.readonly, + node.optional, + name, + type_ann, + type_param, + ) + } + TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node), + TsType::TsTypePredicate(node) => { + let param_name = match &node.param_name { + TsThisTypeOrIdent::TsThisType(node) => { + ctx.write_ts_this_type(&node.span) + } + TsThisTypeOrIdent::Ident(ident) => serialize_ident(ctx, ident, None), + }; + + let type_ann = maybe_serialize_ts_type_ann(ctx, &node.type_ann); + + ctx.write_ts_type_predicate( + &node.span, + node.asserts, + param_name, + type_ann, + ) } TsType::TsImportType(node) => { - let raw = ctx.header(AstNode::TSTypePredicate, parent, &node.span); - let arg_pos = ctx.ref_field(AstProp::Argument); - let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - let qualifier_pos = ctx.ref_field(AstProp::Qualifier); - let pos = ctx.commit_schema(raw); - let arg = serialize_ts_lit_type( ctx, &TsLitType { lit: TsLit::Str(node.arg.clone()), span: node.arg.span, }, - pos, ); - let type_arg = node.type_args.clone().map(|param_node| { - serialize_ts_param_inst(ctx, param_node.as_ref(), pos) - }); + let type_arg = node + .type_args + .clone() + .map(|param_node| serialize_ts_param_inst(ctx, param_node.as_ref())); - let qualifier = node.qualifier.clone().map_or(NodeRef(0), |quali| { - serialize_ts_entity_name(ctx, &quali, pos) - }); + let qualifier = node + .qualifier + .clone() + .map(|quali| serialize_ts_entity_name(ctx, &quali)); - ctx.write_ref(arg_pos, arg); - ctx.write_ref(qualifier_pos, qualifier); - ctx.write_maybe_ref(type_args_pos, type_arg); - - pos + ctx.write_ts_import_type(&node.span, arg, qualifier, type_arg) } } } @@ -2940,80 +2476,47 @@ fn serialize_ts_type( fn serialize_ts_lit_type( ctx: &mut TsEsTreeBuilder, node: &TsLitType, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSLiteralType, parent, &node.span); - let lit_pos = ctx.ref_field(AstProp::Literal); - let pos = ctx.commit_schema(raw); - - let lit = match &node.lit { - TsLit::Number(lit) => serialize_lit(ctx, &Lit::Num(lit.clone()), pos), - TsLit::Str(lit) => serialize_lit(ctx, &Lit::Str(lit.clone()), pos), - TsLit::Bool(lit) => serialize_lit(ctx, &Lit::Bool(*lit), pos), - TsLit::BigInt(lit) => serialize_lit(ctx, &Lit::BigInt(lit.clone()), pos), - TsLit::Tpl(lit) => serialize_expr( - ctx, - &Expr::Tpl(Tpl { - span: lit.span, - exprs: vec![], - quasis: lit.quasis.clone(), - }), - pos, - ), - }; - - ctx.write_ref(lit_pos, lit); - - pos -} - -fn create_true_plus_minus_field( - ctx: &mut TsEsTreeBuilder, - prop: AstProp, - value: Option, -) -> NodePos { - if let Some(v) = value { - match v { - TruePlusMinus::True => NodePos::Bool(ctx.bool_field(prop)), - TruePlusMinus::Plus | TruePlusMinus::Minus => { - NodePos::Str(ctx.str_field(prop)) - } + match &node.lit { + TsLit::Number(lit) => { + let lit = serialize_lit(ctx, &Lit::Num(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) } - } else { - NodePos::Undef(ctx.undefined_field(prop)) - } -} + TsLit::Str(lit) => { + let lit = serialize_lit(ctx, &Lit::Str(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Bool(lit) => { + let lit = serialize_lit(ctx, &Lit::Bool(*lit)); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::BigInt(lit) => { + let lit = serialize_lit(ctx, &Lit::BigInt(lit.clone())); + ctx.write_ts_lit_type(&node.span, lit) + } + TsLit::Tpl(lit) => { + let quasis = lit + .quasis + .iter() + .map(|quasi| { + ctx.write_template_elem( + &quasi.span, + quasi.tail, + &quasi.raw, + &quasi + .cooked + .as_ref() + .map_or("".to_string(), |v| v.to_string()), + ) + }) + .collect::>(); + let types = lit + .types + .iter() + .map(|ts_type| serialize_ts_type(ctx, ts_type)) + .collect::>(); -fn extract_pos(pos: NodePos) -> usize { - match pos { - NodePos::Bool(bool_pos) => bool_pos.0, - NodePos::Field(field_pos) => field_pos.0, - NodePos::FieldArr(field_arr_pos) => field_arr_pos.0, - NodePos::Str(str_pos) => str_pos.0, - NodePos::Undef(undef_pos) => undef_pos.0, - NodePos::Null(null_pos) => null_pos.0, - } -} - -fn write_true_plus_minus( - ctx: &mut TsEsTreeBuilder, - pos: NodePos, - value: Option, -) { - if let Some(v) = value { - match v { - TruePlusMinus::True => { - let bool_pos = BoolPos(extract_pos(pos)); - ctx.write_bool(bool_pos, true); - } - TruePlusMinus::Plus => { - let str_pos = StrPos(extract_pos(pos)); - ctx.write_str(str_pos, "+") - } - TruePlusMinus::Minus => { - let str_pos = StrPos(extract_pos(pos)); - ctx.write_str(str_pos, "-") - } + ctx.write_ts_tpl_lit(&node.span, quasis, types) } } } @@ -3021,114 +2524,91 @@ fn write_true_plus_minus( fn serialize_ts_entity_name( ctx: &mut TsEsTreeBuilder, node: &TsEntityName, - parent: NodeRef, ) -> NodeRef { match &node { - TsEntityName::TsQualifiedName(_) => todo!(), - TsEntityName::Ident(ident) => serialize_ident(ctx, ident, parent), + TsEntityName::TsQualifiedName(node) => { + let left = serialize_ts_entity_name(ctx, &node.left); + let right = serialize_ident_name(ctx, &node.right); + + ctx.write_ts_qualified_name(&node.span, left, right) + } + TsEntityName::Ident(ident) => serialize_ident(ctx, ident, None), } } fn maybe_serialize_ts_type_ann( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { node .as_ref() - .map(|type_ann| serialize_ts_type_ann(ctx, type_ann, parent)) + .map(|type_ann| serialize_ts_type_ann(ctx, type_ann)) } fn serialize_ts_type_ann( ctx: &mut TsEsTreeBuilder, node: &TsTypeAnn, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSTypeAnnotation, parent, &node.span); - let type_pos = ctx.ref_field(AstProp::TypeAnnotation); - let pos = ctx.commit_schema(raw); - - let v_type = serialize_ts_type(ctx, &node.type_ann, pos); - - ctx.write_ref(type_pos, v_type); - - pos + let v_type = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_type_ann(&node.span, v_type) } fn maybe_serialize_ts_type( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { - node - .as_ref() - .map(|item| serialize_ts_type(ctx, item, parent)) + node.as_ref().map(|item| serialize_ts_type(ctx, item)) } fn serialize_ts_type_param( ctx: &mut TsEsTreeBuilder, node: &TsTypeParam, - parent: NodeRef, ) -> NodeRef { - let raw = ctx.header(AstNode::TSTypeParameter, parent, &node.span); - let name_pos = ctx.ref_field(AstProp::Name); - let constraint_pos = ctx.ref_field(AstProp::Constraint); - let default_pos = ctx.ref_field(AstProp::Default); - let const_pos = ctx.bool_field(AstProp::Const); - let in_pos = ctx.bool_field(AstProp::In); - let out_pos = ctx.bool_field(AstProp::Out); - let pos = ctx.commit_schema(raw); + let name = serialize_ident(ctx, &node.name, None); + let constraint = maybe_serialize_ts_type(ctx, &node.constraint); + let default = maybe_serialize_ts_type(ctx, &node.default); - let name = serialize_ident(ctx, &node.name, pos); - let constraint = maybe_serialize_ts_type(ctx, &node.constraint, pos); - let default = maybe_serialize_ts_type(ctx, &node.default, pos); - - ctx.write_bool(const_pos, node.is_const); - ctx.write_bool(in_pos, node.is_in); - ctx.write_bool(out_pos, node.is_out); - ctx.write_ref(name_pos, name); - ctx.write_maybe_ref(constraint_pos, constraint); - ctx.write_maybe_ref(default_pos, default); - - pos + ctx.write_ts_type_param( + &node.span, + node.is_in, + node.is_out, + node.is_const, + name, + constraint, + default, + ) } -fn maybe_serialize_ts_type_param( +fn maybe_serialize_ts_type_param_decl( ctx: &mut TsEsTreeBuilder, node: &Option>, - parent: NodeRef, ) -> Option { - node.as_ref().map(|node| { - let raw = - ctx.header(AstNode::TSTypeParameterDeclaration, parent, &node.span); - let params_pos = ctx.ref_vec_field(AstProp::Params, node.params.len()); - let pos = ctx.commit_schema(raw); + node + .as_ref() + .map(|node| serialize_ts_type_param_decl(ctx, node)) +} - let params = node - .params - .iter() - .map(|param| serialize_ts_type_param(ctx, param, pos)) - .collect::>(); +fn serialize_ts_type_param_decl( + ctx: &mut TsEsTreeBuilder, + node: &TsTypeParamDecl, +) -> NodeRef { + let params = node + .params + .iter() + .map(|param| serialize_ts_type_param(ctx, param)) + .collect::>(); - ctx.write_refs(params_pos, params); - - pos - }) + ctx.write_ts_type_param_decl(&node.span, params) } fn serialize_ts_fn_param( ctx: &mut TsEsTreeBuilder, node: &TsFnParam, - parent: NodeRef, ) -> NodeRef { match node { - TsFnParam::Ident(ident) => serialize_ident(ctx, ident, parent), - TsFnParam::Array(pat) => { - serialize_pat(ctx, &Pat::Array(pat.clone()), parent) - } - TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone()), parent), - TsFnParam::Object(pat) => { - serialize_pat(ctx, &Pat::Object(pat.clone()), parent) - } + TsFnParam::Ident(ident) => serialize_binding_ident(ctx, ident), + TsFnParam::Array(pat) => serialize_pat(ctx, &Pat::Array(pat.clone())), + TsFnParam::Rest(pat) => serialize_pat(ctx, &Pat::Rest(pat.clone())), + TsFnParam::Object(pat) => serialize_pat(ctx, &Pat::Object(pat.clone())), } } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 967bbef32a..340f9f3225 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -5,22 +5,17 @@ use std::fmt::Debug; use std::fmt::Display; use deno_ast::swc::common::Span; +use deno_ast::view::TruePlusMinus; use super::buffer::AstBufSerializer; -use super::buffer::BoolPos; -use super::buffer::FieldArrPos; -use super::buffer::FieldPos; use super::buffer::NodeRef; -use super::buffer::NullPos; -use super::buffer::PendingNodeRef; use super::buffer::SerializeCtx; -use super::buffer::StrPos; -use super::buffer::UndefPos; #[derive(Debug, Clone, PartialEq)] pub enum AstNode { // First node must always be the empty/invalid node Invalid, + RefArray, // Typically the Program, @@ -29,17 +24,27 @@ pub enum AstNode { ExportDefaultDeclaration, ExportNamedDeclaration, ImportDeclaration, - TsExportAssignment, - TsImportEquals, - TsNamespaceExport, + ImportSpecifier, + ImportAttribute, + ImportDefaultSpecifier, + ImportNamespaceSpecifier, + TSExportAssignment, + TSImportEqualss, + TSNamespaceExport, + TSNamespaceExportDeclaration, + TSImportEqualsDeclaration, + TSExternalModuleReference, + TSModuleDeclaration, + TSModuleBlock, // Decls ClassDeclaration, FunctionDeclaration, TSEnumDeclaration, TSInterface, - TsModule, - TsTypeAlias, + TSInterfaceDeclaration, + TSModule, + TSTypeAliasDeclaration, Using, VariableDeclaration, @@ -74,12 +79,13 @@ pub enum AstNode { ChainExpression, ClassExpression, ConditionalExpression, + EmptyExpr, FunctionExpression, Identifier, ImportExpression, LogicalExpression, MemberExpression, - MetaProp, + MetaProperty, NewExpression, ObjectExpression, PrivateIdentifier, @@ -89,8 +95,6 @@ pub enum AstNode { TemplateLiteral, ThisExpression, TSAsExpression, - TsConstAssertion, - TsInstantiation, TSNonNullExpression, TSSatisfiesExpression, TSTypeAssertion, @@ -98,16 +102,8 @@ pub enum AstNode { UpdateExpression, YieldExpression, - // TODO: TSEsTree uses a single literal node - // Literals - StringLiteral, - Bool, - Null, - NumericLiteral, - BigIntLiteral, - RegExpLiteral, - - EmptyExpr, + // Other + Literal, SpreadElement, Property, VariableDeclarator, @@ -117,6 +113,10 @@ pub enum AstNode { TemplateElement, MethodDefinition, ClassBody, + PropertyDefinition, + Decorator, + StaticBlock, + AccessorProperty, // Patterns ArrayPattern, @@ -150,6 +150,7 @@ pub enum AstNode { TSTypeReference, TSThisType, TSLiteralType, + TSTypeLiteral, TSInferType, TSConditionalType, TSUnionType, @@ -159,7 +160,7 @@ pub enum AstNode { TSTupleType, TSNamedTupleMember, TSFunctionType, - TsCallSignatureDeclaration, + TSCallSignatureDeclaration, TSPropertySignature, TSMethodSignature, TSIndexSignature, @@ -170,6 +171,13 @@ pub enum AstNode { TSRestType, TSArrayType, TSClassImplements, + TSAbstractMethodDefinition, + TSEmptyBodyFunctionExpression, + TSParameterProperty, + TSConstructSignatureDeclaration, + TSQualifiedName, + TSOptionalType, + TSTemplateLiteralType, TSAnyKeyword, TSBigIntKeyword, @@ -219,6 +227,7 @@ pub enum AstProp { Async, Attributes, Await, + BigInt, Block, Body, Callee, @@ -235,6 +244,7 @@ pub enum AstProp { Declaration, Declarations, Declare, + Decorators, Default, Definite, Delegate, @@ -246,12 +256,14 @@ pub enum AstProp { Expression, Expressions, Exported, + ExportKind, Extends, ExtendsType, FalseType, Finalizer, Flags, Generator, + Global, Handler, Id, In, @@ -259,6 +271,8 @@ pub enum AstProp { Init, Initializer, Implements, + Imported, + ImportKind, Key, Kind, Label, @@ -268,6 +282,7 @@ pub enum AstProp { Members, Meta, Method, + ModuleReference, Name, Namespace, NameType, @@ -277,8 +292,12 @@ pub enum AstProp { OpeningFragment, Operator, Optional, + Options, Out, + Override, Param, + Parameter, + Parameters, ParameterName, Params, Pattern, @@ -290,6 +309,7 @@ pub enum AstProp { Quasis, Raw, Readonly, + Regex, ReturnType, Right, SelfClosing, @@ -314,8 +334,6 @@ pub enum AstProp { Value, // Last value is used for max value } -// TODO: Feels like there should be an easier way to iterater over an -// enum in Rust and lowercase the first letter. impl Display for AstProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { @@ -333,6 +351,7 @@ impl Display for AstProp { AstProp::Async => "async", AstProp::Attributes => "attributes", AstProp::Await => "await", + AstProp::BigInt => "bigint", AstProp::Block => "block", AstProp::Body => "body", AstProp::Callee => "callee", @@ -349,6 +368,7 @@ impl Display for AstProp { AstProp::Declaration => "declaration", AstProp::Declarations => "declarations", AstProp::Declare => "declare", + AstProp::Decorators => "decorators", AstProp::Default => "default", AstProp::Definite => "definite", AstProp::Delegate => "delegate", @@ -359,6 +379,7 @@ impl Display for AstProp { AstProp::ExprName => "exprName", AstProp::Expression => "expression", AstProp::Expressions => "expressions", + AstProp::ExportKind => "exportKind", AstProp::Exported => "exported", AstProp::Extends => "extends", AstProp::ExtendsType => "extendsType", @@ -366,6 +387,7 @@ impl Display for AstProp { AstProp::Finalizer => "finalizer", AstProp::Flags => "flags", AstProp::Generator => "generator", + AstProp::Global => "global", AstProp::Handler => "handler", AstProp::Id => "id", AstProp::In => "in", @@ -373,6 +395,8 @@ impl Display for AstProp { AstProp::Init => "init", AstProp::Initializer => "initializer", AstProp::Implements => "implements", + AstProp::Imported => "imported", + AstProp::ImportKind => "importKind", AstProp::Key => "key", AstProp::Kind => "kind", AstProp::Label => "label", @@ -382,6 +406,7 @@ impl Display for AstProp { AstProp::Members => "members", AstProp::Meta => "meta", AstProp::Method => "method", + AstProp::ModuleReference => "moduleReference", AstProp::Name => "name", AstProp::Namespace => "namespace", AstProp::NameType => "nameType", @@ -391,8 +416,12 @@ impl Display for AstProp { AstProp::OpeningFragment => "openingFragment", AstProp::Operator => "operator", AstProp::Optional => "optional", + AstProp::Options => "options", AstProp::Out => "out", + AstProp::Override => "override", AstProp::Param => "param", + AstProp::Parameter => "parameter", + AstProp::Parameters => "parameters", AstProp::ParameterName => "parameterName", AstProp::Params => "params", AstProp::Pattern => "pattern", @@ -404,6 +433,7 @@ impl Display for AstProp { AstProp::Quasis => "quasis", AstProp::Raw => "raw", AstProp::Readonly => "readonly", + AstProp::Regex => "regex", AstProp::ReturnType => "returnType", AstProp::Right => "right", AstProp::SelfClosing => "selfClosing", @@ -442,79 +472,2277 @@ pub struct TsEsTreeBuilder { ctx: SerializeCtx, } -// TODO: Add a builder API to make it easier to convert from different source -// ast formats. +impl AstBufSerializer for TsEsTreeBuilder { + fn serialize(&mut self) -> Vec { + self.ctx.serialize() + } +} + impl TsEsTreeBuilder { pub fn new() -> Self { // Max values - // TODO: Maybe there is a rust macro to grab the last enum value? let kind_max_count: u8 = u8::from(AstNode::TSEnumBody) + 1; let prop_max_count: u8 = u8::from(AstProp::Value) + 1; Self { ctx: SerializeCtx::new(kind_max_count, prop_max_count), } } -} -impl AstBufSerializer for TsEsTreeBuilder { - fn header( + pub fn write_program( &mut self, - kind: AstNode, - parent: NodeRef, span: &Span, - ) -> PendingNodeRef { - self.ctx.header(kind, parent, span) + source_kind: &str, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Program, span); + + self.ctx.write_str(AstProp::SourceType, source_kind); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + + self.ctx.set_root_idx(id.0); + + self.ctx.commit_node(id) } - fn commit_schema(&mut self, offset: PendingNodeRef) -> NodeRef { - self.ctx.commit_schema(offset) + pub fn write_import_decl( + &mut self, + span: &Span, + type_only: bool, + source: NodeRef, + specifiers: Vec, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDeclaration, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) } - fn ref_field(&mut self, prop: AstProp) -> FieldPos { - FieldPos(self.ctx.ref_field(prop)) + pub fn write_import_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + imported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, kind); + + self.ctx.write_ref(AstProp::Imported, &id, imported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) } - fn ref_vec_field(&mut self, prop: AstProp, len: usize) -> FieldArrPos { - FieldArrPos(self.ctx.ref_vec_field(prop, len)) + pub fn write_import_attr( + &mut self, + span: &Span, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportAttribute, span); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) } - fn str_field(&mut self, prop: AstProp) -> StrPos { - StrPos(self.ctx.str_field(prop)) + pub fn write_import_default_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportDefaultSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) } - fn bool_field(&mut self, prop: AstProp) -> BoolPos { - BoolPos(self.ctx.bool_field(prop)) + pub fn write_import_ns_spec( + &mut self, + span: &Span, + local: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ImportNamespaceSpecifier, span); + self.ctx.write_ref(AstProp::Local, &id, local); + self.ctx.commit_node(id) } - fn undefined_field(&mut self, prop: AstProp) -> UndefPos { - UndefPos(self.ctx.undefined_field(prop)) + pub fn write_export_decl(&mut self, span: &Span, decl: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) } - fn null_field(&mut self, prop: AstProp) -> NullPos { - NullPos(self.ctx.null_field(prop)) + pub fn write_export_all_decl( + &mut self, + span: &Span, + is_type_only: bool, + source: NodeRef, + exported: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportAllDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + + self.ctx.write_maybe_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + self.ctx.commit_node(id) } - fn write_ref(&mut self, pos: FieldPos, value: NodeRef) { - self.ctx.write_ref(pos.0, value); + pub fn write_export_default_decl( + &mut self, + span: &Span, + is_type_only: bool, + decl: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::ExportDefaultDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, value); + self.ctx.write_ref(AstProp::Declaration, &id, decl); + self.ctx.commit_node(id) } - fn write_maybe_ref(&mut self, pos: FieldPos, value: Option) { - self.ctx.write_maybe_ref(pos.0, value); + pub fn write_export_named_decl( + &mut self, + span: &Span, + specifiers: Vec, + source: Option, + attributes: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportNamedDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Specifiers, &id, specifiers); + self.ctx.write_maybe_ref(AstProp::Source, &id, source); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attributes); + + self.ctx.commit_node(id) } - fn write_refs(&mut self, pos: FieldArrPos, value: Vec) { - self.ctx.write_refs(pos.0, value); + pub fn write_export_ts_namespace( + &mut self, + span: &Span, + ident: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSNamespaceExportDeclaration, span); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.commit_node(id) } - fn write_str(&mut self, pos: StrPos, value: &str) { - self.ctx.write_str(pos.0, value); + pub fn write_export_ts_import_equals( + &mut self, + span: &Span, + is_type_only: bool, + ident: NodeRef, + reference: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSImportEqualsDeclaration, span); + + let value = if is_type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ImportKind, value); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::ModuleReference, &id, reference); + + self.ctx.commit_node(id) } - fn write_bool(&mut self, pos: BoolPos, value: bool) { - self.ctx.write_bool(pos.0, value); + pub fn write_ts_external_mod_ref( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSExternalModuleReference, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) } - fn serialize(&mut self) -> Vec { - self.ctx.serialize() + pub fn write_export_spec( + &mut self, + span: &Span, + type_only: bool, + local: NodeRef, + exported: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExportSpecifier, span); + + let kind = if type_only { "type" } else { "value" }; + self.ctx.write_str(AstProp::ExportKind, kind); + + self.ctx.write_ref(AstProp::Exported, &id, exported); + self.ctx.write_ref(AstProp::Local, &id, local); + + self.ctx.commit_node(id) + } + + pub fn write_var_decl( + &mut self, + span: &Span, + declare: bool, + kind: &str, + decls: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref_vec(AstProp::Declarations, &id, decls); + + self.ctx.commit_node(id) + } + + pub fn write_var_declarator( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::VariableDeclarator, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_decl( + &mut self, + span: &Span, + is_declare: bool, + is_async: bool, + is_generator: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + type_param: Option, + return_type: Option, + body: Option, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_decorator(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::Decorator, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_decl( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + // Ident is required in most cases, but optional as default export + // declaration. TsEstree is weird... + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassDeclaration, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_expr( + &mut self, + span: &Span, + is_declare: bool, + is_abstract: bool, + ident: Option, + super_class: Option, + implements: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassExpression, span); + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Abstract, is_abstract); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::SuperClass, &id, super_class); + self.ctx.write_ref_vec(AstProp::Implements, &id, implements); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_class_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ClassBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_static_block(&mut self, span: &Span, body: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::StaticBlock, span); + self.ctx.write_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_accessor_property( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AccessorProperty, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_prop( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_readonly: bool, + is_static: bool, + accessibility: Option, + decorators: Vec, + key: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PropertyDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_class_method( + &mut self, + span: &Span, + is_declare: bool, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + kind: &str, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MethodDefinition, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + self.ctx.write_str(AstProp::Kind, kind); + self.write_accessibility(accessibility); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_block_stmt( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BlockStatement, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_debugger_stmt(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::DebuggerStatement, span); + self.ctx.commit_node(id) + } + + pub fn write_with_stmt( + &mut self, + span: &Span, + obj: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WithStatement, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_return_stmt( + &mut self, + span: &Span, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ReturnStatement, span); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_labeled_stmt( + &mut self, + span: &Span, + label: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LabeledStatement, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_break_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BreakStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_continue_stmt( + &mut self, + span: &Span, + label: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ContinueStatement, span); + self.ctx.write_maybe_ref(AstProp::Label, &id, label); + self.ctx.commit_node(id) + } + + pub fn write_if_stmt( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::IfStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_maybe_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_switch_stmt( + &mut self, + span: &Span, + discriminant: NodeRef, + cases: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchStatement, span); + + self.ctx.write_ref(AstProp::Discriminant, &id, discriminant); + self.ctx.write_ref_vec(AstProp::Cases, &id, cases); + + self.ctx.commit_node(id) + } + + pub fn write_switch_case( + &mut self, + span: &Span, + test: Option, + consequent: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SwitchCase, span); + + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_ref_vec(AstProp::Consequent, &id, consequent); + + self.ctx.commit_node(id) + } + + pub fn write_throw_stmt(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThrowStatement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::WhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_do_while_stmt( + &mut self, + span: &Span, + test: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::DoWhileStatement, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_stmt( + &mut self, + span: &Span, + init: Option, + test: Option, + update: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForStatement, span); + + self.ctx.write_maybe_ref(AstProp::Init, &id, init); + self.ctx.write_maybe_ref(AstProp::Test, &id, test); + self.ctx.write_maybe_ref(AstProp::Update, &id, update); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_in_stmt( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForInStatement, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_for_of_stmt( + &mut self, + span: &Span, + is_await: bool, + left: NodeRef, + right: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ForOfStatement, span); + + self.ctx.write_bool(AstProp::Await, is_await); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_expr_stmt(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ExpressionStatement, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_try_stmt( + &mut self, + span: &Span, + block: NodeRef, + handler: Option, + finalizer: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TryStatement, span); + + self.ctx.write_ref(AstProp::Block, &id, block); + self.ctx.write_maybe_ref(AstProp::Handler, &id, handler); + self.ctx.write_maybe_ref(AstProp::Finalizer, &id, finalizer); + + self.ctx.commit_node(id) + } + + pub fn write_catch_clause( + &mut self, + span: &Span, + param: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CatchClause, span); + + self.ctx.write_maybe_ref(AstProp::Param, &id, param); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_arr_expr( + &mut self, + span: &Span, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayExpression, span); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + self.ctx.commit_node(id) + } + + pub fn write_obj_expr( + &mut self, + span: &Span, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectExpression, span); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + self.ctx.commit_node(id) + } + + pub fn write_bin_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::BinaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_logical_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::LogicalExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::FunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_arrow_fn_expr( + &mut self, + span: &Span, + is_async: bool, + is_generator: bool, + type_params: Option, + params: Vec, + return_type: Option, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrowFunctionExpression, span); + + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_this_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::ThisExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_super(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Super, span); + self.ctx.commit_node(id) + } + + pub fn write_unary_expr( + &mut self, + span: &Span, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UnaryExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_new_expr( + &mut self, + span: &Span, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::NewExpression, span); + + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_import_expr( + &mut self, + span: &Span, + source: NodeRef, + options: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ImportExpression, span); + + self.ctx.write_ref(AstProp::Source, &id, source); + self.ctx.write_ref(AstProp::Options, &id, options); + + self.ctx.commit_node(id) + } + + pub fn write_call_expr( + &mut self, + span: &Span, + optional: bool, + callee: NodeRef, + type_args: Option, + args: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::CallExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_ref(AstProp::Callee, &id, callee); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref_vec(AstProp::Arguments, &id, args); + + self.ctx.commit_node(id) + } + + pub fn write_update_expr( + &mut self, + span: &Span, + prefix: bool, + operator: &str, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::UpdateExpression, span); + + self.ctx.write_bool(AstProp::Prefix, prefix); + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_assignment_expr( + &mut self, + span: &Span, + operator: &str, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentExpression, span); + + self.ctx.write_str(AstProp::Operator, operator); + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_conditional_expr( + &mut self, + span: &Span, + test: NodeRef, + consequent: NodeRef, + alternate: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ConditionalExpression, span); + + self.ctx.write_ref(AstProp::Test, &id, test); + self.ctx.write_ref(AstProp::Consequent, &id, consequent); + self.ctx.write_ref(AstProp::Alternate, &id, alternate); + + self.ctx.commit_node(id) + } + + pub fn write_member_expr( + &mut self, + span: &Span, + optional: bool, + computed: bool, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::MemberExpression, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_chain_expr(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::ChainExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_sequence_expr( + &mut self, + span: &Span, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::SequenceExpression, span); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + self.ctx.commit_node(id) + } + + pub fn write_template_lit( + &mut self, + span: &Span, + quasis: Vec, + exprs: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateLiteral, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Expressions, &id, exprs); + + self.ctx.commit_node(id) + } + + pub fn write_template_elem( + &mut self, + span: &Span, + tail: bool, + raw: &str, + cooked: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TemplateElement, span); + + self.ctx.write_bool(AstProp::Tail, tail); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Cooked, cooked); + + self.ctx.commit_node(id) + } + + pub fn write_tagged_template_expr( + &mut self, + span: &Span, + tag: NodeRef, + type_args: Option, + quasi: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TaggedTemplateExpression, span); + + self.ctx.write_ref(AstProp::Tag, &id, tag); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + self.ctx.write_ref(AstProp::Quasi, &id, quasi); + + self.ctx.commit_node(id) + } + + pub fn write_yield_expr( + &mut self, + span: &Span, + delegate: bool, + arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::YieldExpression, span); + + self.ctx.write_bool(AstProp::Delegate, delegate); + self.ctx.write_maybe_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_await_expr(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::AwaitExpression, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_meta_prop(&mut self, span: &Span, prop: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::MetaProperty, span); + self.ctx.write_ref(AstProp::Property, &id, prop); + self.ctx.commit_node(id) + } + + pub fn write_identifier( + &mut self, + span: &Span, + name: &str, + optional: bool, + type_annotation: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Identifier, span); + + self.ctx.write_str(AstProp::Name, name); + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_annotation); + + self.ctx.commit_node(id) + } + + pub fn write_private_identifier( + &mut self, + span: &Span, + name: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::PrivateIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_assign_pat( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::AssignmentPattern, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + pub fn write_arr_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + elems: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ArrayPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Elements, &id, elems); + + self.ctx.commit_node(id) + } + + pub fn write_obj_pat( + &mut self, + span: &Span, + optional: bool, + type_ann: Option, + props: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::ObjectPattern, span); + + self.ctx.write_bool(AstProp::Optional, optional); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Properties, &id, props); + + self.ctx.commit_node(id) + } + + pub fn write_rest_elem( + &mut self, + span: &Span, + type_ann: Option, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::RestElement, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::Argument, &id, arg); + + self.ctx.commit_node(id) + } + + pub fn write_spread(&mut self, span: &Span, arg: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::SpreadElement, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_property( + &mut self, + span: &Span, + shorthand: bool, + computed: bool, + method: bool, + kind: &str, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Property, span); + + self.ctx.write_bool(AstProp::Shorthand, shorthand); + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Method, method); + self.ctx.write_str(AstProp::Kind, kind); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_str_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_str(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bool_lit(&mut self, span: &Span, value: bool) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + let raw = &format!("{}", value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_bool(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_null_lit(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_null(AstProp::Value); + self.ctx.write_str(AstProp::Raw, "null"); + + self.ctx.commit_node(id) + } + + pub fn write_num_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_num(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + + self.ctx.commit_node(id) + } + + pub fn write_bigint_lit( + &mut self, + span: &Span, + value: &str, + raw: &str, + bigint_value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_bigint(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::BigInt, bigint_value); + + self.ctx.commit_node(id) + } + + pub fn write_regex_lit( + &mut self, + span: &Span, + pattern: &str, + flags: &str, + value: &str, + raw: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::Literal, span); + + self.ctx.write_regex(AstProp::Value, value); + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.open_obj(); + self.ctx.write_str(AstProp::Flags, flags); + self.ctx.write_str(AstProp::Pattern, pattern); + self.ctx.commit_obj(AstProp::Regex); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_identifier(&mut self, span: &Span, name: &str) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXIdentifier, span); + self.ctx.write_str(AstProp::Name, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_namespaced_name( + &mut self, + span: &Span, + namespace: NodeRef, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXNamespacedName, span); + + self.ctx.write_ref(AstProp::Namespace, &id, namespace); + self.ctx.write_ref(AstProp::Name, &id, name); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_empty_expr(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXEmptyExpression, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_elem( + &mut self, + span: &Span, + opening: NodeRef, + closing: Option, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXElement, span); + + self.ctx.write_ref(AstProp::OpeningElement, &id, opening); + self + .ctx + .write_maybe_ref(AstProp::ClosingElement, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_elem( + &mut self, + span: &Span, + self_closing: bool, + name: NodeRef, + attrs: Vec, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningElement, span); + + self.ctx.write_bool(AstProp::SelfClosing, self_closing); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_ref_vec(AstProp::Attributes, &id, attrs); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_attr( + &mut self, + span: &Span, + name: NodeRef, + value: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXAttribute, span); + + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.write_maybe_ref(AstProp::Value, &id, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_spread_attr( + &mut self, + span: &Span, + arg: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXSpreadAttribute, span); + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_elem( + &mut self, + span: &Span, + name: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingElement, span); + self.ctx.write_ref(AstProp::Name, &id, name); + self.ctx.commit_node(id) + } + + pub fn write_jsx_frag( + &mut self, + span: &Span, + opening: NodeRef, + closing: NodeRef, + children: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXFragment, span); + + self.ctx.write_ref(AstProp::OpeningFragment, &id, opening); + self.ctx.write_ref(AstProp::ClosingFragment, &id, closing); + self.ctx.write_ref_vec(AstProp::Children, &id, children); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_opening_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXOpeningFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_closing_frag(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXClosingFragment, span); + self.ctx.commit_node(id) + } + + pub fn write_jsx_expr_container( + &mut self, + span: &Span, + expr: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXExpressionContainer, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_jsx_text( + &mut self, + span: &Span, + raw: &str, + value: &str, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXText, span); + + self.ctx.write_str(AstProp::Raw, raw); + self.ctx.write_str(AstProp::Value, value); + + self.ctx.commit_node(id) + } + + pub fn write_jsx_member_expr( + &mut self, + span: &Span, + obj: NodeRef, + prop: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::JSXMemberExpression, span); + + self.ctx.write_ref(AstProp::Object, &id, obj); + self.ctx.write_ref(AstProp::Property, &id, prop); + + self.ctx.commit_node(id) + } + + pub fn write_ts_module_decl( + &mut self, + span: &Span, + is_declare: bool, + is_global: bool, + ident: NodeRef, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Global, is_global); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_module_block( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleBlock, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_class_implements( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSClassImplements, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_abstract_method_def( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + is_override: bool, + is_static: bool, + accessibility: Option, + key: NodeRef, + value: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSAbstractMethodDefinition, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Static, is_static); + + self.write_accessibility(accessibility); + + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref(AstProp::Key, &id, value); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_empty_body_fn_expr( + &mut self, + span: &Span, + is_declare: bool, + is_expression: bool, + is_async: bool, + is_generator: bool, + ident: Option, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSEmptyBodyFunctionExpression, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Expression, is_expression); + self.ctx.write_bool(AstProp::Async, is_async); + self.ctx.write_bool(AstProp::Generator, is_generator); + self.ctx.write_maybe_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_param_prop( + &mut self, + span: &Span, + is_override: bool, + is_readonly: bool, + accessibility: Option, + decorators: Vec, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSParameterProperty, span); + + self.ctx.write_bool(AstProp::Override, is_override); + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_bool(AstProp::Static, false); + self.write_accessibility(accessibility); + self.ctx.write_ref_vec(AstProp::Decorators, &id, decorators); + self.ctx.write_ref(AstProp::Parameter, &id, param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_call_sig_decl( + &mut self, + span: &Span, + type_ann: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSCallSignatureDeclaration, span); + + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_property_sig( + &mut self, + span: &Span, + computed: bool, + optional: bool, + readonly: bool, + key: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSPropertySignature, span); + + self.ctx.write_bool(AstProp::Computed, computed); + self.ctx.write_bool(AstProp::Optional, optional); + self.ctx.write_bool(AstProp::Readonly, readonly); + // TODO(@marvinhagemeister) not sure where this is coming from + self.ctx.write_bool(AstProp::Static, false); + + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum( + &mut self, + span: &Span, + declare: bool, + is_const: bool, + ident: NodeRef, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_body( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumBody, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_enum_member( + &mut self, + span: &Span, + ident: NodeRef, + init: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSEnumMember, span); + + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Initializer, &id, init); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_assertion( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAssertion, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_inst( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterInstantiation, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_alias( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAliasDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_param); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_satisfies_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSSatisfiesExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_as_expr( + &mut self, + span: &Span, + expr: NodeRef, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSAsExpression, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_non_null(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNonNullExpression, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_this_type(&mut self, span: &Span) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSThisType, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_interface( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterface, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_decl( + &mut self, + span: &Span, + declare: bool, + ident: NodeRef, + type_param: Option, + extends: Vec, + body: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, declare); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Extends, &id, type_param); + self + .ctx + .write_ref_vec(AstProp::TypeParameters, &id, extends); + self.ctx.write_ref(AstProp::Body, &id, body); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_body( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceBody, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_construct_sig( + &mut self, + span: &Span, + type_params: Option, + params: Vec, + return_type: NodeRef, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSConstructSignatureDeclaration, span); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.write_ref(AstProp::ReturnType, &id, return_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_getter_sig( + &mut self, + span: &Span, + key: NodeRef, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "getter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_setter_sig( + &mut self, + span: &Span, + key: NodeRef, + param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, false); + self.ctx.write_bool(AstProp::Optional, false); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "setter"); + self.ctx.write_ref(AstProp::Key, &id, key); + self.ctx.write_ref_vec(AstProp::Params, &id, vec![param]); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_method_sig( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + key: NodeRef, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_interface_heritage( + &mut self, + span: &Span, + expr: NodeRef, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInterfaceHeritage, span); + + self.ctx.write_ref(AstProp::Expression, &id, expr); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_ts_index_sig( + &mut self, + span: &Span, + is_readonly: bool, + params: Vec, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexSignature, span); + + self.ctx.write_bool(AstProp::Readonly, is_readonly); + self.ctx.write_ref_vec(AstProp::Parameters, &id, params); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_union_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSUnionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_intersection_type( + &mut self, + span: &Span, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIntersectionType, span); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + self.ctx.commit_node(id) + } + + pub fn write_ts_infer_type( + &mut self, + span: &Span, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSInferType, span); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_op( + &mut self, + span: &Span, + op: &str, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeOperator, span); + + self.ctx.write_str(AstProp::Operator, op); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_indexed_access_type( + &mut self, + span: &Span, + index: NodeRef, + obj: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSIndexedAccessType, span); + + self.ctx.write_ref(AstProp::IndexType, &id, index); + self.ctx.write_ref(AstProp::ObjectType, &id, obj); + + self.ctx.commit_node(id) + } + + pub fn write_ts_keyword( + &mut self, + kind: TsKeywordKind, + span: &Span, + ) -> NodeRef { + let kind = match kind { + TsKeywordKind::Any => AstNode::TSAnyKeyword, + TsKeywordKind::Unknown => AstNode::TSUnknownKeyword, + TsKeywordKind::Number => AstNode::TSNumberKeyword, + TsKeywordKind::Object => AstNode::TSObjectKeyword, + TsKeywordKind::Boolean => AstNode::TSBooleanKeyword, + TsKeywordKind::BigInt => AstNode::TSBigIntKeyword, + TsKeywordKind::String => AstNode::TSStringKeyword, + TsKeywordKind::Symbol => AstNode::TSSymbolKeyword, + TsKeywordKind::Void => AstNode::TSVoidKeyword, + TsKeywordKind::Undefined => AstNode::TSUndefinedKeyword, + TsKeywordKind::Null => AstNode::TSNullKeyword, + TsKeywordKind::Never => AstNode::TSNeverKeyword, + TsKeywordKind::Intrinsic => AstNode::TSIntrinsicKeyword, + }; + + let id = self.ctx.append_node(kind, span); + self.ctx.commit_node(id) + } + + pub fn write_ts_rest_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSRestType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_conditional_type( + &mut self, + span: &Span, + check: NodeRef, + extends: NodeRef, + true_type: NodeRef, + false_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSConditionalType, span); + + self.ctx.write_ref(AstProp::CheckType, &id, check); + self.ctx.write_ref(AstProp::ExtendsType, &id, extends); + self.ctx.write_ref(AstProp::TrueType, &id, true_type); + self.ctx.write_ref(AstProp::FalseType, &id, false_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_mapped_type( + &mut self, + span: &Span, + readonly: Option, + optional: Option, + name: Option, + type_ann: Option, + type_param: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMappedType, span); + + self.write_plus_minus_true(AstProp::Readonly, readonly); + self.write_plus_minus_true(AstProp::Optional, optional); + self.ctx.write_maybe_ref(AstProp::NameType, &id, name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.write_ref(AstProp::TypeParameter, &id, type_param); + + self.ctx.commit_node(id) + } + + pub fn write_ts_lit_type(&mut self, span: &Span, lit: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSLiteralType, span); + self.ctx.write_ref(AstProp::Literal, &id, lit); + self.ctx.commit_node(id) + } + + pub fn write_ts_tpl_lit( + &mut self, + span: &Span, + quasis: Vec, + types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTemplateLiteralType, span); + + self.ctx.write_ref_vec(AstProp::Quasis, &id, quasis); + self.ctx.write_ref_vec(AstProp::Types, &id, types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_lit( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeLiteral, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_optional_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSOptionalType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ann( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeAnnotation, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + + pub fn write_ts_array_type( + &mut self, + span: &Span, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSArrayType, span); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + self.ctx.commit_node(id) + } + + pub fn write_ts_type_query( + &mut self, + span: &Span, + expr_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeQuery, span); + + self.ctx.write_ref(AstProp::ExprName, &id, expr_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_ref( + &mut self, + span: &Span, + type_name: NodeRef, + type_arg: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeReference, span); + + self.ctx.write_ref(AstProp::TypeName, &id, type_name); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_arg); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_predicate( + &mut self, + span: &Span, + asserts: bool, + param_name: NodeRef, + type_ann: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypePredicate, span); + + self.ctx.write_bool(AstProp::Asserts, asserts); + self.ctx.write_ref(AstProp::ParameterName, &id, param_name); + self + .ctx + .write_maybe_ref(AstProp::TypeAnnotation, &id, type_ann); + + self.ctx.commit_node(id) + } + + pub fn write_ts_tuple_type( + &mut self, + span: &Span, + elem_types: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTupleType, span); + + self + .ctx + .write_ref_vec(AstProp::ElementTypes, &id, elem_types); + + self.ctx.commit_node(id) + } + + pub fn write_ts_named_tuple_member( + &mut self, + span: &Span, + label: NodeRef, + elem_type: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSNamedTupleMember, span); + + self.ctx.write_ref(AstProp::Label, &id, label); + self.ctx.write_ref(AstProp::ElementType, &id, elem_type); + + self.ctx.commit_node(id) + } + + pub fn write_ts_type_param_decl( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self + .ctx + .append_node(AstNode::TSTypeParameterDeclaration, span); + + self.ctx.write_ref_vec(AstProp::Params, &id, params); + + self.ctx.commit_node(id) + } + + #[allow(clippy::too_many_arguments)] + pub fn write_ts_type_param( + &mut self, + span: &Span, + is_in: bool, + is_out: bool, + is_const: bool, + name: NodeRef, + constraint: Option, + default: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeParameter, span); + + self.ctx.write_bool(AstProp::In, is_in); + self.ctx.write_bool(AstProp::Out, is_out); + self.ctx.write_bool(AstProp::Const, is_const); + self.ctx.write_ref(AstProp::Name, &id, name); + self + .ctx + .write_maybe_ref(AstProp::Constraint, &id, constraint); + self.ctx.write_maybe_ref(AstProp::Default, &id, default); + + self.ctx.commit_node(id) + } + + pub fn write_ts_import_type( + &mut self, + span: &Span, + arg: NodeRef, + qualifier: Option, + type_args: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSImportType, span); + + self.ctx.write_ref(AstProp::Argument, &id, arg); + self.ctx.write_maybe_ref(AstProp::Qualifier, &id, qualifier); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); + + self.ctx.commit_node(id) + } + + pub fn write_export_assign(&mut self, span: &Span, expr: NodeRef) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSExportAssignment, span); + self.ctx.write_ref(AstProp::Expression, &id, expr); + self.ctx.commit_node(id) + } + + pub fn write_ts_fn_type( + &mut self, + span: &Span, + params: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSFunctionType, span); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self.ctx.commit_node(id) + } + + pub fn write_ts_qualified_name( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSQualifiedName, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + + fn write_accessibility(&mut self, accessibility: Option) { + if let Some(value) = accessibility { + self.ctx.write_str(AstProp::Accessibility, &value); + } else { + self.ctx.write_undefined(AstProp::Accessibility); + } + } + + fn write_plus_minus_true( + &mut self, + prop: AstProp, + value: Option, + ) { + match value { + Some(TruePlusMinus::Plus) => self.ctx.write_str(prop, "+"), + Some(TruePlusMinus::Minus) => self.ctx.write_str(prop, "-"), + Some(TruePlusMinus::True) => self.ctx.write_bool(prop, true), + _ => self.ctx.write_undefined(prop), + } } } + +#[derive(Debug)] +pub enum TsKeywordKind { + Any, + Unknown, + Number, + Object, + Boolean, + BigInt, + String, + Symbol, + Void, + Undefined, + Null, + Never, + Intrinsic, +} diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 2b1266bafb..0b27b1a3e3 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -861,10 +861,8 @@ async fn npm_install_after_modification( // make a new CliFactory to pick up the updated config file let cli_factory = CliFactory::from_flags(flags); // surface any errors in the package.json - let npm_resolver = cli_factory.npm_resolver().await?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_no_pkg_json_dep_errors()?; - } + let npm_installer = cli_factory.npm_installer()?; + npm_installer.ensure_no_pkg_json_dep_errors()?; // npm install cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?; diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 3137939cea..5683a30cc8 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -12,16 +12,19 @@ use crate::factory::CliFactory; use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_util::CreateGraphOptions; +use crate::npm::installer::PackageCaching; pub async fn cache_top_level_deps( // todo(dsherret): don't pass the factory into this function. Instead use ctor deps factory: &CliFactory, jsr_resolver: Option>, ) -> Result<(), AnyError> { - let npm_resolver = factory.npm_resolver().await?; + let npm_installer = factory.npm_installer_if_managed()?; let cli_options = factory.cli_options()?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = &npm_installer { + npm_installer + .ensure_top_level_package_json_install() + .await?; if let Some(lockfile) = cli_options.maybe_lockfile() { lockfile.error_if_changed()?; } @@ -138,10 +141,8 @@ pub async fn cache_top_level_deps( maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots); } - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + if let Some(npm_installer) = &npm_installer { + npm_installer.cache_packages(PackageCaching::All).await?; } maybe_graph_error?; diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 708f95f987..1dbb464dbe 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -42,6 +42,7 @@ use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphUpdatePermit; use crate::jsr::JsrFetchResolver; use crate::module_loader::ModuleLoadPreparer; +use crate::npm::installer::NpmInstaller; use crate::npm::CliNpmResolver; use crate::npm::NpmFetchResolver; use crate::util::sync::AtomicFlag; @@ -451,6 +452,7 @@ pub struct DepManager { pub(crate) jsr_fetch_resolver: Arc, pub(crate) npm_fetch_resolver: Arc, npm_resolver: Arc, + npm_installer: Arc, permissions_container: PermissionsContainer, main_module_graph_container: Arc, lockfile: Option>, @@ -460,6 +462,7 @@ pub struct DepManagerArgs { pub module_load_preparer: Arc, pub jsr_fetch_resolver: Arc, pub npm_fetch_resolver: Arc, + pub npm_installer: Arc, pub npm_resolver: Arc, pub permissions_container: PermissionsContainer, pub main_module_graph_container: Arc, @@ -477,6 +480,7 @@ impl DepManager { module_load_preparer, jsr_fetch_resolver, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -490,6 +494,7 @@ impl DepManager { dependencies_resolved: AtomicFlag::lowered(), module_load_preparer, npm_fetch_resolver, + npm_installer, npm_resolver, permissions_container, main_module_graph_container, @@ -556,7 +561,10 @@ impl DepManager { return Ok(()); } - npm_resolver.ensure_top_level_package_json_install().await?; + self + .npm_installer + .ensure_top_level_package_json_install() + .await?; let mut roots = Vec::new(); let mut info_futures = FuturesUnordered::new(); for dep in &self.deps { diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index e7fc88c31f..939c30b5c1 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -445,6 +445,7 @@ async fn dep_manager_args( jsr_fetch_resolver, npm_fetch_resolver, npm_resolver: factory.npm_resolver().await?.clone(), + npm_installer: factory.npm_installer()?.clone(), permissions_container: factory.root_permissions_container()?.clone(), main_module_graph_container: factory .main_module_graph_container() diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 1bc6a2f9ea..05e2f320eb 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -164,7 +164,7 @@ pub async fn run( let cli_options = factory.cli_options()?; let main_module = cli_options.resolve_main_module()?; let permissions = factory.root_permissions_container()?; - let npm_resolver = factory.npm_resolver().await?.clone(); + let npm_installer = factory.npm_installer_if_managed()?.cloned(); let resolver = factory.resolver().await?.clone(); let file_fetcher = factory.file_fetcher()?; let worker_factory = factory.create_cli_main_worker_factory().await?; @@ -187,7 +187,7 @@ pub async fn run( let worker = worker.into_main_worker(); let session = ReplSession::initialize( cli_options, - npm_resolver, + npm_installer, resolver, worker, main_module.clone(), diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index ed9dd61e2d..5ea4523c48 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -45,7 +45,7 @@ use crate::args::CliOptions; use crate::cdp; use crate::colors; use crate::lsp::ReplLanguageServer; -use crate::npm::CliNpmResolver; +use crate::npm::installer::NpmInstaller; use crate::resolver::CliResolver; use crate::tools::test::report_tests; use crate::tools::test::reporters::PrettyTestReporter; @@ -181,7 +181,7 @@ struct ReplJsxState { } pub struct ReplSession { - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, pub worker: MainWorker, session: LocalInspectorSession, @@ -200,7 +200,7 @@ pub struct ReplSession { impl ReplSession { pub async fn initialize( cli_options: &CliOptions, - npm_resolver: Arc, + npm_installer: Option>, resolver: Arc, mut worker: MainWorker, main_module: ModuleSpecifier, @@ -265,7 +265,7 @@ impl ReplSession { )?; let experimental_decorators = transpile_options.use_ts_decorators; let mut repl_session = ReplSession { - npm_resolver, + npm_installer, resolver, worker, session, @@ -704,8 +704,8 @@ impl ReplSession { &mut self, program: &swc_ast::Program, ) -> Result<(), AnyError> { - let Some(npm_resolver) = self.npm_resolver.as_managed() else { - return Ok(()); // don't auto-install for byonm + let Some(npm_installer) = &self.npm_installer else { + return Ok(()); }; let mut collector = ImportCollector::new(); @@ -737,13 +737,13 @@ impl ReplSession { let has_node_specifier = resolved_imports.iter().any(|url| url.scheme() == "node"); if !npm_imports.is_empty() || has_node_specifier { - npm_resolver + npm_installer .add_and_cache_package_reqs(&npm_imports) .await?; // prevent messages in the repl about @types/node not being cached if has_node_specifier { - npm_resolver.inject_synthetic_types_node_package().await?; + npm_installer.inject_synthetic_types_node_package().await?; } } Ok(()) diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index a10ce32947..ecf1bd52f3 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -12,6 +12,7 @@ use crate::args::EvalFlags; use crate::args::Flags; use crate::args::WatchFlagsWithPaths; use crate::factory::CliFactory; +use crate::npm::installer::PackageCaching; use crate::util; use crate::util::file_watcher::WatcherRestartMode; @@ -202,18 +203,17 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> { // ensure an "npm install" is done if the user has explicitly // opted into using a managed node_modules directory if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) { - if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { - let already_done = - npm_resolver.ensure_top_level_package_json_install().await?; + if let Some(npm_installer) = factory.npm_installer_if_managed()? { + let already_done = npm_installer + .ensure_top_level_package_json_install() + .await?; if !already_done && matches!( cli_options.default_npm_caching_strategy(), crate::graph_util::NpmCachingStrategy::Eager ) { - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) - .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } } } diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 85834a0bfc..d6d87dd45e 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -36,6 +36,8 @@ use crate::args::TaskFlags; use crate::colors; use crate::factory::CliFactory; use crate::node::CliNodeResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::task_runner; use crate::task_runner::run_future_forwarding_signals; @@ -203,6 +205,7 @@ pub async fn execute_script( }] }; + let npm_installer = factory.npm_installer_if_managed()?; let npm_resolver = factory.npm_resolver().await?; let node_resolver = factory.node_resolver().await?; let env_vars = task_runner::real_env_vars(); @@ -216,6 +219,7 @@ pub async fn execute_script( let task_runner = TaskRunner { task_flags: &task_flags, + npm_installer: npm_installer.map(|n| n.as_ref()), npm_resolver: npm_resolver.as_ref(), node_resolver: node_resolver.as_ref(), env_vars, @@ -266,6 +270,7 @@ struct RunSingleOptions<'a> { struct TaskRunner<'a> { task_flags: &'a TaskFlags, + npm_installer: Option<&'a NpmInstaller>, npm_resolver: &'a dyn CliNpmResolver, node_resolver: &'a CliNodeResolver, env_vars: HashMap, @@ -458,11 +463,11 @@ impl<'a> TaskRunner<'a> { return Ok(0); }; - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { @@ -497,11 +502,11 @@ impl<'a> TaskRunner<'a> { argv: &[String], ) -> Result { // ensure the npm packages are installed if using a managed resolver - if let Some(npm_resolver) = self.npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - npm_resolver - .cache_packages(crate::npm::PackageCaching::All) + if let Some(npm_installer) = self.npm_installer { + npm_installer + .ensure_top_level_package_json_install() .await?; + npm_installer.cache_packages(PackageCaching::All).await?; } let cwd = match &self.task_flags.cwd { diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 25813c3f9d..b3279f54ac 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -723,7 +723,7 @@ delete Object.prototype.__proto__; } : arg; if (fileReference.fileName.startsWith("npm:")) { - /** @type {[string, ts.Extension] | undefined} */ + /** @type {[string, ts.Extension | null] | undefined} */ const resolved = ops.op_resolve( containingFilePath, [ @@ -735,7 +735,7 @@ delete Object.prototype.__proto__; ], ], )?.[0]; - if (resolved) { + if (resolved && resolved[1]) { return { resolvedTypeReferenceDirective: { primary: true, @@ -785,7 +785,7 @@ delete Object.prototype.__proto__; debug(` base: ${base}`); debug(` specifiers: ${specifiers.map((s) => s[1]).join(", ")}`); } - /** @type {Array<[string, ts.Extension] | undefined>} */ + /** @type {Array<[string, ts.Extension | null] | undefined>} */ const resolved = ops.op_resolve( base, specifiers, @@ -793,7 +793,7 @@ delete Object.prototype.__proto__; if (resolved) { /** @type {Array} */ const result = resolved.map((item) => { - if (item) { + if (item && item[1]) { const [resolvedFileName, extension] = item; return { resolvedModule: { diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 1473b8a8d9..b6cf691c38 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -28,6 +28,7 @@ use deno_graph::GraphKind; use deno_graph::Module; use deno_graph::ModuleGraph; use deno_graph::ResolutionResolved; +use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_semver::npm::NpmPackageReqReference; use node_resolver::errors::NodeJsErrorCode; @@ -709,9 +710,7 @@ pub enum ResolveError { PackageSubpathResolve(PackageSubpathResolveError), #[class(inherit)] #[error("{0}")] - ResolvePkgFolderFromDenoModule( - #[from] crate::npm::ResolvePkgFolderFromDenoModuleError, - ), + ResolvePkgFolderFromDenoModule(#[from] ResolvePkgFolderFromDenoModuleError), #[class(inherit)] #[error("{0}")] ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError), @@ -746,7 +745,7 @@ fn op_resolve( state: &mut OpState, #[string] base: String, #[serde] specifiers: Vec<(bool, String)>, -) -> Result, ResolveError> { +) -> Result)>, ResolveError> { op_resolve_inner(state, ResolveArgs { base, specifiers }) } @@ -754,9 +753,9 @@ fn op_resolve( fn op_resolve_inner( state: &mut OpState, args: ResolveArgs, -) -> Result, ResolveError> { +) -> Result)>, ResolveError> { let state = state.borrow_mut::(); - let mut resolved: Vec<(String, &'static str)> = + 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) @@ -770,14 +769,14 @@ fn op_resolve_inner( if specifier.starts_with("node:") { resolved.push(( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), )); continue; } if specifier.starts_with("asset:///") { let ext = MediaType::from_str(&specifier).as_ts_extension(); - resolved.push((specifier, ext)); + resolved.push((specifier, Some(ext))); continue; } @@ -857,14 +856,15 @@ fn op_resolve_inner( ( specifier_str, match media_type { - MediaType::Css => ".js", // surface these as .js for typescript - media_type => media_type.as_ts_extension(), + MediaType::Css => Some(".js"), // surface these as .js for typescript + MediaType::Unknown => None, + media_type => Some(media_type.as_ts_extension()), }, ) } None => ( MISSING_DEPENDENCY_SPECIFIER.to_string(), - MediaType::Dts.as_ts_extension(), + Some(MediaType::Dts.as_ts_extension()), ), }; log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result); @@ -1441,7 +1441,10 @@ mod tests { }, ) .expect("should have invoked op"); - assert_eq!(actual, vec![("https://deno.land/x/b.ts".into(), ".ts")]); + assert_eq!( + actual, + vec![("https://deno.land/x/b.ts".into(), Some(".ts"))] + ); } #[tokio::test] @@ -1460,7 +1463,10 @@ mod tests { }, ) .expect("should have not errored"); - assert_eq!(actual, vec![(MISSING_DEPENDENCY_SPECIFIER.into(), ".d.ts")]); + assert_eq!( + actual, + vec![(MISSING_DEPENDENCY_SPECIFIER.into(), Some(".d.ts"))] + ); } #[tokio::test] diff --git a/cli/worker.rs b/cli/worker.rs index eee89d663c..d93c6c5f40 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -54,6 +54,8 @@ use crate::args::NpmCachingStrategy; use crate::args::StorageKeyResolver; use crate::node::CliNodeResolver; use crate::node::CliPackageJsonResolver; +use crate::npm::installer::NpmInstaller; +use crate::npm::installer::PackageCaching; use crate::npm::CliNpmResolver; use crate::sys::CliSys; use crate::util::checksum; @@ -148,6 +150,7 @@ struct SharedWorkerState { maybe_lockfile: Option>, module_loader_factory: Box, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, pkg_json_resolver: Arc, root_cert_store_provider: Arc, @@ -423,6 +426,7 @@ impl CliMainWorkerFactory { maybe_lockfile: Option>, module_loader_factory: Box, node_resolver: Arc, + npm_installer: Option>, npm_resolver: Arc, pkg_json_resolver: Arc, root_cert_store_provider: Arc, @@ -447,6 +451,7 @@ impl CliMainWorkerFactory { maybe_lockfile, module_loader_factory, node_resolver, + npm_installer, npm_resolver, pkg_json_resolver, root_cert_store_provider, @@ -496,18 +501,18 @@ impl CliMainWorkerFactory { let main_module = if let Ok(package_ref) = NpmPackageReqReference::from_specifier(&main_module) { - if let Some(npm_resolver) = shared.npm_resolver.as_managed() { + if let Some(npm_installer) = &shared.npm_installer { let reqs = &[package_ref.req().clone()]; - npm_resolver + npm_installer .add_package_reqs( reqs, if matches!( shared.default_npm_caching_strategy, NpmCachingStrategy::Lazy ) { - crate::npm::PackageCaching::Only(reqs.into()) + PackageCaching::Only(reqs.into()) } else { - crate::npm::PackageCaching::All + PackageCaching::All }, ) .await?; diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 64b6c006a1..731cd3a9c7 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -487,7 +487,7 @@ deno_core::extension!(deno_node, "_fs/_fs_watch.ts", "_fs/_fs_write.mjs", "_fs/_fs_writeFile.ts", - "_fs/_fs_writev.mjs", + "_fs/_fs_writev.ts", "_next_tick.ts", "_process/exiting.ts", "_process/process.ts", diff --git a/ext/node/polyfills/_fs/_fs_writev.d.ts b/ext/node/polyfills/_fs/_fs_writev.d.ts deleted file mode 100644 index 1ba5511ff1..0000000000 --- a/ext/node/polyfills/_fs/_fs_writev.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. -// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts - -import { ErrnoException } from "ext:deno_node/_global.d.ts"; - -/** - * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. - * - * `position` is the offset from the beginning of the file where this data - * should be written. If `typeof position !== 'number'`, the data will be written - * at the current position. - * - * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. - * - * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. - * - * It is unsafe to use `fs.writev()` multiple times on the same file without - * waiting for the callback. For this scenario, use {@link createWriteStream}. - * - * On Linux, positional writes don't work when the file is opened in append mode. - * The kernel ignores the position argument and always appends the data to - * the end of the file. - * @since v12.9.0 - */ -export function writev( - fd: number, - buffers: ReadonlyArray, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export function writev( - fd: number, - buffers: ReadonlyArray, - position: number | null, - cb: ( - err: ErrnoException | null, - bytesWritten: number, - buffers: ArrayBufferView[], - ) => void, -): void; -export interface WriteVResult { - bytesWritten: number; - buffers: ArrayBufferView[]; -} -export namespace writev { - function __promisify__( - fd: number, - buffers: ReadonlyArray, - position?: number, - ): Promise; -} -/** - * For detailed information, see the documentation of the asynchronous version of - * this API: {@link writev}. - * @since v12.9.0 - * @return The number of bytes written. - */ -export function writevSync( - fd: number, - buffers: ReadonlyArray, - position?: number, -): number; diff --git a/ext/node/polyfills/_fs/_fs_writev.mjs b/ext/node/polyfills/_fs/_fs_writev.ts similarity index 52% rename from ext/node/polyfills/_fs/_fs_writev.mjs rename to ext/node/polyfills/_fs/_fs_writev.ts index 61807c7f68..e38b44b19a 100644 --- a/ext/node/polyfills/_fs/_fs_writev.mjs +++ b/ext/node/polyfills/_fs/_fs_writev.ts @@ -5,15 +5,53 @@ // deno-lint-ignore-file prefer-primordials import { Buffer } from "node:buffer"; +import process from "node:process"; +import { ErrnoException } from "ext:deno_node/_global.d.ts"; import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs"; import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs"; +import { WriteVResult } from "ext:deno_node/internal/fs/handle.ts"; import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; import * as io from "ext:deno_io/12_io.js"; import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops"; -export function writev(fd, buffers, position, callback) { +export interface WriteVResult { + bytesWritten: number; + buffers: ReadonlyArray; +} + +type writeVCallback = ( + err: ErrnoException | null, + bytesWritten: number, + buffers: ReadonlyArray, +) => void; + +/** + * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. + * + * `position` is the offset from the beginning of the file where this data + * should be written. If `typeof position !== 'number'`, the data will be written + * at the current position. + * + * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. + * + * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. + * + * It is unsafe to use `fs.writev()` multiple times on the same file without + * waiting for the callback. For this scenario, use {@link createWriteStream}. + * + * On Linux, positional writes don't work when the file is opened in append mode. + * The kernel ignores the position argument and always appends the data to + * the end of the file. + * @since v12.9.0 + */ +export function writev( + fd: number, + buffers: ReadonlyArray, + position?: number | null, + callback?: writeVCallback, +): void { const innerWritev = async (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -45,16 +83,24 @@ export function writev(fd, buffers, position, callback) { if (typeof position !== "number") position = null; innerWritev(fd, buffers, position).then( - (nwritten) => { - callback(null, nwritten, buffers); - }, + (nwritten) => callback(null, nwritten, buffers), (err) => callback(err), ); } -export function writevSync(fd, buffers, position) { +/** + * For detailed information, see the documentation of the asynchronous version of + * this API: {@link writev}. + * @since v12.9.0 + * @return The number of bytes written. + */ +export function writevSync( + fd: number, + buffers: ArrayBufferView[], + position?: number | null, +): number { const innerWritev = (fd, buffers, position) => { - const chunks = []; + const chunks: Buffer[] = []; const offset = 0; for (let i = 0; i < buffers.length; i++) { if (Buffer.isBuffer(buffers[i])) { @@ -85,3 +131,16 @@ export function writevSync(fd, buffers, position) { return innerWritev(fd, buffers, position); } + +export function writevPromise( + fd: number, + buffers: ArrayBufferView[], + position?: number, +): Promise { + return new Promise((resolve, reject) => { + writev(fd, buffers, position, (err, bytesWritten, buffers) => { + if (err) reject(err); + else resolve({ bytesWritten, buffers }); + }); + }); +} diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 76ff9ebd12..a6e43b5baa 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -119,7 +119,7 @@ import { // @deno-types="./_fs/_fs_write.d.ts" import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs"; // @deno-types="./_fs/_fs_writev.d.ts" -import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.ts"; import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts"; import { writeFile, diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index 17809cc559..569ee7d328 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -277,6 +277,7 @@ export class ChildProcess extends EventEmitter { try { this.#process = new Deno.Command(cmd, { args: cmdArgs, + clearEnv: true, cwd, env: stringEnv, stdin: toDenoStdio(stdin), @@ -839,6 +840,7 @@ export function normalizeSpawnArguments( args, cwd, detached: !!options.detached, + env, envPairs, file, windowsHide: !!options.windowsHide, diff --git a/ext/node/polyfills/internal/fs/handle.ts b/ext/node/polyfills/internal/fs/handle.ts index 2d787a9f3b..774b36dfc2 100644 --- a/ext/node/polyfills/internal/fs/handle.ts +++ b/ext/node/polyfills/internal/fs/handle.ts @@ -6,7 +6,7 @@ import { EventEmitter } from "node:events"; import { Buffer } from "node:buffer"; import { Mode, promises, read, write } from "node:fs"; -export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { core } from "ext:core/mod.js"; import { BinaryOptionsArgument, FileOptionsArgument, @@ -14,7 +14,8 @@ import { TextOptionsArgument, } from "ext:deno_node/_fs/_fs_common.ts"; import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; -import { core } from "ext:core/mod.js"; +export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; +import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts"; interface WriteResult { bytesWritten: number; @@ -64,7 +65,7 @@ export class FileHandle extends EventEmitter { position, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }, ); }); @@ -72,7 +73,7 @@ export class FileHandle extends EventEmitter { return new Promise((resolve, reject) => { read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => { if (err) reject(err); - else resolve({ buffer: buffer, bytesRead: bytesRead }); + else resolve({ buffer, bytesRead }); }); }); } @@ -137,6 +138,10 @@ export class FileHandle extends EventEmitter { return fsCall(promises.writeFile, this, data, options); } + writev(buffers: ArrayBufferView[], position?: number): Promise { + return fsCall(writevPromise, this, buffers, position); + } + close(): Promise { // Note that Deno.close is not async return Promise.resolve(core.close(this.fd)); @@ -152,6 +157,14 @@ export class FileHandle extends EventEmitter { assertNotClosed(this, promises.chmod.name); return promises.chmod(this.#path, mode); } + + utimes( + atime: number | string | Date, + mtime: number | string | Date, + ): Promise { + assertNotClosed(this, promises.utimes.name); + return promises.utimes(this.#path, atime, mtime); + } } function assertNotClosed(handle: FileHandle, syscall: string) { diff --git a/ext/node/polyfills/internal/fs/streams.mjs b/ext/node/polyfills/internal/fs/streams.mjs index 5adb8da225..d2ebecbe9a 100644 --- a/ext/node/polyfills/internal/fs/streams.mjs +++ b/ext/node/polyfills/internal/fs/streams.mjs @@ -18,7 +18,7 @@ import { errorOrDestroy } from "ext:deno_node/internal/streams/destroy.mjs"; import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts"; import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts"; import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs"; -import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.mjs"; +import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.ts"; import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts"; import { Buffer } from "node:buffer"; import { diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts index 3f7603079e..bebdd8dade 100644 --- a/ext/node/polyfills/net.ts +++ b/ext/node/polyfills/net.ts @@ -1224,15 +1224,20 @@ export class Socket extends Duplex { super(options); - // Note: If the socket is created from @npmcli/agent, the 'socket' event - // on ClientRequest object happens after 'connect' event on Socket object. + // Note: If the socket is created from one of: + // - @npmcli/agent + // - npm-check-updates (bundles @npmcli/agent as a dependency) + // the 'socket' event on ClientRequest object happens after 'connect' event on Socket object. // That swaps the sequence of op_node_http_request_with_conn() call and // initial socket read. That causes op_node_http_request_with_conn() not // working. // To avoid the above situation, we detect the socket created from // @npmcli/agent and pause the socket (and also skips the startTls call // if it's TLSSocket) - this._isNpmAgent = new Error().stack?.includes("@npmcli/agent") || false; + // TODO(kt3k): Remove this workaround + const errorStack = new Error().stack; + this._isNpmAgent = errorStack?.includes("@npmcli/agent") || + errorStack?.includes("npm-check-updates") || false; if (this._isNpmAgent) { this.pause(); } diff --git a/resolvers/deno/npm/managed/global.rs b/resolvers/deno/npm/managed/global.rs index cffe68a30e..d16234d8f4 100644 --- a/resolvers/deno/npm/managed/global.rs +++ b/resolvers/deno/npm/managed/global.rs @@ -16,7 +16,7 @@ use node_resolver::errors::ReferrerNotFoundError; use node_resolver::NpmPackageFolderResolver; use url::Url; -use super::resolution::NpmResolutionRc; +use super::resolution::NpmResolutionCellRc; use super::NpmCacheDirRc; use super::NpmPackageFsResolver; use crate::ResolvedNpmRcRc; @@ -26,14 +26,14 @@ use crate::ResolvedNpmRcRc; pub struct GlobalNpmPackageResolver { cache: NpmCacheDirRc, npm_rc: ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, } impl GlobalNpmPackageResolver { pub fn new( cache: NpmCacheDirRc, npm_rc: ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, ) -> Self { Self { cache, diff --git a/resolvers/deno/npm/managed/local.rs b/resolvers/deno/npm/managed/local.rs index 3aa1275d66..72b0b0d451 100644 --- a/resolvers/deno/npm/managed/local.rs +++ b/resolvers/deno/npm/managed/local.rs @@ -19,7 +19,7 @@ use sys_traits::FsCanonicalize; use sys_traits::FsMetadata; use url::Url; -use super::resolution::NpmResolutionRc; +use super::resolution::NpmResolutionCellRc; use super::NpmPackageFsResolver; use crate::npm::local::get_package_folder_id_folder_name_from_parts; use crate::npm::local::get_package_folder_id_from_folder_name; @@ -32,7 +32,7 @@ use crate::sync::MaybeSync; pub struct LocalNpmPackageResolver< TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync, > { - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, root_node_modules_path: PathBuf, root_node_modules_url: Url, @@ -43,7 +43,7 @@ impl { #[allow(clippy::too_many_arguments)] pub fn new( - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, node_modules_folder: PathBuf, ) -> Self { diff --git a/resolvers/deno/npm/managed/mod.rs b/resolvers/deno/npm/managed/mod.rs index a9775ee374..62147b2ac3 100644 --- a/resolvers/deno/npm/managed/mod.rs +++ b/resolvers/deno/npm/managed/mod.rs @@ -8,10 +8,13 @@ mod resolution; use std::path::Path; use std::path::PathBuf; +use deno_npm::resolution::PackageCacheFolderIdNotFoundError; +use deno_npm::resolution::PackageNvNotFoundError; use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageId; use deno_path_util::fs::canonicalize_path_maybe_not_exists; +use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use node_resolver::InNpmPackageChecker; use node_resolver::NpmPackageFolderResolver; @@ -23,14 +26,24 @@ use self::common::NpmPackageFsResolver; use self::common::NpmPackageFsResolverRc; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; -pub use self::resolution::NpmResolution; -pub use self::resolution::NpmResolutionRc; +pub use self::resolution::NpmResolutionCell; +pub use self::resolution::NpmResolutionCellRc; use crate::sync::new_rc; use crate::sync::MaybeSend; use crate::sync::MaybeSync; use crate::NpmCacheDirRc; use crate::ResolvedNpmRcRc; +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgFolderFromDenoModuleError { + #[class(inherit)] + #[error(transparent)] + PackageNvNotFound(#[from] PackageNvNotFoundError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), +} + #[derive(Debug, thiserror::Error, deno_error::JsError)] #[error(transparent)] pub enum ResolvePkgFolderFromPkgIdError { @@ -66,6 +79,16 @@ pub enum ManagedResolvePkgFolderFromDenoReqError { ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolvePkgIdFromSpecifierError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + NotFound(#[from] PackageCacheFolderIdNotFoundError), +} + #[allow(clippy::disallowed_types)] pub type ManagedNpmResolverRc = crate::sync::MaybeArc>; @@ -73,7 +96,7 @@ pub type ManagedNpmResolverRc = #[derive(Debug)] pub struct ManagedNpmResolver { fs_resolver: NpmPackageFsResolverRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TSys, } @@ -89,7 +112,7 @@ impl ManagedNpmResolver { >( npm_cache_dir: &NpmCacheDirRc, npm_rc: &ResolvedNpmRcRc, - resolution: NpmResolutionRc, + resolution: NpmResolutionCellRc, sys: TCreateSys, maybe_node_modules_path: Option, ) -> ManagedNpmResolver { @@ -144,6 +167,14 @@ impl ManagedNpmResolver { Ok(path) } + pub fn resolve_pkg_folder_from_deno_module( + &self, + nv: &PackageNv, + ) -> Result { + let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?; + Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?) + } + pub fn resolve_pkg_folder_from_deno_module_req( &self, req: &PackageReq, @@ -162,6 +193,24 @@ impl ManagedNpmResolver { .fs_resolver .resolve_package_cache_folder_id_from_specifier(specifier) } + + /// Resolves the package id from the provided specifier. + pub fn resolve_pkg_id_from_specifier( + &self, + specifier: &Url, + ) -> Result, ResolvePkgIdFromSpecifierError> { + let Some(cache_folder_id) = self + .fs_resolver + .resolve_package_cache_folder_id_from_specifier(specifier)? + else { + return Ok(None); + }; + Ok(Some( + self + .resolution + .resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?, + )) + } } impl diff --git a/resolvers/deno/npm/managed/resolution.rs b/resolvers/deno/npm/managed/resolution.rs index 8ddd1ac70f..08dabf5a76 100644 --- a/resolvers/deno/npm/managed/resolution.rs +++ b/resolvers/deno/npm/managed/resolution.rs @@ -18,16 +18,17 @@ use deno_semver::package::PackageReq; use parking_lot::RwLock; #[allow(clippy::disallowed_types)] -pub type NpmResolutionRc = crate::sync::MaybeArc; +pub type NpmResolutionCellRc = crate::sync::MaybeArc; /// Handles updating and storing npm resolution in memory. /// /// This does not interact with the file system. -pub struct NpmResolution { +#[derive(Default)] +pub struct NpmResolutionCell { snapshot: RwLock, } -impl std::fmt::Debug for NpmResolution { +impl std::fmt::Debug for NpmResolutionCell { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let snapshot = self.snapshot.read(); f.debug_struct("NpmResolution") @@ -36,7 +37,7 @@ impl std::fmt::Debug for NpmResolution { } } -impl NpmResolution { +impl NpmResolutionCell { pub fn from_serialized( initial_snapshot: Option, ) -> Self { diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs index 7d8c98eafc..faee4f1645 100644 --- a/resolvers/node/lib.rs +++ b/resolvers/node/lib.rs @@ -9,6 +9,7 @@ mod npm; mod package_json; mod path; mod resolution; + mod sync; pub use deno_package_json::PackageJson; @@ -22,6 +23,7 @@ pub use package_json::PackageJsonThreadLocalCache; pub use path::PathClean; pub use resolution::parse_npm_pkg_name; pub use resolution::resolve_specifier_into_node_modules; +pub use resolution::ConditionsFromResolutionMode; pub use resolution::IsBuiltInNodeModuleChecker; pub use resolution::NodeResolution; pub use resolution::NodeResolutionKind; diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs index d0b52213f3..a60a6bec9e 100644 --- a/resolvers/node/resolution.rs +++ b/resolvers/node/resolution.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use std::fmt::Debug; use std::path::Path; use std::path::PathBuf; @@ -54,12 +55,32 @@ pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; static TYPES_ONLY_CONDITIONS: &[&str] = &["types"]; -fn conditions_from_resolution_mode( - resolution_mode: ResolutionMode, -) -> &'static [&'static str] { - match resolution_mode { - ResolutionMode::Import => DEFAULT_CONDITIONS, - ResolutionMode::Require => REQUIRE_CONDITIONS, +type ConditionsFromResolutionModeFn = Box< + dyn Fn(ResolutionMode) -> &'static [&'static str] + Send + Sync + 'static, +>; + +#[derive(Default)] +pub struct ConditionsFromResolutionMode(Option); + +impl Debug for ConditionsFromResolutionMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConditionsFromResolutionMode").finish() + } +} + +impl ConditionsFromResolutionMode { + pub fn new(func: ConditionsFromResolutionModeFn) -> Self { + Self(Some(func)) + } + + fn resolve( + &self, + resolution_mode: ResolutionMode, + ) -> &'static [&'static str] { + match &self.0 { + Some(func) => func(ResolutionMode::Import), + None => resolution_mode.default_conditions(), + } } } @@ -69,6 +90,15 @@ pub enum ResolutionMode { Require, } +impl ResolutionMode { + pub fn default_conditions(&self) -> &'static [&'static str] { + match self { + ResolutionMode::Import => DEFAULT_CONDITIONS, + ResolutionMode::Require => REQUIRE_CONDITIONS, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum NodeResolutionKind { Execution, @@ -120,6 +150,7 @@ pub struct NodeResolver< npm_pkg_folder_resolver: NpmPackageFolderResolverRc, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, } impl< @@ -133,6 +164,7 @@ impl< npm_pkg_folder_resolver: NpmPackageFolderResolverRc, pkg_json_resolver: PackageJsonResolverRc, sys: TSys, + conditions_from_resolution_mode: ConditionsFromResolutionMode, ) -> Self { Self { in_npm_pkg_checker, @@ -140,6 +172,7 @@ impl< npm_pkg_folder_resolver, pkg_json_resolver, sys, + conditions_from_resolution_mode, } } @@ -197,11 +230,14 @@ impl< } } + let conditions = self + .conditions_from_resolution_mode + .resolve(resolution_mode); let url = self.module_resolve( specifier, referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, resolution_kind, )?; @@ -211,6 +247,7 @@ impl< &file_path, Some(referrer), resolution_mode, + conditions, )? } else { url @@ -343,7 +380,9 @@ impl< &package_subpath, maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + self + .conditions_from_resolution_mode + .resolve(resolution_mode), resolution_kind, )?; // TODO(bartlomieju): skipped checking errors for commonJS resolution and @@ -411,6 +450,7 @@ impl< path: &Path, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], ) -> Result { fn probe_extensions( sys: &TSys, @@ -474,7 +514,7 @@ impl< /* sub path */ ".", maybe_referrer, resolution_mode, - conditions_from_resolution_mode(resolution_mode), + conditions, NodeResolutionKind::Types, ); if let Ok(resolution) = resolution_result { @@ -855,6 +895,7 @@ impl< &path, maybe_referrer, resolution_mode, + conditions, )?)); } else { return Ok(Some(url)); @@ -1212,6 +1253,7 @@ impl< package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1249,6 +1291,7 @@ impl< package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1268,6 +1311,7 @@ impl< package_json, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1281,6 +1325,7 @@ impl< package_subpath, referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| { @@ -1294,12 +1339,18 @@ impl< package_subpath: &str, referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { assert_ne!(package_subpath, "."); let file_path = directory.join(package_subpath); if resolution_kind.is_types() { - Ok(self.path_to_declaration_url(&file_path, referrer, resolution_mode)?) + Ok(self.path_to_declaration_url( + &file_path, + referrer, + resolution_mode, + conditions, + )?) } else { Ok(url_from_file_path(&file_path).unwrap()) } @@ -1311,6 +1362,7 @@ impl< package_subpath: &str, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { if package_subpath == "." { @@ -1327,6 +1379,7 @@ impl< package_subpath, maybe_referrer, resolution_mode, + conditions, resolution_kind, ) .map_err(|err| err.into()) @@ -1338,6 +1391,7 @@ impl< package_json: &PackageJson, maybe_referrer: Option<&Url>, resolution_mode: ResolutionMode, + conditions: &[&str], resolution_kind: NodeResolutionKind, ) -> Result { let pkg_json_kind = match resolution_mode { @@ -1356,6 +1410,7 @@ impl< &main, maybe_referrer, resolution_mode, + conditions, ); // don't surface errors, fallback to checking the index now if let Ok(url) = decl_url_result { diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 4c737f1126..4682085aea 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -140,7 +140,9 @@ pub trait NpmProcessStateProvider: #[derive(Debug)] pub struct EmptyNpmProcessStateProvider; + impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {} + deno_core::extension!( deno_process, ops = [ diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 247851da9c..bbeb6e7c13 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -11690,6 +11690,46 @@ fn lsp_format_exclude_default_config() { client.shutdown(); } +#[test] +fn lsp_format_untitled() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + "languageId": "typescript", + "version": 1, + "text": " console.log();\n", + }, + })); + let res = client.write_request( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "untitled:Untitled-1", + }, + "options": { + "tabSize": 2, + "insertSpaces": true, + }, + }), + ); + assert_eq!( + res, + json!([ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 2 }, + }, + "newText": "", + }, + ]) + ); + client.shutdown(); +} + #[test] fn lsp_format_json() { let context = TestContextBuilder::new().use_temp_cwd().build(); @@ -17221,3 +17261,38 @@ fn lsp_wasm_module() { ); client.shutdown(); } + +#[test] +fn wildcard_augment() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + let temp_dir = context.temp_dir().path(); + let source = source_file( + temp_dir.join("index.ts"), + r#" + import styles from "./hello_world.scss"; + + function bar(v: string): string { + return v; + } + + bar(styles); + "#, + ); + temp_dir.join("index.d.ts").write( + r#" + declare module '*.scss' { + const content: string; + export default content; + } + "#, + ); + temp_dir + .join("hello_world.scss") + .write("body { color: red; }"); + + client.initialize_default(); + + let diagnostics = client.did_open_file(&source); + assert_eq!(diagnostics.all().len(), 0); +} diff --git a/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz b/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz new file mode 100644 index 0000000000..4a95f26159 Binary files /dev/null and b/tests/registry/npm/npm-check-updates/npm-check-updates-17.1.13.tgz differ diff --git a/tests/registry/npm/npm-check-updates/registry.json b/tests/registry/npm/npm-check-updates/registry.json new file mode 100644 index 0000000000..71d96fd83d --- /dev/null +++ b/tests/registry/npm/npm-check-updates/registry.json @@ -0,0 +1,201 @@ +{ + "name": "npm-check-updates", + "dist-tags": { + "latest": "17.1.13" + }, + "versions": { + "17.1.13": { + "name": "npm-check-updates", + "version": "17.1.13", + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "description": "Find newer versions of dependencies than what your package.json allows", + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + }, + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "rimraf build && npm run build:options && vite build", + "build:options": "vite-node src/scripts/build-options.ts", + "build:analyze": "rimraf build && npm run build:options && ANALYZER=true vite build", + "lint": "cross-env FORCE_COLOR=1 npm-run-all --parallel --aggregate-output lint:*", + "lint:lockfile": "lockfile-lint", + "lint:markdown": "markdownlint \"**/*.md\" --ignore \"**/node_modules/**/*.md\" --ignore build --config .markdownlint.js", + "lint:src": "eslint --cache --cache-location node_modules/.cache/.eslintcache --ignore-path .gitignore --report-unused-disable-directives .", + "prepare": "src/scripts/install-hooks", + "prepublishOnly": "npm run build", + "prettier": "prettier . --check", + "test": "npm run test:unit && npm run test:e2e", + "test:bun": "test/bun-install.sh && mocha test/bun", + "test:unit": "mocha test test/package-managers/*", + "test:e2e": "./test/e2e.sh", + "ncu": "node build/cli.js" + }, + "bin": { + "npm-check-updates": "build/cli.js", + "ncu": "build/cli.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "overrides": { + "ip": "2.0.1", + "jsonparse": "https://github.com/ARitz-Cracker/jsonparse/tree/patch-1", + "@yarnpkg/parsers": "2.6.0" + }, + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/chai": "^4.3.19", + "@types/chai-as-promised": "^8.0.0", + "@types/chai-string": "^1.4.5", + "@types/cli-table": "^0.3.4", + "@types/hosted-git-info": "^3.0.5", + "@types/ini": "^4.1.1", + "@types/js-yaml": "^4.0.9", + "@types/json-parse-helpfulerror": "^1.0.3", + "@types/jsonlines": "^0.1.5", + "@types/lodash": "^4.17.10", + "@types/mocha": "^10.0.9", + "@types/node": "^22.7.5", + "@types/npm-registry-fetch": "^8.0.7", + "@types/parse-github-url": "^1.0.3", + "@types/picomatch": "^3.0.1", + "@types/progress": "^2.0.7", + "@types/prompts": "^2.4.9", + "@types/remote-git-tags": "^4.0.2", + "@types/semver": "^7.5.8", + "@types/semver-utils": "^1.1.3", + "@types/sinon": "^17.0.3", + "@types/update-notifier": "^6.0.8", + "@typescript-eslint/eslint-plugin": "^8.9.0", + "@typescript-eslint/parser": "^8.9.0", + "camelcase": "^6.3.0", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.2", + "chai-string": "^1.5.0", + "chalk": "^5.3.0", + "cli-table3": "^0.6.5", + "commander": "^12.1.0", + "cross-env": "^7.0.3", + "dequal": "^2.0.3", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-config-raine": "^0.5.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.4.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.6.0", + "fast-glob": "^3.3.2", + "fast-memoize": "^2.5.2", + "find-up": "5.0.0", + "fp-and-or": "^1.0.2", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "js-yaml": "^4.1.0", + "json-parse-helpfulerror": "^1.0.3", + "jsonlines": "^0.1.1", + "lockfile-lint": "^4.14.0", + "lodash": "^4.17.21", + "markdownlint-cli": "^0.42.0", + "mocha": "^10.7.3", + "npm-registry-fetch": "^18.0.2", + "npm-run-all": "^4.1.5", + "p-map": "^4.0.0", + "parse-github-url": "^1.0.3", + "picomatch": "^4.0.2", + "prettier": "^3.3.3", + "progress": "^2.0.3", + "prompts-ncu": "^3.0.2", + "rc-config-loader": "^4.1.3", + "remote-git-tags": "^3.0.0", + "rfdc": "^1.4.1", + "rimraf": "^6.0.1", + "rollup-plugin-node-externals": "^7.1.3", + "semver": "^7.6.3", + "semver-utils": "^1.1.4", + "should": "^13.2.3", + "sinon": "^19.0.2", + "source-map-support": "^0.5.21", + "spawn-please": "^3.0.0", + "strip-ansi": "^7.1.0", + "strip-json-comments": "^5.0.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.3", + "typescript-json-schema": "^0.65.1", + "untildify": "^4.0.0", + "update-notifier": "^7.3.1", + "verdaccio": "^6.0.1", + "vite": "^5.4.9", + "vite-bundle-analyzer": "^0.12.1", + "vite-node": "^2.1.3", + "vite-plugin-dts": "^4.2.4", + "yarn": "^1.22.22" + }, + "lockfile-lint": { + "allowed-schemes": [ + "https:", + "git+ssh:" + ], + "allowed-hosts": [ + "npm", + "github.com" + ], + "empty-hostname": false, + "type": "npm ", + "path": "package-lock.json" + }, + "mocha": { + "check-leaks": true, + "extension": [ + "test.ts" + ], + "require": [ + "source-map-support/register", + "ts-node/register" + ], + "timeout": 60000, + "trace-deprecation": true, + "trace-warnings": true, + "use_strict": true + }, + "_id": "npm-check-updates@17.1.13", + "gitHead": "f514093647f1833c83653765493c694372d14fea", + "_nodeVersion": "22.0.0", + "_npmVersion": "10.9.1", + "dist": { + "integrity": "sha512-m9Woo2J5XVab0VcQpYvrQ0hx3ySI1mGbiHR595mc6Lr1/FIaTWvv+oU+T1WKSfXRiluKC/V5P6Bdk5agaYpqqg==", + "shasum": "93e1c5fa5b8e11bca0bd143650b14ffcf9fc6b5a", + "tarball": "http://localhost:4260/npm-check-updates/npm-check-updates-17.1.13.tgz", + "fileCount": 19, + "unpackedSize": 5336239 + }, + "directories": {}, + "_hasShrinkwrap": false + } + }, + "bugs": { + "url": "https://github.com/raineorshine/npm-check-updates/issues" + }, + "author": { + "name": "Tomas Junnonen", + "email": "tomas1@gmail.com" + }, + "license": "Apache-2.0", + "homepage": "https://github.com/raineorshine/npm-check-updates", + "repository": { + "type": "git", + "url": "git+https://github.com/raineorshine/npm-check-updates.git" + }, + "description": "Find newer versions of dependencies than what your package.json allows", + "readmeFilename": "README.md" +} diff --git a/tests/specs/compile/case_insensitive_building/__test__.jsonc b/tests/specs/compile/case_insensitive_building/__test__.jsonc new file mode 100644 index 0000000000..38636dc273 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/__test__.jsonc @@ -0,0 +1,24 @@ +{ + "tempDir": true, + "steps": [{ + "if": "mac", + "args": "compile --output main --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "mac", + "commandName": "./main", + "args": [], + "output": "main.out", + "exitCode": 0 + }, { + "if": "windows", + "args": "compile --output main.exe --include file.txt --include FILE.txt main.js", + "output": "compile.out" + }, { + "if": "windows", + "commandName": "./main.exe", + "args": [], + "output": "main.out", + "exitCode": 0 + }] +} diff --git a/tests/specs/compile/case_insensitive_building/compile.out b/tests/specs/compile/case_insensitive_building/compile.out new file mode 100644 index 0000000000..895c2f1d46 --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/compile.out @@ -0,0 +1,10 @@ +Compile file:///[WILDLINE]/main.js to main[WILDLINE] + +Embedded Files + +main[WILDLINE] +├── file.txt ([WILDLINE]) +└── main.js ([WILDLINE]) + +Size: [WILDLINE] + diff --git a/tests/specs/compile/case_insensitive_building/file.txt b/tests/specs/compile/case_insensitive_building/file.txt new file mode 100644 index 0000000000..40816a2b5a --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/file.txt @@ -0,0 +1 @@ +Hi \ No newline at end of file diff --git a/tests/specs/compile/case_insensitive_building/main.js b/tests/specs/compile/case_insensitive_building/main.js new file mode 100644 index 0000000000..0fafe8a4dd --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.js @@ -0,0 +1 @@ +console.log(Deno.readTextFileSync(import.meta.dirname + "/file.txt")); diff --git a/tests/specs/compile/case_insensitive_building/main.out b/tests/specs/compile/case_insensitive_building/main.out new file mode 100644 index 0000000000..b14df6442e --- /dev/null +++ b/tests/specs/compile/case_insensitive_building/main.out @@ -0,0 +1 @@ +Hi diff --git a/tests/specs/lint/with_malformed_config/with_malformed_config.out b/tests/specs/lint/with_malformed_config/with_malformed_config.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config/with_malformed_config.out +++ b/tests/specs/lint/with_malformed_config/with_malformed_config.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` diff --git a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out index 1c0f0fff6e..928ec02fb1 100644 --- a/tests/specs/lint/with_malformed_config2/with_malformed_config2.out +++ b/tests/specs/lint/with_malformed_config2/with_malformed_config2.out @@ -1,4 +1,4 @@ error: Failed to parse "lint" configuration Caused by: - unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report` + unknown field `dont_know_this_field`, expected one of `rules`, `include`, `exclude`, `files`, `report`, `plugins` diff --git a/tests/specs/npm/npm_check_updates/__test__.jsonc b/tests/specs/npm/npm_check_updates/__test__.jsonc new file mode 100644 index 0000000000..27b84c5f05 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/__test__.jsonc @@ -0,0 +1,7 @@ +{ + "tempDir": true, + "steps": [{ + "args": "run -A npm:npm-check-updates", + "output": "output.out" + }] +} diff --git a/tests/specs/npm/npm_check_updates/output.out b/tests/specs/npm/npm_check_updates/output.out new file mode 100644 index 0000000000..b8c1ae4957 --- /dev/null +++ b/tests/specs/npm/npm_check_updates/output.out @@ -0,0 +1,2 @@ +[WILDCARD] +All dependencies match the latest package versions[WILDCARD] \ No newline at end of file diff --git a/tests/specs/npm/npm_check_updates/package.json b/tests/specs/npm/npm_check_updates/package.json new file mode 100644 index 0000000000..82afc391cd --- /dev/null +++ b/tests/specs/npm/npm_check_updates/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chalk": "^5.0.1" + } +} diff --git a/tests/specs/run/node_prefix_missing/__test__.jsonc b/tests/specs/run/node_prefix_missing/__test__.jsonc index 305020ed97..fa4504fdc2 100644 --- a/tests/specs/run/node_prefix_missing/__test__.jsonc +++ b/tests/specs/run/node_prefix_missing/__test__.jsonc @@ -5,6 +5,11 @@ "output": "main.ts.out", "exitCode": 1 }, + "basic_no_config": { + "args": "run --quiet --no-config main.ts", + "output": "main_no_config.out", + "exitCode": 1 + }, "unstable_bare_node_builtins_enabled": { "args": "run --unstable-bare-node-builtins main.ts", "output": "feature_enabled.out" diff --git a/tests/specs/run/node_prefix_missing/config.json b/tests/specs/run/node_prefix_missing/config.json index 72f40aaf36..52f5f4d4f4 100644 --- a/tests/specs/run/node_prefix_missing/config.json +++ b/tests/specs/run/node_prefix_missing/config.json @@ -1,3 +1,4 @@ { + "imports": {}, "unstable": ["bare-node-builtins"] } diff --git a/tests/specs/run/node_prefix_missing/deno.json b/tests/specs/run/node_prefix_missing/deno.json new file mode 100644 index 0000000000..f6ca8454c5 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/deno.json @@ -0,0 +1,3 @@ +{ + "imports": {} +} diff --git a/tests/specs/run/node_prefix_missing/main.ts.out b/tests/specs/run/node_prefix_missing/main.ts.out index 48b4e37e27..c7067c6026 100644 --- a/tests/specs/run/node_prefix_missing/main.ts.out +++ b/tests/specs/run/node_prefix_missing/main.ts.out @@ -1,3 +1,3 @@ -error: Relative import path "fs" not prefixed with / or ./ or ../ +error: Relative import path "fs" not prefixed with / or ./ or ../ and not in import map from "[WILDLINE]/main.ts" hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/specs/run/node_prefix_missing/main_no_config.out b/tests/specs/run/node_prefix_missing/main_no_config.out new file mode 100644 index 0000000000..48b4e37e27 --- /dev/null +++ b/tests/specs/run/node_prefix_missing/main_no_config.out @@ -0,0 +1,3 @@ +error: Relative import path "fs" not prefixed with / or ./ or ../ + hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). + at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/unit/__snapshots__/lint_plugin_test.ts.snap b/tests/unit/__snapshots__/lint_plugin_test.ts.snap new file mode 100644 index 0000000000..337fcecc8f --- /dev/null +++ b/tests/unit/__snapshots__/lint_plugin_test.ts.snap @@ -0,0 +1,8608 @@ +export const snapshot = {}; + +snapshot[`Plugin - Program 1`] = ` +{ + body: [], + range: [ + 1, + 1, + ], + sourceType: "script", + type: "Program", +} +`; + +snapshot[`Plugin - ImportDeclaration 1`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 14, + ], + source: { + range: [ + 8, + 13, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 2`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 23, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 3`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 28, + ], + source: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 16, + ], + type: "ImportNamespaceSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 4`] = ` +{ + attributes: [], + importKind: "value", + range: [ + 1, + 39, + ], + source: { + range: [ + 33, + 38, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + importKind: "value", + imported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ImportSpecifier", + }, + { + importKind: "value", + imported: { + name: "bar", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "baz", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 25, + ], + type: "ImportSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ImportDeclaration 5`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 30, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 30, + 42, + ], + type: "ImportAttribute", + value: { + range: [ + 36, + 42, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + importKind: "value", + range: [ + 1, + 45, + ], + source: { + range: [ + 17, + 22, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + local: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "ImportDefaultSpecifier", + }, + ], + type: "ImportDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 1`] = ` +{ + attributes: [], + range: [ + 1, + 27, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 2`] = ` +{ + attributes: [], + range: [ + 1, + 34, + ], + source: { + range: [ + 28, + 33, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "baz", + optional: false, + range: [ + 17, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "bar", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 20, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportNamedDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 34, + 46, + ], + type: "ImportAttribute", + value: { + range: [ + 40, + 46, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 1, + 49, + ], + source: { + range: [ + 21, + 26, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + specifiers: [ + { + exportKind: "value", + exported: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + local: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + type: "ExportSpecifier", + }, + ], + type: "ExportNamedDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 1`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 31, + 33, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 16, + 33, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 33, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 2`] = ` +{ + declaration: { + async: false, + body: { + body: [], + range: [ + 28, + 30, + ], + type: "BlockStatement", + }, + declare: false, + generator: false, + id: null, + params: [], + range: [ + 16, + 30, + ], + returnType: null, + type: "FunctionDeclaration", + typeParameters: null, + }, + exportKind: "value", + range: [ + 1, + 30, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 3`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 28, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 22, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 16, + 28, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 28, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 4`] = ` +{ + declaration: { + abstract: false, + body: { + body: [], + range: [ + 16, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 16, + 24, + ], + superClass: null, + type: "ClassDeclaration", + }, + exportKind: "value", + range: [ + 1, + 24, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 5`] = ` +{ + declaration: { + name: "bar", + optional: false, + range: [ + 16, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + exportKind: "value", + range: [ + 1, + 20, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportDefaultDeclaration 6`] = ` +{ + declaration: { + body: { + body: [], + range: [ + 30, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "Foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 16, + 32, + ], + type: "TSInterfaceDeclaration", + typeParameters: [], + }, + exportKind: "type", + range: [ + 1, + 32, + ], + type: "ExportDefaultDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 1`] = ` +{ + attributes: [], + exportKind: "value", + exported: null, + range: [ + 1, + 21, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 2`] = ` +{ + attributes: [], + exportKind: "value", + exported: { + range: [ + 22, + 27, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 28, + ], + source: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - ExportAllDeclaration 3`] = ` +{ + attributes: [ + { + key: { + name: "type", + optional: false, + range: [ + 28, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 28, + 40, + ], + type: "ImportAttribute", + value: { + range: [ + 34, + 40, + ], + raw: '"json"', + type: "Literal", + value: "json", + }, + }, + ], + exportKind: "value", + exported: null, + range: [ + 1, + 43, + ], + source: { + range: [ + 15, + 20, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + type: "ExportAllDeclaration", +} +`; + +snapshot[`Plugin - TSExportAssignment 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSExportAssignment", +} +`; + +snapshot[`Plugin - TSNamespaceExportDeclaration 1`] = ` +{ + id: { + name: "A", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 23, + ], + type: "TSNamespaceExportDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 1`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - TSImportEqualsDeclaration 2`] = ` +{ + id: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + importKind: "value", + moduleReference: { + expression: { + range: [ + 20, + 25, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 12, + 26, + ], + type: "TSExternalModuleReference", + }, + range: [ + 1, + 26, + ], + type: "TSImportEqualsDeclaration", +} +`; + +snapshot[`Plugin - BlockStatement 1`] = ` +{ + body: [ + { + expression: { + name: "foo", + optional: false, + range: [ + 3, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 3, + 7, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 1, + 9, + ], + type: "BlockStatement", +} +`; + +snapshot[`Plugin - BreakStatement 1`] = ` +{ + label: null, + range: [ + 15, + 21, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - BreakStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 20, + 30, + ], + type: "BreakStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 1`] = ` +{ + label: null, + range: [ + 1, + 10, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - ContinueStatement 2`] = ` +{ + label: { + name: "foo", + optional: false, + range: [ + 10, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "ContinueStatement", +} +`; + +snapshot[`Plugin - DebuggerStatement 1`] = ` +{ + range: [ + 1, + 10, + ], + type: "DebuggerStatement", +} +`; + +snapshot[`Plugin - DoWhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 4, + 6, + ], + type: "BlockStatement", + }, + range: [ + 1, + 19, + ], + test: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "DoWhileStatement", +} +`; + +snapshot[`Plugin - ExpressionStatement 1`] = ` +{ + expression: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "ExpressionStatement", +} +`; + +snapshot[`Plugin - ForInStatement 1`] = ` +{ + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForInStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 1`] = ` +{ + await: false, + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForOfStatement 2`] = ` +{ + await: true, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + left: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 22, + ], + right: { + name: "b", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForOfStatement", +} +`; + +snapshot[`Plugin - ForStatement 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + init: null, + range: [ + 1, + 12, + ], + test: null, + type: "ForStatement", + update: null, +} +`; + +snapshot[`Plugin - ForStatement 2`] = ` +{ + body: { + body: [], + range: [ + 15, + 17, + ], + type: "BlockStatement", + }, + init: { + name: "a", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 17, + ], + test: { + name: "b", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ForStatement", + update: { + name: "c", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, +} +`; + +snapshot[`Plugin - IfStatement 1`] = ` +{ + alternate: null, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 12, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - IfStatement 2`] = ` +{ + alternate: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + consequent: { + body: [], + range: [ + 10, + 12, + ], + type: "BlockStatement", + }, + range: [ + 1, + 20, + ], + test: { + name: "foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "IfStatement", +} +`; + +snapshot[`Plugin - LabeledStatement 1`] = ` +{ + body: { + body: [], + range: [ + 6, + 8, + ], + type: "BlockStatement", + }, + label: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 8, + ], + type: "LabeledStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 1`] = ` +{ + argument: null, + range: [ + 1, + 7, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - ReturnStatement 2`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "ReturnStatement", +} +`; + +snapshot[`Plugin - SwitchStatement 1`] = ` +{ + cases: [ + { + consequent: [], + range: [ + 22, + 31, + ], + test: { + name: "foo", + optional: false, + range: [ + 27, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + label: null, + range: [ + 56, + 62, + ], + type: "BreakStatement", + }, + ], + range: [ + 38, + 62, + ], + test: { + name: "bar", + optional: false, + range: [ + 43, + 46, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "SwitchCase", + }, + { + consequent: [ + { + body: [], + range: [ + 86, + 88, + ], + type: "BlockStatement", + }, + ], + range: [ + 69, + 88, + ], + test: null, + type: "SwitchCase", + }, + ], + discriminant: { + name: "foo", + optional: false, + range: [ + 9, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 94, + ], + type: "SwitchStatement", +} +`; + +snapshot[`Plugin - ThrowStatement 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "ThrowStatement", +} +`; + +snapshot[`Plugin - TryStatement 1`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 14, + 16, + ], + type: "BlockStatement", + }, + param: null, + range: [ + 8, + 16, + ], + type: "CatchClause", + }, + range: [ + 1, + 16, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 2`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: null, + handler: { + body: { + body: [], + range: [ + 18, + 20, + ], + type: "BlockStatement", + }, + param: { + name: "e", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 20, + ], + type: "CatchClause", + }, + range: [ + 1, + 20, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - TryStatement 3`] = ` +{ + block: { + body: [], + range: [ + 5, + 7, + ], + type: "BlockStatement", + }, + finalizer: { + body: [], + range: [ + 16, + 18, + ], + type: "BlockStatement", + }, + handler: null, + range: [ + 1, + 18, + ], + type: "TryStatement", +} +`; + +snapshot[`Plugin - WhileStatement 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + range: [ + 1, + 15, + ], + test: { + name: "foo", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "WhileStatement", +} +`; + +snapshot[`Plugin - WithStatement 1`] = ` +{ + body: { + body: [], + range: [ + 11, + 13, + ], + type: "BlockStatement", + }, + object: { + elements: [], + range: [ + 7, + 9, + ], + type: "ArrayExpression", + }, + range: [ + 1, + 13, + ], + type: "WithStatement", +} +`; + +snapshot[`Plugin - ArrayExpression 1`] = ` +{ + elements: [ + { + elements: [], + range: [ + 2, + 4, + ], + type: "ArrayExpression", + }, + ], + range: [ + 1, + 9, + ], + type: "ArrayExpression", +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 7, + 9, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 9, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 2`] = ` +{ + async: true, + body: { + body: [], + range: [ + 13, + 15, + ], + type: "BlockStatement", + }, + generator: false, + params: [], + range: [ + 1, + 15, + ], + returnType: null, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - ArrowFunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 34, + 36, + ], + type: "BlockStatement", + }, + generator: false, + params: [ + { + name: "a", + optional: false, + range: [ + 2, + 11, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 3, + 11, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 5, + 11, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 13, + 24, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 17, + 24, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 19, + 22, + ], + type: "TSAnyKeyword", + }, + range: [ + 19, + 24, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 1, + 36, + ], + returnType: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + type: "ArrowFunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - AssignmentExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AssignmentExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 1, + 12, + ], + right: { + left: { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??=", + range: [ + 5, + 12, + ], + right: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + type: "AssignmentExpression", +} +`; + +snapshot[`Plugin - AwaitExpression 1`] = ` +{ + argument: { + name: "foo", + optional: false, + range: [ + 7, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "AwaitExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 4`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 5`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "==", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 6`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "===", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 7`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 8`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "!=", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 9`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "<<", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 10`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 11`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: ">>>", + range: [ + 1, + 8, + ], + right: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 12`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 13`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 14`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "*", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 15`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "/", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 16`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "%", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 17`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 18`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "^", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 19`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&", + range: [ + 1, + 6, + ], + right: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 20`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "in", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - BinaryExpression 21`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "**", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", +} +`; + +snapshot[`Plugin - CallExpression 1`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 6, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 8, + 11, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 13, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 3`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 1, + 8, + ], + type: "CallExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - CallExpression 4`] = ` +{ + arguments: [], + callee: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 1, + 9, + ], + type: "CallExpression", + typeArguments: { + params: [ + { + range: [ + 5, + 6, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 4, + 7, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ChainExpression 1`] = ` +{ + expression: { + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + property: { + name: "b", + optional: false, + range: [ + 4, + 5, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 5, + ], + type: "MemberExpression", + }, + range: [ + 1, + 5, + ], + type: "ChainExpression", +} +`; + +snapshot[`Plugin - ClassExpression 1`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 13, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 13, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 2`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 17, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 17, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 3`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 29, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 29, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 4`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 50, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [ + { + expression: { + name: "Baz", + optional: false, + range: [ + 38, + 41, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 38, + 41, + ], + type: "TSClassImplements", + typeArguments: null, + }, + { + expression: { + name: "Baz2", + optional: false, + range: [ + 43, + 47, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 43, + 47, + ], + type: "TSClassImplements", + typeArguments: null, + }, + ], + range: [ + 5, + 50, + ], + superClass: { + name: "Bar", + optional: false, + range: [ + 23, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 5`] = ` +{ + abstract: false, + body: { + body: [], + range: [ + 5, + 20, + ], + type: "ClassBody", + }, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + implements: [], + range: [ + 5, + 20, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 6`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 21, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 19, + 21, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 21, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 23, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 23, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 7`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + kind: "method", + optional: false, + override: false, + range: [ + 13, + 22, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 13, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 8`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: null, + }, + ], + range: [ + 5, + 26, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 26, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 9`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 13, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 22, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 19, + 22, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 24, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 24, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 10`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + key: { + name: "constructor", + optional: false, + range: [ + 13, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "constructor", + optional: false, + override: false, + range: [ + 13, + 47, + ], + static: false, + type: "MethodDefinition", + value: { + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + accessibility: undefined, + decorators: [], + override: false, + parameter: { + name: "foo", + optional: false, + range: [ + 32, + 35, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 35, + 43, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 37, + 43, + ], + type: "TSStringKeyword", + }, + }, + }, + range: [ + 25, + 43, + ], + readonly: false, + static: false, + type: "TSParameterProperty", + }, + ], + range: [ + 13, + 47, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, + }, + }, + ], + range: [ + 5, + 49, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 49, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 11`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", + }, + optional: false, + override: false, + range: [ + 13, + 31, + ], + readonly: false, + static: false, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 28, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 33, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 33, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 12`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 29, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: { + name: "bar", + optional: false, + range: [ + 26, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 31, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 31, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ClassExpression 13`] = ` +{ + abstract: false, + body: { + body: [ + { + accessibility: undefined, + computed: false, + declare: false, + decorators: [], + key: { + name: "foo", + optional: false, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + override: false, + range: [ + 13, + 24, + ], + readonly: false, + static: true, + type: "PropertyDefinition", + value: null, + }, + { + body: { + body: [ + { + expression: { + left: { + name: "foo", + optional: false, + range: [ + 34, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "=", + range: [ + 34, + 43, + ], + right: { + name: "bar", + optional: false, + range: [ + 40, + 43, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "AssignmentExpression", + }, + range: [ + 34, + 43, + ], + type: "ExpressionStatement", + }, + ], + range: [ + 32, + 45, + ], + type: "BlockStatement", + }, + range: [ + 25, + 45, + ], + type: "StaticBlock", + }, + ], + range: [ + 5, + 47, + ], + type: "ClassBody", + }, + declare: false, + id: null, + implements: [], + range: [ + 5, + 47, + ], + superClass: null, + type: "ClassExpression", +} +`; + +snapshot[`Plugin - ConditionalExpression 1`] = ` +{ + alternate: { + name: "c", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + consequent: { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + test: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "ConditionalExpression", +} +`; + +snapshot[`Plugin - FunctionExpression 1`] = ` +{ + async: false, + body: { + body: [], + range: [ + 17, + 19, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [], + range: [ + 5, + 19, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 2`] = ` +{ + async: false, + body: { + body: [], + range: [ + 20, + 22, + ], + type: "BlockStatement", + }, + generator: false, + id: { + name: "foo", + optional: false, + range: [ + 14, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 5, + 22, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 3`] = ` +{ + async: false, + body: { + body: [], + range: [ + 45, + 47, + ], + type: "BlockStatement", + }, + generator: false, + id: null, + params: [ + { + name: "a", + optional: true, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 17, + 25, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 19, + 25, + ], + type: "TSNumberKeyword", + }, + }, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 27, + 38, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 33, + 36, + ], + type: "TSAnyKeyword", + }, + range: [ + 33, + 38, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 5, + 47, + ], + returnType: { + range: [ + 39, + 44, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 44, + ], + type: "TSAnyKeyword", + }, + }, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - FunctionExpression 4`] = ` +{ + async: true, + body: { + body: [], + range: [ + 24, + 26, + ], + type: "BlockStatement", + }, + generator: true, + id: null, + params: [], + range: [ + 5, + 26, + ], + returnType: null, + type: "FunctionExpression", + typeParameters: null, +} +`; + +snapshot[`Plugin - Identifier 1`] = ` +{ + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, +} +`; + +snapshot[`Plugin - ImportExpression 1`] = ` +{ + options: { + properties: [ + { + computed: false, + key: { + name: "with", + optional: false, + range: [ + 17, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 17, + 39, + ], + shorthand: false, + type: "Property", + value: { + properties: [ + { + computed: false, + key: { + name: "type", + optional: false, + range: [ + 25, + 29, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 25, + 37, + ], + shorthand: false, + type: "Property", + value: { + range: [ + 31, + 37, + ], + raw: "'json'", + type: "Literal", + value: "json", + }, + }, + ], + range: [ + 23, + 39, + ], + type: "ObjectExpression", + }, + }, + ], + range: [ + 15, + 41, + ], + type: "ObjectExpression", + }, + range: [ + 1, + 42, + ], + source: { + range: [ + 8, + 13, + ], + raw: "'foo'", + type: "Literal", + value: "foo", + }, + type: "ImportExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "&&", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 2`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "||", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - LogicalExpression 3`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "??", + range: [ + 1, + 7, + ], + right: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "LogicalExpression", +} +`; + +snapshot[`Plugin - MemberExpression 1`] = ` +{ + computed: false, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + name: "b", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 4, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MemberExpression 2`] = ` +{ + computed: true, + object: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + property: { + range: [ + 3, + 6, + ], + raw: "'b'", + type: "Literal", + value: "b", + }, + range: [ + 1, + 7, + ], + type: "MemberExpression", +} +`; + +snapshot[`Plugin - MetaProperty 1`] = ` +{ + property: { + name: "meta", + optional: false, + range: [ + 1, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "MetaProperty", +} +`; + +snapshot[`Plugin - NewExpression 1`] = ` +{ + arguments: [], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 10, + ], + type: "NewExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - NewExpression 2`] = ` +{ + arguments: [ + { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + argument: { + name: "b", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 18, + ], + type: "SpreadElement", + }, + ], + callee: { + name: "Foo", + optional: false, + range: [ + 5, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 20, + ], + type: "NewExpression", + typeArguments: { + params: [ + { + range: [ + 9, + 10, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 9, + 10, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 8, + 11, + ], + type: "TSTypeParameterInstantiation", + }, +} +`; + +snapshot[`Plugin - ObjectExpression 1`] = ` +{ + properties: [], + range: [ + 5, + 7, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 2`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 8, + ], + shorthand: true, + type: "Property", + value: { + name: "a", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 10, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - ObjectExpression 3`] = ` +{ + properties: [ + { + computed: false, + key: { + name: "b", + optional: false, + range: [ + 7, + 8, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 7, + 11, + ], + shorthand: false, + type: "Property", + value: { + name: "c", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + computed: true, + key: { + name: "c", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "init", + method: false, + range: [ + 13, + 19, + ], + shorthand: false, + type: "Property", + value: { + name: "d", + optional: false, + range: [ + 18, + 19, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 21, + ], + type: "ObjectExpression", +} +`; + +snapshot[`Plugin - PrivateIdentifier 1`] = ` +{ + name: "foo", + range: [ + 13, + 17, + ], + type: "PrivateIdentifier", +} +`; + +snapshot[`Plugin - SequenceExpression 1`] = ` +{ + expressions: [ + { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + { + name: "b", + optional: false, + range: [ + 5, + 6, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + range: [ + 2, + 6, + ], + type: "SequenceExpression", +} +`; + +snapshot[`Plugin - Super 1`] = ` +{ + range: [ + 41, + 46, + ], + type: "Super", +} +`; + +snapshot[`Plugin - TaggedTemplateExpression 1`] = ` +{ + quasi: { + expressions: [ + { + name: "bar", + optional: false, + range: [ + 11, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 5, + 9, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 15, + 19, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 4, + 20, + ], + type: "TemplateLiteral", + }, + range: [ + 1, + 20, + ], + tag: { + name: "foo", + optional: false, + range: [ + 1, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TaggedTemplateExpression", + typeArguments: null, +} +`; + +snapshot[`Plugin - TemplateLiteral 1`] = ` +{ + expressions: [ + { + name: "bar", + optional: false, + range: [ + 8, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + ], + quasis: [ + { + cooked: "foo ", + range: [ + 2, + 6, + ], + raw: "foo ", + tail: false, + type: "TemplateElement", + }, + { + cooked: " baz", + range: [ + 12, + 16, + ], + raw: " baz", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 1, + 17, + ], + type: "TemplateLiteral", +} +`; + +snapshot[`Plugin - ThisExpression 1`] = ` +{ + range: [ + 1, + 5, + ], + type: "ThisExpression", +} +`; + +snapshot[`Plugin - TSAsExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 7, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSAsExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 11, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 1`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 14, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 13, + 14, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "b", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - UnaryExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "typeof", + range: [ + 1, + 9, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 2`] = ` +{ + argument: { + range: [ + 6, + 7, + ], + raw: "0", + type: "Literal", + value: 0, + }, + operator: "void", + range: [ + 1, + 7, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "-", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UnaryExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 2, + 3, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "+", + range: [ + 1, + 3, + ], + type: "UnaryExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 1`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 2`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "++", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 3`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: false, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - UpdateExpression 4`] = ` +{ + argument: { + name: "a", + optional: false, + range: [ + 3, + 4, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "--", + prefix: true, + range: [ + 1, + 4, + ], + type: "UpdateExpression", +} +`; + +snapshot[`Plugin - YieldExpression 1`] = ` +{ + argument: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + delegate: false, + range: [ + 19, + 28, + ], + type: "YieldExpression", +} +`; + +snapshot[`Plugin - Literal 1`] = ` +{ + range: [ + 1, + 2, + ], + raw: "1", + type: "Literal", + value: 1, +} +`; + +snapshot[`Plugin - Literal 2`] = ` +{ + range: [ + 1, + 6, + ], + raw: "'foo'", + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 3`] = ` +{ + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", +} +`; + +snapshot[`Plugin - Literal 4`] = ` +{ + range: [ + 1, + 5, + ], + raw: "true", + type: "Literal", + value: true, +} +`; + +snapshot[`Plugin - Literal 5`] = ` +{ + range: [ + 1, + 6, + ], + raw: "false", + type: "Literal", + value: false, +} +`; + +snapshot[`Plugin - Literal 6`] = ` +{ + range: [ + 1, + 5, + ], + raw: "null", + type: "Literal", + value: null, +} +`; + +snapshot[`Plugin - Literal 7`] = ` +{ + bigint: "1", + range: [ + 1, + 3, + ], + raw: "1n", + type: "Literal", + value: 1n, +} +`; + +snapshot[`Plugin - Literal 8`] = ` +{ + range: [ + 1, + 7, + ], + raw: "/foo/g", + regex: { + flags: "g", + pattern: "foo", + }, + type: "Literal", + value: /foo/g, +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 1`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 2`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 8, + 11, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 12, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 12, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 3`] = ` +{ + children: [], + closingElement: { + name: { + name: "div", + range: [ + 10, + 13, + ], + type: "JSXIdentifier", + }, + range: [ + 8, + 14, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 7, + ], + type: "JSXAttribute", + value: null, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 4`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + range: [ + 8, + 11, + ], + raw: '"b"', + type: "Literal", + value: "b", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 5`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 11, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 9, + 10, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 8, + 11, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 14, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 14, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 6`] = ` +{ + children: [ + { + range: [ + 6, + 9, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 10, + 11, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 9, + 12, + ], + type: "JSXExpressionContainer", + }, + ], + closingElement: { + name: { + name: "div", + range: [ + 14, + 17, + ], + type: "JSXIdentifier", + }, + range: [ + 12, + 18, + ], + type: "JSXClosingElement", + }, + openingElement: { + attributes: [], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 6, + ], + selfClosing: false, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 18, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 7`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + object: { + name: "a", + range: [ + 2, + 3, + ], + type: "JSXIdentifier", + }, + property: { + name: "b", + range: [ + 4, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 2, + 5, + ], + type: "JSXMemberExpression", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 8`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [ + { + name: { + name: { + name: "b", + range: [ + 8, + 9, + ], + type: "JSXIdentifier", + }, + namespace: { + name: "a", + range: [ + 6, + 7, + ], + type: "JSXIdentifier", + }, + range: [ + 6, + 9, + ], + type: "JSXNamespacedName", + }, + range: [ + 6, + 13, + ], + type: "JSXAttribute", + value: { + expression: { + range: [ + 11, + 12, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 10, + 13, + ], + type: "JSXExpressionContainer", + }, + }, + ], + name: { + name: "div", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 16, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 16, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 9`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 8, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: null, + }, + range: [ + 1, + 8, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr 10`] = ` +{ + children: [], + closingElement: null, + openingElement: { + attributes: [], + name: { + name: "Foo", + range: [ + 2, + 5, + ], + type: "JSXIdentifier", + }, + range: [ + 1, + 11, + ], + selfClosing: true, + type: "JSXOpeningElement", + typeArguments: { + params: [ + { + range: [ + 6, + 7, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 5, + 8, + ], + type: "TSTypeParameterInstantiation", + }, + }, + range: [ + 1, + 11, + ], + type: "JSXElement", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 1`] = ` +{ + children: [], + closingFragment: { + range: [ + 3, + 6, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 6, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment 2`] = ` +{ + children: [ + { + range: [ + 3, + 6, + ], + raw: "foo", + type: "JSXText", + value: "foo", + }, + { + expression: { + range: [ + 7, + 8, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 6, + 9, + ], + type: "JSXExpressionContainer", + }, + ], + closingFragment: { + range: [ + 9, + 12, + ], + type: "JSXClosingFragment", + }, + openingFragment: { + range: [ + 1, + 3, + ], + type: "JSXOpeningFragment", + }, + range: [ + 1, + 12, + ], + type: "JSXFragment", +} +`; + +snapshot[`Plugin - TSAsExpression 3`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 9, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 6, + 9, + ], + type: "TSAnyKeyword", + }, +} +`; + +snapshot[`Plugin - TSAsExpression 4`] = ` +{ + expression: { + range: [ + 1, + 6, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 1, + 15, + ], + type: "TSAsExpression", + typeAnnotation: { + range: [ + 1, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "const", + optional: false, + range: [ + 1, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSEnumDeclaration 1`] = ` +{ + body: { + members: [], + range: [ + 1, + 12, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 2`] = ` +{ + body: { + members: [], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: true, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 12, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 3`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 12, + 13, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: null, + range: [ + 15, + 16, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 18, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 4`] = ` +{ + body: { + members: [ + { + id: { + range: [ + 12, + 17, + ], + raw: '"a-b"', + type: "Literal", + value: "a-b", + }, + initializer: null, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 19, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 19, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSEnumDeclaration 5`] = ` +{ + body: { + members: [ + { + id: { + name: "A", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 16, + 17, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 12, + 17, + ], + type: "TSEnumMember", + }, + { + id: { + name: "B", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + range: [ + 23, + 24, + ], + raw: "2", + type: "Literal", + value: 2, + }, + range: [ + 19, + 24, + ], + type: "TSEnumMember", + }, + { + id: { + name: "C", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + initializer: { + left: { + name: "A", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + operator: "|", + range: [ + 30, + 35, + ], + right: { + name: "B", + optional: false, + range: [ + 34, + 35, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "BinaryExpression", + }, + range: [ + 26, + 35, + ], + type: "TSEnumMember", + }, + ], + range: [ + 1, + 37, + ], + type: "TSEnumBody", + }, + const: false, + declare: false, + id: { + name: "Foo", + optional: false, + range: [ + 6, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 37, + ], + type: "TSEnumDeclaration", +} +`; + +snapshot[`Plugin - TSInterface 1`] = ` +{ + body: { + body: [], + range: [ + 13, + 15, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 15, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 2`] = ` +{ + body: { + body: [], + range: [ + 16, + 18, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 13, + 14, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 13, + 14, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 12, + 15, + ], + type: "TSTypeParameterDeclaration", + }, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 18, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 3`] = ` +{ + body: { + body: [], + range: [ + 36, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [ + { + expression: { + name: "Foo", + optional: false, + range: [ + 21, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 21, + 27, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 25, + 26, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 24, + 27, + ], + type: "TSTypeParameterInstantiation", + }, + }, + { + expression: { + name: "Bar", + optional: false, + range: [ + 29, + 32, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 29, + 35, + ], + type: "TSInterfaceHeritage", + typeArguments: { + params: [ + { + range: [ + 33, + 34, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 33, + 34, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], + range: [ + 32, + 35, + ], + type: "TSTypeParameterInstantiation", + }, + }, + ], +} +`; + +snapshot[`Plugin - TSInterface 4`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "foo", + optional: false, + range: [ + 15, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 24, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 18, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + computed: false, + key: { + name: "bar", + optional: false, + range: [ + 25, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: true, + range: [ + 25, + 34, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 29, + 34, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 31, + 34, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 36, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 36, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 5`] = ` +{ + body: { + body: [ + { + parameters: [ + { + name: "key", + optional: false, + range: [ + 25, + 36, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 28, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 36, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 42, + ], + readonly: true, + type: "TSIndexSignature", + typeAnnotation: { + range: [ + 37, + 42, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 39, + 42, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 44, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 44, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 6`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 30, + ], + readonly: true, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 25, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSAnyKeyword", + }, + }, + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 7`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 20, + 23, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 22, + 23, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 22, + 23, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 27, + ], + returnType: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSCallSignatureDeclaration", + typeAnnotation: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 29, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 29, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 8`] = ` +{ + body: { + body: [ + { + params: [ + { + name: "a", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 27, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 27, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 15, + 31, + ], + returnType: { + range: [ + 28, + 31, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 30, + 31, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 30, + 31, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 20, + 21, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 19, + 22, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 9`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 15, + 36, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 16, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + params: [ + { + name: "a", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 27, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 29, + 30, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 29, + 30, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + }, + ], + range: [ + 18, + 36, + ], + returnType: { + range: [ + 32, + 36, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 35, + 36, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 35, + 36, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + type: "TSConstructSignatureDeclaration", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 23, + 24, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 23, + 24, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 22, + 25, + ], + type: "TSTypeParameterDeclaration", + }, + }, + }, + }, + ], + range: [ + 13, + 38, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 38, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 10`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "getter", + optional: false, + range: [ + 15, + 30, + ], + readonly: false, + returnType: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 32, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 32, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 11`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 19, + 20, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "setter", + optional: false, + params: [ + { + name: "v", + optional: false, + range: [ + 21, + 22, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 22, + 30, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 24, + 30, + ], + type: "TSStringKeyword", + }, + }, + }, + ], + range: [ + 15, + 31, + ], + readonly: false, + static: false, + type: "TSMethodSignature", + }, + ], + range: [ + 13, + 33, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 33, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSInterface 12`] = ` +{ + body: { + body: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 15, + 16, + ], + type: "Identifier", + typeAnnotation: null, + }, + kind: "method", + optional: false, + params: [ + { + name: "arg", + optional: true, + range: [ + 20, + 23, + ], + type: "Identifier", + typeAnnotation: { + range: [ + 24, + 29, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 26, + 29, + ], + type: "TSAnyKeyword", + }, + }, + }, + { + argument: { + name: "args", + optional: false, + range: [ + 34, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 31, + 45, + ], + type: "RestElement", + typeAnnotation: { + range: [ + 38, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + elementType: { + range: [ + 40, + 43, + ], + type: "TSAnyKeyword", + }, + range: [ + 40, + 45, + ], + type: "TSArrayType", + }, + }, + }, + ], + range: [ + 15, + 51, + ], + readonly: false, + returnType: { + range: [ + 46, + 51, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 48, + 51, + ], + type: "TSAnyKeyword", + }, + }, + static: false, + type: "TSMethodSignature", + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 17, + 18, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 16, + 19, + ], + type: "TSTypeParameterDeclaration", + }, + }, + ], + range: [ + 13, + 53, + ], + type: "TSInterfaceBody", + }, + declare: false, + extends: null, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 53, + ], + type: "TSInterface", + typeParameters: [], +} +`; + +snapshot[`Plugin - TSSatisfiesExpression 2`] = ` +{ + expression: { + properties: [], + range: [ + 11, + 13, + ], + type: "ObjectExpression", + }, + range: [ + 11, + 25, + ], + type: "TSSatisfiesExpression", + typeAnnotation: { + range: [ + 24, + 25, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "A", + optional: false, + range: [ + 24, + 25, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 1`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 13, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", + }, + typeParameters: null, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 2`] = ` +{ + declare: false, + id: { + name: "A", + optional: false, + range: [ + 6, + 7, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 16, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 8, + 9, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 7, + 10, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSTypeAliasDeclaration 3`] = ` +{ + declare: true, + id: { + name: "A", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 24, + ], + type: "TSTypeAliasDeclaration", + typeAnnotation: { + range: [ + 21, + 24, + ], + type: "TSAnyKeyword", + }, + typeParameters: { + params: [ + { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "T", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 17, + ], + type: "TSTypeParameter", + }, + ], + range: [ + 15, + 18, + ], + type: "TSTypeParameterDeclaration", + }, +} +`; + +snapshot[`Plugin - TSNonNullExpression 2`] = ` +{ + expression: { + name: "a", + optional: false, + range: [ + 1, + 2, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 3, + ], + type: "TSNonNullExpression", +} +`; + +snapshot[`Plugin - TSUnionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSUnionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSIntersectionType 1`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSIntersectionType", + types: [ + { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + { + range: [ + 14, + 15, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + ], +} +`; + +snapshot[`Plugin - TSModuleDeclaration 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration 2`] = ` +{ + body: { + body: [ + { + declaration: { + async: false, + body: null, + declare: false, + generator: false, + id: { + name: "A", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + params: [], + range: [ + 27, + 45, + ], + returnType: { + range: [ + 39, + 45, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + range: [ + 41, + 45, + ], + type: "TSVoidKeyword", + }, + }, + type: "FunctionDeclaration", + typeParameters: null, + }, + range: [ + 20, + 45, + ], + type: "ExportNamedDeclaration", + }, + ], + range: [ + 18, + 47, + ], + type: "TSModuleBlock", + }, + declare: true, + global: false, + id: { + name: "A", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 47, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 1`] = ` +{ + body: { + body: [], + range: [ + 10, + 12, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 8, + 9, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 12, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSModuleDeclaration + TSModuleBlock 2`] = ` +{ + body: { + body: [ + { + body: { + body: [], + range: [ + 27, + 29, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "B", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 15, + 29, + ], + type: "TSModuleDeclaration", + }, + ], + range: [ + 13, + 31, + ], + type: "TSModuleBlock", + }, + declare: false, + global: false, + id: { + name: "A", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 1, + 31, + ], + type: "TSModuleDeclaration", +} +`; + +snapshot[`Plugin - TSQualifiedName 1`] = ` +{ + left: { + name: "a", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 13, + ], + right: { + name: "b", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + type: "TSQualifiedName", +} +`; + +snapshot[`Plugin - TSTypeLiteral 1`] = ` +{ + members: [ + { + computed: false, + key: { + name: "a", + optional: false, + range: [ + 12, + 13, + ], + type: "Identifier", + typeAnnotation: null, + }, + optional: false, + range: [ + 12, + 16, + ], + readonly: false, + static: false, + type: "TSPropertySignature", + typeAnnotation: { + range: [ + 13, + 16, + ], + type: "TSTypeAnnotation", + typeAnnotation: { + literal: { + range: [ + 15, + 16, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 15, + 16, + ], + type: "TSLiteralType", + }, + }, + }, + ], + range: [ + 10, + 18, + ], + type: "TSTypeLiteral", +} +`; + +snapshot[`Plugin - TSOptionalType 1`] = ` +{ + range: [ + 11, + 18, + ], + type: "TSOptionalType", + typeAnnotation: { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, +} +`; + +snapshot[`Plugin - TSRestType 1`] = ` +{ + range: [ + 11, + 22, + ], + type: "TSRestType", + typeAnnotation: { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + range: [ + 14, + 22, + ], + type: "TSArrayType", + }, +} +`; + +snapshot[`Plugin - TSConditionalType 1`] = ` +{ + checkType: { + range: [ + 10, + 11, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 10, + 11, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + extendsType: { + range: [ + 20, + 21, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "C", + optional: false, + range: [ + 20, + 21, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + falseType: { + range: [ + 33, + 39, + ], + type: "TSStringKeyword", + }, + range: [ + 10, + 39, + ], + trueType: { + range: [ + 24, + 30, + ], + type: "TSNumberKeyword", + }, + type: "TSConditionalType", +} +`; + +snapshot[`Plugin - TSInferType 1`] = ` +{ + range: [ + 29, + 39, + ], + type: "TSInferType", + typeParameter: { + const: false, + constraint: null, + default: null, + in: false, + name: { + name: "Item", + optional: false, + range: [ + 35, + 39, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 35, + 39, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 1`] = ` +{ + operator: "keyof", + range: [ + 10, + 17, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 16, + 17, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "B", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 2`] = ` +{ + operator: "unique", + range: [ + 21, + 34, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 28, + 34, + ], + type: "TSSymbolKeyword", + }, +} +`; + +snapshot[`Plugin - TSTypeOperator 3`] = ` +{ + operator: "readonly", + range: [ + 10, + 21, + ], + type: "TSTypeOperator", + typeAnnotation: { + elementTypes: [], + range: [ + 19, + 21, + ], + type: "TSTupleType", + }, +} +`; + +snapshot[`Plugin - TSMappedType 1`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 41, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 31, + 38, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 2`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 45, + ], + readonly: true, + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 40, + 42, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 30, + 37, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 36, + 37, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 36, + 37, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 25, + 26, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 25, + 37, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 3`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "-", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 4`] = ` +{ + nameType: null, + optional: undefined, + range: [ + 13, + 46, + ], + readonly: "+", + type: "TSMappedType", + typeAnnotation: { + elementTypes: [], + range: [ + 41, + 43, + ], + type: "TSTupleType", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 31, + 38, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 37, + 38, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 37, + 38, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 26, + 27, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 26, + 38, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 5`] = ` +{ + nameType: null, + optional: true, + range: [ + 13, + 42, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 32, + 39, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 6`] = ` +{ + nameType: null, + optional: "-", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSMappedType 7`] = ` +{ + nameType: null, + optional: "+", + range: [ + 13, + 43, + ], + readonly: undefined, + type: "TSMappedType", + typeAnnotation: { + range: [ + 33, + 40, + ], + type: "TSBooleanKeyword", + }, + typeParameter: { + const: false, + constraint: { + operator: "keyof", + range: [ + 21, + 28, + ], + type: "TSTypeOperator", + typeAnnotation: { + range: [ + 27, + 28, + ], + type: "TSTypeReference", + typeArguments: null, + typeName: { + name: "T", + optional: false, + range: [ + 27, + 28, + ], + type: "Identifier", + typeAnnotation: null, + }, + }, + }, + default: null, + in: false, + name: { + name: "P", + optional: false, + range: [ + 16, + 17, + ], + type: "Identifier", + typeAnnotation: null, + }, + out: false, + range: [ + 16, + 28, + ], + type: "TSTypeParameter", + }, +} +`; + +snapshot[`Plugin - TSLiteralType 1`] = ` +{ + literal: { + range: [ + 10, + 14, + ], + raw: "true", + type: "Literal", + value: true, + }, + range: [ + 10, + 14, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 2`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: "false", + type: "Literal", + value: false, + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 3`] = ` +{ + literal: { + range: [ + 10, + 11, + ], + raw: "1", + type: "Literal", + value: 1, + }, + range: [ + 10, + 11, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSLiteralType 4`] = ` +{ + literal: { + range: [ + 10, + 15, + ], + raw: '"foo"', + type: "Literal", + value: "foo", + }, + range: [ + 10, + 15, + ], + type: "TSLiteralType", +} +`; + +snapshot[`Plugin - TSTemplateLiteralType 1`] = ` +{ + quasis: [ + { + cooked: "a ", + range: [ + 11, + 13, + ], + raw: "a ", + tail: false, + type: "TemplateElement", + }, + { + cooked: "", + range: [ + 22, + 22, + ], + raw: "", + tail: true, + type: "TemplateElement", + }, + ], + range: [ + 10, + 23, + ], + type: "TSTemplateLiteralType", + types: [ + { + range: [ + 15, + 21, + ], + type: "TSStringKeyword", + }, + ], +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 1`] = ` +{ + elementTypes: [ + { + range: [ + 11, + 17, + ], + type: "TSNumberKeyword", + }, + ], + range: [ + 10, + 18, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 2`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 3`] = ` +{ + elementTypes: [ + { + elementType: { + range: [ + 14, + 20, + ], + type: "TSNumberKeyword", + }, + label: { + name: "x", + optional: false, + range: [ + 11, + 12, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 20, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 21, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSTupleType + TSArrayType 4`] = ` +{ + elementTypes: [ + { + elementType: { + elementType: { + range: [ + 17, + 23, + ], + type: "TSNumberKeyword", + }, + range: [ + 17, + 25, + ], + type: "TSArrayType", + }, + label: { + argument: { + name: "x", + optional: false, + range: [ + 14, + 15, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 11, + 16, + ], + type: "RestElement", + typeAnnotation: null, + }, + range: [ + 11, + 25, + ], + type: "TSNamedTupleMember", + }, + ], + range: [ + 10, + 26, + ], + type: "TSTupleType", +} +`; + +snapshot[`Plugin - TSArrayType 1`] = ` +{ + elementType: { + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", + }, + range: [ + 10, + 18, + ], + type: "TSArrayType", +} +`; + +snapshot[`Plugin - TSTypeQuery 1`] = ` +{ + exprName: { + name: "B", + optional: false, + range: [ + 17, + 18, + ], + type: "Identifier", + typeAnnotation: null, + }, + range: [ + 10, + 18, + ], + type: "TSTypeQuery", + typeArguments: null, +} +`; + +snapshot[`Plugin - TS keywords 1`] = ` +{ + range: [ + 10, + 13, + ], + type: "TSAnyKeyword", +} +`; + +snapshot[`Plugin - TS keywords 2`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSBigIntKeyword", +} +`; + +snapshot[`Plugin - TS keywords 3`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSBooleanKeyword", +} +`; + +snapshot[`Plugin - TS keywords 4`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSIntrinsicKeyword", +} +`; + +snapshot[`Plugin - TS keywords 5`] = ` +{ + range: [ + 10, + 15, + ], + type: "TSNeverKeyword", +} +`; + +snapshot[`Plugin - TS keywords 6`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSNullKeyword", +} +`; + +snapshot[`Plugin - TS keywords 7`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSNumberKeyword", +} +`; + +snapshot[`Plugin - TS keywords 8`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSObjectKeyword", +} +`; + +snapshot[`Plugin - TS keywords 9`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSStringKeyword", +} +`; + +snapshot[`Plugin - TS keywords 10`] = ` +{ + range: [ + 10, + 16, + ], + type: "TSSymbolKeyword", +} +`; + +snapshot[`Plugin - TS keywords 11`] = ` +{ + range: [ + 10, + 19, + ], + type: "TSUndefinedKeyword", +} +`; + +snapshot[`Plugin - TS keywords 12`] = ` +{ + range: [ + 10, + 17, + ], + type: "TSUnknownKeyword", +} +`; + +snapshot[`Plugin - TS keywords 13`] = ` +{ + range: [ + 10, + 14, + ], + type: "TSVoidKeyword", +} +`; diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index 5b00f49d01..6c71501c46 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; +import { assertSnapshot } from "@std/testing/snapshot"; // TODO(@marvinhagemeister) Remove once we land "official" types export interface LintReportData { @@ -85,9 +86,12 @@ function testVisit( return result; } -function testLintNode(source: string, ...selectors: string[]) { - // deno-lint-ignore no-explicit-any - const log: any[] = []; +async function testSnapshot( + t: Deno.TestContext, + source: string, + ...selectors: string[] +) { + const log: unknown[] = []; testPlugin(source, { create() { @@ -103,7 +107,8 @@ function testLintNode(source: string, ...selectors: string[]) { }, }); - return log; + assertEquals(log.length > 0, true); + await assertSnapshot(t, log[0]); } Deno.test("Plugin - visitor enter/exit", () => { @@ -313,303 +318,157 @@ Deno.test("Plugin - visitor :nth-child", () => { assertEquals(result[1].node.name, "foobar"); }); -Deno.test("Plugin - Program", () => { - const node = testLintNode("", "Program"); - assertEquals(node[0], { - type: "Program", - sourceType: "script", - range: [1, 1], - body: [], - }); +Deno.test("Plugin - Program", async (t) => { + await testSnapshot(t, "", "Program"); }); -Deno.test("Plugin - BlockStatement", () => { - const node = testLintNode("{ foo; }", "BlockStatement"); - assertEquals(node[0], { - type: "BlockStatement", - range: [1, 9], - body: [{ - type: "ExpressionStatement", - range: [3, 7], - expression: { - type: "Identifier", - name: "foo", - range: [3, 6], - }, - }], - }); +Deno.test("Plugin - ImportDeclaration", async (t) => { + await testSnapshot(t, 'import "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import foo from "foo";', "ImportDeclaration"); + await testSnapshot(t, 'import * as foo from "foo";', "ImportDeclaration"); + await testSnapshot( + t, + 'import { foo, bar as baz } from "foo";', + "ImportDeclaration", + ); + await testSnapshot( + t, + 'import foo from "foo" with { type: "json" };', + "ImportDeclaration", + ); }); -Deno.test("Plugin - BreakStatement", () => { - let node = testLintNode("break;", "BreakStatement"); - assertEquals(node[0], { - type: "BreakStatement", - range: [1, 7], - label: null, - }); - - node = testLintNode("break foo;", "BreakStatement"); - assertEquals(node[0], { - type: "BreakStatement", - range: [1, 11], - label: { - type: "Identifier", - range: [7, 10], - name: "foo", - }, - }); +Deno.test("Plugin - ExportNamedDeclaration", async (t) => { + await testSnapshot(t, 'export { foo } from "foo";', "ExportNamedDeclaration"); + await testSnapshot( + t, + 'export { bar as baz } from "foo";', + "ExportNamedDeclaration", + ); + await testSnapshot( + t, + 'export { foo } from "foo" with { type: "json" };', + "ExportNamedDeclaration", + ); }); -Deno.test("Plugin - ContinueStatement", () => { - let node = testLintNode("continue;", "ContinueStatement"); - assertEquals(node[0], { - type: "ContinueStatement", - range: [1, 10], - label: null, - }); - - node = testLintNode("continue foo;", "ContinueStatement"); - assertEquals(node[0], { - type: "ContinueStatement", - range: [1, 14], - label: { - type: "Identifier", - range: [10, 13], - name: "foo", - }, - }); +Deno.test("Plugin - ExportDefaultDeclaration", async (t) => { + await testSnapshot( + t, + "export default function foo() {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default function () {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class Foo {}", + "ExportDefaultDeclaration", + ); + await testSnapshot( + t, + "export default class {}", + "ExportDefaultDeclaration", + ); + await testSnapshot(t, "export default bar;", "ExportDefaultDeclaration"); + await testSnapshot( + t, + "export default interface Foo {};", + "ExportDefaultDeclaration", + ); }); -Deno.test("Plugin - DebuggerStatement", () => { - const node = testLintNode("debugger;", "DebuggerStatement"); - assertEquals(node[0], { - type: "DebuggerStatement", - range: [1, 10], - }); +Deno.test("Plugin - ExportAllDeclaration", async (t) => { + await testSnapshot(t, 'export * from "foo";', "ExportAllDeclaration"); + await testSnapshot(t, 'export * as foo from "foo";', "ExportAllDeclaration"); + await testSnapshot( + t, + 'export * from "foo" with { type: "json" };', + "ExportAllDeclaration", + ); }); -Deno.test("Plugin - DoWhileStatement", () => { - const node = testLintNode("do {} while (foo);", "DoWhileStatement"); - assertEquals(node[0], { - type: "DoWhileStatement", - range: [1, 19], - test: { - type: "Identifier", - range: [14, 17], - name: "foo", - }, - body: { - type: "BlockStatement", - range: [4, 6], - body: [], - }, - }); +Deno.test("Plugin - TSExportAssignment", async (t) => { + await testSnapshot(t, "export = foo;", "TSExportAssignment"); }); -Deno.test("Plugin - ExpressionStatement", () => { - const node = testLintNode("foo;", "ExpressionStatement"); - assertEquals(node[0], { - type: "ExpressionStatement", - range: [1, 5], - expression: { - type: "Identifier", - range: [1, 4], - name: "foo", - }, - }); +Deno.test("Plugin - TSNamespaceExportDeclaration", async (t) => { + await testSnapshot( + t, + "export as namespace A;", + "TSNamespaceExportDeclaration", + ); }); -Deno.test("Plugin - ForInStatement", () => { - const node = testLintNode("for (a in b) {}", "ForInStatement"); - assertEquals(node[0], { - type: "ForInStatement", - range: [1, 16], - left: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - right: { - type: "Identifier", - range: [11, 12], - name: "b", - }, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }); +Deno.test("Plugin - TSImportEqualsDeclaration", async (t) => { + await testSnapshot(t, "import a = b", "TSImportEqualsDeclaration"); + await testSnapshot( + t, + 'import a = require("foo")', + "TSImportEqualsDeclaration", + ); }); -Deno.test("Plugin - ForOfStatement", () => { - let node = testLintNode("for (a of b) {}", "ForOfStatement"); - assertEquals(node[0], { - type: "ForOfStatement", - range: [1, 16], - await: false, - left: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - right: { - type: "Identifier", - range: [11, 12], - name: "b", - }, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }); - - node = testLintNode("for await (a of b) {}", "ForOfStatement"); - assertEquals(node[0], { - type: "ForOfStatement", - range: [1, 22], - await: true, - left: { - type: "Identifier", - range: [12, 13], - name: "a", - }, - right: { - type: "Identifier", - range: [17, 18], - name: "b", - }, - body: { - type: "BlockStatement", - range: [20, 22], - body: [], - }, - }); +Deno.test("Plugin - BlockStatement", async (t) => { + await testSnapshot(t, "{ foo; }", "BlockStatement"); }); -Deno.test("Plugin - ForStatement", () => { - let node = testLintNode("for (;;) {}", "ForStatement"); - assertEquals(node[0], { - type: "ForStatement", - range: [1, 12], - init: null, - test: null, - update: null, - body: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - }); - - node = testLintNode("for (a; b; c) {}", "ForStatement"); - assertEquals(node[0], { - type: "ForStatement", - range: [1, 17], - init: { - type: "Identifier", - range: [6, 7], - name: "a", - }, - test: { - type: "Identifier", - range: [9, 10], - name: "b", - }, - update: { - type: "Identifier", - range: [12, 13], - name: "c", - }, - body: { - type: "BlockStatement", - range: [15, 17], - body: [], - }, - }); +Deno.test("Plugin - BreakStatement", async (t) => { + await testSnapshot(t, "while (false) break;", "BreakStatement"); + await testSnapshot(t, "foo: while (false) break foo;", "BreakStatement"); }); -Deno.test("Plugin - IfStatement", () => { - let node = testLintNode("if (foo) {}", "IfStatement"); - assertEquals(node[0], { - type: "IfStatement", - range: [1, 12], - test: { - type: "Identifier", - name: "foo", - range: [5, 8], - }, - consequent: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - alternate: null, - }); - - node = testLintNode("if (foo) {} else {}", "IfStatement"); - assertEquals(node[0], { - type: "IfStatement", - range: [1, 20], - test: { - type: "Identifier", - name: "foo", - range: [5, 8], - }, - consequent: { - type: "BlockStatement", - range: [10, 12], - body: [], - }, - alternate: { - type: "BlockStatement", - range: [18, 20], - body: [], - }, - }); +Deno.test("Plugin - ContinueStatement", async (t) => { + await testSnapshot(t, "continue;", "ContinueStatement"); + await testSnapshot(t, "continue foo;", "ContinueStatement"); }); -Deno.test("Plugin - LabeledStatement", () => { - const node = testLintNode("foo: {};", "LabeledStatement"); - assertEquals(node[0], { - type: "LabeledStatement", - range: [1, 8], - label: { - type: "Identifier", - name: "foo", - range: [1, 4], - }, - body: { - type: "BlockStatement", - range: [6, 8], - body: [], - }, - }); +Deno.test("Plugin - DebuggerStatement", async (t) => { + await testSnapshot(t, "debugger;", "DebuggerStatement"); }); -Deno.test("Plugin - ReturnStatement", () => { - let node = testLintNode("return", "ReturnStatement"); - assertEquals(node[0], { - type: "ReturnStatement", - range: [1, 7], - argument: null, - }); - - node = testLintNode("return foo;", "ReturnStatement"); - assertEquals(node[0], { - type: "ReturnStatement", - range: [1, 12], - argument: { - type: "Identifier", - name: "foo", - range: [8, 11], - }, - }); +Deno.test("Plugin - DoWhileStatement", async (t) => { + await testSnapshot(t, "do {} while (foo);", "DoWhileStatement"); }); -Deno.test("Plugin - SwitchStatement", () => { - const node = testLintNode( +Deno.test("Plugin - ExpressionStatement", async (t) => { + await testSnapshot(t, "foo;", "ExpressionStatement"); +}); + +Deno.test("Plugin - ForInStatement", async (t) => { + await testSnapshot(t, "for (a in b) {}", "ForInStatement"); +}); + +Deno.test("Plugin - ForOfStatement", async (t) => { + await testSnapshot(t, "for (a of b) {}", "ForOfStatement"); + await testSnapshot(t, "for await (a of b) {}", "ForOfStatement"); +}); + +Deno.test("Plugin - ForStatement", async (t) => { + await testSnapshot(t, "for (;;) {}", "ForStatement"); + await testSnapshot(t, "for (a; b; c) {}", "ForStatement"); +}); + +Deno.test("Plugin - IfStatement", async (t) => { + await testSnapshot(t, "if (foo) {}", "IfStatement"); + await testSnapshot(t, "if (foo) {} else {}", "IfStatement"); +}); + +Deno.test("Plugin - LabeledStatement", async (t) => { + await testSnapshot(t, "foo: {};", "LabeledStatement"); +}); + +Deno.test("Plugin - ReturnStatement", async (t) => { + await testSnapshot(t, "return", "ReturnStatement"); + await testSnapshot(t, "return foo;", "ReturnStatement"); +}); + +Deno.test("Plugin - SwitchStatement", async (t) => { + await testSnapshot( + t, `switch (foo) { case foo: case bar: @@ -619,151 +478,449 @@ Deno.test("Plugin - SwitchStatement", () => { }`, "SwitchStatement", ); - assertEquals(node[0], { - type: "SwitchStatement", - range: [1, 94], - discriminant: { - type: "Identifier", - range: [9, 12], - name: "foo", - }, - cases: [ - { - type: "SwitchCase", - range: [22, 31], - test: { - type: "Identifier", - range: [27, 30], - name: "foo", - }, - consequent: [], - }, - { - type: "SwitchCase", - range: [38, 62], - test: { - type: "Identifier", - range: [43, 46], - name: "bar", - }, - consequent: [ - { - type: "BreakStatement", - label: null, - range: [56, 62], - }, - ], - }, - { - type: "SwitchCase", - range: [69, 88], - test: null, - consequent: [ - { - type: "BlockStatement", - range: [86, 88], - body: [], - }, - ], - }, - ], - }); }); -Deno.test("Plugin - ThrowStatement", () => { - const node = testLintNode("throw foo;", "ThrowStatement"); - assertEquals(node[0], { - type: "ThrowStatement", - range: [1, 11], - argument: { - type: "Identifier", - range: [7, 10], - name: "foo", - }, - }); +Deno.test("Plugin - ThrowStatement", async (t) => { + await testSnapshot(t, "throw foo;", "ThrowStatement"); }); -Deno.test("Plugin - TryStatement", () => { - let node = testLintNode("try {} catch {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 16], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: { - type: "CatchClause", - range: [8, 16], - param: null, - body: { - type: "BlockStatement", - range: [14, 16], - body: [], - }, - }, - finalizer: null, - }); - - node = testLintNode("try {} catch (e) {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 20], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: { - type: "CatchClause", - range: [8, 20], - param: { - type: "Identifier", - range: [15, 16], - name: "e", - }, - body: { - type: "BlockStatement", - range: [18, 20], - body: [], - }, - }, - finalizer: null, - }); - - node = testLintNode("try {} finally {};", "TryStatement"); - assertEquals(node[0], { - type: "TryStatement", - range: [1, 18], - block: { - type: "BlockStatement", - range: [5, 7], - body: [], - }, - handler: null, - finalizer: { - type: "BlockStatement", - range: [16, 18], - body: [], - }, - }); +Deno.test("Plugin - TryStatement", async (t) => { + await testSnapshot(t, "try {} catch {};", "TryStatement"); + await testSnapshot(t, "try {} catch (e) {};", "TryStatement"); + await testSnapshot(t, "try {} finally {};", "TryStatement"); }); -Deno.test("Plugin - WhileStatement", () => { - const node = testLintNode("while (foo) {}", "WhileStatement"); - assertEquals(node[0], { - type: "WhileStatement", - range: [1, 15], - test: { - type: "Identifier", - range: [8, 11], - name: "foo", - }, - body: { - type: "BlockStatement", - range: [13, 15], - body: [], - }, - }); +Deno.test("Plugin - WhileStatement", async (t) => { + await testSnapshot(t, "while (foo) {}", "WhileStatement"); +}); + +Deno.test("Plugin - WithStatement", async (t) => { + await testSnapshot(t, "with ([]) {}", "WithStatement"); +}); + +Deno.test("Plugin - ArrayExpression", async (t) => { + await testSnapshot(t, "[[],,[]]", "ArrayExpression"); +}); + +Deno.test("Plugin - ArrowFunctionExpression", async (t) => { + await testSnapshot(t, "() => {}", "ArrowFunctionExpression"); + await testSnapshot(t, "async () => {}", "ArrowFunctionExpression"); + await testSnapshot( + t, + "(a: number, ...b: any[]): any => {}", + "ArrowFunctionExpression", + ); +}); + +Deno.test("Plugin - AssignmentExpression", async (t) => { + await testSnapshot(t, "a = b", "AssignmentExpression"); + await testSnapshot(t, "a = a ??= b", "AssignmentExpression"); +}); + +Deno.test("Plugin - AwaitExpression", async (t) => { + await testSnapshot(t, "await foo;", "AwaitExpression"); +}); + +Deno.test("Plugin - BinaryExpression", async (t) => { + await testSnapshot(t, "a > b", "BinaryExpression"); + await testSnapshot(t, "a >= b", "BinaryExpression"); + await testSnapshot(t, "a < b", "BinaryExpression"); + await testSnapshot(t, "a <= b", "BinaryExpression"); + await testSnapshot(t, "a == b", "BinaryExpression"); + await testSnapshot(t, "a === b", "BinaryExpression"); + await testSnapshot(t, "a != b", "BinaryExpression"); + await testSnapshot(t, "a !== b", "BinaryExpression"); + await testSnapshot(t, "a << b", "BinaryExpression"); + await testSnapshot(t, "a >> b", "BinaryExpression"); + await testSnapshot(t, "a >>> b", "BinaryExpression"); + await testSnapshot(t, "a + b", "BinaryExpression"); + await testSnapshot(t, "a - b", "BinaryExpression"); + await testSnapshot(t, "a * b", "BinaryExpression"); + await testSnapshot(t, "a / b", "BinaryExpression"); + await testSnapshot(t, "a % b", "BinaryExpression"); + await testSnapshot(t, "a | b", "BinaryExpression"); + await testSnapshot(t, "a ^ b", "BinaryExpression"); + await testSnapshot(t, "a & b", "BinaryExpression"); + await testSnapshot(t, "a in b", "BinaryExpression"); + await testSnapshot(t, "a ** b", "BinaryExpression"); +}); + +Deno.test("Plugin - CallExpression", async (t) => { + await testSnapshot(t, "foo();", "CallExpression"); + await testSnapshot(t, "foo(a, ...b);", "CallExpression"); + await testSnapshot(t, "foo?.();", "CallExpression"); + await testSnapshot(t, "foo();", "CallExpression"); +}); + +Deno.test("Plugin - ChainExpression", async (t) => { + await testSnapshot(t, "a?.b", "ChainExpression"); +}); + +Deno.test("Plugin - ClassExpression", async (t) => { + await testSnapshot(t, "a = class {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class Foo extends Bar {}", "ClassExpression"); + await testSnapshot( + t, + "a = class Foo extends Bar implements Baz, Baz2 {}", + "ClassExpression", + ); + await testSnapshot(t, "a = class Foo {}", "ClassExpression"); + await testSnapshot(t, "a = class { foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { #foo() {} }", "ClassExpression"); + await testSnapshot(t, "a = class { foo: number }", "ClassExpression"); + await testSnapshot(t, "a = class { foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { constructor(public foo: string) {} }", + "ClassExpression", + ); + await testSnapshot(t, "a = class { #foo: number = bar }", "ClassExpression"); + await testSnapshot(t, "a = class { static foo = bar }", "ClassExpression"); + await testSnapshot( + t, + "a = class { static foo; static { foo = bar } }", + "ClassExpression", + ); +}); + +Deno.test("Plugin - ConditionalExpression", async (t) => { + await testSnapshot(t, "a ? b : c", "ConditionalExpression"); +}); + +Deno.test("Plugin - FunctionExpression", async (t) => { + await testSnapshot(t, "a = function () {}", "FunctionExpression"); + await testSnapshot(t, "a = function foo() {}", "FunctionExpression"); + await testSnapshot( + t, + "a = function (a?: number, ...b: any[]): any {}", + "FunctionExpression", + ); + await testSnapshot(t, "a = async function* () {}", "FunctionExpression"); +}); + +Deno.test("Plugin - Identifier", async (t) => { + await testSnapshot(t, "a", "Identifier"); +}); + +Deno.test("Plugin - ImportExpression", async (t) => { + await testSnapshot( + t, + "import('foo', { with: { type: 'json' } })", + "ImportExpression", + ); +}); + +Deno.test("Plugin - LogicalExpression", async (t) => { + await testSnapshot(t, "a && b", "LogicalExpression"); + await testSnapshot(t, "a || b", "LogicalExpression"); + await testSnapshot(t, "a ?? b", "LogicalExpression"); +}); + +Deno.test("Plugin - MemberExpression", async (t) => { + await testSnapshot(t, "a.b", "MemberExpression"); + await testSnapshot(t, "a['b']", "MemberExpression"); +}); + +Deno.test("Plugin - MetaProperty", async (t) => { + await testSnapshot(t, "import.meta", "MetaProperty"); +}); + +Deno.test("Plugin - NewExpression", async (t) => { + await testSnapshot(t, "new Foo()", "NewExpression"); + await testSnapshot(t, "new Foo(a, ...b)", "NewExpression"); +}); + +Deno.test("Plugin - ObjectExpression", async (t) => { + await testSnapshot(t, "a = {}", "ObjectExpression"); + await testSnapshot(t, "a = { a }", "ObjectExpression"); + await testSnapshot(t, "a = { b: c, [c]: d }", "ObjectExpression"); +}); + +Deno.test("Plugin - PrivateIdentifier", async (t) => { + await testSnapshot(t, "class Foo { #foo = foo }", "PrivateIdentifier"); +}); + +Deno.test("Plugin - SequenceExpression", async (t) => { + await testSnapshot(t, "(a, b)", "SequenceExpression"); +}); + +Deno.test("Plugin - Super", async (t) => { + await testSnapshot( + t, + "class Foo extends Bar { constructor() { super(); } }", + "Super", + ); +}); + +Deno.test("Plugin - TaggedTemplateExpression", async (t) => { + await testSnapshot(t, "foo`foo ${bar} baz`", "TaggedTemplateExpression"); +}); + +Deno.test("Plugin - TemplateLiteral", async (t) => { + await testSnapshot(t, "`foo ${bar} baz`", "TemplateLiteral"); +}); + +Deno.test("Plugin - ThisExpression", async (t) => { + await testSnapshot(t, "this", "ThisExpression"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as b", "TSAsExpression"); + await testSnapshot(t, "a as const", "TSAsExpression"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "a satisfies b", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - UnaryExpression", async (t) => { + await testSnapshot(t, "typeof a", "UnaryExpression"); + await testSnapshot(t, "void 0", "UnaryExpression"); + await testSnapshot(t, "-a", "UnaryExpression"); + await testSnapshot(t, "+a", "UnaryExpression"); +}); + +Deno.test("Plugin - UpdateExpression", async (t) => { + await testSnapshot(t, "a++", "UpdateExpression"); + await testSnapshot(t, "++a", "UpdateExpression"); + await testSnapshot(t, "a--", "UpdateExpression"); + await testSnapshot(t, "--a", "UpdateExpression"); +}); + +Deno.test("Plugin - YieldExpression", async (t) => { + await testSnapshot(t, "function* foo() { yield bar; }", "YieldExpression"); +}); + +Deno.test("Plugin - Literal", async (t) => { + await testSnapshot(t, "1", "Literal"); + await testSnapshot(t, "'foo'", "Literal"); + await testSnapshot(t, '"foo"', "Literal"); + await testSnapshot(t, "true", "Literal"); + await testSnapshot(t, "false", "Literal"); + await testSnapshot(t, "null", "Literal"); + await testSnapshot(t, "1n", "Literal"); + await testSnapshot(t, "/foo/g", "Literal"); +}); + +Deno.test("Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr", async (t) => { + await testSnapshot(t, "

", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, '
', "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
foo{2}
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, " />", "JSXElement"); +}); + +Deno.test("Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment", async (t) => { + await testSnapshot(t, "<>", "JSXFragment"); + await testSnapshot(t, "<>foo{2}", "JSXFragment"); +}); + +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as any", "TSAsExpression"); + await testSnapshot(t, '"foo" as const', "TSAsExpression"); +}); + +Deno.test("Plugin - TSEnumDeclaration", async (t) => { + await testSnapshot(t, "enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "const enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "enum Foo { A, B }", "TSEnumDeclaration"); + await testSnapshot(t, 'enum Foo { "a-b" }', "TSEnumDeclaration"); + await testSnapshot( + t, + "enum Foo { A = 1, B = 2, C = A | B }", + "TSEnumDeclaration", + ); +}); + +Deno.test("Plugin - TSInterface", async (t) => { + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A {}", "TSInterface"); + await testSnapshot(t, "interface A extends Foo, Bar {}", "TSInterface"); + await testSnapshot(t, "interface A { foo: any, bar?: any }", "TSInterface"); + await testSnapshot( + t, + "interface A { readonly [key: string]: any }", + "TSInterface", + ); + + await testSnapshot(t, "interface A { readonly a: any }", "TSInterface"); + await testSnapshot(t, "interface A { (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { new (a: T): T }", "TSInterface"); + await testSnapshot(t, "interface A { a: new (a: T) => T }", "TSInterface"); + await testSnapshot(t, "interface A { get a(): string }", "TSInterface"); + await testSnapshot(t, "interface A { set a(v: string) }", "TSInterface"); + + await testSnapshot( + t, + "interface A { a(arg?: any, ...args: any[]): any }", + "TSInterface", + ); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "const a = {} satisfies A", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - TSTypeAliasDeclaration", async (t) => { + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "declare type A = any", "TSTypeAliasDeclaration"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSUnionType", async (t) => { + await testSnapshot(t, "type A = B | C", "TSUnionType"); +}); + +Deno.test("Plugin - TSIntersectionType", async (t) => { + await testSnapshot(t, "type A = B & C", "TSIntersectionType"); +}); + +Deno.test("Plugin - TSModuleDeclaration", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "declare module A { export function A(): void }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSModuleDeclaration + TSModuleBlock", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "namespace A { namespace B {} }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSQualifiedName", async (t) => { + await testSnapshot(t, "type A = a.b;", "TSQualifiedName"); +}); + +Deno.test("Plugin - TSTypeLiteral", async (t) => { + await testSnapshot(t, "type A = { a: 1 };", "TSTypeLiteral"); +}); + +Deno.test("Plugin - TSOptionalType", async (t) => { + await testSnapshot(t, "type A = [number?]", "TSOptionalType"); +}); + +Deno.test("Plugin - TSRestType", async (t) => { + await testSnapshot(t, "type A = [...number[]]", "TSRestType"); +}); + +Deno.test("Plugin - TSConditionalType", async (t) => { + await testSnapshot( + t, + "type A = B extends C ? number : string;", + "TSConditionalType", + ); +}); + +Deno.test("Plugin - TSInferType", async (t) => { + await testSnapshot( + t, + "type A = T extends Array ? Item : T;", + "TSInferType", + ); +}); + +Deno.test("Plugin - TSTypeOperator", async (t) => { + await testSnapshot(t, "type A = keyof B", "TSTypeOperator"); + await testSnapshot(t, "declare const sym1: unique symbol;", "TSTypeOperator"); + await testSnapshot(t, "type A = readonly []", "TSTypeOperator"); +}); + +Deno.test("Plugin - TSMappedType", async (t) => { + await testSnapshot( + t, + "type A = { [P in keyof T]: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { -readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { +readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]-?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]+?: boolean; };", + "TSMappedType", + ); +}); + +Deno.test("Plugin - TSLiteralType", async (t) => { + await testSnapshot(t, "type A = true", "TSLiteralType"); + await testSnapshot(t, "type A = false", "TSLiteralType"); + await testSnapshot(t, "type A = 1", "TSLiteralType"); + await testSnapshot(t, 'type A = "foo"', "TSLiteralType"); +}); + +Deno.test("Plugin - TSTemplateLiteralType", async (t) => { + await testSnapshot(t, "type A = `a ${string}`", "TSTemplateLiteralType"); +}); + +Deno.test("Plugin - TSTupleType + TSArrayType", async (t) => { + await testSnapshot(t, "type A = [number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [...x: number[]]", "TSTupleType"); +}); + +Deno.test("Plugin - TSArrayType", async (t) => { + await testSnapshot(t, "type A = number[]", "TSArrayType"); +}); + +Deno.test("Plugin - TSTypeQuery", async (t) => { + await testSnapshot(t, "type A = typeof B", "TSTypeQuery"); +}); + +Deno.test("Plugin - TS keywords", async (t) => { + await testSnapshot(t, "type A = any", "TSAnyKeyword"); + await testSnapshot(t, "type A = bigint", "TSBigIntKeyword"); + await testSnapshot(t, "type A = boolean", "TSBooleanKeyword"); + await testSnapshot(t, "type A = intrinsic", "TSIntrinsicKeyword"); + await testSnapshot(t, "type A = never", "TSNeverKeyword"); + await testSnapshot(t, "type A = null", "TSNullKeyword"); + await testSnapshot(t, "type A = number", "TSNumberKeyword"); + await testSnapshot(t, "type A = object", "TSObjectKeyword"); + await testSnapshot(t, "type A = string", "TSStringKeyword"); + await testSnapshot(t, "type A = symbol", "TSSymbolKeyword"); + await testSnapshot(t, "type A = undefined", "TSUndefinedKeyword"); + await testSnapshot(t, "type A = unknown", "TSUnknownKeyword"); + await testSnapshot(t, "type A = void", "TSVoidKeyword"); }); diff --git a/tests/unit_node/_fs/_fs_handle_test.ts b/tests/unit_node/_fs/_fs_handle_test.ts index e4a41f8ba7..9134c2cb0a 100644 --- a/tests/unit_node/_fs/_fs_handle_test.ts +++ b/tests/unit_node/_fs/_fs_handle_test.ts @@ -118,6 +118,45 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () { assertEquals(decoder.decode(data), "hello world"); }); +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + const res = await fileHandle.writev([buffer1, buffer2]); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(res.bytesWritten, 11); + assertEquals(decoder.decode(data), "hello world"); + }, +); + +Deno.test( + "[node/fs filehandle.writev] Write array of buffers to file with position", + async function () { + const tempFile: string = await Deno.makeTempFile(); + const fileHandle = await fs.open(tempFile, "w"); + + const buffer1 = Buffer.from("hello "); + const buffer2 = Buffer.from("world"); + await fileHandle.writev([buffer1, buffer2], 0); + const buffer3 = Buffer.from("lorem ipsum"); + await fileHandle.writev([buffer3], 6); + + const data = Deno.readFileSync(tempFile); + await Deno.remove(tempFile); + await fileHandle.close(); + + assertEquals(decoder.decode(data), "hello lorem ipsum"); + }, +); + Deno.test( "[node/fs filehandle.truncate] Truncate file with length", async function () { @@ -217,3 +256,20 @@ Deno.test({ await fileHandle.close(); }, }); + +Deno.test({ + name: + "[node/fs filehandle.utimes] Change the file system timestamps of the file", + async fn() { + const fileHandle = await fs.open(testData); + + const atime = new Date(); + const mtime = new Date(0); + + await fileHandle.utimes(atime, mtime); + assertEquals(Deno.statSync(testData).atime!, atime); + assertEquals(Deno.statSync(testData).mtime!, mtime); + + await fileHandle.close(); + }, +}); diff --git a/tests/unit_node/child_process_test.ts b/tests/unit_node/child_process_test.ts index bd875ad419..1732b9d2bf 100644 --- a/tests/unit_node/child_process_test.ts +++ b/tests/unit_node/child_process_test.ts @@ -656,6 +656,73 @@ Deno.test({ }, }); +Deno.test({ + name: + "[node/child_process spawn] child inherits Deno.env when options.env is not provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAR", "BAR"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAR"`, + { + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAR"); + } + const value = await deferred.promise; + assertEquals(value, "BAR"); + }, +}); + +Deno.test({ + name: + "[node/child_process spawn] child doesn't inherit Deno.env when options.env is provided", + async fn() { + const deferred = withTimeout(); + Deno.env.set("BAZ", "BAZ"); + const env = spawn( + `"${Deno.execPath()}" eval -p "Deno.env.toObject().BAZ"`, + { + env: {}, + shell: true, + }, + ); + try { + let envOutput = ""; + + assert(env.stdout); + env.on("error", (err: Error) => deferred.reject(err)); + env.stdout.on("data", (data) => { + envOutput += data; + }); + env.on("close", () => { + deferred.resolve(envOutput.trim()); + }); + await deferred.promise; + } finally { + env.kill(); + Deno.env.delete("BAZ"); + } + const value = await deferred.promise; + assertEquals(value, "undefined"); + }, +}); + // Regression test for https://github.com/denoland/deno/issues/20373 Deno.test(async function undefinedValueInEnvVar() { const deferred = withTimeout(); diff --git a/tools/core_import_map.json b/tools/core_import_map.json index d38221eb4c..feeae1ac70 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -38,7 +38,7 @@ "ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.ts", "ext:deno_node/_fs/_fs_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts", "ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs", - "ext:deno_node/_fs/_fs_writev.mjs": "../ext/node/polyfills/_fs/_fs_writev.mjs", + "ext:deno_node/_fs/_fs_writev.ts": "../ext/node/polyfills/_fs/_fs_writev.ts", "ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts", "node:_http_agent": "../ext/node/polyfills/_http_agent.mjs", "node:_http_common": "../ext/node/polyfills/_http_common.ts",